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