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) 1577 @SuppressWarnings("NullArgumentForNonNullParameter") testIsGraphicCharNull()1578 public void testIsGraphicCharNull() { 1579 assertFalse(TextUtils.isGraphic((Character) null)); 1580 } 1581 1582 @Test testIsGraphicCharSequence()1583 public void testIsGraphicCharSequence() { 1584 assertTrue(TextUtils.isGraphic("printable characters")); 1585 1586 assertFalse(TextUtils.isGraphic("\u2028\u2029\u0085\uFFFF\uD800\u0020")); 1587 1588 assertTrue(TextUtils.isGraphic("a\u2028\u2029\u0085\uFFFF\uD800\u0020")); 1589 1590 assertTrue(TextUtils.isGraphic("\uD83D\uDC0C")); // U+1F40C SNAIL 1591 assertFalse(TextUtils.isGraphic("\uDB40\uDC01")); // U+E0000 (unassigned) 1592 assertFalse(TextUtils.isGraphic("\uDB3D")); // unpaired high surrogate 1593 assertFalse(TextUtils.isGraphic("\uDC0C")); // unpaired low surrogate 1594 } 1595 1596 @Test(expected=NullPointerException.class) testIsGraphicCharSequenceNull()1597 public void testIsGraphicCharSequenceNull() { 1598 TextUtils.isGraphic(null); 1599 } 1600 1601 @Test testJoinIterable()1602 public void testJoinIterable() { 1603 ArrayList<CharSequence> charTokens = new ArrayList<>(); 1604 charTokens.add("string1"); 1605 charTokens.add("string2"); 1606 charTokens.add("string3"); 1607 assertEquals("string1|string2|string3", TextUtils.join("|", charTokens)); 1608 assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens)); 1609 assertEquals("string1string2string3", TextUtils.join("", charTokens)); 1610 1611 // issue 1695243, not clear what is supposed result if the delimiter or tokens are null. 1612 assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens)); 1613 1614 ArrayList<SpannableString> spannableStringTokens = new ArrayList<SpannableString>(); 1615 spannableStringTokens.add(new SpannableString("span 1")); 1616 spannableStringTokens.add(new SpannableString("span 2")); 1617 spannableStringTokens.add(new SpannableString("span 3")); 1618 assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens)); 1619 1620 assertEquals("", TextUtils.join("|", new ArrayList<CharSequence>())); 1621 } 1622 1623 @Test(expected=NullPointerException.class) testJoinIterableNull()1624 public void testJoinIterableNull() { 1625 TextUtils.join("|", (Iterable) null); 1626 } 1627 1628 @Test testJoinArray()1629 public void testJoinArray() { 1630 CharSequence[] charTokens = new CharSequence[] { "string1", "string2", "string3" }; 1631 assertEquals("string1|string2|string3", TextUtils.join("|", charTokens)); 1632 assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens)); 1633 assertEquals("string1string2string3", TextUtils.join("", charTokens)); 1634 1635 // issue 1695243, not clear what is supposed result if the delimiter or tokens are null. 1636 assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens)); 1637 1638 SpannableString[] spannableStringTokens = new SpannableString[] { 1639 new SpannableString("span 1"), 1640 new SpannableString("span 2"), 1641 new SpannableString("span 3") }; 1642 assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens)); 1643 1644 assertEquals("", TextUtils.join("|", new String[0])); 1645 } 1646 1647 @Test(expected=NullPointerException.class) testJoinArrayNull()1648 public void testJoinArrayNull() { 1649 TextUtils.join("|", (Object[]) null); 1650 } 1651 1652 @Test testLastIndexOf1()1653 public void testLastIndexOf1() { 1654 String searchString = "string to be searched"; 1655 final int INDEX_OF_LAST_R = 16; 1656 final int INDEX_OF_LAST_T = 7; 1657 final int INDEX_OF_LAST_D = searchString.length() - 1; 1658 1659 assertEquals(INDEX_OF_LAST_T, TextUtils.lastIndexOf(searchString, 't')); 1660 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(searchString, 'r')); 1661 assertEquals(INDEX_OF_LAST_D, TextUtils.lastIndexOf(searchString, 'd')); 1662 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'f')); 1663 1664 StringBuffer stringBuffer = new StringBuffer(searchString); 1665 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuffer, 'r')); 1666 1667 StringBuilder stringBuilder = new StringBuilder(searchString); 1668 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuilder, 'r')); 1669 1670 MockGetChars mockGetChars = new MockGetChars(); 1671 TextUtils.lastIndexOf(mockGetChars, 'r'); 1672 assertTrue(mockGetChars.hasCalledGetChars()); 1673 1674 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1675 assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(mockCharSequence, 'r')); 1676 } 1677 1678 @Test testLastIndexOf2()1679 public void testLastIndexOf2() { 1680 String searchString = "string to be searched"; 1681 final int INDEX_OF_FIRST_R = 2; 1682 final int INDEX_OF_SECOND_R = 16; 1683 1684 assertEquals(INDEX_OF_SECOND_R, 1685 TextUtils.lastIndexOf(searchString, 'r', searchString.length())); 1686 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0)); 1687 assertEquals(INDEX_OF_FIRST_R, 1688 TextUtils.lastIndexOf(searchString, 'r', INDEX_OF_FIRST_R)); 1689 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE)); 1690 assertEquals(INDEX_OF_SECOND_R, 1691 TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE)); 1692 1693 StringBuffer stringBuffer = new StringBuffer(searchString); 1694 assertEquals(INDEX_OF_FIRST_R, 1695 TextUtils.lastIndexOf(stringBuffer, 'r', INDEX_OF_FIRST_R)); 1696 assertEquals(-1, TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MIN_VALUE)); 1697 assertEquals(INDEX_OF_SECOND_R, 1698 TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MAX_VALUE)); 1699 1700 StringBuilder stringBuilder = new StringBuilder(searchString); 1701 assertEquals(INDEX_OF_FIRST_R, 1702 TextUtils.lastIndexOf(stringBuilder, 'r', INDEX_OF_FIRST_R)); 1703 1704 MockGetChars mockGetChars = new MockGetChars(); 1705 TextUtils.lastIndexOf(mockGetChars, 'r', INDEX_OF_FIRST_R); 1706 assertTrue(mockGetChars.hasCalledGetChars()); 1707 1708 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1709 assertEquals(INDEX_OF_FIRST_R, 1710 TextUtils.lastIndexOf(mockCharSequence, 'r', INDEX_OF_FIRST_R)); 1711 } 1712 1713 @Test testLastIndexOf3()1714 public void testLastIndexOf3() { 1715 String searchString = "string to be searched"; 1716 final int INDEX_OF_FIRST_R = 2; 1717 final int INDEX_OF_SECOND_R = 16; 1718 1719 assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1720 searchString.length())); 1721 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1722 INDEX_OF_SECOND_R - 1)); 1723 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, INDEX_OF_FIRST_R - 1)); 1724 1725 try { 1726 TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R - 1); 1727 fail("Should throw IndexOutOfBoundsException!"); 1728 } catch (IndexOutOfBoundsException e) { 1729 // expect 1730 } 1731 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE, 1732 INDEX_OF_SECOND_R - 1)); 1733 assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, Integer.MIN_VALUE)); 1734 assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0, 1735 Integer.MAX_VALUE)); 1736 1737 StringBuffer stringBuffer = new StringBuffer(searchString); 1738 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuffer, 'r', 0, 1739 INDEX_OF_SECOND_R - 1)); 1740 1741 StringBuilder stringBuilder = new StringBuilder(searchString); 1742 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuilder, 'r', 0, 1743 INDEX_OF_SECOND_R - 1)); 1744 1745 MockGetChars mockGetChars = new MockGetChars(); 1746 TextUtils.lastIndexOf(mockGetChars, 'r', 0, INDEX_OF_SECOND_R - 1); 1747 assertTrue(mockGetChars.hasCalledGetChars()); 1748 1749 MockCharSequence mockCharSequence = new MockCharSequence(searchString); 1750 assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(mockCharSequence, 'r', 0, 1751 INDEX_OF_SECOND_R - 1)); 1752 } 1753 1754 @Test testRegionMatches()1755 public void testRegionMatches() { 1756 assertFalse(TextUtils.regionMatches("one", 0, "two", 0, "one".length())); 1757 assertTrue(TextUtils.regionMatches("one", 0, "one", 0, "one".length())); 1758 try { 1759 TextUtils.regionMatches("one", 0, "one", 0, "one".length() + 1); 1760 fail("Should throw IndexOutOfBoundsException!"); 1761 } catch (IndexOutOfBoundsException e) { 1762 } 1763 1764 String one = "Hello Android, hello World!"; 1765 String two = "Hello World"; 1766 // match "Hello" 1767 assertTrue(TextUtils.regionMatches(one, 0, two, 0, "Hello".length())); 1768 1769 // match "Hello A" and "Hello W" 1770 assertFalse(TextUtils.regionMatches(one, 0, two, 0, "Hello A".length())); 1771 1772 // match "World" 1773 assertTrue(TextUtils.regionMatches(one, "Hello Android, hello ".length(), 1774 two, "Hello ".length(), "World".length())); 1775 assertFalse(TextUtils.regionMatches(one, "Hello Android, hello ".length(), 1776 two, 0, "World".length())); 1777 1778 try { 1779 TextUtils.regionMatches(one, Integer.MIN_VALUE, two, 0, "Hello".length()); 1780 fail("Should throw IndexOutOfBoundsException!"); 1781 } catch (IndexOutOfBoundsException e) { 1782 } 1783 try { 1784 TextUtils.regionMatches(one, Integer.MAX_VALUE, two, 0, "Hello".length()); 1785 fail("Should throw IndexOutOfBoundsException!"); 1786 } catch (IndexOutOfBoundsException e) { 1787 } 1788 1789 try { 1790 TextUtils.regionMatches(one, 0, two, Integer.MIN_VALUE, "Hello".length()); 1791 fail("Should throw IndexOutOfBoundsException!"); 1792 } catch (IndexOutOfBoundsException e) { 1793 } 1794 try { 1795 TextUtils.regionMatches(one, 0, two, Integer.MAX_VALUE, "Hello".length()); 1796 fail("Should throw IndexOutOfBoundsException!"); 1797 } catch (IndexOutOfBoundsException e) { 1798 } 1799 1800 try { 1801 TextUtils.regionMatches(one, 0, two, 0, Integer.MIN_VALUE); 1802 fail("Should throw IndexOutOfBoundsException!"); 1803 } catch (IndexOutOfBoundsException e) { 1804 } 1805 try { 1806 TextUtils.regionMatches(one, 0, two, 0, Integer.MAX_VALUE); 1807 fail("Should throw IndexOutOfBoundsException!"); 1808 } catch (IndexOutOfBoundsException e) { 1809 } 1810 1811 try { 1812 TextUtils.regionMatches(null, 0, two, 0, "Hello".length()); 1813 fail("Should throw NullPointerException!"); 1814 } catch (NullPointerException e) { 1815 // expect 1816 } 1817 try { 1818 TextUtils.regionMatches(one, 0, null, 0, "Hello".length()); 1819 fail("Should throw NullPointerException!"); 1820 } catch (NullPointerException e) { 1821 // expect 1822 } 1823 } 1824 1825 @Test testReplace()1826 public void testReplace() { 1827 String template = "this is a string to be as the template for replacement"; 1828 1829 String sources[] = new String[] { "string" }; 1830 CharSequence destinations[] = new CharSequence[] { "text" }; 1831 SpannableStringBuilder replacedString = (SpannableStringBuilder) TextUtils.replace(template, 1832 sources, destinations); 1833 assertEquals("this is a text to be as the template for replacement", 1834 replacedString.toString()); 1835 1836 sources = new String[] {"is", "the", "for replacement"}; 1837 destinations = new CharSequence[] {"was", "", "to be replaced"}; 1838 replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations); 1839 assertEquals("thwas is a string to be as template to be replaced", 1840 replacedString.toString()); 1841 1842 sources = new String[] {"is", "for replacement"}; 1843 destinations = new CharSequence[] {"was", "", "to be replaced"}; 1844 replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations); 1845 assertEquals("thwas is a string to be as the template ", replacedString.toString()); 1846 1847 sources = new String[] {"is", "the", "for replacement"}; 1848 destinations = new CharSequence[] {"was", "to be replaced"}; 1849 try { 1850 TextUtils.replace(template, sources, destinations); 1851 fail("Should throw ArrayIndexOutOfBoundsException!"); 1852 } catch (ArrayIndexOutOfBoundsException e) { 1853 // expected 1854 } 1855 1856 try { 1857 TextUtils.replace(null, sources, destinations); 1858 fail("Should throw NullPointerException!"); 1859 } catch (NullPointerException e) { 1860 // expected 1861 } 1862 try { 1863 TextUtils.replace(template, null, destinations); 1864 fail("Should throw NullPointerException!"); 1865 } catch (NullPointerException e) { 1866 // expected 1867 } 1868 try { 1869 TextUtils.replace(template, sources, null); 1870 fail("Should throw NullPointerException!"); 1871 } catch (NullPointerException e) { 1872 // expected 1873 } 1874 } 1875 1876 @Test testSplitPattern()1877 public void testSplitPattern() { 1878 assertEquals(0, TextUtils.split("", Pattern.compile("")).length); 1879 assertEquals(0, TextUtils.split("", Pattern.compile("not found")).length); 1880 1881 String testString = "abccbadecdebz"; 1882 assertEquals(calculateCharsCount(testString, "c") + 1, 1883 TextUtils.split(testString, Pattern.compile("c")).length); 1884 assertEquals(calculateCharsCount(testString, "a") + 1, 1885 TextUtils.split(testString, Pattern.compile("a")).length); 1886 assertEquals(calculateCharsCount(testString, "z") + 1, 1887 TextUtils.split(testString, Pattern.compile("z")).length); 1888 assertEquals(calculateCharsCount(testString, "de") + 1, 1889 TextUtils.split(testString, Pattern.compile("de")).length); 1890 int totalCount = 1 + calculateCharsCount(testString, "a") 1891 + calculateCharsCount(testString, "b") + calculateCharsCount(testString, "c"); 1892 assertEquals(totalCount, 1893 TextUtils.split(testString, Pattern.compile("[a-c]")).length); 1894 assertEquals(0, TextUtils.split("", Pattern.compile("a")).length); 1895 // issue 1695243, not clear what is supposed result if the pattern string is empty. 1896 assertEquals( 1897 Arrays.asList("a", "b", "c", "c", "b", "a", "d", "e", "c", "d", "e", "b", "z", ""), 1898 Arrays.asList(TextUtils.split(testString, Pattern.compile("")))); 1899 } 1900 1901 @Test(expected=NullPointerException.class) testSplitPatternNullText()1902 public void testSplitPatternNullText() { 1903 TextUtils.split(null, Pattern.compile("a")); 1904 } 1905 1906 @Test(expected=NullPointerException.class) testSplitPatternNullPattern()1907 public void testSplitPatternNullPattern() { 1908 TextUtils.split("abccbadecdebz", (Pattern) null); 1909 } 1910 1911 /* 1912 * return the appearance count of searched chars in text. 1913 */ calculateCharsCount(CharSequence text, CharSequence searches)1914 private static int calculateCharsCount(CharSequence text, CharSequence searches) { 1915 int count = 0; 1916 int start = TextUtils.indexOf(text, searches, 0); 1917 1918 while (start != -1) { 1919 count++; 1920 start = TextUtils.indexOf(text, searches, start + 1); 1921 } 1922 return count; 1923 } 1924 1925 @Test testSplitString()1926 public void testSplitString() { 1927 assertEquals(0, TextUtils.split("", "").length); 1928 assertEquals(0, TextUtils.split("", "not found").length); 1929 1930 // The case mentioned in the documentation. 1931 assertEquals(Arrays.asList("a", ""), Arrays.asList(TextUtils.split("a,", ","))); 1932 1933 String testString = "abccbadecdebz"; 1934 assertEquals(calculateCharsCount(testString, "c") + 1, 1935 TextUtils.split("abccbadecdebz", "c").length); 1936 assertEquals(calculateCharsCount(testString, "a") + 1, 1937 TextUtils.split("abccbadecdebz", "a").length); 1938 assertEquals(calculateCharsCount(testString, "z") + 1, 1939 TextUtils.split("abccbadecdebz", "z").length); 1940 assertEquals(calculateCharsCount(testString, "de") + 1, 1941 TextUtils.split("abccbadecdebz", "de").length); 1942 assertEquals(0, TextUtils.split("", "a").length); 1943 // issue 1695243, not clear what is supposed result if the pattern string is empty. 1944 assertEquals( 1945 Arrays.asList("a", "b", "c", "c", "b", "a", "d", "e", "c", "d", "e", "b", "z", ""), 1946 Arrays.asList(TextUtils.split("abccbadecdebz", ""))); 1947 } 1948 1949 @Test(expected=NullPointerException.class) testSplitStringNullText()1950 public void testSplitStringNullText() { 1951 TextUtils.split(null, "a"); 1952 } 1953 1954 @Test(expected=NullPointerException.class) testSplitStringNullPattern()1955 public void testSplitStringNullPattern() { 1956 TextUtils.split("abccbadecdebz", (String) null); 1957 } 1958 1959 @Test testStringOrSpannedString()1960 public void testStringOrSpannedString() { 1961 assertNull(TextUtils.stringOrSpannedString(null)); 1962 1963 SpannedString spannedString = new SpannedString("Spanned String"); 1964 assertSame(spannedString, TextUtils.stringOrSpannedString(spannedString)); 1965 1966 SpannableString spannableString = new SpannableString("Spannable String"); 1967 assertEquals("Spannable String", 1968 TextUtils.stringOrSpannedString(spannableString).toString()); 1969 assertEquals(SpannedString.class, 1970 TextUtils.stringOrSpannedString(spannableString).getClass()); 1971 1972 StringBuffer stringBuffer = new StringBuffer("String Buffer"); 1973 assertEquals("String Buffer", 1974 TextUtils.stringOrSpannedString(stringBuffer).toString()); 1975 assertEquals(String.class, 1976 TextUtils.stringOrSpannedString(stringBuffer).getClass()); 1977 } 1978 1979 @Test testSubString()1980 public void testSubString() { 1981 String string = "String"; 1982 assertSame(string, TextUtils.substring(string, 0, string.length())); 1983 assertEquals("Strin", TextUtils.substring(string, 0, string.length() - 1)); 1984 assertEquals("", TextUtils.substring(string, 1, 1)); 1985 1986 try { 1987 TextUtils.substring(string, string.length(), 0); 1988 fail("Should throw IndexOutOfBoundsException!"); 1989 } catch (IndexOutOfBoundsException e) { 1990 // expected 1991 } 1992 1993 try { 1994 TextUtils.substring(string, -1, string.length()); 1995 fail("Should throw IndexOutOfBoundsException!"); 1996 } catch (IndexOutOfBoundsException e) { 1997 // expected 1998 } 1999 2000 try { 2001 TextUtils.substring(string, Integer.MAX_VALUE, string.length()); 2002 fail("Should throw IndexOutOfBoundsException!"); 2003 } catch (IndexOutOfBoundsException e) { 2004 // expected 2005 } 2006 2007 try { 2008 TextUtils.substring(string, 0, -1); 2009 fail("Should throw IndexOutOfBoundsException!"); 2010 } catch (IndexOutOfBoundsException e) { 2011 // expected 2012 } 2013 2014 try { 2015 TextUtils.substring(string, 0, Integer.MAX_VALUE); 2016 fail("Should throw IndexOutOfBoundsException!"); 2017 } catch (IndexOutOfBoundsException e) { 2018 // expected 2019 } 2020 2021 try { 2022 TextUtils.substring(null, 0, string.length()); 2023 fail("Should throw NullPointerException!"); 2024 } catch (NullPointerException e) { 2025 // expected 2026 } 2027 2028 StringBuffer stringBuffer = new StringBuffer("String Buffer"); 2029 assertEquals("Strin", TextUtils.substring(stringBuffer, 0, string.length() - 1)); 2030 assertEquals("", TextUtils.substring(stringBuffer, 1, 1)); 2031 2032 MockGetChars mockGetChars = new MockGetChars(); 2033 TextUtils.substring(mockGetChars, 0, string.length()); 2034 assertTrue(mockGetChars.hasCalledGetChars()); 2035 } 2036 2037 @Test testWriteToParcel()2038 public void testWriteToParcel() { 2039 Parcelable.Creator<CharSequence> creator = TextUtils.CHAR_SEQUENCE_CREATOR; 2040 String string = "String"; 2041 Parcel p = Parcel.obtain(); 2042 try { 2043 TextUtils.writeToParcel(string, p, 0); 2044 p.setDataPosition(0); 2045 assertEquals(string, creator.createFromParcel(p).toString()); 2046 } finally { 2047 p.recycle(); 2048 } 2049 2050 p = Parcel.obtain(); 2051 try { 2052 TextUtils.writeToParcel(null, p, 0); 2053 p.setDataPosition(0); 2054 assertNull(creator.createFromParcel(p)); 2055 } finally { 2056 p.recycle(); 2057 } 2058 2059 SpannableString spannableString = new SpannableString("Spannable String"); 2060 int urlSpanStart = spannableString.length() >> 1; 2061 int urlSpanEnd = spannableString.length(); 2062 p = Parcel.obtain(); 2063 try { 2064 URLSpan urlSpan = new URLSpan("URL Span"); 2065 spannableString.setSpan(urlSpan, urlSpanStart, urlSpanEnd, 2066 Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2067 TextUtils.writeToParcel(spannableString, p, 0); 2068 p.setDataPosition(0); 2069 SpannableString ret = (SpannableString) creator.createFromParcel(p); 2070 assertEquals("Spannable String", ret.toString()); 2071 Object[] spans = ret.getSpans(0, ret.length(), Object.class); 2072 assertEquals(1, spans.length); 2073 assertEquals("URL Span", ((URLSpan) spans[0]).getURL()); 2074 assertEquals(urlSpanStart, ret.getSpanStart(spans[0])); 2075 assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0])); 2076 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0])); 2077 } finally { 2078 p.recycle(); 2079 } 2080 2081 p = Parcel.obtain(); 2082 try { 2083 ColorStateList colors = new ColorStateList(new int[][] { 2084 new int[] {android.R.attr.state_focused}, new int[0]}, 2085 new int[] {Color.rgb(0, 255, 0), Color.BLACK}); 2086 int textSize = 20; 2087 TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan( 2088 null, Typeface.ITALIC, textSize, colors, null); 2089 int textAppearanceSpanStart = 0; 2090 int textAppearanceSpanEnd = spannableString.length() >> 1; 2091 spannableString.setSpan(textAppearanceSpan, textAppearanceSpanStart, 2092 textAppearanceSpanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); 2093 TextUtils.writeToParcel(spannableString, p, -1); 2094 p.setDataPosition(0); 2095 SpannableString ret = (SpannableString) creator.createFromParcel(p); 2096 assertEquals("Spannable String", ret.toString()); 2097 Object[] spans = ret.getSpans(0, ret.length(), Object.class); 2098 assertEquals(2, spans.length); 2099 assertEquals("URL Span", ((URLSpan) spans[0]).getURL()); 2100 assertEquals(urlSpanStart, ret.getSpanStart(spans[0])); 2101 assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0])); 2102 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0])); 2103 assertEquals(null, ((TextAppearanceSpan) spans[1]).getFamily()); 2104 2105 assertEquals(Typeface.ITALIC, ((TextAppearanceSpan) spans[1]).getTextStyle()); 2106 assertEquals(textSize, ((TextAppearanceSpan) spans[1]).getTextSize()); 2107 2108 assertEquals(colors.toString(), ((TextAppearanceSpan) spans[1]).getTextColor().toString()); 2109 assertEquals(null, ((TextAppearanceSpan) spans[1]).getLinkTextColor()); 2110 assertEquals(textAppearanceSpanStart, ret.getSpanStart(spans[1])); 2111 assertEquals(textAppearanceSpanEnd, ret.getSpanEnd(spans[1])); 2112 assertEquals(Spanned.SPAN_INCLUSIVE_EXCLUSIVE, ret.getSpanFlags(spans[1])); 2113 } finally { 2114 p.recycle(); 2115 } 2116 2117 try { 2118 TextUtils.writeToParcel(spannableString, null, 0); 2119 fail("Should throw NullPointerException!"); 2120 } catch (NullPointerException e) { 2121 // expected 2122 } 2123 } 2124 2125 @Test testGetCapsMode()2126 public void testGetCapsMode() { 2127 final int CAP_MODE_ALL = TextUtils.CAP_MODE_CHARACTERS 2128 | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES; 2129 final int CAP_MODE_CHARACTERS_AND_WORD = 2130 TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS; 2131 String testString = "Start. Sentence word!No space before\n\t" + 2132 "Paragraph? (\"\'skip begin\'\"). skip end"; 2133 2134 // CAP_MODE_SENTENCES should be in effect in the whole text. 2135 for (int i = 0; i < testString.length(); i++) { 2136 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2137 TextUtils.getCapsMode(testString, i, TextUtils.CAP_MODE_CHARACTERS)); 2138 } 2139 2140 // all modes should be in effect at the start of the text. 2141 assertEquals(TextUtils.CAP_MODE_WORDS, 2142 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_WORDS)); 2143 // issue 1586346 2144 assertEquals(TextUtils.CAP_MODE_WORDS, 2145 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_SENTENCES)); 2146 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2147 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL)); 2148 2149 // all mode should be in effect at the position after "." or "?" or "!" + " ". 2150 int offset = testString.indexOf("Sentence word!"); 2151 assertEquals(TextUtils.CAP_MODE_WORDS, 2152 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2153 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2154 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2155 // issue 1586346 2156 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2157 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL)); 2158 2159 // CAP_MODE_SENTENCES should NOT be in effect at the position after other words + " ". 2160 offset = testString.indexOf("word!"); 2161 assertEquals(TextUtils.CAP_MODE_WORDS, 2162 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2163 assertEquals(0, 2164 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2165 // issue 1586346 2166 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2167 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2168 2169 // if no space after "." or "?" or "!", CAP_MODE_SENTENCES and CAP_MODE_WORDS 2170 // should NOT be in effect. 2171 offset = testString.indexOf("No space before"); 2172 assertEquals(0, 2173 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2174 assertEquals(0, 2175 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2176 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2177 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2178 2179 // all mode should be in effect at a beginning of a new paragraph. 2180 offset = testString.indexOf("Paragraph"); 2181 assertEquals(TextUtils.CAP_MODE_WORDS, 2182 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2183 // issue 1586346 2184 assertEquals(TextUtils.CAP_MODE_WORDS, 2185 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2186 assertEquals(CAP_MODE_CHARACTERS_AND_WORD, 2187 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2188 2189 // some special word which means the start of a sentence should be skipped. 2190 offset = testString.indexOf("skip begin"); 2191 assertEquals(TextUtils.CAP_MODE_WORDS, 2192 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2193 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2194 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2195 // issue 1586346 2196 assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS, 2197 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2198 2199 // some special word which means the end of a sentence should be skipped. 2200 offset = testString.indexOf("skip end"); 2201 assertEquals(TextUtils.CAP_MODE_WORDS, 2202 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS)); 2203 assertEquals(TextUtils.CAP_MODE_SENTENCES, 2204 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES)); 2205 // issue 1586346 2206 assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS, 2207 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL)); 2208 } 2209 2210 @Test testGetCapsModeException()2211 public void testGetCapsModeException() { 2212 String testString = "Start. Sentence word!No space before\n\t" + 2213 "Paragraph? (\"\'skip begin\'\"). skip end"; 2214 2215 int offset = testString.indexOf("Sentence word!"); 2216 assertEquals(TextUtils.CAP_MODE_CHARACTERS, 2217 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_CHARACTERS)); 2218 2219 try { 2220 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_SENTENCES); 2221 fail("Should throw NullPointerException!"); 2222 } catch (NullPointerException e) { 2223 // expected 2224 } 2225 2226 assertEquals(0, TextUtils.getCapsMode(testString, -1, TextUtils.CAP_MODE_SENTENCES)); 2227 2228 try { 2229 TextUtils.getCapsMode(testString, testString.length() + 1, 2230 TextUtils.CAP_MODE_SENTENCES); 2231 fail("Should throw IndexOutOfBoundsException!"); 2232 } catch (IndexOutOfBoundsException e) { 2233 // expected 2234 } 2235 } 2236 2237 @Test testDumpSpans()2238 public void testDumpSpans() { 2239 StringBuilder builder = new StringBuilder(); 2240 StringBuilderPrinter printer = new StringBuilderPrinter(builder); 2241 CharSequence source = "test dump spans"; 2242 String prefix = "prefix"; 2243 2244 assertEquals(0, builder.length()); 2245 TextUtils.dumpSpans(source, printer, prefix); 2246 assertTrue(builder.length() > 0); 2247 2248 builder = new StringBuilder(); 2249 printer = new StringBuilderPrinter(builder); 2250 assertEquals(0, builder.length()); 2251 SpannableString spanned = new SpannableString(source); 2252 spanned.setSpan(new Object(), 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2253 TextUtils.dumpSpans(spanned, printer, prefix); 2254 assertTrue(builder.length() > 0); 2255 } 2256 2257 @Test testGetLayoutDirectionFromLocale()2258 public void testGetLayoutDirectionFromLocale() { 2259 assertEquals(LAYOUT_DIRECTION_LTR, 2260 TextUtils.getLayoutDirectionFromLocale(null)); 2261 2262 assertEquals(LAYOUT_DIRECTION_LTR, 2263 TextUtils.getLayoutDirectionFromLocale(Locale.ENGLISH)); 2264 assertEquals(LAYOUT_DIRECTION_LTR, 2265 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA)); 2266 assertEquals(LAYOUT_DIRECTION_LTR, 2267 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH)); 2268 assertEquals(LAYOUT_DIRECTION_LTR, 2269 TextUtils.getLayoutDirectionFromLocale(Locale.FRANCE)); 2270 assertEquals(LAYOUT_DIRECTION_LTR, 2271 TextUtils.getLayoutDirectionFromLocale(Locale.FRENCH)); 2272 assertEquals(LAYOUT_DIRECTION_LTR, 2273 TextUtils.getLayoutDirectionFromLocale(Locale.GERMAN)); 2274 assertEquals(LAYOUT_DIRECTION_LTR, 2275 TextUtils.getLayoutDirectionFromLocale(Locale.GERMANY)); 2276 assertEquals(LAYOUT_DIRECTION_LTR, 2277 TextUtils.getLayoutDirectionFromLocale(Locale.ITALIAN)); 2278 assertEquals(LAYOUT_DIRECTION_LTR, 2279 TextUtils.getLayoutDirectionFromLocale(Locale.ITALY)); 2280 assertEquals(LAYOUT_DIRECTION_LTR, 2281 TextUtils.getLayoutDirectionFromLocale(Locale.UK)); 2282 assertEquals(LAYOUT_DIRECTION_LTR, 2283 TextUtils.getLayoutDirectionFromLocale(Locale.US)); 2284 2285 assertEquals(LAYOUT_DIRECTION_LTR, 2286 TextUtils.getLayoutDirectionFromLocale(Locale.ROOT)); 2287 2288 assertEquals(LAYOUT_DIRECTION_LTR, 2289 TextUtils.getLayoutDirectionFromLocale(Locale.CHINA)); 2290 assertEquals(LAYOUT_DIRECTION_LTR, 2291 TextUtils.getLayoutDirectionFromLocale(Locale.CHINESE)); 2292 assertEquals(LAYOUT_DIRECTION_LTR, 2293 TextUtils.getLayoutDirectionFromLocale(Locale.JAPAN)); 2294 assertEquals(LAYOUT_DIRECTION_LTR, 2295 TextUtils.getLayoutDirectionFromLocale(Locale.JAPANESE)); 2296 assertEquals(LAYOUT_DIRECTION_LTR, 2297 TextUtils.getLayoutDirectionFromLocale(Locale.KOREA)); 2298 assertEquals(LAYOUT_DIRECTION_LTR, 2299 TextUtils.getLayoutDirectionFromLocale(Locale.KOREAN)); 2300 assertEquals(LAYOUT_DIRECTION_LTR, 2301 TextUtils.getLayoutDirectionFromLocale(Locale.PRC)); 2302 assertEquals(LAYOUT_DIRECTION_LTR, 2303 TextUtils.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE)); 2304 assertEquals(LAYOUT_DIRECTION_LTR, 2305 TextUtils.getLayoutDirectionFromLocale(Locale.TAIWAN)); 2306 assertEquals(LAYOUT_DIRECTION_LTR, 2307 TextUtils.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE)); 2308 2309 // Some languages always use an RTL script. 2310 for (Locale l : Locale.getAvailableLocales()) { 2311 String languageCode = l.getLanguage(); 2312 if (languageCode.equals("ar") || 2313 languageCode.equals("fa") || 2314 languageCode.equals("iw") || 2315 languageCode.equals("he") || 2316 languageCode.equals("ps") || 2317 languageCode.equals("ur")) { 2318 int direction = TextUtils.getLayoutDirectionFromLocale(l); 2319 assertEquals(l.toLanguageTag() + " not RTL: " + direction, 2320 LAYOUT_DIRECTION_RTL, direction); 2321 } 2322 } 2323 2324 // Other languages have some cases where they use an RTL script. 2325 String[] tags = { 2326 "pa-Arab", 2327 "pa-Arab-PK", 2328 "ps", 2329 "ps-AF", 2330 "uz-Arab", 2331 "uz-Arab-AF", 2332 }; 2333 for (String tag : tags) { 2334 Locale l = Locale.forLanguageTag(tag); 2335 int direction = TextUtils.getLayoutDirectionFromLocale(l); 2336 assertEquals(l.toLanguageTag() + " not RTL: " + direction, 2337 LAYOUT_DIRECTION_RTL, direction); 2338 } 2339 2340 // Locale without a real language 2341 Locale locale = Locale.forLanguageTag("zz"); 2342 assertEquals(LAYOUT_DIRECTION_LTR, 2343 TextUtils.getLayoutDirectionFromLocale(locale)); 2344 } 2345 } 2346