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