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