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; 18 19 import static android.text.TextUtils.formatSimple; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotNull; 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 29 import android.os.Parcel; 30 import android.platform.test.annotations.Presubmit; 31 import android.test.MoreAsserts; 32 import android.text.style.StyleSpan; 33 import android.text.util.Rfc822Token; 34 import android.text.util.Rfc822Tokenizer; 35 import android.view.View; 36 37 import androidx.test.filters.LargeTest; 38 import androidx.test.filters.SmallTest; 39 import androidx.test.runner.AndroidJUnit4; 40 41 import com.google.android.collect.Lists; 42 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.Locale; 49 50 /** 51 * TextUtilsTest tests {@link TextUtils}. 52 */ 53 @Presubmit 54 @SmallTest 55 @RunWith(AndroidJUnit4.class) 56 public class TextUtilsTest { 57 58 @Test testBasic()59 public void testBasic() { 60 assertEquals("", TextUtils.concat()); 61 assertEquals("foo", TextUtils.concat("foo")); 62 assertEquals("foobar", TextUtils.concat("foo", "bar")); 63 assertEquals("foobarbaz", TextUtils.concat("foo", "bar", "baz")); 64 65 SpannableString foo = new SpannableString("foo"); 66 foo.setSpan("foo", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); 67 68 SpannableString bar = new SpannableString("bar"); 69 bar.setSpan("bar", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); 70 71 SpannableString baz = new SpannableString("baz"); 72 baz.setSpan("baz", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); 73 74 assertEquals("foo", TextUtils.concat(foo).toString()); 75 assertEquals("foobar", TextUtils.concat(foo, bar).toString()); 76 assertEquals("foobarbaz", TextUtils.concat(foo, bar, baz).toString()); 77 78 assertEquals(1, ((Spanned) TextUtils.concat(foo)).getSpanStart("foo")); 79 80 assertEquals(1, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("foo")); 81 assertEquals(4, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("bar")); 82 83 assertEquals(1, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("foo")); 84 assertEquals(4, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("bar")); 85 assertEquals(7, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("baz")); 86 87 assertTrue(TextUtils.concat("foo", "bar") instanceof String); 88 assertTrue(TextUtils.concat(foo, bar) instanceof SpannedString); 89 } 90 91 @Test testTemplateString()92 public void testTemplateString() { 93 CharSequence result; 94 95 result = TextUtils.expandTemplate("This is a ^1 of the ^2 broadcast ^3.", 96 "test", "emergency", "system"); 97 assertEquals("This is a test of the emergency broadcast system.", 98 result.toString()); 99 100 result = TextUtils.expandTemplate("^^^1^^^2^3^a^1^^b^^^c", 101 "one", "two", "three"); 102 assertEquals("^one^twothree^aone^b^^c", 103 result.toString()); 104 105 result = TextUtils.expandTemplate("^"); 106 assertEquals("^", result.toString()); 107 108 result = TextUtils.expandTemplate("^^"); 109 assertEquals("^", result.toString()); 110 111 result = TextUtils.expandTemplate("^^^"); 112 assertEquals("^^", result.toString()); 113 114 result = TextUtils.expandTemplate("shorter ^1 values ^2.", "a", ""); 115 assertEquals("shorter a values .", result.toString()); 116 117 try { 118 TextUtils.expandTemplate("Only ^1 value given, but ^2 used.", "foo"); 119 fail(); 120 } catch (IllegalArgumentException e) { 121 } 122 123 try { 124 TextUtils.expandTemplate("^1 value given, and ^0 used.", "foo"); 125 fail(); 126 } catch (IllegalArgumentException e) { 127 } 128 129 result = TextUtils.expandTemplate("^1 value given, and ^9 used.", 130 "one", "two", "three", "four", "five", 131 "six", "seven", "eight", "nine"); 132 assertEquals("one value given, and nine used.", result.toString()); 133 134 try { 135 TextUtils.expandTemplate("^1 value given, and ^10 used.", 136 "one", "two", "three", "four", "five", 137 "six", "seven", "eight", "nine", "ten"); 138 fail(); 139 } catch (IllegalArgumentException e) { 140 } 141 142 // putting carets in the values: expansion is not recursive. 143 144 result = TextUtils.expandTemplate("^2", "foo", "^^"); 145 assertEquals("^^", result.toString()); 146 147 result = TextUtils.expandTemplate("^^2", "foo", "1"); 148 assertEquals("^2", result.toString()); 149 150 result = TextUtils.expandTemplate("^1", "value with ^2 in it", "foo"); 151 assertEquals("value with ^2 in it", result.toString()); 152 } 153 154 /** Fail unless text+spans contains a span 'spanName' with the given start and end. */ checkContains(Spanned text, String[] spans, String spanName, int start, int end)155 private void checkContains(Spanned text, String[] spans, String spanName, 156 int start, int end) { 157 for (String i: spans) { 158 if (i.equals(spanName)) { 159 assertEquals(start, text.getSpanStart(i)); 160 assertEquals(end, text.getSpanEnd(i)); 161 return; 162 } 163 } 164 fail(); 165 } 166 167 @Test testTemplateSpan()168 public void testTemplateSpan() { 169 SpannableString template; 170 Spanned result; 171 String[] spans; 172 173 // ordinary replacement 174 175 template = new SpannableString("a^1b"); 176 template.setSpan("before", 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 177 template.setSpan("during", 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 178 template.setSpan("after", 3, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 179 template.setSpan("during+after", 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 180 181 result = (Spanned) TextUtils.expandTemplate(template, "foo"); 182 assertEquals(5, result.length()); 183 spans = result.getSpans(0, result.length(), String.class); 184 185 // value is one character longer, so span endpoints should change. 186 assertEquals(4, spans.length); 187 checkContains(result, spans, "before", 0, 1); 188 checkContains(result, spans, "during", 1, 4); 189 checkContains(result, spans, "after", 4, 5); 190 checkContains(result, spans, "during+after", 1, 5); 191 192 193 // replacement with empty string 194 195 result = (Spanned) TextUtils.expandTemplate(template, ""); 196 assertEquals(2, result.length()); 197 spans = result.getSpans(0, result.length(), String.class); 198 199 // the "during" span should disappear. 200 assertEquals(3, spans.length); 201 checkContains(result, spans, "before", 0, 1); 202 checkContains(result, spans, "after", 1, 2); 203 checkContains(result, spans, "during+after", 1, 2); 204 } 205 206 @Test testStringSplitterSimple()207 public void testStringSplitterSimple() { 208 stringSplitterTestHelper("a,b,cde", new String[] {"a", "b", "cde"}); 209 } 210 211 @Test testStringSplitterEmpty()212 public void testStringSplitterEmpty() { 213 stringSplitterTestHelper("", new String[] {}); 214 } 215 216 @Test testStringSplitterWithLeadingEmptyString()217 public void testStringSplitterWithLeadingEmptyString() { 218 stringSplitterTestHelper(",a,b,cde", new String[] {"", "a", "b", "cde"}); 219 } 220 221 @Test testStringSplitterWithInternalEmptyString()222 public void testStringSplitterWithInternalEmptyString() { 223 stringSplitterTestHelper("a,b,,cde", new String[] {"a", "b", "", "cde"}); 224 } 225 226 @Test testStringSplitterWithTrailingEmptyString()227 public void testStringSplitterWithTrailingEmptyString() { 228 // A single trailing emtpy string should be ignored. 229 stringSplitterTestHelper("a,b,cde,", new String[] {"a", "b", "cde"}); 230 } 231 stringSplitterTestHelper(String string, String[] expectedStrings)232 private void stringSplitterTestHelper(String string, String[] expectedStrings) { 233 TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(','); 234 splitter.setString(string); 235 List<String> strings = Lists.newArrayList(); 236 for (String s : splitter) { 237 strings.add(s); 238 } 239 MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{})); 240 } 241 242 @Test testTrim()243 public void testTrim() { 244 String[] strings = { "abc", " abc", " abc", "abc ", "abc ", 245 " abc ", " abc ", "\nabc\n", "\nabc", "abc\n" }; 246 247 for (String s : strings) { 248 assertEquals(s.trim().length(), TextUtils.getTrimmedLength(s)); 249 } 250 } 251 252 @Test testRfc822TokenizerFullAddress()253 public void testRfc822TokenizerFullAddress() { 254 Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo@google.com>"); 255 assertNotNull(tokens); 256 assertEquals(1, tokens.length); 257 assertEquals("foo@google.com", tokens[0].getAddress()); 258 assertEquals("Foo Bar", tokens[0].getName()); 259 assertEquals("something",tokens[0].getComment()); 260 } 261 262 @Test testRfc822TokenizeItemWithError()263 public void testRfc822TokenizeItemWithError() { 264 Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\"); 265 assertNotNull(tokens); 266 assertEquals(1, tokens.length); 267 assertEquals("Foo Bar", tokens[0].getAddress()); 268 } 269 270 @Test testRfc822FindToken()271 public void testRfc822FindToken() { 272 Rfc822Tokenizer tokenizer = new Rfc822Tokenizer(); 273 // 0 1 2 3 4 274 // 0 1234 56789012345678901234 5678 90123456789012345 275 String address = "\"Foo\" <foo@google.com>, \"Bar\" <bar@google.com>"; 276 assertEquals(0, tokenizer.findTokenStart(address, 21)); 277 assertEquals(22, tokenizer.findTokenEnd(address, 21)); 278 assertEquals(24, tokenizer.findTokenStart(address, 25)); 279 assertEquals(46, tokenizer.findTokenEnd(address, 25)); 280 } 281 282 @Test testRfc822FindTokenWithError()283 public void testRfc822FindTokenWithError() { 284 assertEquals(9, new Rfc822Tokenizer().findTokenEnd("\"Foo Bar\\", 0)); 285 } 286 287 @LargeTest 288 @Test testEllipsize()289 public void testEllipsize() { 290 CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog."; 291 CharSequence s2 = new Wrapper(s1); 292 Spannable s3 = new SpannableString(s1); 293 s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 294 TextPaint p = new TextPaint(); 295 p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG); 296 297 for (int i = 0; i < 100; i++) { 298 for (int j = 0; j < 3; j++) { 299 TextUtils.TruncateAt kind = null; 300 301 switch (j) { 302 case 0: 303 kind = TextUtils.TruncateAt.START; 304 break; 305 306 case 1: 307 kind = TextUtils.TruncateAt.END; 308 break; 309 310 case 2: 311 kind = TextUtils.TruncateAt.MIDDLE; 312 break; 313 } 314 315 String out1 = TextUtils.ellipsize(s1, p, i, kind).toString(); 316 String out2 = TextUtils.ellipsize(s2, p, i, kind).toString(); 317 String out3 = TextUtils.ellipsize(s3, p, i, kind).toString(); 318 319 String keep1 = TextUtils.ellipsize(s1, p, i, kind, true, null).toString(); 320 String keep2 = TextUtils.ellipsize(s2, p, i, kind, true, null).toString(); 321 String keep3 = TextUtils.ellipsize(s3, p, i, kind, true, null).toString(); 322 323 String trim1 = keep1.replace("\uFEFF", ""); 324 325 // Are all normal output strings identical? 326 assertEquals("wid " + i + " pass " + j, out1, out2); 327 assertEquals("wid " + i + " pass " + j, out2, out3); 328 329 // Are preserved output strings identical? 330 assertEquals("wid " + i + " pass " + j, keep1, keep2); 331 assertEquals("wid " + i + " pass " + j, keep2, keep3); 332 333 // Does trimming padding from preserved yield normal? 334 assertEquals("wid " + i + " pass " + j, out1, trim1); 335 336 // Did preserved output strings preserve length? 337 assertEquals("wid " + i + " pass " + j, keep1.length(), s1.length()); 338 339 // Does the output string actually fit in the space? 340 assertTrue("wid " + i + " pass " + j, p.measureText(out1) <= i); 341 342 // Is the padded output the same width as trimmed output? 343 assertTrue("wid " + i + " pass " + j, p.measureText(keep1) == p.measureText(out1)); 344 } 345 } 346 } 347 348 @Test testEllipsize_multiCodepoint()349 public void testEllipsize_multiCodepoint() { 350 final TextPaint paint = new TextPaint(); 351 final float wordWidth = paint.measureText("MMMM"); 352 353 // Establish the ground rules first, for single-codepoint cases. 354 final String ellipsis = "."; // one full stop character 355 assertEquals( 356 "MM.\uFEFF", 357 TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth, 358 TextUtils.TruncateAt.END, true /* preserve length */, 359 null /* no callback */, TextDirectionHeuristics.LTR, 360 ellipsis)); 361 assertEquals( 362 "MM.", 363 TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth, 364 TextUtils.TruncateAt.END, false /* preserve length */, 365 null /* no callback */, TextDirectionHeuristics.LTR, 366 ellipsis)); 367 assertEquals( 368 "M.", 369 TextUtils.ellipsize("MM", paint, 0.45f * wordWidth, 370 TextUtils.TruncateAt.END, true /* preserve length */, 371 null /* no callback */, TextDirectionHeuristics.LTR, 372 ellipsis)); 373 assertEquals( 374 "M.", 375 TextUtils.ellipsize("MM", paint, 0.45f * wordWidth, 376 TextUtils.TruncateAt.END, false /* preserve length */, 377 null /* no callback */, TextDirectionHeuristics.LTR, 378 ellipsis)); 379 380 // Now check the differences for multi-codepoint ellipsis. 381 final String longEllipsis = ".."; // two full stop characters 382 assertEquals( 383 "MM..", 384 TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth, 385 TextUtils.TruncateAt.END, true /* preserve length */, 386 null /* no callback */, TextDirectionHeuristics.LTR, 387 longEllipsis)); 388 assertEquals( 389 "MM..", 390 TextUtils.ellipsize("MMMM", paint, 0.7f * wordWidth, 391 TextUtils.TruncateAt.END, false /* preserve length */, 392 null /* no callback */, TextDirectionHeuristics.LTR, 393 longEllipsis)); 394 assertEquals( 395 "M\uFEFF", 396 TextUtils.ellipsize("MM", paint, 0.45f * wordWidth, 397 TextUtils.TruncateAt.END, true /* preserve length */, 398 null /* no callback */, TextDirectionHeuristics.LTR, 399 longEllipsis)); 400 assertEquals( 401 "M..", 402 TextUtils.ellipsize("MM", paint, 0.45f * wordWidth, 403 TextUtils.TruncateAt.END, false /* preserve length */, 404 null /* no callback */, TextDirectionHeuristics.LTR, 405 longEllipsis)); 406 } 407 408 @Test testDelimitedStringContains()409 public void testDelimitedStringContains() { 410 assertFalse(TextUtils.delimitedStringContains("", ',', null)); 411 assertFalse(TextUtils.delimitedStringContains(null, ',', "")); 412 // Whole match 413 assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps")); 414 // At beginning. 415 assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps")); 416 assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps")); 417 // In middle, both without, before & after a false match. 418 assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps")); 419 assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps")); 420 assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps")); 421 // At the end. 422 assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps")); 423 assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps")); 424 // Not present (but with a false match) 425 assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps")); 426 } 427 428 @Test testCharSequenceCreator()429 public void testCharSequenceCreator() { 430 Parcel p = Parcel.obtain(); 431 CharSequence text; 432 try { 433 TextUtils.writeToParcel(null, p, 0); 434 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 435 assertNull("null CharSequence should generate null from parcel", text); 436 } finally { 437 p.recycle(); 438 } 439 p = Parcel.obtain(); 440 try { 441 TextUtils.writeToParcel("test", p, 0); 442 p.setDataPosition(0); 443 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 444 assertEquals("conversion to/from parcel failed", "test", text); 445 } finally { 446 p.recycle(); 447 } 448 } 449 450 @Test testCharSequenceCreatorNull()451 public void testCharSequenceCreatorNull() { 452 Parcel p; 453 CharSequence text; 454 p = Parcel.obtain(); 455 try { 456 TextUtils.writeToParcel(null, p, 0); 457 p.setDataPosition(0); 458 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 459 assertNull("null CharSequence should generate null from parcel", text); 460 } finally { 461 p.recycle(); 462 } 463 } 464 465 @Test testCharSequenceCreatorSpannable()466 public void testCharSequenceCreatorSpannable() { 467 Parcel p; 468 CharSequence text; 469 p = Parcel.obtain(); 470 try { 471 TextUtils.writeToParcel(new SpannableString("test"), p, 0); 472 p.setDataPosition(0); 473 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 474 assertEquals("conversion to/from parcel failed", "test", text.toString()); 475 } finally { 476 p.recycle(); 477 } 478 } 479 480 @Test testCharSequenceCreatorString()481 public void testCharSequenceCreatorString() { 482 Parcel p; 483 CharSequence text; 484 p = Parcel.obtain(); 485 try { 486 TextUtils.writeToParcel("test", p, 0); 487 p.setDataPosition(0); 488 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p); 489 assertEquals("conversion to/from parcel failed", "test", text.toString()); 490 } finally { 491 p.recycle(); 492 } 493 } 494 495 /** 496 * CharSequence wrapper for testing the cases where text is copied into 497 * a char array instead of working from a String or a Spanned. 498 */ 499 private static class Wrapper implements CharSequence { 500 private CharSequence mString; 501 Wrapper(CharSequence s)502 public Wrapper(CharSequence s) { 503 mString = s; 504 } 505 506 @Override length()507 public int length() { 508 return mString.length(); 509 } 510 511 @Override charAt(int off)512 public char charAt(int off) { 513 return mString.charAt(off); 514 } 515 516 @Override toString()517 public String toString() { 518 return mString.toString(); 519 } 520 521 @Override subSequence(int start, int end)522 public CharSequence subSequence(int start, int end) { 523 return new Wrapper(mString.subSequence(start, end)); 524 } 525 } 526 527 @Test testRemoveEmptySpans()528 public void testRemoveEmptySpans() { 529 MockSpanned spanned = new MockSpanned(); 530 531 spanned.test(); 532 spanned.addSpan().test(); 533 spanned.addSpan().test(); 534 spanned.addSpan().test(); 535 spanned.addEmptySpan().test(); 536 spanned.addSpan().test(); 537 spanned.addEmptySpan().test(); 538 spanned.addEmptySpan().test(); 539 spanned.addSpan().test(); 540 541 spanned.clear(); 542 spanned.addEmptySpan().test(); 543 spanned.addEmptySpan().test(); 544 spanned.addEmptySpan().test(); 545 spanned.addSpan().test(); 546 spanned.addEmptySpan().test(); 547 spanned.addSpan().test(); 548 549 spanned.clear(); 550 spanned.addSpan().test(); 551 spanned.addEmptySpan().test(); 552 spanned.addSpan().test(); 553 spanned.addEmptySpan().test(); 554 spanned.addSpan().test(); 555 spanned.addSpan().test(); 556 } 557 558 protected static class MockSpanned implements Spanned { 559 560 private List<Object> allSpans = new ArrayList<Object>(); 561 private List<Object> nonEmptySpans = new ArrayList<Object>(); 562 clear()563 public void clear() { 564 allSpans.clear(); 565 nonEmptySpans.clear(); 566 } 567 addSpan()568 public MockSpanned addSpan() { 569 Object o = new Object(); 570 allSpans.add(o); 571 nonEmptySpans.add(o); 572 return this; 573 } 574 addEmptySpan()575 public MockSpanned addEmptySpan() { 576 Object o = new Object(); 577 allSpans.add(o); 578 return this; 579 } 580 test()581 public void test() { 582 Object[] nonEmpty = TextUtils.removeEmptySpans(allSpans.toArray(), this, Object.class); 583 assertEquals("Mismatched array size", nonEmptySpans.size(), nonEmpty.length); 584 for (int i=0; i<nonEmpty.length; i++) { 585 assertEquals("Span differ", nonEmptySpans.get(i), nonEmpty[i]); 586 } 587 } 588 589 @Override charAt(int arg0)590 public char charAt(int arg0) { 591 return 0; 592 } 593 594 @Override length()595 public int length() { 596 return 0; 597 } 598 599 @Override subSequence(int arg0, int arg1)600 public CharSequence subSequence(int arg0, int arg1) { 601 return null; 602 } 603 604 @Override getSpans(int start, int end, Class<T> type)605 public <T> T[] getSpans(int start, int end, Class<T> type) { 606 return null; 607 } 608 609 @Override getSpanStart(Object tag)610 public int getSpanStart(Object tag) { 611 return 0; 612 } 613 614 @Override getSpanEnd(Object tag)615 public int getSpanEnd(Object tag) { 616 return nonEmptySpans.contains(tag) ? 1 : 0; 617 } 618 619 @Override getSpanFlags(Object tag)620 public int getSpanFlags(Object tag) { 621 return 0; 622 } 623 624 @Override nextSpanTransition(int start, int limit, Class type)625 public int nextSpanTransition(int start, int limit, Class type) { 626 return 0; 627 } 628 } 629 630 @Test testGetLayoutDirectionFromLocale()631 public void testGetLayoutDirectionFromLocale() { 632 assertEquals(View.LAYOUT_DIRECTION_LTR, TextUtils.getLayoutDirectionFromLocale(null)); 633 assertEquals(View.LAYOUT_DIRECTION_LTR, 634 TextUtils.getLayoutDirectionFromLocale(Locale.ROOT)); 635 assertEquals(View.LAYOUT_DIRECTION_LTR, 636 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("en"))); 637 assertEquals(View.LAYOUT_DIRECTION_LTR, 638 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("en-US"))); 639 assertEquals(View.LAYOUT_DIRECTION_LTR, 640 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("az"))); 641 assertEquals(View.LAYOUT_DIRECTION_LTR, 642 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("az-AZ"))); 643 assertEquals(View.LAYOUT_DIRECTION_LTR, 644 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("az-Latn"))); 645 assertEquals(View.LAYOUT_DIRECTION_LTR, 646 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("en-EG"))); 647 assertEquals(View.LAYOUT_DIRECTION_LTR, 648 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("ar-Latn"))); 649 650 assertEquals(View.LAYOUT_DIRECTION_RTL, 651 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("ar"))); 652 assertEquals(View.LAYOUT_DIRECTION_RTL, 653 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("fa"))); 654 assertEquals(View.LAYOUT_DIRECTION_RTL, 655 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("he"))); 656 assertEquals(View.LAYOUT_DIRECTION_RTL, 657 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("iw"))); 658 assertEquals(View.LAYOUT_DIRECTION_RTL, 659 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("ur"))); 660 assertEquals(View.LAYOUT_DIRECTION_RTL, 661 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("dv"))); 662 assertEquals(View.LAYOUT_DIRECTION_RTL, 663 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("az-Arab"))); 664 assertEquals(View.LAYOUT_DIRECTION_RTL, 665 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("az-IR"))); 666 assertEquals(View.LAYOUT_DIRECTION_RTL, 667 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("fa-US"))); 668 assertEquals(View.LAYOUT_DIRECTION_RTL, 669 TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("tr-Arab"))); 670 } 671 672 @Test testToUpperCase()673 public void testToUpperCase() { 674 { 675 final CharSequence result = TextUtils.toUpperCase(null, "abc", false); 676 assertEquals(StringBuilder.class, result.getClass()); 677 assertEquals("ABC", result.toString()); 678 } 679 { 680 final SpannableString str = new SpannableString("abc"); 681 Object span = new Object(); 682 str.setSpan(span, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 683 684 final CharSequence result = TextUtils.toUpperCase(null, str, true /* copySpans */); 685 assertEquals(SpannableStringBuilder.class, result.getClass()); 686 assertEquals("ABC", result.toString()); 687 final Spanned spanned = (Spanned) result; 688 final Object[] resultSpans = spanned.getSpans(0, result.length(), Object.class); 689 assertEquals(1, resultSpans.length); 690 assertSame(span, resultSpans[0]); 691 assertEquals(1, spanned.getSpanStart(span)); 692 assertEquals(2, spanned.getSpanEnd(span)); 693 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, spanned.getSpanFlags(span)); 694 } 695 { 696 final Locale turkish = new Locale("tr", "TR"); 697 final CharSequence result = TextUtils.toUpperCase(turkish, "i", false); 698 assertEquals(StringBuilder.class, result.getClass()); 699 assertEquals("İ", result.toString()); 700 } 701 { 702 final String str = "ABC"; 703 assertSame(str, TextUtils.toUpperCase(null, str, false)); 704 } 705 { 706 final SpannableString str = new SpannableString("ABC"); 707 str.setSpan(new Object(), 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 708 assertSame(str, TextUtils.toUpperCase(null, str, true /* copySpans */)); 709 } 710 } 711 712 // Copied from cts/tests/tests/widget/src/android/widget/cts/TextViewTest.java and modified 713 // for the TextUtils.toUpperCase method. 714 @Test testToUpperCase_SpansArePreserved()715 public void testToUpperCase_SpansArePreserved() { 716 final Locale greek = new Locale("el", "GR"); 717 final String lowerString = "ι\u0301ριδα"; // ίριδα with first letter decomposed 718 final String upperString = "ΙΡΙΔΑ"; // uppercased 719 // expected lowercase to uppercase index map 720 final int[] indexMap = {0, 1, 1, 2, 3, 4, 5}; 721 final int flags = Spanned.SPAN_INCLUSIVE_INCLUSIVE; 722 723 final Spannable source = new SpannableString(lowerString); 724 source.setSpan(new Object(), 0, 1, flags); 725 source.setSpan(new Object(), 1, 2, flags); 726 source.setSpan(new Object(), 2, 3, flags); 727 source.setSpan(new Object(), 3, 4, flags); 728 source.setSpan(new Object(), 4, 5, flags); 729 source.setSpan(new Object(), 5, 6, flags); 730 source.setSpan(new Object(), 0, 2, flags); 731 source.setSpan(new Object(), 1, 3, flags); 732 source.setSpan(new Object(), 2, 4, flags); 733 source.setSpan(new Object(), 0, 6, flags); 734 final Object[] sourceSpans = source.getSpans(0, source.length(), Object.class); 735 736 final CharSequence uppercase = TextUtils.toUpperCase(greek, source, true /* copySpans */); 737 assertEquals(SpannableStringBuilder.class, uppercase.getClass()); 738 final Spanned result = (Spanned) uppercase; 739 740 assertEquals(upperString, result.toString()); 741 final Object[] resultSpans = result.getSpans(0, result.length(), Object.class); 742 assertEquals(sourceSpans.length, resultSpans.length); 743 for (int i = 0; i < sourceSpans.length; i++) { 744 assertSame(sourceSpans[i], resultSpans[i]); 745 final Object span = sourceSpans[i]; 746 assertEquals(indexMap[source.getSpanStart(span)], result.getSpanStart(span)); 747 assertEquals(indexMap[source.getSpanEnd(span)], result.getSpanEnd(span)); 748 assertEquals(source.getSpanFlags(span), result.getSpanFlags(span)); 749 } 750 } 751 752 @Test testTrimToSize()753 public void testTrimToSize() { 754 final String testString = "a\uD800\uDC00a"; 755 assertEquals("Should return text as it is if size is longer than length", 756 testString, TextUtils.trimToSize(testString, 5)); 757 assertEquals("Should return text as it is if size is equal to length", 758 testString, TextUtils.trimToSize(testString, 4)); 759 assertEquals("Should trim text", 760 "a\uD800\uDC00", TextUtils.trimToSize(testString, 3)); 761 assertEquals("Should trim surrogate pairs if size is in the middle of a pair", 762 "a", TextUtils.trimToSize(testString, 2)); 763 assertEquals("Should trim text", 764 "a", TextUtils.trimToSize(testString, 1)); 765 assertEquals("Should handle null", 766 null, TextUtils.trimToSize(null, 1)); 767 768 assertEquals("Should trim high surrogate if invalid surrogate", 769 "a\uD800", TextUtils.trimToSize("a\uD800\uD800", 2)); 770 assertEquals("Should trim low surrogate if invalid surrogate", 771 "a\uDC00", TextUtils.trimToSize("a\uDC00\uDC00", 2)); 772 } 773 774 @Test(expected = IllegalArgumentException.class) testTrimToSizeThrowsExceptionForNegativeSize()775 public void testTrimToSizeThrowsExceptionForNegativeSize() { 776 TextUtils.trimToSize("", -1); 777 } 778 779 @Test(expected = IllegalArgumentException.class) testTrimToSizeThrowsExceptionForZeroSize()780 public void testTrimToSizeThrowsExceptionForZeroSize() { 781 TextUtils.trimToSize("abc", 0); 782 } 783 784 @Test length()785 public void length() { 786 assertEquals(0, TextUtils.length(null)); 787 assertEquals(0, TextUtils.length("")); 788 assertEquals(2, TextUtils.length(" ")); 789 assertEquals(6, TextUtils.length("Hello!")); 790 } 791 792 @Test testTrimToLengthWithEllipsis()793 public void testTrimToLengthWithEllipsis() { 794 assertEquals("ABC...", TextUtils.trimToLengthWithEllipsis("ABCDEF", 3)); 795 assertEquals("ABC", TextUtils.trimToLengthWithEllipsis("ABC", 3)); 796 assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3)); 797 assertNull(TextUtils.trimToLengthWithEllipsis(null, 3)); 798 } 799 800 @Test testFormatSimple_Types()801 public void testFormatSimple_Types() { 802 assertEquals("true", formatSimple("%b", true)); 803 assertEquals("false", formatSimple("%b", false)); 804 assertEquals("true", formatSimple("%b", this)); 805 assertEquals("false", formatSimple("%b", new Object[] { null })); 806 807 assertEquals("!", formatSimple("%c", '!')); 808 809 assertEquals("42", formatSimple("%d", 42)); 810 assertEquals("281474976710656", formatSimple("%d", 281474976710656L)); 811 812 assertEquals("3.14159", formatSimple("%f", 3.14159)); 813 assertEquals("NaN", formatSimple("%f", Float.NaN)); 814 815 assertEquals("example", formatSimple("%s", "example")); 816 assertEquals("null", formatSimple("%s", new Object[] { null })); 817 818 assertEquals("2a", formatSimple("%x", 42)); 819 assertEquals("1000000000000", formatSimple("%x", 281474976710656L)); 820 821 assertEquals("%", formatSimple("%%")); 822 } 823 824 @Test testFormatSimple_Width()825 public void testFormatSimple_Width() { 826 assertEquals("42", formatSimple("%1d", 42)); 827 assertEquals("42", formatSimple("%2d", 42)); 828 assertEquals(" 42", formatSimple("%3d", 42)); 829 assertEquals(" 42", formatSimple("%4d", 42)); 830 assertEquals(" 42 42", formatSimple("%4d%4d", 42, 42)); 831 assertEquals(" -42", formatSimple("%4d", -42)); 832 assertEquals(" 42", formatSimple("%10d", 42)); 833 834 assertEquals("42", formatSimple("%01d", 42)); 835 assertEquals("42", formatSimple("%02d", 42)); 836 assertEquals("042", formatSimple("%03d", 42)); 837 assertEquals("0042", formatSimple("%04d", 42)); 838 assertEquals("00420042", formatSimple("%04d%04d", 42, 42)); 839 assertEquals("-042", formatSimple("%04d", -42)); 840 assertEquals("0000000042", formatSimple("%010d", 42)); 841 } 842 843 @Test testFormatSimple_Empty()844 public void testFormatSimple_Empty() { 845 assertEquals("", formatSimple("")); 846 } 847 848 @Test testFormatSimple_Typical()849 public void testFormatSimple_Typical() { 850 assertEquals("String foobar and %% number -42 together", 851 formatSimple("String %s%s and %%%% number %d%d together", "foo", "bar", -4, 2)); 852 } 853 854 @Test testFormatSimple_Advanced()855 public void testFormatSimple_Advanced() { 856 assertEquals("000000000000002a.ext", 857 formatSimple("%016x.%s", 42, "ext")); 858 assertEquals("crtcl=0x002a:intrsv=Y:grnk=0x0018:gsmry=A:example:rnk=0x0000", 859 formatSimple("crtcl=0x%04x:intrsv=%c:grnk=0x%04x:gsmry=%c:%s:rnk=0x%04x", 860 42, 'Y', 24, 'A', "example", 0)); 861 } 862 863 @Test testFormatSimple_Mismatch()864 public void testFormatSimple_Mismatch() { 865 try { 866 formatSimple("%s"); 867 fail(); 868 } catch (IllegalArgumentException expected) { 869 } 870 try { 871 formatSimple("%s", "foo", "bar"); 872 fail(); 873 } catch (IllegalArgumentException expected) { 874 } 875 } 876 } 877