Home | History | Annotate | Download | only in text
      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 com.google.android.collect.Lists;
     20 
     21 import android.test.MoreAsserts;
     22 import android.os.Parcel;
     23 import android.test.suitebuilder.annotation.LargeTest;
     24 import android.test.suitebuilder.annotation.SmallTest;
     25 import android.text.style.StyleSpan;
     26 import android.text.util.Rfc822Token;
     27 import android.text.util.Rfc822Tokenizer;
     28 
     29 import java.util.ArrayList;
     30 import java.util.List;
     31 
     32 import junit.framework.TestCase;
     33 
     34 /**
     35  * TextUtilsTest tests {@link TextUtils}.
     36  */
     37 public class TextUtilsTest extends TestCase {
     38 
     39     @SmallTest
     40     public void testBasic() throws Exception {
     41         assertEquals("", TextUtils.concat());
     42         assertEquals("foo", TextUtils.concat("foo"));
     43         assertEquals("foobar", TextUtils.concat("foo", "bar"));
     44         assertEquals("foobarbaz", TextUtils.concat("foo", "bar", "baz"));
     45 
     46         SpannableString foo = new SpannableString("foo");
     47         foo.setSpan("foo", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     48 
     49         SpannableString bar = new SpannableString("bar");
     50         bar.setSpan("bar", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     51 
     52         SpannableString baz = new SpannableString("baz");
     53         baz.setSpan("baz", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     54 
     55         assertEquals("foo", TextUtils.concat(foo).toString());
     56         assertEquals("foobar", TextUtils.concat(foo, bar).toString());
     57         assertEquals("foobarbaz", TextUtils.concat(foo, bar, baz).toString());
     58 
     59         assertEquals(1, ((Spanned) TextUtils.concat(foo)).getSpanStart("foo"));
     60 
     61         assertEquals(1, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("foo"));
     62         assertEquals(4, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("bar"));
     63 
     64         assertEquals(1, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("foo"));
     65         assertEquals(4, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("bar"));
     66         assertEquals(7, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("baz"));
     67 
     68         assertTrue(TextUtils.concat("foo", "bar") instanceof String);
     69         assertTrue(TextUtils.concat(foo, bar) instanceof SpannedString);
     70     }
     71 
     72     @SmallTest
     73     public void testTemplateString() throws Exception {
     74         CharSequence result;
     75 
     76         result = TextUtils.expandTemplate("This is a ^1 of the ^2 broadcast ^3.",
     77                                           "test", "emergency", "system");
     78         assertEquals("This is a test of the emergency broadcast system.",
     79                      result.toString());
     80 
     81         result = TextUtils.expandTemplate("^^^1^^^2^3^a^1^^b^^^c",
     82                                           "one", "two", "three");
     83         assertEquals("^one^twothree^aone^b^^c",
     84                      result.toString());
     85 
     86         result = TextUtils.expandTemplate("^");
     87         assertEquals("^", result.toString());
     88 
     89         result = TextUtils.expandTemplate("^^");
     90         assertEquals("^", result.toString());
     91 
     92         result = TextUtils.expandTemplate("^^^");
     93         assertEquals("^^", result.toString());
     94 
     95         result = TextUtils.expandTemplate("shorter ^1 values ^2.", "a", "");
     96         assertEquals("shorter a values .", result.toString());
     97 
     98         try {
     99             TextUtils.expandTemplate("Only ^1 value given, but ^2 used.", "foo");
    100             fail();
    101         } catch (IllegalArgumentException e) {
    102         }
    103 
    104         try {
    105             TextUtils.expandTemplate("^1 value given, and ^0 used.", "foo");
    106             fail();
    107         } catch (IllegalArgumentException e) {
    108         }
    109 
    110         result = TextUtils.expandTemplate("^1 value given, and ^9 used.",
    111                                           "one", "two", "three", "four", "five",
    112                                           "six", "seven", "eight", "nine");
    113         assertEquals("one value given, and nine used.", result.toString());
    114 
    115         try {
    116             TextUtils.expandTemplate("^1 value given, and ^10 used.",
    117                                      "one", "two", "three", "four", "five",
    118                                      "six", "seven", "eight", "nine", "ten");
    119             fail();
    120         } catch (IllegalArgumentException e) {
    121         }
    122 
    123         // putting carets in the values: expansion is not recursive.
    124 
    125         result = TextUtils.expandTemplate("^2", "foo", "^^");
    126         assertEquals("^^", result.toString());
    127 
    128         result = TextUtils.expandTemplate("^^2", "foo", "1");
    129         assertEquals("^2", result.toString());
    130 
    131         result = TextUtils.expandTemplate("^1", "value with ^2 in it", "foo");
    132         assertEquals("value with ^2 in it", result.toString());
    133     }
    134 
    135     /** Fail unless text+spans contains a span 'spanName' with the given start and end. */
    136     private void checkContains(Spanned text, String[] spans, String spanName,
    137                                int start, int end) throws Exception {
    138         for (String i: spans) {
    139             if (i.equals(spanName)) {
    140                 assertEquals(start, text.getSpanStart(i));
    141                 assertEquals(end, text.getSpanEnd(i));
    142                 return;
    143             }
    144         }
    145         fail();
    146     }
    147 
    148     @SmallTest
    149     public void testTemplateSpan() throws Exception {
    150         SpannableString template;
    151         Spanned result;
    152         String[] spans;
    153 
    154         // ordinary replacement
    155 
    156         template = new SpannableString("a^1b");
    157         template.setSpan("before", 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    158         template.setSpan("during", 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    159         template.setSpan("after", 3, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    160         template.setSpan("during+after", 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    161 
    162         result = (Spanned) TextUtils.expandTemplate(template, "foo");
    163         assertEquals(5, result.length());
    164         spans = result.getSpans(0, result.length(), String.class);
    165 
    166         // value is one character longer, so span endpoints should change.
    167         assertEquals(4, spans.length);
    168         checkContains(result, spans, "before", 0, 1);
    169         checkContains(result, spans, "during", 1, 4);
    170         checkContains(result, spans, "after", 4, 5);
    171         checkContains(result, spans, "during+after", 1, 5);
    172 
    173 
    174         // replacement with empty string
    175 
    176         result = (Spanned) TextUtils.expandTemplate(template, "");
    177         assertEquals(2, result.length());
    178         spans = result.getSpans(0, result.length(), String.class);
    179 
    180         // the "during" span should disappear.
    181         assertEquals(3, spans.length);
    182         checkContains(result, spans, "before", 0, 1);
    183         checkContains(result, spans, "after", 1, 2);
    184         checkContains(result, spans, "during+after", 1, 2);
    185     }
    186 
    187     @SmallTest
    188     public void testStringSplitterSimple() {
    189         stringSplitterTestHelper("a,b,cde", new String[] {"a", "b", "cde"});
    190     }
    191 
    192     @SmallTest
    193     public void testStringSplitterEmpty() {
    194         stringSplitterTestHelper("", new String[] {});
    195     }
    196 
    197     @SmallTest
    198     public void testStringSplitterWithLeadingEmptyString() {
    199         stringSplitterTestHelper(",a,b,cde", new String[] {"", "a", "b", "cde"});
    200     }
    201 
    202     @SmallTest
    203     public void testStringSplitterWithInternalEmptyString() {
    204         stringSplitterTestHelper("a,b,,cde", new String[] {"a", "b", "", "cde"});
    205     }
    206 
    207     @SmallTest
    208     public void testStringSplitterWithTrailingEmptyString() {
    209         // A single trailing emtpy string should be ignored.
    210         stringSplitterTestHelper("a,b,cde,", new String[] {"a", "b", "cde"});
    211     }
    212 
    213     private void stringSplitterTestHelper(String string, String[] expectedStrings) {
    214         TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
    215         splitter.setString(string);
    216         List<String> strings = Lists.newArrayList();
    217         for (String s : splitter) {
    218             strings.add(s);
    219         }
    220         MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
    221     }
    222 
    223     @SmallTest
    224     public void testTrim() {
    225         String[] strings = { "abc", " abc", "  abc", "abc ", "abc  ",
    226                              " abc ", "  abc  ", "\nabc\n", "\nabc", "abc\n" };
    227 
    228         for (String s : strings) {
    229             assertEquals(s.trim().length(), TextUtils.getTrimmedLength(s));
    230         }
    231     }
    232 
    233     @SmallTest
    234     public void testRfc822TokenizerFullAddress() {
    235         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo (at) google.com>");
    236         assertNotNull(tokens);
    237         assertEquals(1, tokens.length);
    238         assertEquals("foo (at) google.com", tokens[0].getAddress());
    239         assertEquals("Foo Bar", tokens[0].getName());
    240         assertEquals("something",tokens[0].getComment());
    241     }
    242 
    243     @SmallTest
    244     public void testRfc822TokenizeItemWithError() {
    245         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\");
    246         assertNotNull(tokens);
    247         assertEquals(1, tokens.length);
    248         assertEquals("Foo Bar", tokens[0].getAddress());
    249     }
    250 
    251     @SmallTest
    252     public void testRfc822FindToken() {
    253         Rfc822Tokenizer tokenizer = new Rfc822Tokenizer();
    254         //                0           1         2           3         4
    255         //                0 1234 56789012345678901234 5678 90123456789012345
    256         String address = "\"Foo\" <foo (at) google.com>, \"Bar\" <bar (at) google.com>";
    257         assertEquals(0, tokenizer.findTokenStart(address, 21));
    258         assertEquals(22, tokenizer.findTokenEnd(address, 21));
    259         assertEquals(24, tokenizer.findTokenStart(address, 25));
    260         assertEquals(46, tokenizer.findTokenEnd(address, 25));
    261     }
    262 
    263     @SmallTest
    264     public void testRfc822FindTokenWithError() {
    265         assertEquals(9, new Rfc822Tokenizer().findTokenEnd("\"Foo Bar\\", 0));
    266     }
    267 
    268     @LargeTest
    269     public void testEllipsize() {
    270         CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog.";
    271         CharSequence s2 = new Wrapper(s1);
    272         Spannable s3 = new SpannableString(s1);
    273         s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    274         TextPaint p = new TextPaint();
    275         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
    276 
    277         for (int i = 0; i < 100; i++) {
    278             for (int j = 0; j < 3; j++) {
    279                 TextUtils.TruncateAt kind = null;
    280 
    281                 switch (j) {
    282                 case 0:
    283                     kind = TextUtils.TruncateAt.START;
    284                     break;
    285 
    286                 case 1:
    287                     kind = TextUtils.TruncateAt.END;
    288                     break;
    289 
    290                 case 2:
    291                     kind = TextUtils.TruncateAt.MIDDLE;
    292                     break;
    293                 }
    294 
    295                 String out1 = TextUtils.ellipsize(s1, p, i, kind).toString();
    296                 String out2 = TextUtils.ellipsize(s2, p, i, kind).toString();
    297                 String out3 = TextUtils.ellipsize(s3, p, i, kind).toString();
    298 
    299                 String keep1 = TextUtils.ellipsize(s1, p, i, kind, true, null).toString();
    300                 String keep2 = TextUtils.ellipsize(s2, p, i, kind, true, null).toString();
    301                 String keep3 = TextUtils.ellipsize(s3, p, i, kind, true, null).toString();
    302 
    303                 String trim1 = keep1.replace("\uFEFF", "");
    304 
    305                 // Are all normal output strings identical?
    306                 assertEquals("wid " + i + " pass " + j, out1, out2);
    307                 assertEquals("wid " + i + " pass " + j, out2, out3);
    308 
    309                 // Are preserved output strings identical?
    310                 assertEquals("wid " + i + " pass " + j, keep1, keep2);
    311                 assertEquals("wid " + i + " pass " + j, keep2, keep3);
    312 
    313                 // Does trimming padding from preserved yield normal?
    314                 assertEquals("wid " + i + " pass " + j, out1, trim1);
    315 
    316                 // Did preserved output strings preserve length?
    317                 assertEquals("wid " + i + " pass " + j, keep1.length(), s1.length());
    318 
    319                 // Does the output string actually fit in the space?
    320                 assertTrue("wid " + i + " pass " + j, p.measureText(out1) <= i);
    321 
    322                 // Is the padded output the same width as trimmed output?
    323                 assertTrue("wid " + i + " pass " + j, p.measureText(keep1) == p.measureText(out1));
    324             }
    325         }
    326     }
    327 
    328     @SmallTest
    329     public void testDelimitedStringContains() {
    330         assertFalse(TextUtils.delimitedStringContains("", ',', null));
    331         assertFalse(TextUtils.delimitedStringContains(null, ',', ""));
    332         // Whole match
    333         assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps"));
    334         // At beginning.
    335         assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps"));
    336         assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps"));
    337         // In middle, both without, before & after a false match.
    338         assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps"));
    339         assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps"));
    340         assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps"));
    341         // At the end.
    342         assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps"));
    343         assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps"));
    344         // Not present (but with a false match)
    345         assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
    346     }
    347 
    348     @SmallTest
    349     public void testCharSequenceCreator() {
    350         Parcel p = Parcel.obtain();
    351         TextUtils.writeToParcel(null, p, 0);
    352         CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
    353         assertNull("null CharSequence should generate null from parcel", text);
    354         p = Parcel.obtain();
    355         TextUtils.writeToParcel("test", p, 0);
    356         text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
    357         assertEquals("conversion to/from parcel failed", "test", text);
    358     }
    359 
    360     @SmallTest
    361     public void testCharSequenceCreatorNull() {
    362         Parcel p;
    363         CharSequence text;
    364         p = Parcel.obtain();
    365         TextUtils.writeToParcel(null, p, 0);
    366         p.setDataPosition(0);
    367         text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
    368         assertNull("null CharSequence should generate null from parcel", text);
    369     }
    370 
    371     @SmallTest
    372     public void testCharSequenceCreatorSpannable() {
    373         Parcel p;
    374         CharSequence text;
    375         p = Parcel.obtain();
    376         TextUtils.writeToParcel(new SpannableString("test"), p, 0);
    377         p.setDataPosition(0);
    378         text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
    379         assertEquals("conversion to/from parcel failed", "test", text.toString());
    380     }
    381 
    382     @SmallTest
    383     public void testCharSequenceCreatorString() {
    384         Parcel p;
    385         CharSequence text;
    386         p = Parcel.obtain();
    387         TextUtils.writeToParcel("test", p, 0);
    388         p.setDataPosition(0);
    389         text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
    390         assertEquals("conversion to/from parcel failed", "test", text.toString());
    391     }
    392 
    393     /**
    394      * CharSequence wrapper for testing the cases where text is copied into
    395      * a char array instead of working from a String or a Spanned.
    396      */
    397     private static class Wrapper implements CharSequence {
    398         private CharSequence mString;
    399 
    400         public Wrapper(CharSequence s) {
    401             mString = s;
    402         }
    403 
    404         public int length() {
    405             return mString.length();
    406         }
    407 
    408         public char charAt(int off) {
    409             return mString.charAt(off);
    410         }
    411 
    412         @Override
    413         public String toString() {
    414             return mString.toString();
    415         }
    416 
    417         public CharSequence subSequence(int start, int end) {
    418             return new Wrapper(mString.subSequence(start, end));
    419         }
    420     }
    421 
    422     @LargeTest
    423     public void testRemoveEmptySpans() {
    424         MockSpanned spanned = new MockSpanned();
    425 
    426         spanned.test();
    427         spanned.addSpan().test();
    428         spanned.addSpan().test();
    429         spanned.addSpan().test();
    430         spanned.addEmptySpan().test();
    431         spanned.addSpan().test();
    432         spanned.addEmptySpan().test();
    433         spanned.addEmptySpan().test();
    434         spanned.addSpan().test();
    435 
    436         spanned.clear();
    437         spanned.addEmptySpan().test();
    438         spanned.addEmptySpan().test();
    439         spanned.addEmptySpan().test();
    440         spanned.addSpan().test();
    441         spanned.addEmptySpan().test();
    442         spanned.addSpan().test();
    443 
    444         spanned.clear();
    445         spanned.addSpan().test();
    446         spanned.addEmptySpan().test();
    447         spanned.addSpan().test();
    448         spanned.addEmptySpan().test();
    449         spanned.addSpan().test();
    450         spanned.addSpan().test();
    451     }
    452 
    453     protected static class MockSpanned implements Spanned {
    454 
    455         private List<Object> allSpans = new ArrayList<Object>();
    456         private List<Object> nonEmptySpans = new ArrayList<Object>();
    457 
    458         public void clear() {
    459             allSpans.clear();
    460             nonEmptySpans.clear();
    461         }
    462 
    463         public MockSpanned addSpan() {
    464             Object o = new Object();
    465             allSpans.add(o);
    466             nonEmptySpans.add(o);
    467             return this;
    468         }
    469 
    470         public MockSpanned addEmptySpan() {
    471             Object o = new Object();
    472             allSpans.add(o);
    473             return this;
    474         }
    475 
    476         public void test() {
    477             Object[] nonEmpty = TextUtils.removeEmptySpans(allSpans.toArray(), this, Object.class);
    478             assertEquals("Mismatched array size", nonEmptySpans.size(), nonEmpty.length);
    479             for (int i=0; i<nonEmpty.length; i++) {
    480                 assertEquals("Span differ", nonEmptySpans.get(i), nonEmpty[i]);
    481             }
    482         }
    483 
    484         public char charAt(int arg0) {
    485             return 0;
    486         }
    487 
    488         public int length() {
    489             return 0;
    490         }
    491 
    492         public CharSequence subSequence(int arg0, int arg1) {
    493             return null;
    494         }
    495 
    496         @Override
    497         public <T> T[] getSpans(int start, int end, Class<T> type) {
    498             return null;
    499         }
    500 
    501         @Override
    502         public int getSpanStart(Object tag) {
    503             return 0;
    504         }
    505 
    506         @Override
    507         public int getSpanEnd(Object tag) {
    508             return nonEmptySpans.contains(tag) ? 1 : 0;
    509         }
    510 
    511         @Override
    512         public int getSpanFlags(Object tag) {
    513             return 0;
    514         }
    515 
    516         @Override
    517         public int nextSpanTransition(int start, int limit, Class type) {
    518             return 0;
    519         }
    520     }
    521 }
    522