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 android.graphics.Paint;
     20 import android.test.suitebuilder.annotation.LargeTest;
     21 import android.test.suitebuilder.annotation.SmallTest;
     22 import android.text.Spannable;
     23 import android.text.SpannableString;
     24 import android.text.Spanned;
     25 import android.text.SpannedString;
     26 import android.text.TextPaint;
     27 import android.text.TextUtils;
     28 import android.text.style.StyleSpan;
     29 import android.text.util.Rfc822Token;
     30 import android.text.util.Rfc822Tokenizer;
     31 import android.test.MoreAsserts;
     32 
     33 import com.google.android.collect.Lists;
     34 import com.google.android.collect.Maps;
     35 
     36 import junit.framework.TestCase;
     37 
     38 import java.util.List;
     39 import java.util.Map;
     40 
     41 /**
     42  * TextUtilsTest tests {@link TextUtils}.
     43  */
     44 public class TextUtilsTest extends TestCase {
     45 
     46     @SmallTest
     47     public void testBasic() throws Exception {
     48         assertEquals("", TextUtils.concat());
     49         assertEquals("foo", TextUtils.concat("foo"));
     50         assertEquals("foobar", TextUtils.concat("foo", "bar"));
     51         assertEquals("foobarbaz", TextUtils.concat("foo", "bar", "baz"));
     52 
     53         SpannableString foo = new SpannableString("foo");
     54         foo.setSpan("foo", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     55 
     56         SpannableString bar = new SpannableString("bar");
     57         bar.setSpan("bar", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     58 
     59         SpannableString baz = new SpannableString("baz");
     60         baz.setSpan("baz", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
     61 
     62         assertEquals("foo", TextUtils.concat(foo).toString());
     63         assertEquals("foobar", TextUtils.concat(foo, bar).toString());
     64         assertEquals("foobarbaz", TextUtils.concat(foo, bar, baz).toString());
     65 
     66         assertEquals(1, ((Spanned) TextUtils.concat(foo)).getSpanStart("foo"));
     67 
     68         assertEquals(1, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("foo"));
     69         assertEquals(4, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("bar"));
     70 
     71         assertEquals(1, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("foo"));
     72         assertEquals(4, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("bar"));
     73         assertEquals(7, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("baz"));
     74 
     75         assertTrue(TextUtils.concat("foo", "bar") instanceof String);
     76         assertTrue(TextUtils.concat(foo, bar) instanceof SpannedString);
     77     }
     78 
     79     @SmallTest
     80     public void testTemplateString() throws Exception {
     81         CharSequence result;
     82 
     83         result = TextUtils.expandTemplate("This is a ^1 of the ^2 broadcast ^3.",
     84                                           "test", "emergency", "system");
     85         assertEquals("This is a test of the emergency broadcast system.",
     86                      result.toString());
     87 
     88         result = TextUtils.expandTemplate("^^^1^^^2^3^a^1^^b^^^c",
     89                                           "one", "two", "three");
     90         assertEquals("^one^twothree^aone^b^^c",
     91                      result.toString());
     92 
     93         result = TextUtils.expandTemplate("^");
     94         assertEquals("^", result.toString());
     95 
     96         result = TextUtils.expandTemplate("^^");
     97         assertEquals("^", result.toString());
     98 
     99         result = TextUtils.expandTemplate("^^^");
    100         assertEquals("^^", result.toString());
    101 
    102         result = TextUtils.expandTemplate("shorter ^1 values ^2.", "a", "");
    103         assertEquals("shorter a values .", result.toString());
    104 
    105         try {
    106             TextUtils.expandTemplate("Only ^1 value given, but ^2 used.", "foo");
    107             fail();
    108         } catch (IllegalArgumentException e) {
    109         }
    110 
    111         try {
    112             TextUtils.expandTemplate("^1 value given, and ^0 used.", "foo");
    113             fail();
    114         } catch (IllegalArgumentException e) {
    115         }
    116 
    117         result = TextUtils.expandTemplate("^1 value given, and ^9 used.",
    118                                           "one", "two", "three", "four", "five",
    119                                           "six", "seven", "eight", "nine");
    120         assertEquals("one value given, and nine used.", result.toString());
    121 
    122         try {
    123             TextUtils.expandTemplate("^1 value given, and ^10 used.",
    124                                      "one", "two", "three", "four", "five",
    125                                      "six", "seven", "eight", "nine", "ten");
    126             fail();
    127         } catch (IllegalArgumentException e) {
    128         }
    129 
    130         // putting carets in the values: expansion is not recursive.
    131 
    132         result = TextUtils.expandTemplate("^2", "foo", "^^");
    133         assertEquals("^^", result.toString());
    134 
    135         result = TextUtils.expandTemplate("^^2", "foo", "1");
    136         assertEquals("^2", result.toString());
    137 
    138         result = TextUtils.expandTemplate("^1", "value with ^2 in it", "foo");
    139         assertEquals("value with ^2 in it", result.toString());
    140     }
    141 
    142     /** Fail unless text+spans contains a span 'spanName' with the given start and end. */
    143     private void checkContains(Spanned text, String[] spans, String spanName,
    144                                int start, int end) throws Exception {
    145         for (String i: spans) {
    146             if (i.equals(spanName)) {
    147                 assertEquals(start, text.getSpanStart(i));
    148                 assertEquals(end, text.getSpanEnd(i));
    149                 return;
    150             }
    151         }
    152         fail();
    153     }
    154 
    155     @SmallTest
    156     public void testTemplateSpan() throws Exception {
    157         SpannableString template;
    158         Spanned result;
    159         String[] spans;
    160 
    161         // ordinary replacement
    162 
    163         template = new SpannableString("a^1b");
    164         template.setSpan("before", 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    165         template.setSpan("during", 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    166         template.setSpan("after", 3, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    167         template.setSpan("during+after", 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    168 
    169         result = (Spanned) TextUtils.expandTemplate(template, "foo");
    170         assertEquals(5, result.length());
    171         spans = result.getSpans(0, result.length(), String.class);
    172 
    173         // value is one character longer, so span endpoints should change.
    174         assertEquals(4, spans.length);
    175         checkContains(result, spans, "before", 0, 1);
    176         checkContains(result, spans, "during", 1, 4);
    177         checkContains(result, spans, "after", 4, 5);
    178         checkContains(result, spans, "during+after", 1, 5);
    179 
    180 
    181         // replacement with empty string
    182 
    183         result = (Spanned) TextUtils.expandTemplate(template, "");
    184         assertEquals(2, result.length());
    185         spans = result.getSpans(0, result.length(), String.class);
    186 
    187         // the "during" span should disappear.
    188         assertEquals(3, spans.length);
    189         checkContains(result, spans, "before", 0, 1);
    190         checkContains(result, spans, "after", 1, 2);
    191         checkContains(result, spans, "during+after", 1, 2);
    192     }
    193 
    194     @SmallTest
    195     public void testStringSplitterSimple() {
    196         stringSplitterTestHelper("a,b,cde", new String[] {"a", "b", "cde"});
    197     }
    198 
    199     @SmallTest
    200     public void testStringSplitterEmpty() {
    201         stringSplitterTestHelper("", new String[] {});
    202     }
    203 
    204     @SmallTest
    205     public void testStringSplitterWithLeadingEmptyString() {
    206         stringSplitterTestHelper(",a,b,cde", new String[] {"", "a", "b", "cde"});
    207     }
    208 
    209     @SmallTest
    210     public void testStringSplitterWithInternalEmptyString() {
    211         stringSplitterTestHelper("a,b,,cde", new String[] {"a", "b", "", "cde"});
    212     }
    213 
    214     @SmallTest
    215     public void testStringSplitterWithTrailingEmptyString() {
    216         // A single trailing emtpy string should be ignored.
    217         stringSplitterTestHelper("a,b,cde,", new String[] {"a", "b", "cde"});
    218     }
    219 
    220     private void stringSplitterTestHelper(String string, String[] expectedStrings) {
    221         TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
    222         splitter.setString(string);
    223         List<String> strings = Lists.newArrayList();
    224         for (String s : splitter) {
    225             strings.add(s);
    226         }
    227         MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
    228     }
    229 
    230     @SmallTest
    231     public void testTrim() {
    232         String[] strings = { "abc", " abc", "  abc", "abc ", "abc  ",
    233                              " abc ", "  abc  ", "\nabc\n", "\nabc", "abc\n" };
    234 
    235         for (String s : strings) {
    236             assertEquals(s.trim().length(), TextUtils.getTrimmedLength(s));
    237         }
    238     }
    239 
    240     @SmallTest
    241     public void testRfc822TokenizerFullAddress() {
    242         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("Foo Bar (something) <foo (at) google.com>");
    243         assertNotNull(tokens);
    244         assertEquals(1, tokens.length);
    245         assertEquals("foo (at) google.com", tokens[0].getAddress());
    246         assertEquals("Foo Bar", tokens[0].getName());
    247         assertEquals("something",tokens[0].getComment());
    248     }
    249 
    250     @SmallTest
    251     public void testRfc822TokenizeItemWithError() {
    252         Rfc822Token[] tokens = Rfc822Tokenizer.tokenize("\"Foo Bar\\");
    253         assertNotNull(tokens);
    254         assertEquals(1, tokens.length);
    255         assertEquals("Foo Bar", tokens[0].getAddress());
    256     }
    257 
    258     @LargeTest
    259     public void testEllipsize() {
    260         CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog.";
    261         CharSequence s2 = new Wrapper(s1);
    262         Spannable s3 = new SpannableString(s1);
    263         s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    264         TextPaint p = new TextPaint();
    265         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
    266 
    267         for (int i = 0; i < 100; i++) {
    268             for (int j = 0; j < 3; j++) {
    269                 TextUtils.TruncateAt kind = null;
    270 
    271                 switch (j) {
    272                 case 0:
    273                     kind = TextUtils.TruncateAt.START;
    274                     break;
    275 
    276                 case 1:
    277                     kind = TextUtils.TruncateAt.END;
    278                     break;
    279 
    280                 case 2:
    281                     kind = TextUtils.TruncateAt.MIDDLE;
    282                     break;
    283                 }
    284 
    285                 String out1 = TextUtils.ellipsize(s1, p, i, kind).toString();
    286                 String out2 = TextUtils.ellipsize(s2, p, i, kind).toString();
    287                 String out3 = TextUtils.ellipsize(s3, p, i, kind).toString();
    288 
    289                 String keep1 = TextUtils.ellipsize(s1, p, i, kind, true, null).toString();
    290                 String keep2 = TextUtils.ellipsize(s2, p, i, kind, true, null).toString();
    291                 String keep3 = TextUtils.ellipsize(s3, p, i, kind, true, null).toString();
    292 
    293                 String trim1 = keep1.replace("\uFEFF", "");
    294 
    295                 // Are all normal output strings identical?
    296                 assertEquals("wid " + i + " pass " + j, out1, out2);
    297                 assertEquals("wid " + i + " pass " + j, out2, out3);
    298 
    299                 // Are preserved output strings identical?
    300                 assertEquals("wid " + i + " pass " + j, keep1, keep2);
    301                 assertEquals("wid " + i + " pass " + j, keep2, keep3);
    302 
    303                 // Does trimming padding from preserved yield normal?
    304                 assertEquals("wid " + i + " pass " + j, out1, trim1);
    305 
    306                 // Did preserved output strings preserve length?
    307                 assertEquals("wid " + i + " pass " + j, keep1.length(), s1.length());
    308 
    309                 // Does the output string actually fit in the space?
    310                 assertTrue("wid " + i + " pass " + j, p.measureText(out1) <= i);
    311 
    312                 // Is the padded output the same width as trimmed output?
    313                 assertTrue("wid " + i + " pass " + j, p.measureText(keep1) == p.measureText(out1));
    314             }
    315         }
    316     }
    317 
    318     @SmallTest
    319     public void testDelimitedStringContains() {
    320         assertFalse(TextUtils.delimitedStringContains("", ',', null));
    321         assertFalse(TextUtils.delimitedStringContains(null, ',', ""));
    322         // Whole match
    323         assertTrue(TextUtils.delimitedStringContains("gps", ',', "gps"));
    324         // At beginning.
    325         assertTrue(TextUtils.delimitedStringContains("gps,gpsx,network,mock", ',', "gps"));
    326         assertTrue(TextUtils.delimitedStringContains("gps,network,mock", ',', "gps"));
    327         // In middle, both without, before & after a false match.
    328         assertTrue(TextUtils.delimitedStringContains("network,gps,mock", ',', "gps"));
    329         assertTrue(TextUtils.delimitedStringContains("network,gps,gpsx,mock", ',', "gps"));
    330         assertTrue(TextUtils.delimitedStringContains("network,gpsx,gps,mock", ',', "gps"));
    331         // At the end.
    332         assertTrue(TextUtils.delimitedStringContains("network,mock,gps", ',', "gps"));
    333         assertTrue(TextUtils.delimitedStringContains("network,mock,gpsx,gps", ',', "gps"));
    334         // Not present (but with a false match)
    335         assertFalse(TextUtils.delimitedStringContains("network,mock,gpsx", ',', "gps"));
    336     }
    337 
    338     /**
    339      * CharSequence wrapper for testing the cases where text is copied into
    340      * a char array instead of working from a String or a Spanned.
    341      */
    342     private static class Wrapper implements CharSequence {
    343         private CharSequence mString;
    344 
    345         public Wrapper(CharSequence s) {
    346             mString = s;
    347         }
    348 
    349         public int length() {
    350             return mString.length();
    351         }
    352 
    353         public char charAt(int off) {
    354             return mString.charAt(off);
    355         }
    356 
    357         public String toString() {
    358             return mString.toString();
    359         }
    360 
    361         public CharSequence subSequence(int start, int end) {
    362             return new Wrapper(mString.subSequence(start, end));
    363         }
    364     }
    365 }
    366