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