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