blob: 63dd0cc234f2837f5de9b9a60f3e515458fc7b44 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Neal Nguyen1a44d5d2010-01-13 10:42:43 -080017package android.text;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.graphics.Paint;
Martin Wallgrencee20512011-04-07 14:45:43 +020020import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import 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;
The Android Open Source Projectee7e6a72010-06-03 09:03:58 -070030import android.text.util.Rfc822Token;
31import android.text.util.Rfc822Tokenizer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.test.MoreAsserts;
33
Paul Westbrook7762d932009-12-11 14:13:48 -080034import com.google.android.collect.Lists;
35import com.google.android.collect.Maps;
36
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import 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
The Android Open Source Projectee7e6a72010-06-03 09:03:58 -0700241 @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
Mattias Niklewski114f98a2011-01-18 14:27:23 +0100259 @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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 @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();
Eric Fischer1f0dac32009-07-01 18:03:09 -0700283 p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284
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
Brad Fitzpatrick11fe1812010-09-10 16:07:52 -0700336 @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
Martin Wallgrencee20512011-04-07 14:45:43 +0200356 @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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 /**
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}