1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text.cts; 18 19 import static android.view.View.LAYOUT_DIRECTION_LTR; 20 import static android.view.View.LAYOUT_DIRECTION_RTL; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertFalse; 24 import static org.junit.Assert.assertNull; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 import static org.mockito.Matchers.any; 29 import static org.mockito.Matchers.anyInt; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.when; 32 33 import android.content.Context; 34 import android.content.res.ColorStateList; 35 import android.content.res.Configuration; 36 import android.content.res.Resources; 37 import android.graphics.Color; 38 import android.graphics.Typeface; 39 import android.os.LocaleList; 40 import android.os.Parcel; 41 import android.os.Parcelable; 42 import android.text.GetChars; 43 import android.text.SpannableString; 44 import android.text.SpannableStringBuilder; 45 import android.text.Spanned; 46 import android.text.SpannedString; 47 import android.text.TextPaint; 48 import android.text.TextUtils; 49 import android.text.TextUtils.TruncateAt; 50 import android.text.style.BackgroundColorSpan; 51 import android.text.style.ReplacementSpan; 52 import android.text.style.TextAppearanceSpan; 53 import android.text.style.URLSpan; 54 import android.util.StringBuilderPrinter; 55 56 import androidx.test.InstrumentationRegistry; 57 import androidx.test.filters.SmallTest; 58 import androidx.test.runner.AndroidJUnit4; 59 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 64 import java.util.ArrayList; 65 import java.util.Arrays; 66 import java.util.List; 67 import java.util.Locale; 68 import java.util.regex.Pattern; 69 70 /** 71 * Test {@link TextUtils}. 72 */ 73 @SmallTest 74 @RunWith(AndroidJUnit4.class) 75 public class TextUtilsTest { 76 private Context mContext; 77 private String mEllipsis; 78 private int mStart; 79 private int mEnd; 80 81 @Before setup()82 public void setup() { 83 mContext = InstrumentationRegistry.getTargetContext(); 84 mEllipsis = getEllipsis(); 85 resetRange(); 86 } 87 resetRange()88 private void resetRange() { 89 mStart = -1; 90 mEnd = -1; 91 } 92 93 /** 94 * Get the ellipsis from system. 95 * @return the string of ellipsis. 96 */ getEllipsis()97 private static String getEllipsis() { 98 String text = "xxxxx"; 99 TextPaint p = new TextPaint(); 100 float width = p.measureText(text.substring(1)); 101 String re = TextUtils.ellipsize(text, p, width, TruncateAt.START).toString(); 102 return re.substring(0, re.indexOf("x")); 103 } 104 105 /** 106 * @return the number of times the code unit appears in the CharSequence. 107 */ countChars(CharSequence s, char c)108 private static int countChars(CharSequence s, char c) { 109 int count = 0; 110 for (int i = 0; i < s.length(); i++) { 111 if (s.charAt(i) == c) { 112 count++; 113 } 114 } 115 return count; 116 } 117 118 @Test testListEllipsize()119 public void testListEllipsize() { 120 final TextPaint paint = new TextPaint(); 121 final int moreId = R.plurals.list_ellipsize_test; // "one more" for 1, "%d more" for other 122 123 final List fullList = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J"); 124 final String separator = ", "; 125 final String fullString = TextUtils.join(separator, fullList); 126 final float fullWidth = paint.measureText(fullString); 127 assertEquals("", 128 TextUtils.listEllipsize(mContext, null, separator, paint, fullWidth, moreId)); 129 130 final List<CharSequence> emptyList = new ArrayList<>(); 131 assertEquals("", 132 TextUtils.listEllipsize(mContext, emptyList, separator, paint, fullWidth, moreId)); 133 134 // Null context should cause ellipsis to be used at the end. 135 final String ellipsizedWithNull = TextUtils.listEllipsize( 136 null, fullList, separator, paint, fullWidth / 2, 0).toString(); 137 assertTrue(ellipsizedWithNull.endsWith(getEllipsis())); 138 139 // Test that the empty string gets returned if there's no space. 140 assertEquals("", 141 TextUtils.listEllipsize(mContext, fullList, separator, paint, 1.0f, moreId)); 142 143 // Test that the full string itself can get returned if there's enough space. 144 assertEquals(fullString, 145 TextUtils.listEllipsize(mContext, fullList, separator, paint, fullWidth, moreId) 146 .toString()); 147 assertEquals(fullString, 148 TextUtils.listEllipsize(mContext, fullList, separator, paint, fullWidth * 2, 149 moreId).toString()); 150 151 final float epsilon = fullWidth / 20; 152 for (float width = epsilon; width < fullWidth - epsilon / 2; width += epsilon) { 153 final String ellipsized = TextUtils.listEllipsize( 154 mContext, fullList, separator, paint, width, moreId).toString(); 155 // Since we don't have the full space, test that we are not getting the full string. 156 assertFalse(fullString.equals(ellipsized)); 157 158 if (!ellipsized.isEmpty()) { 159 assertTrue(ellipsized.endsWith(" more")); 160 // Test that the number of separators (which equals the number of output elements), 161 // plus the number output before more always equals the number of original elements. 162 final int lastSpace = ellipsized.lastIndexOf(' '); 163 final int penultimateSpace = ellipsized.lastIndexOf(' ', lastSpace - 1); 164 assertEquals(',', ellipsized.charAt(penultimateSpace - 1)); 165 final String moreCountString = ellipsized.substring( 166 penultimateSpace + 1, lastSpace); 167 final int moreCount = (moreCountString.equals("one")) 168 ? 1 : Integer.parseInt(moreCountString); 169 final int commaCount = countChars(ellipsized, ','); 170 assertEquals(fullList.size(), commaCount + moreCount); 171 } 172 } 173 } 174 175 @Test testListEllipsize_rtl()176 public void testListEllipsize_rtl() { 177 final Resources res = mContext.getResources(); 178 final Configuration newConfig = new Configuration(res.getConfiguration()); 179 180 // save the locales and set them to just Arabic 181 final LocaleList previousLocales = newConfig.getLocales(); 182 newConfig.setLocales(LocaleList.forLanguageTags("ar")); 183 res.updateConfiguration(newConfig, null); 184 185 try { 186 final TextPaint paint = new TextPaint(); 187 final int moreId = R.plurals.list_ellipsize_test; // "one more" for 1, else "%d more" 188 final String RLM = "\u200F"; 189 final String LRE = "\u202A"; 190 final String PDF = "\u202C"; 191 192 final List fullList = Arrays.asList("A", "B"); 193 final String separator = ", "; 194 final String expectedString = 195 RLM + LRE + "A" + PDF + RLM + ", " + RLM + LRE + "B" + PDF + RLM; 196 final float enoughWidth = paint.measureText(expectedString); 197 198 assertEquals(expectedString, 199 TextUtils.listEllipsize(mContext, fullList, separator, paint, enoughWidth, 200 moreId).toString()); 201 } finally { 202 // Restore the original locales 203 newConfig.setLocales(previousLocales); 204 res.updateConfiguration(newConfig, null); 205 } 206 } 207 208 @Test testCommaEllipsize()209 public void testCommaEllipsize() { 210 TextPaint p = new TextPaint(); 211 String text = "long, string, to, truncate"; 212 213 float textWidth = p.measureText("long, 3 plus"); 214 // avail is shorter than text width for only one item plus the appropriate ellipsis. 215 // issue 1688347, the expected result for this case does not be described 216 // in the javadoc of commaEllipsize(). 217 assertEquals("", 218 TextUtils.commaEllipsize(text, p, textWidth - 1.4f, "plus 1", "%d plus").toString()); 219 // avail is long enough for only one item plus the appropriate ellipsis. 220 assertEquals("long, 3 plus", 221 TextUtils.commaEllipsize(text, p, textWidth, "plus 1", "%d plus").toString()); 222 223 // avail is long enough for two item plus the appropriate ellipsis. 224 textWidth = p.measureText("long, string, 2 more"); 225 assertEquals("long, string, 2 more", 226 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString()); 227 228 // avail is long enough for the whole sentence. 229 textWidth = p.measureText("long, string, to, truncate"); 230 assertEquals("long, string, to, truncate", 231 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString()); 232 233 // the sentence is extended, avail is NOT long enough for the whole sentence. 234 assertEquals("long, string, to, more 1", TextUtils.commaEllipsize( 235 text + "-extended", p, textWidth, "more 1", "%d more").toString()); 236 237 // exceptional value 238 assertEquals("", TextUtils.commaEllipsize(text, p, -1f, "plus 1", "%d plus").toString()); 239 240 assertEquals(text, TextUtils.commaEllipsize( 241 text, p, Float.MAX_VALUE, "more 1", "%d more").toString()); 242 243 assertEquals("long, string, to, null", TextUtils.commaEllipsize( 244 text + "-extended", p, textWidth, null, "%d more").toString()); 245 246 try { 247 TextUtils.commaEllipsize(null, p, textWidth, "plus 1", "%d plus"); 248 fail("Should throw NullPointerException"); 249 } catch (NullPointerException e) { 250 // issue 1688347, not clear what is supposed to happen if the text to truncate is null. 251 } 252 253 try { 254 TextUtils.commaEllipsize(text, null, textWidth, "plus 1", "%d plus"); 255 fail("Should throw NullPointerException"); 256 } catch (NullPointerException e) { 257 // issue 1688347, not clear what is supposed to happen if TextPaint is null. 258 } 259 } 260 261 @Test testConcat()262 public void testConcat() { 263 assertEquals("", TextUtils.concat().toString()); 264 265 assertEquals("first", TextUtils.concat("first").toString()); 266 267 assertEquals("first, second", TextUtils.concat("first", ", ", "second").toString()); 268 269 SpannableString string1 = new SpannableString("first"); 270 SpannableString string2 = new SpannableString("second"); 271 final String url = "www.test_url.com"; 272 URLSpan urlSpan = new URLSpan(url); 273 string1.setSpan(urlSpan, 0, string1.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 274 BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN); 275 string2.setSpan(bgColorSpan, 0, string2.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 276 277 final String comma = ", "; 278 Spanned strResult = (Spanned) TextUtils.concat(string1, comma, string2); 279 assertEquals(string1.toString() + comma + string2.toString(), strResult.toString()); 280 Object spans[] = strResult.getSpans(0, strResult.length(), Object.class); 281 assertEquals(2, spans.length); 282 assertTrue(spans[0] instanceof URLSpan); 283 assertEquals(url, ((URLSpan) spans[0]).getURL()); 284 assertTrue(spans[1] instanceof BackgroundColorSpan); 285 assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor()); 286 assertEquals(0, strResult.getSpanStart(urlSpan)); 287 assertEquals(string1.length() - 1, strResult.getSpanEnd(urlSpan)); 288 assertEquals(string1.length() + comma.length(), strResult.getSpanStart(bgColorSpan)); 289 assertEquals(strResult.length() - 1, strResult.getSpanEnd(bgColorSpan)); 290 291 assertEquals(string1, TextUtils.concat(string1)); 292 293 assertEquals(null, TextUtils.concat((CharSequence) null)); 294 } 295 296 @Test(expected = NullPointerException.class) testConcat_NullArray()297 public void testConcat_NullArray() { 298 TextUtils.concat((CharSequence[]) null); 299 } 300 301 @Test testConcat_NullParameters()302 public void testConcat_NullParameters() { 303 assertEquals("nullA", TextUtils.concat(null, "A")); 304 assertEquals("Anull", TextUtils.concat("A", null)); 305 assertEquals("AnullB", TextUtils.concat("A", null, "B")); 306 307 final SpannableString piece = new SpannableString("A"); 308 final Object span = new Object(); 309 piece.setSpan(span, 0, piece.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 310 final Spanned result = (Spanned) TextUtils.concat(piece, null); 311 assertEquals("Anull", result.toString()); 312 final Object[] spans = result.getSpans(0, result.length(), Object.class); 313 assertEquals(1, spans.length); 314 assertSame(span, spans[0]); 315 assertEquals(0, result.getSpanStart(spans[0])); 316 assertEquals(piece.length(), result.getSpanEnd(spans[0])); 317 } 318 319 @Test testConcat_twoParagraphSpans()320 public void testConcat_twoParagraphSpans() { 321 // Two paragraph spans. The first will get extended to cover the whole string and the second 322 // will be dropped. 323 final SpannableString string1 = new SpannableString("a"); 324 final SpannableString string2 = new SpannableString("b"); 325 final Object span1 = new Object(); 326 final Object span2 = new Object(); 327 string1.setSpan(span1, 0, string1.length(), Spanned.SPAN_PARAGRAPH); 328 string2.setSpan(span2, 0, string2.length(), Spanned.SPAN_PARAGRAPH); 329 330 final Spanned result = (Spanned) TextUtils.concat(string1, string2); 331 assertEquals("ab", result.toString()); 332 final Object[] spans = result.getSpans(0, result.length(), Object.class); 333 assertEquals(1, spans.length); 334 assertSame(span1, spans[0]); 335 assertEquals(0, result.getSpanStart(spans[0])); 336 assertEquals(result.length(), result.getSpanEnd(spans[0])); 337 } 338 339 @Test testConcat_oneParagraphSpanAndOneInclusiveSpan()340 public void testConcat_oneParagraphSpanAndOneInclusiveSpan() { 341 // One paragraph span and one double-inclusive span. The first will get extended to cover 342 // the whole string and the second will be kept. 343 final SpannableString string1 = new SpannableString("a"); 344 final SpannableString string2 = new SpannableString("b"); 345 final Object span1 = new Object(); 346 final Object span2 = new Object(); 347 string1.setSpan(span1, 0, string1.length(), Spanned.SPAN_PARAGRAPH); 348 string2.setSpan(span2, 0, string2.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 349 350 final Spanned result = (Spanned) TextUtils.concat(string1, string2); 351 assertEquals("ab", result.toString()); 352 final Object[] spans = result.getSpans(0, result.length(), Object.class); 353 assertEquals(2, spans.length); 354 assertSame(span1, spans[0]); 355 assertEquals(0, result.getSpanStart(spans[0])); 356 assertEquals(result.length(), result.getSpanEnd(spans[0])); 357 assertSame(span2, spans[1]); 358 assertEquals(string1.length(), result.getSpanStart(spans[1])); 359 assertEquals(result.length(), result.getSpanEnd(spans[1])); 360 } 361 362 @Test testCopySpansFrom()363 public void testCopySpansFrom() { 364 Object[] spans; 365 String text = "content"; 366 SpannableString source1 = new SpannableString(text); 367 int midPos = source1.length() / 2; 368 final String url = "www.test_url.com"; 369 URLSpan urlSpan = new URLSpan(url); 370 source1.setSpan(urlSpan, 0, midPos, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 371 BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN); 372 source1.setSpan(bgColorSpan, midPos - 1, 373 source1.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 374 375 // normal test 376 SpannableString dest1 = new SpannableString(text); 377 TextUtils.copySpansFrom(source1, 0, source1.length(), Object.class, dest1, 0); 378 spans = dest1.getSpans(0, dest1.length(), Object.class); 379 assertEquals(2, spans.length); 380 assertTrue(spans[0] instanceof URLSpan); 381 assertEquals(url, ((URLSpan) spans[0]).getURL()); 382 assertTrue(spans[1] instanceof BackgroundColorSpan); 383 assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor()); 384 assertEquals(0, dest1.getSpanStart(urlSpan)); 385 assertEquals(midPos, dest1.getSpanEnd(urlSpan)); 386 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, dest1.getSpanFlags(urlSpan)); 387 assertEquals(midPos - 1, dest1.getSpanStart(bgColorSpan)); 388 assertEquals(source1.length() - 1, dest1.getSpanEnd(bgColorSpan)); 389 assertEquals(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, dest1.getSpanFlags(bgColorSpan)); 390 391 SpannableString source2 = new SpannableString(text); 392 source2.setSpan(urlSpan, 0, source2.length() - 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE); 393 SpannableString dest2 = new SpannableString(text); 394 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest2, 0); 395 spans = dest2.getSpans(0, dest2.length(), Object.class); 396 assertEquals(1, spans.length); 397 assertTrue(spans[0] instanceof URLSpan); 398 assertEquals(url, ((URLSpan) spans[0]).getURL()); 399 assertEquals(0, dest2.getSpanStart(urlSpan)); 400 assertEquals(source2.length() - 1, dest2.getSpanEnd(urlSpan)); 401 assertEquals(Spanned.SPAN_EXCLUSIVE_INCLUSIVE, dest2.getSpanFlags(urlSpan)); 402 403 SpannableString dest3 = new SpannableString(text); 404 TextUtils.copySpansFrom(source2, 0, source2.length(), BackgroundColorSpan.class, dest3, 0); 405 spans = dest3.getSpans(0, dest3.length(), Object.class); 406 assertEquals(0, spans.length); 407 TextUtils.copySpansFrom(source2, 0, source2.length(), URLSpan.class, dest3, 0); 408 spans = dest3.getSpans(0, dest3.length(), Object.class); 409 assertEquals(1, spans.length); 410 411 SpannableString dest4 = new SpannableString("short"); 412 try { 413 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest4, 0); 414 fail("Should throw IndexOutOfBoundsException"); 415 } catch (IndexOutOfBoundsException e) { 416 // expected 417 } 418 TextUtils.copySpansFrom(source2, 0, dest4.length(), Object.class, dest4, 0); 419 spans = dest4.getSpans(0, dest4.length(), Object.class); 420 assertEquals(1, spans.length); 421 assertEquals(0, dest4.getSpanStart(spans[0])); 422 // issue 1688347, not clear the expected result when 'start ~ end' only 423 // covered a part of the span. 424 assertEquals(dest4.length(), dest4.getSpanEnd(spans[0])); 425 426 SpannableString dest5 = new SpannableString("longer content"); 427 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 0); 428 spans = dest5.getSpans(0, 1, Object.class); 429 assertEquals(1, spans.length); 430 431 dest5 = new SpannableString("longer content"); 432 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 2); 433 spans = dest5.getSpans(0, 1, Object.class); 434 assertEquals(0, spans.length); 435 spans = dest5.getSpans(2, dest5.length(), Object.class); 436 assertEquals(1, spans.length); 437 try { 438 TextUtils.copySpansFrom(source2, 0, source2.length(), 439 Object.class, dest5, dest5.length() - source2.length() + 2); 440 fail("Should throw IndexOutOfBoundsException"); 441 } catch (IndexOutOfBoundsException e) { 442 // expected 443 } 444 445 // issue 1688347, no javadoc about the expected behavior of the exceptional argument. 446 // exceptional source start 447 SpannableString dest6 = new SpannableString("exceptional test"); 448 TextUtils.copySpansFrom(source2, -1, source2.length(), Object.class, dest6, 0); 449 spans = dest6.getSpans(0, dest6.length(), Object.class); 450 assertEquals(1, spans.length); 451 dest6 = new SpannableString("exceptional test"); 452 TextUtils.copySpansFrom(source2, Integer.MAX_VALUE, source2.length() - 1, 453 Object.class, dest6, 0); 454 spans = dest6.getSpans(0, dest6.length(), Object.class); 455 assertEquals(0, spans.length); 456 457 // exceptional source end 458 dest6 = new SpannableString("exceptional test"); 459 TextUtils.copySpansFrom(source2, 0, -1, Object.class, dest6, 0); 460 spans = dest6.getSpans(0, dest6.length(), Object.class); 461 assertEquals(0, spans.length); 462 TextUtils.copySpansFrom(source2, 0, Integer.MAX_VALUE, Object.class, dest6, 0); 463 spans = dest6.getSpans(0, dest6.length(), Object.class); 464 assertEquals(1, spans.length); 465 466 // exceptional class kind 467 dest6 = new SpannableString("exceptional test"); 468 TextUtils.copySpansFrom(source2, 0, source2.length(), null, dest6, 0); 469 spans = dest6.getSpans(0, dest6.length(), Object.class); 470 assertEquals(1, spans.length); 471 472 // exceptional destination offset 473 dest6 = new SpannableString("exceptional test"); 474 try { 475 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest6, -1); 476 fail("Should throw IndexOutOfBoundsException"); 477 } catch (IndexOutOfBoundsException e) { 478 // expect 479 } 480 try { 481 TextUtils.copySpansFrom(source2, 0, source2.length(), 482 Object.class, dest6, Integer.MAX_VALUE); 483 fail("Should throw IndexOutOfBoundsException"); 484 } catch (IndexOutOfBoundsException e) { 485 // expect 486 } 487 488 // exceptional source 489 try { 490 TextUtils.copySpansFrom(null, 0, source2.length(), Object.class, dest6, 0); 491 fail("Should throw NullPointerException"); 492 } catch (NullPointerException e) { 493 // expect 494 } 495 496 // exceptional destination 497 try { 498 TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, null, 0); 499 fail("Should throw NullPointerException"); 500 } catch (NullPointerException e) { 501 // expect 502 } 503 } 504 505 @Test testEllipsize()506 public void testEllipsize() { 507 TextPaint p = new TextPaint(); 508 509 // turn off kerning. with kerning enabled, different methods of measuring the same text 510 // produce different results. 511 p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG); 512 513 CharSequence text = "long string to truncate"; 514 515 float textWidth = p.measureText(mEllipsis) + p.measureText("uncate"); 516 assertEquals(mEllipsis + "uncate", 517 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString()); 518 519 textWidth = p.measureText("long str") + p.measureText(mEllipsis); 520 assertEquals("long str" + mEllipsis, 521 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString()); 522 523 textWidth = p.measureText("long") + p.measureText(mEllipsis) + p.measureText("ate"); 524 assertEquals("long" + mEllipsis + "ate", 525 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString()); 526 527 // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE. 528 // In the code it looks like this does the same as MIDDLE. 529 // In other methods, MARQUEE is equivalent to END, except for the first line. 530 assertEquals("long" + mEllipsis + "ate", 531 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE).toString()); 532 533 textWidth = p.measureText(mEllipsis); 534 assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString()); 535 assertEquals("", TextUtils.ellipsize(text, p, textWidth - 1, TruncateAt.END).toString()); 536 assertEquals("", TextUtils.ellipsize(text, p, -1f, TruncateAt.END).toString()); 537 assertEquals(text, 538 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END).toString()); 539 540 assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString()); 541 assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString()); 542 543 try { 544 TextUtils.ellipsize(text, null, textWidth, TruncateAt.MIDDLE); 545 fail("Should throw NullPointerException"); 546 } catch (NullPointerException e) { 547 // expected 548 } 549 550 try { 551 TextUtils.ellipsize(null, p, textWidth, TruncateAt.MIDDLE); 552 fail("Should throw NullPointerException"); 553 } catch (NullPointerException e) { 554 // expected 555 } 556 } 557 558 @Test testEllipsize_emoji()559 public void testEllipsize_emoji() { 560 // 2 family emojis (11 code units + 11 code units). 561 final String text = "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC66" 562 + "\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC67\u200D\uD83D\uDC66"; 563 564 final TextPaint p = new TextPaint(); 565 final float width = p.measureText(text); 566 567 final TextUtils.TruncateAt[] kinds = {TextUtils.TruncateAt.START, 568 TextUtils.TruncateAt.MIDDLE, TextUtils.TruncateAt.END}; 569 for (final TextUtils.TruncateAt kind : kinds) { 570 for (int i = 0; i <= 8; i++) { 571 float avail = width * i / 7.0f; 572 final String out = TextUtils.ellipsize(text, p, avail, kind).toString(); 573 assertTrue("kind: " + kind + ", avail: " + avail + ", out length: " + out.length(), 574 out.length() == text.length() 575 || out.length() == text.length() / 2 + 1 576 || out.length() == 0); 577 } 578 } 579 } 580 581 @Test testEllipsizeCallback()582 public void testEllipsizeCallback() { 583 TextPaint p = new TextPaint(); 584 585 // turn off kerning. with kerning enabled, different methods of measuring the same text 586 // produce different results. 587 p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG); 588 589 TextUtils.EllipsizeCallback callback = new TextUtils.EllipsizeCallback() { 590 public void ellipsized(final int start, final int end) { 591 mStart = start; 592 mEnd = end; 593 } 594 }; 595 596 String text = "long string to truncate"; 597 598 // TruncateAt.START, does not specify preserveLength 599 resetRange(); 600 float textWidth = p.measureText(mEllipsis + "uncate"); 601 assertEquals(mEllipsis + "uncate", 602 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, false, 603 callback).toString()); 604 assertEquals(0, mStart); 605 assertEquals(text.length() - "uncate".length(), mEnd); 606 607 // TruncateAt.START, specify preserveLength 608 resetRange(); 609 int ellipsisNum = text.length() - "uncate".length(); 610 assertEquals(getBlankString(true, ellipsisNum) + "uncate", 611 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, true, 612 callback).toString()); 613 assertEquals(0, mStart); 614 assertEquals(text.length() - "uncate".length(), mEnd); 615 616 // TruncateAt.END, specify preserveLength 617 resetRange(); 618 textWidth = p.measureText("long str") + p.measureText(mEllipsis); 619 ellipsisNum = text.length() - "long str".length(); 620 assertEquals("long str" + getBlankString(true, ellipsisNum), 621 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString()); 622 assertEquals("long str".length(), mStart); 623 assertEquals(text.length(), mEnd); 624 625 // TruncateAt.MIDDLE, specify preserveLength 626 resetRange(); 627 textWidth = p.measureText("long" + mEllipsis + "ate"); 628 ellipsisNum = text.length() - "long".length() - "ate".length(); 629 assertEquals("long" + getBlankString(true, ellipsisNum) + "ate", 630 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true, 631 callback).toString()); 632 assertEquals("long".length(), mStart); 633 assertEquals(text.length() - "ate".length(), mEnd); 634 635 // TruncateAt.MIDDLE, specify preserveLength, but does not specify callback. 636 resetRange(); 637 assertEquals("long" + getBlankString(true, ellipsisNum) + "ate", 638 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true, 639 null).toString()); 640 assertEquals(-1, mStart); 641 assertEquals(-1, mEnd); 642 643 // TruncateAt.MARQUEE, specify preserveLength 644 // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE. 645 // In the code it looks like this does the same as MIDDLE. 646 // In other methods, MARQUEE is equivalent to END, except for the first line. 647 resetRange(); 648 textWidth = p.measureText("long" + mEllipsis + "ate"); 649 ellipsisNum = text.length() - "long".length() - "ate".length(); 650 assertEquals("long" + getBlankString(true, ellipsisNum) + "ate", 651 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE, true, 652 callback).toString()); 653 assertEquals("long".length(), mStart); 654 assertEquals(text.length() - "ate".length(), mEnd); 655 656 // avail is not long enough for ELLIPSIS, and preserveLength is specified. 657 resetRange(); 658 textWidth = p.measureText(mEllipsis); 659 assertEquals(getBlankString(false, text.length()), 660 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, true, 661 callback).toString()); 662 assertEquals(0, mStart); 663 assertEquals(text.length(), mEnd); 664 665 // avail is not long enough for ELLIPSIS, and preserveLength doesn't be specified. 666 resetRange(); 667 assertEquals("", 668 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, false, 669 callback).toString()); 670 assertEquals(0, mStart); 671 assertEquals(text.length(), mEnd); 672 673 // avail is long enough for ELLIPSIS, and preserveLength is specified. 674 resetRange(); 675 assertEquals(getBlankString(false, text.length()), 676 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString()); 677 assertEquals(0, mStart); 678 assertEquals(text.length(), mEnd); 679 680 // avail is long enough for ELLIPSIS, and preserveLength doesn't be specified. 681 resetRange(); 682 assertEquals("", 683 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, false, 684 callback).toString()); 685 assertEquals(0, mStart); 686 assertEquals(text.length(), mEnd); 687 688 // avail is long enough for the whole sentence. 689 resetRange(); 690 assertEquals(text, 691 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END, true, 692 callback).toString()); 693 assertEquals(0, mStart); 694 assertEquals(0, mEnd); 695 696 textWidth = p.measureText("long str" + mEllipsis); 697 try { 698 TextUtils.ellipsize(text, null, textWidth, TruncateAt.END, true, callback); 699 } catch (NullPointerException e) { 700 // expected 701 } 702 703 try { 704 TextUtils.ellipsize(null, p, textWidth, TruncateAt.END, true, callback); 705 } catch (NullPointerException e) { 706 // expected 707 } 708 } 709 710 /** 711 * Get a blank string which is filled up by '\uFEFF'. 712 * 713 * @param isNeedStart - boolean whether need to start with char '\u2026' in the string. 714 * @param len - int length of string. 715 * @return a blank string which is filled up by '\uFEFF'. 716 */ getBlankString(boolean isNeedStart, int len)717 private static String getBlankString(boolean isNeedStart, int len) { 718 StringBuilder buf = new StringBuilder(); 719 720 int i = 0; 721 if (isNeedStart) { 722 buf.append('\u2026'); 723 i++; 724 } 725 for (; i < len; i++) { 726 buf.append('\uFEFF'); 727 } 728 729 return buf.toString(); 730 } 731 732 @Test testEquals()733 public void testEquals() { 734 // compare with itself. 735 // String is a subclass of CharSequence and overrides equals(). 736 String string = "same object"; 737 assertTrue(TextUtils.equals(string, string)); 738 739 // SpannableString is a subclass of CharSequence and does NOT override equals(). 740 SpannableString spanString = new SpannableString("same object"); 741 final String url = "www.test_url.com"; 742 spanString.setSpan(new URLSpan(url), 0, spanString.length(), 743 Spanned.SPAN_INCLUSIVE_INCLUSIVE); 744 assertTrue(TextUtils.equals(spanString, spanString)); 745 746 // compare with other objects which have same content. 747 assertTrue(TextUtils.equals("different object", "different object")); 748 749 SpannableString urlSpanString = new SpannableString("same content"); 750 SpannableString bgColorSpanString = new SpannableString( 751 "same content"); 752 URLSpan urlSpan = new URLSpan(url); 753 urlSpanString.setSpan(urlSpan, 0, urlSpanString.length(), 754 Spanned.SPAN_INCLUSIVE_INCLUSIVE); 755 BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN); 756 bgColorSpanString.setSpan(bgColorSpan, 0, bgColorSpanString.length(), 757 Spanned.SPAN_INCLUSIVE_INCLUSIVE); 758 759 assertTrue(TextUtils.equals(bgColorSpanString, urlSpanString)); 760 761 // compare with other objects which have different content. 762 assertFalse(TextUtils.equals("different content A", "different content B")); 763 assertFalse(TextUtils.equals(spanString, urlSpanString)); 764 assertFalse(TextUtils.equals(spanString, bgColorSpanString)); 765 766 // compare with null 767 assertTrue(TextUtils.equals(null, null)); 768 assertFalse(TextUtils.equals(spanString, null)); 769 assertFalse(TextUtils.equals(null, string)); 770 } 771 772 @Test testExpandTemplate()773 public void testExpandTemplate() { 774 // ^1 at the start of template string. 775 assertEquals("value1 template to be expanded", 776 TextUtils.expandTemplate("^1 template to be expanded", "value1").toString()); 777 // ^1 at the end of template string. 778 assertEquals("template to be expanded value1", 779 TextUtils.expandTemplate("template to be expanded ^1", "value1").toString()); 780 // ^1 in the middle of template string. 781 assertEquals("template value1 to be expanded", 782 TextUtils.expandTemplate("template ^1 to be expanded", "value1").toString()); 783 // ^1 followed by a '0' 784 assertEquals("template value10 to be expanded", 785 TextUtils.expandTemplate("template ^10 to be expanded", "value1").toString()); 786 // ^1 followed by a 'a' 787 assertEquals("template value1a to be expanded", 788 TextUtils.expandTemplate("template ^1a to be expanded", "value1").toString()); 789 // no ^1 790 assertEquals("template ^a to be expanded", 791 TextUtils.expandTemplate("template ^a to be expanded", "value1").toString()); 792 assertEquals("template to be expanded", 793 TextUtils.expandTemplate("template to be expanded", "value1").toString()); 794 // two consecutive ^ in the input to produce a single ^ in the output. 795 assertEquals("template ^ to be expanded", 796 TextUtils.expandTemplate("template ^^ to be expanded", "value1").toString()); 797 // two ^ with a space in the middle. 798 assertEquals("template ^ ^ to be expanded", 799 TextUtils.expandTemplate("template ^ ^ to be expanded", "value1").toString()); 800 // ^1 follow a '^' 801 assertEquals("template ^1 to be expanded", 802 TextUtils.expandTemplate("template ^^1 to be expanded", "value1").toString()); 803 // ^1 followed by a '^' 804 assertEquals("template value1^ to be expanded", 805 TextUtils.expandTemplate("template ^1^ to be expanded", "value1").toString()); 806 807 // 9 replacement values 808 final int MAX_SUPPORTED_VALUES_NUM = 9; 809 CharSequence values[] = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM); 810 String expected = "value1 value2 template value3 value4 to value5 value6" + 811 " be value7 value8 expanded value9"; 812 String template = "^1 ^2 template ^3 ^4 to ^5 ^6 be ^7 ^8 expanded ^9"; 813 assertEquals(expected, TextUtils.expandTemplate(template, values).toString()); 814 815 // only up to 9 replacement values are supported 816 values = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM + 1); 817 try { 818 TextUtils.expandTemplate(template, values); 819 fail("Should throw IllegalArgumentException!"); 820 } catch (IllegalArgumentException e) { 821 // expect 822 } 823 } 824 825 @Test(expected=IllegalArgumentException.class) testExpandTemplateCaret0WithValue()826 public void testExpandTemplateCaret0WithValue() { 827 // template string is ^0 828 TextUtils.expandTemplate("template ^0 to be expanded", "value1"); 829 } 830 831 @Test(expected=IllegalArgumentException.class) testExpandTemplateCaret0NoValues()832 public void testExpandTemplateCaret0NoValues() { 833 // template string is ^0 834 TextUtils.expandTemplate("template ^0 to be expanded"); 835 } 836 837 @Test(expected=IllegalArgumentException.class) testExpandTemplateNotEnoughValues()838 public void testExpandTemplateNotEnoughValues() { 839 // the template requests 2 values but only 1 is provided 840 TextUtils.expandTemplate("template ^2 to be expanded", "value1"); 841 } 842 843 @Test(expected=NullPointerException.class) testExpandTemplateNullValues()844 public void testExpandTemplateNullValues() { 845 // values is null 846 TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence[]) null); 847 } 848 849 @Test(expected=IllegalArgumentException.class) testExpandTemplateNotEnoughValuesAndFirstIsNull()850 public void testExpandTemplateNotEnoughValuesAndFirstIsNull() { 851 // the template requests 2 values but only one null value is provided 852 TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence) null); 853 } 854 855 @Test(expected=NullPointerException.class) testExpandTemplateAllValuesAreNull()856 public void testExpandTemplateAllValuesAreNull() { 857 // the template requests 2 values and 2 values is provided, but all values are null. 858 TextUtils.expandTemplate("template ^2 to be expanded", 859 (CharSequence) null, (CharSequence) null); 860 } 861 862 @Test(expected=IllegalArgumentException.class) testExpandTemplateNoValues()863 public void testExpandTemplateNoValues() { 864 // the template requests 2 values but no value is provided. 865 TextUtils.expandTemplate("template ^2 to be expanded"); 866 } 867 868 @Test(expected=NullPointerException.class) testExpandTemplateNullTemplate()869 public void testExpandTemplateNullTemplate() { 870 // template is null 871 TextUtils.expandTemplate(null, "value1"); 872 } 873 874 /** 875 * Create a char sequence array with the specified length 876 * @param len the length of the array 877 * @return The char sequence array with the specified length. 878 * The value of each item is "value[index+1]" 879 */ createCharSequenceArray(int len)880 private static CharSequence[] createCharSequenceArray(int len) { 881 CharSequence array[] = new CharSequence[len]; 882 883 for (int i = 0; i < len; i++) { 884 array[i] = "value" + (i + 1); 885 } 886 887 return array; 888 } 889 890 @Test testGetChars()891 public void testGetChars() { 892 char[] destOriginal = "destination".toCharArray(); 893 char[] destResult = destOriginal.clone(); 894 895 // check whether GetChars.getChars() is called and with the proper parameters. 896 MockGetChars mockGetChars = new MockGetChars(); 897 int start = 1; 898 int end = destResult.length; 899 int destOff = 2; 900 TextUtils.getChars(mockGetChars, start, end, destResult, destOff); 901 assertTrue(mockGetChars.hasCalledGetChars()); 902 assertEquals(start, mockGetChars.ReadGetCharsParams().start); 903 assertEquals(end, mockGetChars.ReadGetCharsParams().end); 904 assertEquals(destResult, mockGetChars.ReadGetCharsParams().dest); 905 assertEquals(destOff, mockGetChars.ReadGetCharsParams().destoff); 906 907 // use MockCharSequence to do the test includes corner cases. 908 MockCharSequence mockCharSequence = new MockCharSequence("source string mock"); 909 // get chars to place at the beginning of the destination except the latest one char. 910 destResult = destOriginal.clone(); 911 start = 0; 912 end = destResult.length - 1; 913 destOff = 0; 914 TextUtils.getChars(mockCharSequence, start, end, destResult, destOff); 915 // chars before end are copied from the mockCharSequence. 916 for (int i = 0; i < end - start; i++) { 917 assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]); 918 } 919 // chars after end doesn't be changed. 920 for (int i = destOff + (end - start); i < destOriginal.length; i++) { 921 assertEquals(destOriginal[i], destResult[i]); 922 } 923 924 // get chars to place at the end of the destination except the earliest two chars. 925 destResult = destOriginal.clone(); 926 start = 0; 927 end = destResult.length - 2; 928 destOff = 2; 929 TextUtils.getChars(mockCharSequence, start, end, destResult, destOff); 930 // chars before start doesn't be changed. 931 for (int i = 0; i < destOff; i++) { 932 assertEquals(destOriginal[i], destResult[i]); 933 } 934 // chars after start are copied from the mockCharSequence. 935 for (int i = 0; i < end - start; i++) { 936 assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]); 937 } 938 939 // get chars to place at the end of the destination except the earliest two chars 940 // and the latest one word. 941 destResult = destOriginal.clone(); 942 start = 1; 943 end = destResult.length - 2; 944 destOff = 0; 945 TextUtils.getChars(mockCharSequence, start, end, destResult, destOff); 946 for (int i = 0; i < destOff; i++) { 947 assertEquals(destOriginal[i], destResult[i]); 948 } 949 for (int i = 0; i < end - start; i++) { 950 assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]); 951 } 952 for (int i = destOff + (end - start); i < destOriginal.length; i++) { 953 assertEquals(destOriginal[i], destResult[i]); 954 } 955 956 // get chars to place the whole of the destination 957 destResult = destOriginal.clone(); 958 start = 0; 959 end = destResult.length; 960 destOff = 0; 961 TextUtils.getChars(mockCharSequence, start, end, destResult, destOff); 962 for (int i = 0; i < end - start; i++) { 963 assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]); 964 } 965 966 // exceptional start. 967 end = 2; 968 destOff = 0; 969 destResult = destOriginal.clone(); 970 try { 971 TextUtils.getChars(mockCharSequence, -1, end, destResult, destOff); 972 fail("Should throw IndexOutOfBoundsException!"); 973 } catch (IndexOutOfBoundsException e) { 974 // expected 975 } 976 977 destResult = destOriginal.clone(); 978 TextUtils.getChars(mockCharSequence, Integer.MAX_VALUE, end, destResult, destOff); 979 for (int i = 0; i < destResult.length; i++) { 980 assertEquals(destOriginal[i], destResult[i]); 981 } 982 983 // exceptional end. 984 destResult = destOriginal.clone(); 985 start = 0; 986 destOff = 0; 987 try { 988 TextUtils.getChars(mockCharSequence, start, destResult.length + 1, destResult, destOff); 989 fail("Should throw IndexOutOfBoundsException!"); 990 } catch (IndexOutOfBoundsException e) { 991 // expected 992 } 993 994 destResult = destOriginal.clone(); 995 TextUtils.getChars(mockCharSequence, start, -1, destResult, destOff); 996 for (int i = 0; i < destResult.length; i++) { 997 assertEquals(destOriginal[i], destResult[i]); 998 } 999 1000 // exceptional destOff. 1001 destResult = destOriginal.clone(); 1002 start = 0; 1003 end = 2; 1004 try { 1005 TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MAX_VALUE); 1006 fail("Should throw IndexOutOfBoundsException!"); 1007 } catch (IndexOutOfBoundsException e) { 1008 // expect 1009 } 1010 try { 1011 TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MIN_VALUE); 1012 fail("Should throw IndexOutOfBoundsException!"); 1013 } catch (IndexOutOfBoundsException e) { 1014 // expect 1015 } 1016 1017 // exceptional source 1018 start = 0; 1019 end = 2; 1020 destOff =0; 1021 try { 1022 TextUtils.getChars(null, start, end, destResult, destOff); 1023 fail("Should throw NullPointerException!"); 1024 } catch (NullPointerException e) { 1025 // expected 1026 } 1027 1028 // exceptional destination 1029 try { 1030 TextUtils.getChars(mockCharSequence, start, end, null, destOff); 1031 fail("Should throw NullPointerException!"); 1032 } catch (NullPointerException e) { 1033 // expected 1034 } 1035 } 1036 1037 /** 1038 * MockGetChars for test. 1039 */ 1040 private static class MockGetChars implements GetChars { 1041 private boolean mHasCalledGetChars; 1042 private GetCharsParams mGetCharsParams = new GetCharsParams(); 1043 1044 class GetCharsParams { 1045 int start; 1046 int end; 1047 char[] dest; 1048 int destoff; 1049 } 1050 hasCalledGetChars()1051 public boolean hasCalledGetChars() { 1052 return mHasCalledGetChars; 1053 } 1054 reset()1055 public void reset() { 1056 mHasCalledGetChars = false; 1057 } 1058 ReadGetCharsParams()1059 public GetCharsParams ReadGetCharsParams() { 1060 return mGetCharsParams; 1061 } 1062 getChars(int start, int end, char[] dest, int destoff)1063 public void getChars(int start, int end, char[] dest, int destoff) { 1064 mHasCalledGetChars = true; 1065 mGetCharsParams.start = start; 1066 mGetCharsParams.end = end; 1067 mGetCharsParams.dest = dest; 1068 mGetCharsParams.destoff = destoff; 1069 } 1070 charAt(int arg0)1071 public char charAt(int arg0) { 1072 return 0; 1073 } 1074 length()1075 public int length() { 1076 return 100; 1077 } 1078 subSequence(int arg0, int arg1)1079 public CharSequence subSequence(int arg0, int arg1) { 1080 return null; 1081 } 1082 } 1083 1084 /** 1085 * MockCharSequence for test. 1086 */ 1087 private static class MockCharSequence implements CharSequence { 1088 private char mText[]; 1089 MockCharSequence(String text)1090 public MockCharSequence(String text) { 1091 mText = text.toCharArray(); 1092 } 1093 charAt(int arg0)1094 public char charAt(int arg0) { 1095 if (arg0 >= 0 && arg0 < mText.length) { 1096 return mText[arg0]; 1097 } 1098 throw new IndexOutOfBoundsException(); 1099 } 1100 length()1101 public int length() { 1102 return mText.length; 1103 } 1104 subSequence(int arg0, int arg1)1105 public CharSequence subSequence(int arg0, int arg1) { 1106 return null; 1107 } 1108 } 1109 1110 @Test testGetOffsetAfter()1111 public void testGetOffsetAfter() { 1112 // the first '\uD800' is index 9, the second 'uD800' is index 16 1113 // the '\uDBFF' is index 26 1114 final int POS_FIRST_D800 = 9; // the position of the first '\uD800'. 1115 final int POS_SECOND_D800 = 16; 1116 final int POS_FIRST_DBFF = 26; 1117 final int SUPPLEMENTARY_CHARACTERS_OFFSET = 2; // the offset for a supplementary characters 1118 final int NORMAL_CHARACTERS_OFFSET = 1; 1119 SpannableString text = new SpannableString( 1120 "string to\uD800\uDB00 get \uD800\uDC00 offset \uDBFF\uDFFF after"); 1121 assertEquals(0 + 1, TextUtils.getOffsetAfter(text, 0)); 1122 assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length())); 1123 assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length() - 1)); 1124 assertEquals(POS_FIRST_D800 + NORMAL_CHARACTERS_OFFSET, 1125 TextUtils.getOffsetAfter(text, POS_FIRST_D800)); 1126 assertEquals(POS_SECOND_D800 + SUPPLEMENTARY_CHARACTERS_OFFSET, 1127 TextUtils.getOffsetAfter(text, POS_SECOND_D800)); 1128 assertEquals(POS_FIRST_DBFF + SUPPLEMENTARY_CHARACTERS_OFFSET, 1129 TextUtils.getOffsetAfter(text, POS_FIRST_DBFF)); 1130 1131 // the CharSequence string has a span. 1132 ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class); 1133 when(mockReplacementSpan.getSize(any(), any(), anyInt(), anyInt(), any())).thenReturn(0); 1134 text.setSpan(mockReplacementSpan, POS_FIRST_D800 - 1, text.length() - 1, 1135 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1136 assertEquals(text.length() - 1, TextUtils.getOffsetAfter(text, POS_FIRST_D800)); 1137 1138 try { 1139 TextUtils.getOffsetAfter(text, -1); 1140 fail("Should throw IndexOutOfBoundsException!"); 1141 } catch (IndexOutOfBoundsException e) { 1142 } 1143 1144 try { 1145 TextUtils.getOffsetAfter(text, Integer.MAX_VALUE); 1146 fail("Should throw IndexOutOfBoundsException!"); 1147 } catch (IndexOutOfBoundsException e) { 1148 } 1149 1150 try { 1151 TextUtils.getOffsetAfter(null, 0); 1152 fail("Should throw NullPointerException!"); 1153 } catch (NullPointerException e) { 1154 // expected 1155 } 1156 } 1157 1158 @Test testGetOffsetBefore()1159 public void testGetOffsetBefore() { 1160 // the first '\uDC00' is index 10, the second 'uDC00' is index 17 1161 // the '\uDFFF' is index 27 1162 final int POS_FIRST_DC00 = 10; 1163 final int POS_SECOND_DC00 = 17; 1164 final int POS_FIRST_DFFF = 27; 1165 final int SUPPLYMENTARY_CHARACTERS_OFFSET = 2; 1166 final int NORMAL_CHARACTERS_OFFSET = 1; 1167 SpannableString text = new SpannableString( 1168 "string to\uD700\uDC00 get \uD800\uDC00 offset \uDBFF\uDFFF before"); 1169 assertEquals(0, TextUtils.getOffsetBefore(text, 0)); 1170 assertEquals(0, TextUtils.getOffsetBefore(text, 1)); 1171 assertEquals(text.length() - 1, TextUtils.getOffsetBefore(text, text.length())); 1172 assertEquals(POS_FIRST_DC00 + 1 - NORMAL_CHARACTERS_OFFSET, 1173 TextUtils.getOffsetBefore(text, POS_FIRST_DC00 + 1)); 1174 assertEquals(POS_SECOND_DC00 + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET, 1175 TextUtils.getOffsetBefore(text, POS_SECOND_DC00 + 1)); 1176 assertEquals(POS_FIRST_DFFF + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET, 1177 TextUtils.getOffsetBefore(text, POS_FIRST_DFFF + 1)); 1178 1179 // the CharSequence string has a span. 1180 ReplacementSpan mockReplacementSpan = mock(ReplacementSpan.class); 1181 when(mockReplacementSpan.getSize(any(), any(), anyInt(), anyInt(), any())).thenReturn(0); 1182 text.setSpan(mockReplacementSpan, 0, POS_FIRST_DC00 + 1, 1183 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 1184 assertEquals(0, TextUtils.getOffsetBefore(text, POS_FIRST_DC00)); 1185 1186 try { 1187 TextUtils.getOffsetBefore(text, -1); 1188 fail("Should throw IndexOutOfBoundsException!"); 1189 } catch (IndexOutOfBoundsException e) { 1190 } 1191 1192 try { 1193 TextUtils.getOffsetBefore(text, Integer.MAX_VALUE); 1194 fail("Should throw IndexOutOfBoundsException!"); 1195 } catch (IndexOutOfBoundsException e) { 1196 } 1197 1198 try { 1199 TextUtils.getOffsetBefore(null, POS_FIRST_DC00); 1200 fail("Should throw NullPointerException!"); 1201 } catch (NullPointerException e) { 1202 // expected 1203 } 1204 } 1205 1206 @Test testGetReverse()1207 public void testGetReverse() { 1208 String source = "string to be reversed"; 1209 assertEquals("gnirts", TextUtils.getReverse(source, 0, "string".length()).toString()); 1210 assertEquals("desrever", 1211 TextUtils.getReverse(source, source.length() - "reversed".length(), 1212 source.length()).toString()); 1213 assertEquals("", TextUtils.getReverse(source, 0, 0).toString()); 1214 1215 // issue 1695243, exception is thrown after the result of some cases 1216 // convert to a string, is this expected? 1217 CharSequence result = TextUtils.getReverse(source, -1, "string".length()); 1218 try { 1219 result.toString(); 1220 fail("Should throw IndexOutOfBoundsException!"); 1221 } catch (IndexOutOfBoundsException e) { 1222 } 1223 1224 TextUtils.getReverse(source, 0, source.length() + 1); 1225 try { 1226 result.toString(); 1227 fail("Should throw IndexOutOfBoundsException!"); 1228 } catch (IndexOutOfBoundsException e) { 1229 } 1230 1231 TextUtils.getReverse(source, "string".length(), 0); 1232 try { 1233 result.toString(); 1234 fail("Should throw IndexOutOfBoundsException!"); 1235 } catch (IndexOutOfBoundsException e) { 1236 } 1237 1238 TextUtils.getReverse(source, 0, Integer.MAX_VALUE); 1239 try { 1240 result.toString(); 1241 fail("Should throw IndexOutOfBoundsException!"); 1242 } catch (IndexOutOfBoundsException e) { 1243 } 1244 1245 TextUtils.getReverse(source, Integer.MIN_VALUE, "string".length()); 1246 try { 1247 result.toString(); 1248 fail("Should throw IndexOutOfBoundsException!"); 1249 } catch (IndexOutOfBoundsException e) { 1250 } 1251 1252 TextUtils.getReverse(null, 0, "string".length()); 1253 try { 1254 result.toString(); 1255 fail("Should throw IndexOutOfBoundsException!"); 1256 } catch (IndexOutOfBoundsException e) { 1257 // expected 1258 } 1259 } 1260 1261 @Test testGetTrimmedLength()1262 public void testGetTrimmedLength() { 1263 assertEquals("normalstring".length(), TextUtils.getTrimmedLength("normalstring")); 1264 assertEquals("normal string".length(), TextUtils.getTrimmedLength("normal string")); 1265 assertEquals("blank before".length(), TextUtils.getTrimmedLength(" \t blank before")); 1266 assertEquals("blank after".length(), TextUtils.getTrimmedLength("blank after \n ")); 1267 assertEquals("blank both".length(), TextUtils.getTrimmedLength(" \t blank both \n ")); 1268 1269 char[] allTrimmedChars = new char[]{ 1270 '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007', 1271 '\u0008', '\u0009', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015', 1272 '\u0016', '\u0017', '\u0018', '\u0019', '\u0020' 1273 }; 1274 assertEquals(0, TextUtils.getTrimmedLength(String.valueOf(allTrimmedChars))); 1275 } 1276 1277 @Test(expected=NullPointerException.class) testGetTrimmedLengthNull()1278 public void testGetTrimmedLengthNull() { 1279 TextUtils.getTrimmedLength(null); 1280 } 1281 1282 @Test testHtmlEncode()1283 public void testHtmlEncode() { 1284 assertEquals("<_html_>\\ &"'string'"", 1285 TextUtils.htmlEncode("<_html_>\\ &\"'string'\"")); 1286 } 1287 1288 @Test(expected=NullPointerException.class) testHtmlEncodeNull()1289 public void testHtmlEncodeNull() { 1290 TextUtils.htmlEncode(null); 1291 } 1292 1293 @Test testIndexOf1()1294 public void testIndexOf1() { 1295 String searchString = "string to be searched"; 1296 final int INDEX_OF_FIRST_R = 2; // first occurrence of 'r' 1297 final int INDEX_OF_FIRST_T = 1; 1298 final int INDEX_OF_FIRST_D = searchString.length() - 1; 1299 1300 assertEquals(INDEX_OF_FIRST_T, TextUtils.indexOf(searchString, 't')); 1301 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r')); 1302 assertEquals(INDEX_OF_FIRST_D, TextUtils.indexOf(searchString, 'd')); 1303 assertEquals(-1, TextUtils.indexOf(searchString, 'f')); 1304 1305 StringBuffer stringBuffer = new StringBuffer(searchString); 1306 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuffer, 'r')); 1307 1308 StringBuilder stringBuilder = new StringBuilder(searchString); 1309 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuilder, 'r')); 1310 1311 MockGetChars mockGetChars = new MockGetChars(); 1312 assertFalse(mockGetChars.hasCalledGetChars()); 1313 TextUtils.indexOf(mockGetChars, 'r'); 1314 assertTrue(mockGetChars.hasCalledGetChars()); 1315 1316 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1317 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(mockCharSequence, 'r')); 1318 } 1319 1320 @Test testIndexOf2()1321 public void testIndexOf2() { 1322 String searchString = "string to be searched"; 1323 final int INDEX_OF_FIRST_R = 2; 1324 final int INDEX_OF_SECOND_R = 16; 1325 1326 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', 0)); 1327 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r', INDEX_OF_FIRST_R + 1)); 1328 assertEquals(-1, TextUtils.indexOf(searchString, 'r', searchString.length())); 1329 assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE)); 1330 assertEquals(-1, TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE)); 1331 1332 StringBuffer stringBuffer = new StringBuffer(searchString); 1333 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r', INDEX_OF_FIRST_R + 1)); 1334 try { 1335 TextUtils.indexOf(stringBuffer, 'r', Integer.MIN_VALUE); 1336 fail("Should throw IndexOutOfBoundsException!"); 1337 } catch (IndexOutOfBoundsException e) { 1338 // expect 1339 } 1340 assertEquals(-1, TextUtils.indexOf(stringBuffer, 'r', Integer.MAX_VALUE)); 1341 1342 StringBuilder stringBuilder = new StringBuilder(searchString); 1343 assertEquals(INDEX_OF_SECOND_R, 1344 TextUtils.indexOf(stringBuilder, 'r', INDEX_OF_FIRST_R + 1)); 1345 1346 MockGetChars mockGetChars = new MockGetChars(); 1347 TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1); 1348 assertTrue(mockGetChars.hasCalledGetChars()); 1349 1350 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1351 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r', 1352 INDEX_OF_FIRST_R + 1)); 1353 } 1354 1355 @Test testIndexOf3()1356 public void testIndexOf3() { 1357 String searchString = "string to be searched"; 1358 final int INDEX_OF_FIRST_R = 2; 1359 final int INDEX_OF_SECOND_R = 16; 1360 1361 assertEquals(INDEX_OF_FIRST_R, 1362 TextUtils.indexOf(searchString, 'r', 0, searchString.length())); 1363 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r', 1364 INDEX_OF_FIRST_R + 1, searchString.length())); 1365 assertEquals(-1, TextUtils.indexOf(searchString, 'r', 1366 INDEX_OF_FIRST_R + 1, INDEX_OF_SECOND_R)); 1367 1368 try { 1369 TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R); 1370 fail("Should throw IndexOutOfBoundsException!"); 1371 } catch (IndexOutOfBoundsException e) { 1372 // expect 1373 } 1374 assertEquals(-1, 1375 TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE, INDEX_OF_SECOND_R)); 1376 assertEquals(-1, TextUtils.indexOf(searchString, 'r', 0, Integer.MIN_VALUE)); 1377 try { 1378 TextUtils.indexOf(searchString, 'r', 0, Integer.MAX_VALUE); 1379 fail("Should throw IndexOutOfBoundsException!"); 1380 } catch (IndexOutOfBoundsException e) { 1381 // expect 1382 } 1383 1384 StringBuffer stringBuffer = new StringBuffer(searchString); 1385 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r', 1386 INDEX_OF_FIRST_R + 1, searchString.length())); 1387 1388 StringBuilder stringBuilder = new StringBuilder(searchString); 1389 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuilder, 'r', 1390 INDEX_OF_FIRST_R + 1, searchString.length())); 1391 1392 MockGetChars mockGetChars = new MockGetChars(); 1393 TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1, searchString.length()); 1394 assertTrue(mockGetChars.hasCalledGetChars()); 1395 1396 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1397 assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r', 1398 INDEX_OF_FIRST_R + 1, searchString.length())); 1399 } 1400 1401 @Test testIndexOf4()1402 public void testIndexOf4() { 1403 String searchString = "string to be searched by string"; 1404 final int SEARCH_INDEX = 13; 1405 1406 assertEquals(0, TextUtils.indexOf(searchString, "string")); 1407 assertEquals(SEARCH_INDEX, TextUtils.indexOf(searchString, "search")); 1408 assertEquals(-1, TextUtils.indexOf(searchString, "tobe")); 1409 assertEquals(0, TextUtils.indexOf(searchString, "")); 1410 1411 StringBuffer stringBuffer = new StringBuffer(searchString); 1412 assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuffer, "search")); 1413 1414 StringBuilder stringBuilder = new StringBuilder(searchString); 1415 assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuilder, "search")); 1416 1417 MockGetChars mockGetChars = new MockGetChars(); 1418 TextUtils.indexOf(mockGetChars, "search"); 1419 assertTrue(mockGetChars.hasCalledGetChars()); 1420 1421 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1422 assertEquals(SEARCH_INDEX, TextUtils.indexOf(mockCharSequence, "search")); 1423 } 1424 1425 @Test testIndexOf5()1426 public void testIndexOf5() { 1427 String searchString = "string to be searched by string"; 1428 final int INDEX_OF_FIRST_STRING = 0; 1429 final int INDEX_OF_SECOND_STRING = 25; 1430 1431 assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0)); 1432 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string", 1433 INDEX_OF_FIRST_STRING + 1)); 1434 assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_SECOND_STRING + 1)); 1435 assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 1436 Integer.MIN_VALUE)); 1437 assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE)); 1438 1439 assertEquals(1, TextUtils.indexOf(searchString, "", 1)); 1440 assertEquals(Integer.MAX_VALUE, TextUtils.indexOf(searchString, "", Integer.MAX_VALUE)); 1441 1442 assertEquals(0, TextUtils.indexOf(searchString, searchString, 0)); 1443 assertEquals(-1, TextUtils.indexOf(searchString, searchString + "longer needle", 0)); 1444 1445 StringBuffer stringBuffer = new StringBuffer(searchString); 1446 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string", 1447 INDEX_OF_FIRST_STRING + 1)); 1448 try { 1449 TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE); 1450 fail("Should throw IndexOutOfBoundsException!"); 1451 } catch (IndexOutOfBoundsException e) { 1452 // expect 1453 } 1454 assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE)); 1455 1456 StringBuilder stringBuilder = new StringBuilder(searchString); 1457 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string", 1458 INDEX_OF_FIRST_STRING + 1)); 1459 1460 MockGetChars mockGetChars = new MockGetChars(); 1461 assertFalse(mockGetChars.hasCalledGetChars()); 1462 TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1); 1463 assertTrue(mockGetChars.hasCalledGetChars()); 1464 1465 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1466 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string", 1467 INDEX_OF_FIRST_STRING + 1)); 1468 } 1469 1470 @Test testIndexOf6()1471 public void testIndexOf6() { 1472 String searchString = "string to be searched by string"; 1473 final int INDEX_OF_FIRST_STRING = 0; 1474 final int INDEX_OF_SECOND_STRING = 25; 1475 1476 assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0, 1477 searchString.length())); 1478 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string", 1479 INDEX_OF_FIRST_STRING + 1, searchString.length())); 1480 assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_FIRST_STRING + 1, 1481 INDEX_OF_SECOND_STRING - 1)); 1482 assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 1483 Integer.MIN_VALUE, INDEX_OF_SECOND_STRING - 1)); 1484 assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE, 1485 INDEX_OF_SECOND_STRING - 1)); 1486 1487 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string", 1488 INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE)); 1489 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string", 1490 INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE)); 1491 1492 StringBuffer stringBuffer = new StringBuffer(searchString); 1493 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string", 1494 INDEX_OF_FIRST_STRING + 1, searchString.length())); 1495 try { 1496 TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE, 1497 INDEX_OF_SECOND_STRING - 1); 1498 fail("Should throw IndexOutOfBoundsException!"); 1499 } catch (IndexOutOfBoundsException e) { 1500 // expect 1501 } 1502 assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE, 1503 searchString.length())); 1504 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, 1505 "string", INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE)); 1506 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, 1507 "string", INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE)); 1508 1509 StringBuilder stringBuilder = new StringBuilder(searchString); 1510 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string", 1511 INDEX_OF_FIRST_STRING + 1, searchString.length())); 1512 1513 MockGetChars mockGetChars = new MockGetChars(); 1514 TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1, searchString.length()); 1515 assertTrue(mockGetChars.hasCalledGetChars()); 1516 1517 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1518 assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string", 1519 INDEX_OF_FIRST_STRING + 1, searchString.length())); 1520 } 1521 1522 @Test testIsDigitsOnly()1523 public void testIsDigitsOnly() { 1524 assertTrue(TextUtils.isDigitsOnly("")); 1525 assertFalse(TextUtils.isDigitsOnly("no digit")); 1526 assertFalse(TextUtils.isDigitsOnly("character and 56 digits")); 1527 assertTrue(TextUtils.isDigitsOnly("0123456789")); 1528 assertFalse(TextUtils.isDigitsOnly("1234 56789")); 1529 1530 // U+104A0 OSMANYA DIGIT ZERO 1531 assertTrue(TextUtils.isDigitsOnly(new String(Character.toChars(0x104A0)))); 1532 // U+10858 IMPERIAL ARAMAIC NUMBER ONE 1533 assertFalse(TextUtils.isDigitsOnly(new String(Character.toChars(0x10858)))); 1534 1535 assertFalse(TextUtils.isDigitsOnly("\uD801")); // lonely lead surrogate 1536 assertFalse(TextUtils.isDigitsOnly("\uDCA0")); // lonely trailing surrogate 1537 } 1538 1539 @Test(expected=NullPointerException.class) testIsDigitsOnlyNull()1540 public void testIsDigitsOnlyNull() { 1541 TextUtils.isDigitsOnly(null); 1542 } 1543 1544 @Test testIsEmpty()1545 public void testIsEmpty() { 1546 assertFalse(TextUtils.isEmpty("not empty")); 1547 assertFalse(TextUtils.isEmpty(" ")); 1548 assertTrue(TextUtils.isEmpty("")); 1549 assertTrue(TextUtils.isEmpty(null)); 1550 } 1551 1552 @Test testIsGraphicChar()1553 public void testIsGraphicChar() { 1554 assertTrue(TextUtils.isGraphic('a')); 1555 assertTrue(TextUtils.isGraphic('\uBA00')); 1556 1557 // LINE_SEPARATOR 1558 assertFalse(TextUtils.isGraphic('\u2028')); 1559 1560 // PARAGRAPH_SEPARATOR 1561 assertFalse(TextUtils.isGraphic('\u2029')); 1562 1563 // CONTROL 1564 assertFalse(TextUtils.isGraphic('\u0085')); 1565 1566 // UNASSIGNED 1567 assertFalse(TextUtils.isGraphic('\uFFFF')); 1568 1569 // SURROGATE 1570 assertFalse(TextUtils.isGraphic('\uD800')); 1571 1572 // SPACE_SEPARATOR 1573 assertFalse(TextUtils.isGraphic('\u0020')); 1574 } 1575 1576 @Test(expected=NullPointerException.class) testIsGraphicCharNull()1577 public void testIsGraphicCharNull() { 1578 assertFalse(TextUtils.isGraphic((Character) null)); 1579 } 1580 1581 @Test testIsGraphicCharSequence()1582 public void testIsGraphicCharSequence() { 1583 assertTrue(TextUtils.isGraphic("printable characters")); 1584 1585 assertFalse(TextUtils.isGraphic("\u2028\u2029\u0085\uFFFF\uD800\u0020")); 1586 1587 assertTrue(TextUtils.isGraphic("a\u2028\u2029\u0085\uFFFF\uD800\u0020")); 1588 1589 assertTrue(TextUtils.isGraphic("\uD83D\uDC0C")); // U+1F40C SNAIL 1590 assertFalse(TextUtils.isGraphic("\uDB40\uDC01")); // U+E0000 (unassigned) 1591 assertFalse(TextUtils.isGraphic("\uDB3D")); // unpaired high surrogate 1592 assertFalse(TextUtils.isGraphic("\uDC0C")); // unpaired low surrogate 1593 } 1594 1595 @Test(expected=NullPointerException.class) testIsGraphicCharSequenceNull()1596 public void testIsGraphicCharSequenceNull() { 1597 TextUtils.isGraphic(null); 1598 } 1599 1600 @Test testJoinIterable()1601 public void testJoinIterable() { 1602 ArrayList<CharSequence> charTokens = new ArrayList<>(); 1603 charTokens.add("string1"); 1604 charTokens.add("string2"); 1605 charTokens.add("string3"); 1606 assertEquals("string1|string2|string3", TextUtils.join("|", charTokens)); 1607 assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens)); 1608 assertEquals("string1string2string3", TextUtils.join("", charTokens)); 1609 1610 // issue 1695243, not clear what is supposed result if the delimiter or tokens are null. 1611 assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens)); 1612 1613 ArrayList<SpannableString> spannableStringTokens = new ArrayList<SpannableString>(); 1614 spannableStringTokens.add(new SpannableString("span 1")); 1615 spannableStringTokens.add(new SpannableString("span 2")); 1616 spannableStringTokens.add(new SpannableString("span 3")); 1617 assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens)); 1618 1619 assertEquals("", TextUtils.join("|", new ArrayList<CharSequence>())); 1620 } 1621 1622 @Test(expected=NullPointerException.class) testJoinIterableNull()1623 public void testJoinIterableNull() { 1624 TextUtils.join("|", (Iterable) null); 1625 } 1626 1627 @Test testJoinArray()1628 public void testJoinArray() { 1629 CharSequence[] charTokens = new CharSequence[] { "string1", "string2", "string3" }; 1630 assertEquals("string1|string2|string3", TextUtils.join("|", charTokens)); 1631 assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens)); 1632 assertEquals("string1string2string3", TextUtils.join("", charTokens)); 1633 1634 // issue 1695243, not clear what is supposed result if the delimiter or tokens are null. 1635 assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens)); 1636 1637 SpannableString[] spannableStringTokens = new SpannableString[] { 1638 new SpannableString("span 1"), 1639 new SpannableString("span 2"), 1640 new SpannableString("span 3") }; 1641 assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens)); 1642 1643 assertEquals("", TextUtils.join("|", new String[0])); 1644 } 1645 1646 @Test(expected=NullPointerException.class) testJoinArrayNull()1647 public void testJoinArrayNull() { 1648 TextUtils.join("|", (Object[]) null); 1649 } 1650 1651 @Test testLastIndexOf1()1652 public void testLastIndexOf1() { 1653 String searchString = "string to be searched"; 1654 final int INDEX_OF_LAST_R = 16; 1655 final int INDEX_OF_LAST_T = 7; 1656 final int INDEX_OF_LAST_D = searchString.length() - 1; 1657 1658 assertEquals(INDEX_OF_LAST_T, TextUtils.lastIndexOf(searchString, 't')); 1659 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(searchString, 'r')); 1660 assertEquals(INDEX_OF_LAST_D, TextUtils.lastIndexOf(searchString, 'd')); 1661 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'f')); 1662 1663 StringBuffer stringBuffer = new StringBuffer(searchString); 1664 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuffer, 'r')); 1665 1666 StringBuilder stringBuilder = new StringBuilder(searchString); 1667 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuilder, 'r')); 1668 1669 MockGetChars mockGetChars = new MockGetChars(); 1670 TextUtils.lastIndexOf(mockGetChars, 'r'); 1671 assertTrue(mockGetChars.hasCalledGetChars()); 1672 1673 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1674 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(mockCharSequence, 'r')); 1675 } 1676 1677 @Test testLastIndexOf2()1678 public void testLastIndexOf2() { 1679 String searchString = "string to be searched"; 1680 final int INDEX_OF_FIRST_R = 2; 1681 final int INDEX_OF_SECOND_R = 16; 1682 1683 assertEquals(INDEX_OF_SECOND_R, 1684 TextUtils.lastIndexOf(searchString, 'r', searchString.length())); 1685 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0)); 1686 assertEquals(INDEX_OF_FIRST_R, 1687 TextUtils.lastIndexOf(searchString, 'r', INDEX_OF_FIRST_R)); 1688 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE)); 1689 assertEquals(INDEX_OF_SECOND_R, 1690 TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE)); 1691 1692 StringBuffer stringBuffer = new StringBuffer(searchString); 1693 assertEquals(INDEX_OF_FIRST_R, 1694 TextUtils.lastIndexOf(stringBuffer, 'r', INDEX_OF_FIRST_R)); 1695 assertEquals(-1, TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MIN_VALUE)); 1696 assertEquals(INDEX_OF_SECOND_R, 1697 TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MAX_VALUE)); 1698 1699 StringBuilder stringBuilder = new StringBuilder(searchString); 1700 assertEquals(INDEX_OF_FIRST_R, 1701 TextUtils.lastIndexOf(stringBuilder, 'r', INDEX_OF_FIRST_R)); 1702 1703 MockGetChars mockGetChars = new MockGetChars(); 1704 TextUtils.lastIndexOf(mockGetChars, 'r', INDEX_OF_FIRST_R); 1705 assertTrue(mockGetChars.hasCalledGetChars()); 1706 1707 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1708 assertEquals(INDEX_OF_FIRST_R, 1709 TextUtils.lastIndexOf(mockCharSequence, 'r', INDEX_OF_FIRST_R)); 1710 } 1711 1712 @Test testLastIndexOf3()1713 public void testLastIndexOf3() { 1714 String searchString = "string to be searched"; 1715 final int INDEX_OF_FIRST_R = 2; 1716 final int INDEX_OF_SECOND_R = 16; 1717 1718 assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1719 searchString.length())); 1720 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1721 INDEX_OF_SECOND_R - 1)); 1722 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, INDEX_OF_FIRST_R - 1)); 1723 1724 try { 1725 TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R - 1); 1726 fail("Should throw IndexOutOfBoundsException!"); 1727 } catch (IndexOutOfBoundsException e) { 1728 // expect 1729 } 1730 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE, 1731 INDEX_OF_SECOND_R - 1)); 1732 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, Integer.MIN_VALUE)); 1733 assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1734 Integer.MAX_VALUE)); 1735 1736 StringBuffer stringBuffer = new StringBuffer(searchString); 1737 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuffer, 'r', 0, 1738 INDEX_OF_SECOND_R - 1)); 1739 1740 StringBuilder stringBuilder = new StringBuilder(searchString); 1741 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuilder, 'r', 0, 1742 INDEX_OF_SECOND_R - 1)); 1743 1744 MockGetChars mockGetChars = new MockGetChars(); 1745 TextUtils.lastIndexOf(mockGetChars, 'r', 0, INDEX_OF_SECOND_R - 1); 1746 assertTrue(mockGetChars.hasCalledGetChars()); 1747 1748 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1749 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(mockCharSequence, 'r', 0, 1750 INDEX_OF_SECOND_R - 1)); 1751 } 1752 1753 @Test testRegionMatches()1754 public void testRegionMatches() { 1755 assertFalse(TextUtils.regionMatches("one", 0, "two", 0, "one".length())); 1756 assertTrue(TextUtils.regionMatches("one", 0, "one", 0, "one".length())); 1757 try { 1758 TextUtils.regionMatches("one", 0, "one", 0, "one".length() + 1); 1759 fail("Should throw IndexOutOfBoundsException!"); 1760 } catch (IndexOutOfBoundsException e) { 1761 } 1762 1763 String one = "Hello Android, hello World!"; 1764 String two = "Hello World"; 1765 // match "Hello" 1766 assertTrue(TextUtils.regionMatches(one, 0, two, 0, "Hello".length())); 1767 1768 // match "Hello A" and "Hello W" 1769 assertFalse(TextUtils.regionMatches(one, 0, two, 0, "Hello A".length())); 1770 1771 // match "World" 1772 assertTrue(TextUtils.regionMatches(one, "Hello Android, hello ".length(), 1773 two, "Hello ".length(), "World".length())); 1774 assertFalse(TextUtils.regionMatches(one, "Hello Android, hello ".length(), 1775 two, 0, "World".length())); 1776 1777 try { 1778 TextUtils.regionMatches(one, Integer.MIN_VALUE, two, 0, "Hello".length()); 1779 fail("Should throw IndexOutOfBoundsException!"); 1780 } catch (IndexOutOfBoundsException e) { 1781 } 1782 try { 1783 TextUtils.regionMatches(one, Integer.MAX_VALUE, two, 0, "Hello".length()); 1784 fail("Should throw IndexOutOfBoundsException!"); 1785 } catch (IndexOutOfBoundsException e) { 1786 } 1787 1788 try { 1789 TextUtils.regionMatches(one, 0, two, Integer.MIN_VALUE, "Hello".length()); 1790 fail("Should throw IndexOutOfBoundsException!"); 1791 } catch (IndexOutOfBoundsException e) { 1792 } 1793 try { 1794 TextUtils.regionMatches(one, 0, two, Integer.MAX_VALUE, "Hello".length()); 1795 fail("Should throw IndexOutOfBoundsException!"); 1796 } catch (IndexOutOfBoundsException e) { 1797 } 1798 1799 try { 1800 TextUtils.regionMatches(one, 0, two, 0, Integer.MIN_VALUE); 1801 fail("Should throw IndexOutOfBoundsException!"); 1802 } catch (IndexOutOfBoundsException e) { 1803 } 1804 try { 1805 TextUtils.regionMatches(one, 0, two, 0, Integer.MAX_VALUE); 1806 fail("Should throw IndexOutOfBoundsException!"); 1807 } catch (IndexOutOfBoundsException e) { 1808 } 1809 1810 try { 1811 TextUtils.regionMatches(null, 0, two, 0, "Hello".length()); 1812 fail("Should throw NullPointerException!"); 1813 } catch (NullPointerException e) { 1814 // expect 1815 } 1816 try { 1817 TextUtils.regionMatches(one, 0, null, 0, "Hello".length()); 1818 fail("Should throw NullPointerException!"); 1819 } catch (NullPointerException e) { 1820 // expect 1821 } 1822 } 1823 1824 @Test testReplace()1825 public void testReplace() { 1826 String template = "this is a string to be as the template for replacement"; 1827 1828 String sources[] = new String[] { "string" }; 1829 CharSequence destinations[] = new CharSequence[] { "text" }; 1830 SpannableStringBuilder replacedString = (SpannableStringBuilder) TextUtils.replace(template, 1831 sources, destinations); 1832 assertEquals("this is a text to be as the template for replacement", 1833 replacedString.toString()); 1834 1835 sources = new String[] {"is", "the", "for replacement"}; 1836 destinations = new CharSequence[] {"was", "", "to be replaced"}; 1837 replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations); 1838 assertEquals("thwas is a string to be as template to be replaced", 1839 replacedString.toString()); 1840 1841 sources = new String[] {"is", "for replacement"}; 1842 destinations = new CharSequence[] {"was", "", "to be replaced"}; 1843 replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations); 1844 assertEquals("thwas is a string to be as the template ", replacedString.toString()); 1845 1846 sources = new String[] {"is", "the", "for replacement"}; 1847 destinations = new CharSequence[] {"was", "to be replaced"}; 1848 try { 1849 TextUtils.replace(template, sources, destinations); 1850 fail("Should throw ArrayIndexOutOfBoundsException!"); 1851 } catch (ArrayIndexOutOfBoundsException e) { 1852 // expected 1853 } 1854 1855 try { 1856 TextUtils.replace(null, sources, destinations); 1857 fail("Should throw NullPointerException!"); 1858 } catch (NullPointerException e) { 1859 // expected 1860 } 1861 try { 1862 TextUtils.replace(template, null, destinations); 1863 fail("Should throw NullPointerException!"); 1864 } catch (NullPointerException e) { 1865 // expected 1866 } 1867 try { 1868 TextUtils.replace(template, sources, null); 1869 fail("Should throw NullPointerException!"); 1870 } catch (NullPointerException e) { 1871 // expected 1872 } 1873 } 1874 1875 @Test testSplitPattern()1876 public void testSplitPattern() { 1877 assertEquals(0, TextUtils.split("", Pattern.compile("")).length); 1878 assertEquals(0, TextUtils.split("", Pattern.compile("not found")).length); 1879 1880 String testString = "abccbadecdebz"; 1881 assertEquals(calculateCharsCount(testString, "c") + 1, 1882 TextUtils.split(testString, Pattern.compile("c")).length); 1883 assertEquals(calculateCharsCount(testString, "a") + 1, 1884 TextUtils.split(testString, Pattern.compile("a")).length); 1885 assertEquals(calculateCharsCount(testString, "z") + 1, 1886 TextUtils.split(testString, Pattern.compile("z")).length); 1887 assertEquals(calculateCharsCount(testString, "de") + 1, 1888 TextUtils.split(testString, Pattern.compile("de")).length); 1889 int totalCount = 1 + calculateCharsCount(testString, "a") 1890 + calculateCharsCount(testString, "b") + calculateCharsCount(testString, "c"); 1891 assertEquals(totalCount, 1892 TextUtils.split(testString, Pattern.compile("[a-c]")).length); 1893 assertEquals(0, TextUtils.split("", Pattern.compile("a")).length); 1894 // issue 1695243, not clear what is supposed result if the pattern string is empty. 1895 assertEquals( 1896 Arrays.asList("a", "b", "c", "c", "b", "a", "d", "e", "c", "d", "e", "b", "z", ""), 1897 Arrays.asList(TextUtils.split(testString, Pattern.compile("")))); 1898 } 1899 1900 @Test(expected=NullPointerException.class) testSplitPatternNullText()1901 public void testSplitPatternNullText() { 1902 TextUtils.split(null, Pattern.compile("a")); 1903 } 1904 1905 @Test(expected=NullPointerException.class) testSplitPatternNullPattern()1906 public void testSplitPatternNullPattern() { 1907 TextUtils.split("abccbadecdebz", (Pattern) null); 1908 } 1909 1910 /* 1911 * return the appearance count of searched chars in text. 1912 */ calculateCharsCount(CharSequence text, CharSequence searches)1913 private static int calculateCharsCount(CharSequence text, CharSequence searches) { 1914 int count = 0; 1915 int start = TextUtils.indexOf(text, searches, 0); 1916 1917 while (start != -1) { 1918 count++; 1919 start = TextUtils.indexOf(text, searches, start + 1); 1920 } 1921 return count; 1922 } 1923 1924 @Test testSplitString()1925 public void testSplitString() { 1926 assertEquals(0, TextUtils.split("", "").length); 1927 assertEquals(0, TextUtils.split("", "not found").length); 1928 1929 // The case mentioned in the documentation. 1930 assertEquals(Arrays.asList("a", ""), Arrays.asList(TextUtils.split("a,", ","))); 1931 1932 String testString = "abccbadecdebz"; 1933 assertEquals(calculateCharsCount(testString, "c") + 1, 1934 TextUtils.split("abccbadecdebz", "c").length); 1935 assertEquals(calculateCharsCount(testString, "a") + 1, 1936 TextUtils.split("abccbadecdebz", "a").length); 1937 assertEquals(calculateCharsCount(testString, "z") + 1, 1938 TextUtils.split("abccbadecdebz", "z").length); 1939 assertEquals(calculateCharsCount(testString, "de") + 1, 1940 TextUtils.split("abccbadecdebz", "de").length); 1941 assertEquals(0, TextUtils.split("", "a").length); 1942 // issue 1695243, not clear what is supposed result if the pattern string is empty. 1943 assertEquals( 1944 Arrays.asList("a", "b", "c", "c", "b", "a", "d", "e", "c", "d", "e", "b", "z", ""), 1945 Arrays.asList(TextUtils.split("abccbadecdebz", ""))); 1946 } 1947 1948 @Test(expected=NullPointerException.class) testSplitStringNullText()1949 public void testSplitStringNullText() { 1950 TextUtils.split(null, "a"); 1951 } 1952 1953 @Test(expected=NullPointerException.class) testSplitStringNullPattern()1954 public void testSplitStringNullPattern() { 1955 TextUtils.split("abccbadecdebz", (String) null); 1956 } 1957 1958 @Test testStringOrSpannedString()1959 public void testStringOrSpannedString() { 1960 assertNull(TextUtils.stringOrSpannedString(null)); 1961 1962 SpannedString spannedString = new SpannedString("Spanned String"); 1963 assertSame(spannedString, TextUtils.stringOrSpannedString(spannedString)); 1964 1965 SpannableString spannableString = new SpannableString("Spannable String"); 1966 assertEquals("Spannable String", 1967 TextUtils.stringOrSpannedString(spannableString).toString()); 1968 assertEquals(SpannedString.class, 1969 TextUtils.stringOrSpannedString(spannableString).getClass()); 1970 1971 StringBuffer stringBuffer = new StringBuffer("String Buffer"); 1972 assertEquals("String Buffer", 1973 TextUtils.stringOrSpannedString(stringBuffer).toString()); 1974 assertEquals(String.class, 1975 TextUtils.stringOrSpannedString(stringBuffer).getClass()); 1976 } 1977 1978 @Test testSubString()1979 public void testSubString() { 1980 String string = "String"; 1981 assertSame(string, TextUtils.substring(string, 0, string.length())); 1982 assertEquals("Strin", TextUtils.substring(string, 0, string.length() - 1)); 1983 assertEquals("", TextUtils.substring(string, 1, 1)); 1984 1985 try { 1986 TextUtils.substring(string, string.length(), 0); 1987 fail("Should throw IndexOutOfBoundsException!"); 1988 } catch (IndexOutOfBoundsException e) { 1989 // expected 1990 } 1991 1992 try { 1993 TextUtils.substring(string, -1, string.length()); 1994 fail("Should throw IndexOutOfBoundsException!"); 1995 } catch (IndexOutOfBoundsException e) { 1996 // expected 1997 } 1998 1999 try { 2000 TextUtils.substring(string, Integer.MAX_VALUE, string.length()); 2001 fail("Should throw IndexOutOfBoundsException!"); 2002 } catch (IndexOutOfBoundsException e) { 2003 // expected 2004 } 2005 2006 try { 2007 TextUtils.substring(string, 0, -1); 2008 fail("Should throw IndexOutOfBoundsException!"); 2009 } catch (IndexOutOfBoundsException e) { 2010 // expected 2011 } 2012 2013 try { 2014 TextUtils.substring(string, 0, Integer.MAX_VALUE); 2015 fail("Should throw IndexOutOfBoundsException!"); 2016 } catch (IndexOutOfBoundsException e) { 2017 // expected 2018 } 2019 2020 try { 2021 TextUtils.substring(null, 0, string.length()); 2022 fail("Should throw NullPointerException!"); 2023 } catch (NullPointerException e) { 2024 // expected 2025 } 2026 2027 StringBuffer stringBuffer = new StringBuffer("String Buffer"); 2028 assertEquals("Strin", TextUtils.substring(stringBuffer, 0, string.length() - 1)); 2029 assertEquals("", TextUtils.substring(stringBuffer, 1, 1)); 2030 2031 MockGetChars mockGetChars = new MockGetChars(); 2032 TextUtils.substring(mockGetChars, 0, string.length()); 2033 assertTrue(mockGetChars.hasCalledGetChars()); 2034 } 2035 2036 @Test testWriteToParcel()2037 public void testWriteToParcel() { 2038 Parcelable.Creator<CharSequence> creator = TextUtils.CHAR_SEQUENCE_CREATOR; 2039 String string = "String"; 2040 Parcel p = Parcel.obtain(); 2041 try { 2042 TextUtils.writeToParcel(string, p, 0); 2043 p.setDataPosition(0); 2044 assertEquals(string, creator.createFromParcel(p).toString()); 2045 } finally { 2046 p.recycle(); 2047 } 2048 2049 p = Parcel.obtain(); 2050 try { 2051 TextUtils.writeToParcel(null, p, 0); 2052 p.setDataPosition(0); 2053 assertNull(creator.createFromParcel(p)); 2054 } finally { 2055 p.recycle(); 2056 } 2057 2058 SpannableString spannableString = new SpannableString("Spannable String"); 2059 int urlSpanStart = spannableString.length() >> 1; 2060 int urlSpanEnd = spannableString.length(); 2061 p = Parcel.obtain(); 2062 try { 2063 URLSpan urlSpan = new URLSpan("URL Span"); 2064 spannableString.setSpan(urlSpan, urlSpanStart, urlSpanEnd, 2065 Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2066 TextUtils.writeToParcel(spannableString, p, 0); 2067 p.setDataPosition(0); 2068 SpannableString ret = (SpannableString) creator.createFromParcel(p); 2069 assertEquals("Spannable String", ret.toString()); 2070 Object[] spans = ret.getSpans(0, ret.length(), Object.class); 2071 assertEquals(1, spans.length); 2072 assertEquals("URL Span", ((URLSpan) spans[0]).getURL()); 2073 assertEquals(urlSpanStart, ret.getSpanStart(spans[0])); 2074 assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0])); 2075 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0])); 2076 } finally { 2077 p.recycle(); 2078 } 2079 2080 p = Parcel.obtain(); 2081 try { 2082 ColorStateList colors = new ColorStateList(new int[][] { 2083 new int[] {android.R.attr.state_focused}, new int[0]}, 2084 new int[] {Color.rgb(0, 255, 0), Color.BLACK}); 2085 int textSize = 20; 2086 TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan( 2087 null, Typeface.ITALIC, textSize, colors, null); 2088 int textAppearanceSpanStart = 0; 2089 int textAppearanceSpanEnd = spannableString.length() >> 1; 2090 spannableString.setSpan(textAppearanceSpan, textAppearanceSpanStart, 2091 textAppearanceSpanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 2092 TextUtils.writeToParcel(spannableString, p, -1); 2093 p.setDataPosition(0); 2094 SpannableString ret = (SpannableString) creator.createFromParcel(p); 2095 assertEquals("Spannable String", ret.toString()); 2096 Object[] spans = ret.getSpans(0, ret.length(), Object.class); 2097 assertEquals(2, spans.length); 2098 assertEquals("URL Span", ((URLSpan) spans[0]).getURL()); 2099 assertEquals(urlSpanStart, ret.getSpanStart(spans[0])); 2100 assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0])); 2101 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0])); 2102 assertEquals(null, ((TextAppearanceSpan) spans[1]).getFamily()); 2103 2104 assertEquals(Typeface.ITALIC, ((TextAppearanceSpan) spans[1]).getTextStyle()); 2105 assertEquals(textSize, ((TextAppearanceSpan) spans[1]).getTextSize()); 2106 2107 assertEquals(colors.toString(), ((TextAppearanceSpan) spans[1]).getTextColor().toString()); 2108 assertEquals(null, ((TextAppearanceSpan) spans[1]).getLinkTextColor()); 2109 assertEquals(textAppearanceSpanStart, ret.getSpanStart(spans[1])); 2110 assertEquals(textAppearanceSpanEnd, ret.getSpanEnd(spans[1])); 2111 assertEquals(Spanned.SPAN_INCLUSIVE_EXCLUSIVE, ret.getSpanFlags(spans[1])); 2112 } finally { 2113 p.recycle(); 2114 } 2115 2116 try { 2117 TextUtils.writeToParcel(spannableString, null, 0); 2118 fail("Should throw NullPointerException!"); 2119 } catch (NullPointerException e) { 2120 // expected 2121 } 2122 } 2123 2124 @Test testGetCapsMode()2125 public void testGetCapsMode() { 2126 final int CAP_MODE_ALL = TextUtils.CAP_MODE_CHARACTERS 2127 | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES; 2128 final int CAP_MODE_CHARACTERS_AND_WORD = 2129 TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS; 2130 String testString = "Start. Sentence word!No space before\n\t" + 2131 "Paragraph? (\"\'skip begin\'\"). skip end"; 2132 2133 // CAP_MODE_SENTENCES should be in effect in the whole text. 2134 for (int i = 0; i < testString.length(); i++) { 2135 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2136 TextUtils.getCapsMode(testString, i, TextUtils.CAP_MODE_CHARACTERS)); 2137 } 2138 2139 // all modes should be in effect at the start of the text. 2140 assertEquals(TextUtils.CAP_MODE_WORDS, 2141 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_WORDS)); 2142 // issue 1586346 2143 assertEquals(TextUtils.CAP_MODE_WORDS, 2144 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_SENTENCES)); 2145 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2146 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL)); 2147 2148 // all mode should be in effect at the position after "." or "?" or "!" + " ". 2149 int offset = testString.indexOf("Sentence word!"); 2150 assertEquals(TextUtils.CAP_MODE_WORDS, 2151 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2152 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2153 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2154 // issue 1586346 2155 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2156 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL)); 2157 2158 // CAP_MODE_SENTENCES should NOT be in effect at the position after other words + " ". 2159 offset = testString.indexOf("word!"); 2160 assertEquals(TextUtils.CAP_MODE_WORDS, 2161 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2162 assertEquals(0, 2163 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2164 // issue 1586346 2165 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2166 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2167 2168 // if no space after "." or "?" or "!", CAP_MODE_SENTENCES and CAP_MODE_WORDS 2169 // should NOT be in effect. 2170 offset = testString.indexOf("No space before"); 2171 assertEquals(0, 2172 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2173 assertEquals(0, 2174 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2175 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2176 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2177 2178 // all mode should be in effect at a beginning of a new paragraph. 2179 offset = testString.indexOf("Paragraph"); 2180 assertEquals(TextUtils.CAP_MODE_WORDS, 2181 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2182 // issue 1586346 2183 assertEquals(TextUtils.CAP_MODE_WORDS, 2184 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2185 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2186 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2187 2188 // some special word which means the start of a sentence should be skipped. 2189 offset = testString.indexOf("skip begin"); 2190 assertEquals(TextUtils.CAP_MODE_WORDS, 2191 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2192 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2193 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2194 // issue 1586346 2195 assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS, 2196 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2197 2198 // some special word which means the end of a sentence should be skipped. 2199 offset = testString.indexOf("skip end"); 2200 assertEquals(TextUtils.CAP_MODE_WORDS, 2201 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2202 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2203 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2204 // issue 1586346 2205 assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS, 2206 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2207 } 2208 2209 @Test testGetCapsModeException()2210 public void testGetCapsModeException() { 2211 String testString = "Start. Sentence word!No space before\n\t" + 2212 "Paragraph? (\"\'skip begin\'\"). skip end"; 2213 2214 int offset = testString.indexOf("Sentence word!"); 2215 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2216 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_CHARACTERS)); 2217 2218 try { 2219 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_SENTENCES); 2220 fail("Should throw NullPointerException!"); 2221 } catch (NullPointerException e) { 2222 // expected 2223 } 2224 2225 assertEquals(0, TextUtils.getCapsMode(testString, -1, TextUtils.CAP_MODE_SENTENCES)); 2226 2227 try { 2228 TextUtils.getCapsMode(testString, testString.length() + 1, 2229 TextUtils.CAP_MODE_SENTENCES); 2230 fail("Should throw IndexOutOfBoundsException!"); 2231 } catch (IndexOutOfBoundsException e) { 2232 // expected 2233 } 2234 } 2235 2236 @Test testDumpSpans()2237 public void testDumpSpans() { 2238 StringBuilder builder = new StringBuilder(); 2239 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 2240 CharSequence source = "test dump spans"; 2241 String prefix = "prefix"; 2242 2243 assertEquals(0, builder.length()); 2244 TextUtils.dumpSpans(source, printer, prefix); 2245 assertTrue(builder.length() > 0); 2246 2247 builder = new StringBuilder(); 2248 printer = new StringBuilderPrinter(builder); 2249 assertEquals(0, builder.length()); 2250 SpannableString spanned = new SpannableString(source); 2251 spanned.setSpan(new Object(), 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2252 TextUtils.dumpSpans(spanned, printer, prefix); 2253 assertTrue(builder.length() > 0); 2254 } 2255 2256 @Test testGetLayoutDirectionFromLocale()2257 public void testGetLayoutDirectionFromLocale() { 2258 assertEquals(LAYOUT_DIRECTION_LTR, 2259 TextUtils.getLayoutDirectionFromLocale(null)); 2260 2261 assertEquals(LAYOUT_DIRECTION_LTR, 2262 TextUtils.getLayoutDirectionFromLocale(Locale.ENGLISH)); 2263 assertEquals(LAYOUT_DIRECTION_LTR, 2264 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA)); 2265 assertEquals(LAYOUT_DIRECTION_LTR, 2266 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH)); 2267 assertEquals(LAYOUT_DIRECTION_LTR, 2268 TextUtils.getLayoutDirectionFromLocale(Locale.FRANCE)); 2269 assertEquals(LAYOUT_DIRECTION_LTR, 2270 TextUtils.getLayoutDirectionFromLocale(Locale.FRENCH)); 2271 assertEquals(LAYOUT_DIRECTION_LTR, 2272 TextUtils.getLayoutDirectionFromLocale(Locale.GERMAN)); 2273 assertEquals(LAYOUT_DIRECTION_LTR, 2274 TextUtils.getLayoutDirectionFromLocale(Locale.GERMANY)); 2275 assertEquals(LAYOUT_DIRECTION_LTR, 2276 TextUtils.getLayoutDirectionFromLocale(Locale.ITALIAN)); 2277 assertEquals(LAYOUT_DIRECTION_LTR, 2278 TextUtils.getLayoutDirectionFromLocale(Locale.ITALY)); 2279 assertEquals(LAYOUT_DIRECTION_LTR, 2280 TextUtils.getLayoutDirectionFromLocale(Locale.UK)); 2281 assertEquals(LAYOUT_DIRECTION_LTR, 2282 TextUtils.getLayoutDirectionFromLocale(Locale.US)); 2283 2284 assertEquals(LAYOUT_DIRECTION_LTR, 2285 TextUtils.getLayoutDirectionFromLocale(Locale.ROOT)); 2286 2287 assertEquals(LAYOUT_DIRECTION_LTR, 2288 TextUtils.getLayoutDirectionFromLocale(Locale.CHINA)); 2289 assertEquals(LAYOUT_DIRECTION_LTR, 2290 TextUtils.getLayoutDirectionFromLocale(Locale.CHINESE)); 2291 assertEquals(LAYOUT_DIRECTION_LTR, 2292 TextUtils.getLayoutDirectionFromLocale(Locale.JAPAN)); 2293 assertEquals(LAYOUT_DIRECTION_LTR, 2294 TextUtils.getLayoutDirectionFromLocale(Locale.JAPANESE)); 2295 assertEquals(LAYOUT_DIRECTION_LTR, 2296 TextUtils.getLayoutDirectionFromLocale(Locale.KOREA)); 2297 assertEquals(LAYOUT_DIRECTION_LTR, 2298 TextUtils.getLayoutDirectionFromLocale(Locale.KOREAN)); 2299 assertEquals(LAYOUT_DIRECTION_LTR, 2300 TextUtils.getLayoutDirectionFromLocale(Locale.PRC)); 2301 assertEquals(LAYOUT_DIRECTION_LTR, 2302 TextUtils.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE)); 2303 assertEquals(LAYOUT_DIRECTION_LTR, 2304 TextUtils.getLayoutDirectionFromLocale(Locale.TAIWAN)); 2305 assertEquals(LAYOUT_DIRECTION_LTR, 2306 TextUtils.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE)); 2307 2308 // Some languages always use an RTL script. 2309 for (Locale l : Locale.getAvailableLocales()) { 2310 String languageCode = l.getLanguage(); 2311 if (languageCode.equals("ar") || 2312 languageCode.equals("fa") || 2313 languageCode.equals("iw") || 2314 languageCode.equals("he") || 2315 languageCode.equals("ps") || 2316 languageCode.equals("ur")) { 2317 int direction = TextUtils.getLayoutDirectionFromLocale(l); 2318 assertEquals(l.toLanguageTag() + " not RTL: " + direction, 2319 LAYOUT_DIRECTION_RTL, direction); 2320 } 2321 } 2322 2323 // Other languages have some cases where they use an RTL script. 2324 String[] tags = { 2325 "pa-Arab", 2326 "pa-Arab-PK", 2327 "ps", 2328 "ps-AF", 2329 "uz-Arab", 2330 "uz-Arab-AF", 2331 }; 2332 for (String tag : tags) { 2333 Locale l = Locale.forLanguageTag(tag); 2334 int direction = TextUtils.getLayoutDirectionFromLocale(l); 2335 assertEquals(l.toLanguageTag() + " not RTL: " + direction, 2336 LAYOUT_DIRECTION_RTL, direction); 2337 } 2338 2339 // Locale without a real language 2340 Locale locale = Locale.forLanguageTag("zz"); 2341 assertEquals(LAYOUT_DIRECTION_LTR, 2342 TextUtils.getLayoutDirectionFromLocale(locale)); 2343 } 2344 } 2345