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