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.cts;
18 
19 
20 import static android.view.View.LAYOUT_DIRECTION_LTR;
21 import static android.view.View.LAYOUT_DIRECTION_RTL;
22 
23 import android.content.res.ColorStateList;
24 import android.graphics.Canvas;
25 import android.graphics.Color;
26 import android.graphics.Paint;
27 import android.graphics.Paint.FontMetricsInt;
28 import android.graphics.Typeface;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.test.AndroidTestCase;
32 import android.text.GetChars;
33 import android.text.SpannableString;
34 import android.text.SpannableStringBuilder;
35 import android.text.Spanned;
36 import android.text.SpannedString;
37 import android.text.TextPaint;
38 import android.text.TextUtils;
39 import android.text.TextUtils.TruncateAt;
40 import android.text.style.BackgroundColorSpan;
41 import android.text.style.ReplacementSpan;
42 import android.text.style.TextAppearanceSpan;
43 import android.text.style.URLSpan;
44 import android.util.Log;
45 import android.util.StringBuilderPrinter;
46 
47 import dalvik.annotation.TestLevel;
48 import dalvik.annotation.TestTargetNew;
49 
50 import java.util.ArrayList;
51 import java.util.Locale;
52 import java.util.regex.Pattern;
53 
54 /**
55  * Test {@link TextUtils}.
56  */
57 public class TextUtilsTest extends AndroidTestCase {
58     private static String mEllipsis;
59     private int mStart;
60     private int mEnd;
61 
62     @Override
setUp()63     protected void setUp() throws Exception {
64         super.setUp();
65         mEllipsis = getEllipsis();
66         resetRange();
67     }
68 
resetRange()69     private void resetRange() {
70         mStart = -1;
71         mEnd = -1;
72     }
73 
74     /**
75      * Get the ellipsis from system.
76      * @return the string of ellipsis.
77      */
getEllipsis()78     private String getEllipsis() {
79         String text = "xxxxx";
80         TextPaint p = new TextPaint();
81         float width = p.measureText(text.substring(1));
82         String re = TextUtils.ellipsize(text, p, width, TruncateAt.START).toString();
83         return re.substring(0, re.indexOf("x"));
84     }
85 
testCommaEllipsize()86     public void testCommaEllipsize() {
87         TextPaint p = new TextPaint();
88         String text = "long, string, to, truncate";
89 
90         float textWidth = p.measureText("long, 3 plus");
91         // avail is shorter than text width for only one item plus the appropriate ellipsis.
92         // issue 1688347, the expected result for this case does not be described
93         // in the javadoc of commaEllipsize().
94         assertEquals("",
95                 TextUtils.commaEllipsize(text, p, textWidth - 1.4f, "plus 1", "%d plus").toString());
96         // avail is long enough for only one item plus the appropriate ellipsis.
97         assertEquals("long, 3 plus",
98                 TextUtils.commaEllipsize(text, p, textWidth, "plus 1", "%d plus").toString());
99 
100         // avail is long enough for two item plus the appropriate ellipsis.
101         textWidth = p.measureText("long, string, 2 more");
102         assertEquals("long, string, 2 more",
103                 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
104 
105         // avail is long enough for the whole sentence.
106         textWidth = p.measureText("long, string, to, truncate");
107         assertEquals("long, string, to, truncate",
108                 TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
109 
110         // the sentence is extended, avail is NOT long enough for the whole sentence.
111         assertEquals("long, string, to, more 1", TextUtils.commaEllipsize(
112                 text + "-extended", p, textWidth, "more 1", "%d more").toString());
113 
114         // exceptional value
115         assertEquals("", TextUtils.commaEllipsize(text, p, -1f, "plus 1", "%d plus").toString());
116 
117         assertEquals(text, TextUtils.commaEllipsize(
118                 text, p, Float.MAX_VALUE, "more 1", "%d more").toString());
119 
120         assertEquals("long, string, to, null", TextUtils.commaEllipsize(
121                 text + "-extended", p, textWidth, null, "%d more").toString());
122 
123         try {
124             TextUtils.commaEllipsize(null, p, textWidth, "plus 1", "%d plus");
125             fail("Should throw NullPointerException");
126         } catch (NullPointerException e) {
127             // issue 1688347, not clear what is supposed to happen if the text to truncate is null.
128         }
129 
130         try {
131             TextUtils.commaEllipsize(text, null, textWidth, "plus 1", "%d plus");
132             fail("Should throw NullPointerException");
133         } catch (NullPointerException e) {
134             // issue 1688347, not clear what is supposed to happen if TextPaint is null.
135         }
136     }
137 
testConcat()138     public void testConcat() {
139         // issue 1695243
140         // the javadoc for concat() doesn't describe the expected result when parameter is empty.
141         assertEquals("", TextUtils.concat().toString());
142 
143         assertEquals("first", TextUtils.concat("first").toString());
144 
145         assertEquals("first, second", TextUtils.concat("first", ", ", "second").toString());
146 
147         SpannableString string1 = new SpannableString("first");
148         SpannableString string2 = new SpannableString("second");
149         final String url = "www.test_url.com";
150         URLSpan urlSpan = new URLSpan(url);
151         string1.setSpan(urlSpan, 0, string1.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
152         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
153         string2.setSpan(bgColorSpan, 0, string2.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
154 
155         final String comma = ", ";
156         Spanned strResult = (Spanned) TextUtils.concat(string1, comma, string2);
157         assertEquals(string1.toString() + comma + string2.toString(), strResult.toString());
158         Object spans[] = strResult.getSpans(0, strResult.length(), Object.class);
159         assertEquals(2, spans.length);
160         assertTrue(spans[0] instanceof URLSpan);
161         assertEquals(url, ((URLSpan) spans[0]).getURL());
162         assertTrue(spans[1] instanceof BackgroundColorSpan);
163         assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
164         assertEquals(0, strResult.getSpanStart(urlSpan));
165         assertEquals(string1.length() - 1, strResult.getSpanEnd(urlSpan));
166         assertEquals(string1.length() + comma.length(), strResult.getSpanStart(bgColorSpan));
167         assertEquals(strResult.length() - 1, strResult.getSpanEnd(bgColorSpan));
168 
169         assertEquals(string1, TextUtils.concat(string1));
170 
171         // issue 1695243, the javadoc for concat() doesn't describe
172         // the expected result when parameters are null.
173         assertEquals(null, TextUtils.concat((CharSequence) null));
174 
175         try {
176             TextUtils.concat((CharSequence[]) null);
177             fail("Should throw NullPointerException");
178         } catch (NullPointerException e) {
179             // expected
180         }
181     }
182 
testCopySpansFrom()183     public void testCopySpansFrom() {
184         Object[] spans;
185         String text = "content";
186         SpannableString source1 = new SpannableString(text);
187         int midPos = source1.length() / 2;
188         final String url = "www.test_url.com";
189         URLSpan urlSpan = new URLSpan(url);
190         source1.setSpan(urlSpan, 0, midPos, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
191         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
192         source1.setSpan(bgColorSpan, midPos - 1,
193                 source1.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
194 
195         // normal test
196         SpannableString dest1 = new SpannableString(text);
197         TextUtils.copySpansFrom(source1, 0, source1.length(), Object.class, dest1, 0);
198         spans = dest1.getSpans(0, dest1.length(), Object.class);
199         assertEquals(2, spans.length);
200         assertTrue(spans[0] instanceof URLSpan);
201         assertEquals(url, ((URLSpan) spans[0]).getURL());
202         assertTrue(spans[1] instanceof BackgroundColorSpan);
203         assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
204         assertEquals(0, dest1.getSpanStart(urlSpan));
205         assertEquals(midPos, dest1.getSpanEnd(urlSpan));
206         assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, dest1.getSpanFlags(urlSpan));
207         assertEquals(midPos - 1, dest1.getSpanStart(bgColorSpan));
208         assertEquals(source1.length() - 1, dest1.getSpanEnd(bgColorSpan));
209         assertEquals(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, dest1.getSpanFlags(bgColorSpan));
210 
211         SpannableString source2 = new SpannableString(text);
212         source2.setSpan(urlSpan, 0, source2.length() - 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
213         SpannableString dest2 = new SpannableString(text);
214         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest2, 0);
215         spans = dest2.getSpans(0, dest2.length(), Object.class);
216         assertEquals(1, spans.length);
217         assertTrue(spans[0] instanceof URLSpan);
218         assertEquals(url, ((URLSpan) spans[0]).getURL());
219         assertEquals(0, dest2.getSpanStart(urlSpan));
220         assertEquals(source2.length() - 1, dest2.getSpanEnd(urlSpan));
221         assertEquals(Spanned.SPAN_EXCLUSIVE_INCLUSIVE, dest2.getSpanFlags(urlSpan));
222 
223         SpannableString dest3 = new SpannableString(text);
224         TextUtils.copySpansFrom(source2, 0, source2.length(), BackgroundColorSpan.class, dest3, 0);
225         spans = dest3.getSpans(0, dest3.length(), Object.class);
226         assertEquals(0, spans.length);
227         TextUtils.copySpansFrom(source2, 0, source2.length(), URLSpan.class, dest3, 0);
228         spans = dest3.getSpans(0, dest3.length(), Object.class);
229         assertEquals(1, spans.length);
230 
231         SpannableString dest4 = new SpannableString("short");
232         try {
233             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest4, 0);
234             fail("Should throw IndexOutOfBoundsException");
235         } catch (IndexOutOfBoundsException e) {
236             // expected
237         }
238         TextUtils.copySpansFrom(source2, 0, dest4.length(), Object.class, dest4, 0);
239         spans = dest4.getSpans(0, dest4.length(), Object.class);
240         assertEquals(1, spans.length);
241         assertEquals(0, dest4.getSpanStart(spans[0]));
242         // issue 1688347, not clear the expected result when 'start ~ end' only
243         // covered a part of the span.
244         assertEquals(dest4.length(), dest4.getSpanEnd(spans[0]));
245 
246         SpannableString dest5 = new SpannableString("longer content");
247         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 0);
248         spans = dest5.getSpans(0, 1, Object.class);
249         assertEquals(1, spans.length);
250 
251         dest5 = new SpannableString("longer content");
252         TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 2);
253         spans = dest5.getSpans(0, 1, Object.class);
254         assertEquals(0, spans.length);
255         spans = dest5.getSpans(2, dest5.length(), Object.class);
256         assertEquals(1, spans.length);
257         try {
258             TextUtils.copySpansFrom(source2, 0, source2.length(),
259                     Object.class, dest5, dest5.length() - source2.length() + 2);
260             fail("Should throw IndexOutOfBoundsException");
261         } catch (IndexOutOfBoundsException e) {
262             // expected
263         }
264 
265         // issue 1688347, no javadoc about the expected behavior of the exceptional argument.
266         // exceptional source start
267         SpannableString dest6 = new SpannableString("exceptional test");
268         TextUtils.copySpansFrom(source2, -1, source2.length(), Object.class, dest6, 0);
269         spans = dest6.getSpans(0, dest6.length(), Object.class);
270         assertEquals(1, spans.length);
271         dest6 = new SpannableString("exceptional test");
272         TextUtils.copySpansFrom(source2, Integer.MAX_VALUE, source2.length() - 1,
273                     Object.class, dest6, 0);
274         spans = dest6.getSpans(0, dest6.length(), Object.class);
275         assertEquals(0, spans.length);
276 
277         // exceptional source end
278         dest6 = new SpannableString("exceptional test");
279         TextUtils.copySpansFrom(source2, 0, -1, Object.class, dest6, 0);
280         spans = dest6.getSpans(0, dest6.length(), Object.class);
281         assertEquals(0, spans.length);
282         TextUtils.copySpansFrom(source2, 0, Integer.MAX_VALUE, Object.class, dest6, 0);
283         spans = dest6.getSpans(0, dest6.length(), Object.class);
284         assertEquals(1, spans.length);
285 
286         // exceptional class kind
287         dest6 = new SpannableString("exceptional test");
288         TextUtils.copySpansFrom(source2, 0, source2.length(), null, dest6, 0);
289         spans = dest6.getSpans(0, dest6.length(), Object.class);
290         assertEquals(1, spans.length);
291 
292         // exceptional destination offset
293         dest6 = new SpannableString("exceptional test");
294         try {
295             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest6, -1);
296             fail("Should throw IndexOutOfBoundsException");
297         } catch (IndexOutOfBoundsException e) {
298             // expect
299         }
300         try {
301             TextUtils.copySpansFrom(source2, 0, source2.length(),
302                     Object.class, dest6, Integer.MAX_VALUE);
303             fail("Should throw IndexOutOfBoundsException");
304         } catch (IndexOutOfBoundsException e) {
305             // expect
306         }
307 
308         // exceptional source
309         try {
310             TextUtils.copySpansFrom(null, 0, source2.length(), Object.class, dest6, 0);
311             fail("Should throw NullPointerException");
312         } catch (NullPointerException e) {
313             // expect
314         }
315 
316         // exceptional destination
317         try {
318             TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, null, 0);
319             fail("Should throw NullPointerException");
320         } catch (NullPointerException e) {
321             // expect
322         }
323     }
324 
testEllipsize()325     public void testEllipsize() {
326         TextPaint p = new TextPaint();
327 
328         // turn off kerning. with kerning enabled, different methods of measuring the same text
329         // produce different results.
330         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
331 
332         CharSequence text = "long string to truncate";
333 
334         float textWidth = p.measureText(mEllipsis) + p.measureText("uncate");
335         assertEquals(mEllipsis + "uncate",
336                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
337 
338         textWidth = p.measureText("long str") + p.measureText(mEllipsis);
339         assertEquals("long str" + mEllipsis,
340                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
341 
342         textWidth = p.measureText("long") + p.measureText(mEllipsis) + p.measureText("ate");
343         assertEquals("long" + mEllipsis + "ate",
344                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
345 
346         // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
347         // In the code it looks like this does the same as MIDDLE.
348         // In other methods, MARQUEE is equivalent to END, except for the first line.
349         assertEquals("long" + mEllipsis + "ate",
350                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE).toString());
351 
352         textWidth = p.measureText(mEllipsis);
353         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
354         assertEquals("", TextUtils.ellipsize(text, p, textWidth - 1, TruncateAt.END).toString());
355         assertEquals("", TextUtils.ellipsize(text, p, -1f, TruncateAt.END).toString());
356         assertEquals(text,
357                 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END).toString());
358 
359         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
360         assertEquals("", TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
361 
362         try {
363             TextUtils.ellipsize(text, null, textWidth, TruncateAt.MIDDLE);
364             fail("Should throw NullPointerException");
365         } catch (NullPointerException e) {
366             // expected
367         }
368 
369         try {
370             TextUtils.ellipsize(null, p, textWidth, TruncateAt.MIDDLE);
371             fail("Should throw NullPointerException");
372         } catch (NullPointerException e) {
373             // expected
374         }
375     }
376 
testEllipsizeCallback()377     public void testEllipsizeCallback() {
378         TextPaint p = new TextPaint();
379 
380         // turn off kerning. with kerning enabled, different methods of measuring the same text
381         // produce different results.
382         p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
383 
384         TextUtils.EllipsizeCallback callback = new TextUtils.EllipsizeCallback() {
385             public void ellipsized(final int start, final int end) {
386                 mStart = start;
387                 mEnd = end;
388             }
389         };
390 
391         String text = "long string to truncate";
392 
393         // TruncateAt.START, does not specify preserveLength
394         resetRange();
395         float textWidth = p.measureText(mEllipsis + "uncate");
396         assertEquals(mEllipsis + "uncate",
397                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, false,
398                         callback).toString());
399         assertEquals(0, mStart);
400         assertEquals(text.length() - "uncate".length(), mEnd);
401 
402         // TruncateAt.START, specify preserveLength
403         resetRange();
404         int ellipsisNum = text.length() - "uncate".length();
405         assertEquals(getBlankString(true, ellipsisNum) + "uncate",
406                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, true,
407                         callback).toString());
408         assertEquals(0, mStart);
409         assertEquals(text.length() - "uncate".length(), mEnd);
410 
411         // TruncateAt.END, specify preserveLength
412         resetRange();
413         textWidth = p.measureText("long str") + p.measureText(mEllipsis);
414         ellipsisNum = text.length() - "long str".length();
415         assertEquals("long str" + getBlankString(true, ellipsisNum),
416                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
417         assertEquals("long str".length(), mStart);
418         assertEquals(text.length(), mEnd);
419 
420         // TruncateAt.MIDDLE, specify preserveLength
421         resetRange();
422         textWidth = p.measureText("long" + mEllipsis + "ate");
423         ellipsisNum = text.length() - "long".length() - "ate".length();
424         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
425                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
426                         callback).toString());
427         assertEquals("long".length(), mStart);
428         assertEquals(text.length() - "ate".length(), mEnd);
429 
430         // TruncateAt.MIDDLE, specify preserveLength, but does not specify callback.
431         resetRange();
432         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
433                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
434                         null).toString());
435         assertEquals(-1, mStart);
436         assertEquals(-1, mEnd);
437 
438         // TruncateAt.MARQUEE, specify preserveLength
439         // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
440         // In the code it looks like this does the same as MIDDLE.
441         // In other methods, MARQUEE is equivalent to END, except for the first line.
442         resetRange();
443         textWidth = p.measureText("long" + mEllipsis + "ate");
444         ellipsisNum = text.length() - "long".length() - "ate".length();
445         assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
446                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE, true,
447                         callback).toString());
448         assertEquals("long".length(), mStart);
449         assertEquals(text.length() - "ate".length(), mEnd);
450 
451         // avail is not long enough for ELLIPSIS, and preserveLength is specified.
452         resetRange();
453         textWidth = p.measureText(mEllipsis);
454         assertEquals(getBlankString(false, text.length()),
455                 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, true,
456                         callback).toString());
457         assertEquals(0, mStart);
458         assertEquals(text.length(), mEnd);
459 
460         // avail is not long enough for ELLIPSIS, and preserveLength doesn't be specified.
461         resetRange();
462         assertEquals("",
463                 TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, false,
464                         callback).toString());
465         assertEquals(0, mStart);
466         assertEquals(text.length(), mEnd);
467 
468         // avail is long enough for ELLIPSIS, and preserveLength is specified.
469         resetRange();
470         assertEquals(getBlankString(false, text.length()),
471                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
472         assertEquals(0, mStart);
473         assertEquals(text.length(), mEnd);
474 
475         // avail is long enough for ELLIPSIS, and preserveLength doesn't be specified.
476         resetRange();
477         assertEquals("",
478                 TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, false,
479                         callback).toString());
480         assertEquals(0, mStart);
481         assertEquals(text.length(), mEnd);
482 
483         // avail is long enough for the whole sentence.
484         resetRange();
485         assertEquals(text,
486                 TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END, true,
487                         callback).toString());
488         assertEquals(0, mStart);
489         assertEquals(0, mEnd);
490 
491         textWidth = p.measureText("long str" + mEllipsis);
492         try {
493             TextUtils.ellipsize(text, null, textWidth, TruncateAt.END, true, callback);
494         } catch (NullPointerException e) {
495             // expected
496         }
497 
498         try {
499             TextUtils.ellipsize(null, p, textWidth, TruncateAt.END, true, callback);
500         } catch (NullPointerException e) {
501             // expected
502         }
503     }
504 
505     /**
506      * Get a blank string which is filled up by '\uFEFF'.
507      *
508      * @param isNeedStart - boolean whether need to start with char '\u2026' in the string.
509      * @param len - int length of string.
510      * @return a blank string which is filled up by '\uFEFF'.
511      */
getBlankString(boolean isNeedStart, int len)512     private String getBlankString(boolean isNeedStart, int len) {
513         StringBuilder buf = new StringBuilder();
514 
515         int i = 0;
516         if (isNeedStart) {
517             buf.append('\u2026');
518             i++;
519         }
520         for (; i < len; i++) {
521             buf.append('\uFEFF');
522         }
523 
524         return buf.toString();
525     }
526 
testEquals()527     public void testEquals() {
528         // compare with itself.
529         // String is a subclass of CharSequence and overrides equals().
530         String string = "same object";
531         assertTrue(TextUtils.equals(string, string));
532 
533         // SpannableString is a subclass of CharSequence and does NOT override equals().
534         SpannableString spanString = new SpannableString("same object");
535         final String url = "www.test_url.com";
536         spanString.setSpan(new URLSpan(url), 0, spanString.length(),
537                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
538         assertTrue(TextUtils.equals(spanString, spanString));
539 
540         // compare with other objects which have same content.
541         assertTrue(TextUtils.equals("different object", "different object"));
542 
543         SpannableString urlSpanString = new SpannableString("same content");
544         SpannableString bgColorSpanString = new SpannableString(
545                 "same content");
546         URLSpan urlSpan = new URLSpan(url);
547         urlSpanString.setSpan(urlSpan, 0, urlSpanString.length(),
548                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
549         BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
550         bgColorSpanString.setSpan(bgColorSpan, 0, bgColorSpanString.length(),
551                 Spanned.SPAN_INCLUSIVE_INCLUSIVE);
552 
553         assertTrue(TextUtils.equals(bgColorSpanString, urlSpanString));
554 
555         // compare with other objects which have different content.
556         assertFalse(TextUtils.equals("different content A", "different content B"));
557         assertFalse(TextUtils.equals(spanString, urlSpanString));
558         assertFalse(TextUtils.equals(spanString, bgColorSpanString));
559 
560         // compare with null
561         assertTrue(TextUtils.equals(null, null));
562         assertFalse(TextUtils.equals(spanString, null));
563         assertFalse(TextUtils.equals(null, string));
564     }
565 
testExpandTemplate()566     public void testExpandTemplate() {
567         // ^1 at the start of template string.
568         assertEquals("value1 template to be expanded",
569                 TextUtils.expandTemplate("^1 template to be expanded", "value1").toString());
570         // ^1 at the end of template string.
571         assertEquals("template to be expanded value1",
572                 TextUtils.expandTemplate("template to be expanded ^1", "value1").toString());
573         // ^1 in the middle of template string.
574         assertEquals("template value1 to be expanded",
575                 TextUtils.expandTemplate("template ^1 to be expanded", "value1").toString());
576         // ^1 followed by a '0'
577         assertEquals("template value10 to be expanded",
578                 TextUtils.expandTemplate("template ^10 to be expanded", "value1").toString());
579         // ^1 followed by a 'a'
580         assertEquals("template value1a to be expanded",
581                 TextUtils.expandTemplate("template ^1a to be expanded", "value1").toString());
582         // no ^1
583         assertEquals("template ^a to be expanded",
584                 TextUtils.expandTemplate("template ^a to be expanded", "value1").toString());
585         assertEquals("template to be expanded",
586                 TextUtils.expandTemplate("template to be expanded", "value1").toString());
587         // two consecutive ^ in the input to produce a single ^ in the output.
588         assertEquals("template ^ to be expanded",
589                 TextUtils.expandTemplate("template ^^ to be expanded", "value1").toString());
590         // two ^ with a space in the middle.
591         assertEquals("template ^ ^ to be expanded",
592                 TextUtils.expandTemplate("template ^ ^ to be expanded", "value1").toString());
593         // ^1 follow a '^'
594         assertEquals("template ^1 to be expanded",
595                 TextUtils.expandTemplate("template ^^1 to be expanded", "value1").toString());
596         // ^1 followed by a '^'
597         assertEquals("template value1^ to be expanded",
598                 TextUtils.expandTemplate("template ^1^ to be expanded", "value1").toString());
599 
600         // 9 replacement values
601         final int MAX_SUPPORTED_VALUES_NUM = 9;
602         CharSequence values[] = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM);
603         String expected = "value1 value2 template value3 value4 to value5 value6" +
604                 " be value7 value8 expanded value9";
605         String template = "^1 ^2 template ^3 ^4 to ^5 ^6 be ^7 ^8 expanded ^9";
606         assertEquals(expected, TextUtils.expandTemplate(template, values).toString());
607 
608         //  only up to 9 replacement values are supported
609         values = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM + 1);
610         try {
611             TextUtils.expandTemplate(template, values);
612             fail("Should throw IllegalArgumentException!");
613         } catch (IllegalArgumentException e) {
614             // expect
615         }
616 
617         // template string is ^0
618         try {
619             TextUtils.expandTemplate("template ^0 to be expanded", "value1");
620         } catch (IllegalArgumentException e) {
621             // issue 1695243, doesn't discuss the case that ^0 in template string.
622         }
623 
624         // template string is ^0
625         try {
626             TextUtils.expandTemplate("template ^0 to be expanded");
627         } catch (IllegalArgumentException e) {
628             // issue 1695243, doesn't discuss the case that ^0 in template string.
629         }
630 
631         // the template requests 2 values but only 1 is provided
632         try {
633             TextUtils.expandTemplate("template ^2 to be expanded", "value1");
634             fail("Should throw IllegalArgumentException!");
635         } catch (IllegalArgumentException e) {
636             // expect
637         }
638 
639         // values is null
640         try {
641             TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence[]) null);
642         } catch (NullPointerException e) {
643             // expected
644         }
645 
646         // the template requests 2 values but only one null value is provided
647         try {
648             TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence) null);
649             fail("Should throw IllegalArgumentException!");
650         } catch (IllegalArgumentException e) {
651             // expect
652         }
653 
654         // the template requests 2 values and 2 values is provided, but all values are null.
655         try {
656             TextUtils.expandTemplate("template ^2 to be expanded",
657                     (CharSequence) null, (CharSequence) null);
658         } catch (NullPointerException e) {
659             // expected
660         }
661 
662         // the template requests 2 values but no value is provided.
663         try {
664             TextUtils.expandTemplate("template ^2 to be expanded");
665             fail("Should throw IllegalArgumentException!");
666         } catch (IllegalArgumentException e) {
667             // expected
668         }
669 
670         // template is null
671         try {
672             TextUtils.expandTemplate(null, "value1");
673         } catch (NullPointerException e) {
674             // expected
675         }
676     }
677 
678     /**
679      * Create a char sequence array with the specified length
680      * @param len the length of the array
681      * @return The char sequence array with the specified length.
682      * The value of each item is "value[index+1]"
683      */
createCharSequenceArray(int len)684     private CharSequence[] createCharSequenceArray(int len) {
685         CharSequence array[] = new CharSequence[len];
686 
687         for (int i = 0; i < len; i++) {
688             array[i] = "value" + (i + 1);
689         }
690 
691         return array;
692     }
693 
testGetChars()694     public void testGetChars() {
695         char[] destOriginal = "destination".toCharArray();
696         char[] destResult = destOriginal.clone();
697 
698         // check whether GetChars.getChars() is called and with the proper parameters.
699         MockGetChars mockGetChars = new MockGetChars();
700         int start = 1;
701         int end = destResult.length;
702         int destOff = 2;
703         TextUtils.getChars(mockGetChars, start, end, destResult, destOff);
704         assertTrue(mockGetChars.hasCalledGetChars());
705         assertEquals(start, mockGetChars.ReadGetCharsParams().start);
706         assertEquals(end, mockGetChars.ReadGetCharsParams().end);
707         assertEquals(destResult, mockGetChars.ReadGetCharsParams().dest);
708         assertEquals(destOff, mockGetChars.ReadGetCharsParams().destoff);
709 
710         // use MockCharSequence to do the test includes corner cases.
711         MockCharSequence mockCharSequence = new MockCharSequence("source string mock");
712         // get chars to place at the beginning of the destination except the latest one char.
713         destResult = destOriginal.clone();
714         start = 0;
715         end = destResult.length - 1;
716         destOff = 0;
717         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
718         // chars before end are copied from the mockCharSequence.
719         for (int i = 0; i < end - start; i++) {
720             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
721         }
722         // chars after end doesn't be changed.
723         for (int i = destOff + (end - start); i < destOriginal.length; i++) {
724             assertEquals(destOriginal[i], destResult[i]);
725         }
726 
727         // get chars to place at the end of the destination except the earliest two chars.
728         destResult = destOriginal.clone();
729         start = 0;
730         end = destResult.length - 2;
731         destOff = 2;
732         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
733         // chars before start doesn't be changed.
734         for (int i = 0; i < destOff; i++) {
735             assertEquals(destOriginal[i], destResult[i]);
736         }
737         // chars after start are copied from the mockCharSequence.
738         for (int i = 0; i < end - start; i++) {
739             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
740         }
741 
742         // get chars to place at the end of the destination except the earliest two chars
743         // and the latest one word.
744         destResult = destOriginal.clone();
745         start = 1;
746         end = destResult.length - 2;
747         destOff = 0;
748         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
749         for (int i = 0; i < destOff; i++) {
750             assertEquals(destOriginal[i], destResult[i]);
751         }
752         for (int i = 0; i < end - start; i++) {
753             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
754         }
755         for (int i = destOff + (end - start); i < destOriginal.length; i++) {
756             assertEquals(destOriginal[i], destResult[i]);
757         }
758 
759         // get chars to place the whole of the destination
760         destResult = destOriginal.clone();
761         start = 0;
762         end = destResult.length;
763         destOff = 0;
764         TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
765         for (int i = 0; i < end - start; i++) {
766             assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
767         }
768 
769         // exceptional start.
770         end = 2;
771         destOff = 0;
772         destResult = destOriginal.clone();
773         try {
774             TextUtils.getChars(mockCharSequence, -1, end, destResult, destOff);
775             fail("Should throw IndexOutOfBoundsException!");
776         } catch (IndexOutOfBoundsException e) {
777             // expected
778         }
779 
780         destResult = destOriginal.clone();
781         TextUtils.getChars(mockCharSequence, Integer.MAX_VALUE, end, destResult, destOff);
782         for (int i = 0; i < destResult.length; i++) {
783             assertEquals(destOriginal[i], destResult[i]);
784         }
785 
786         // exceptional end.
787         destResult = destOriginal.clone();
788         start = 0;
789         destOff = 0;
790         try {
791             TextUtils.getChars(mockCharSequence, start, destResult.length + 1, destResult, destOff);
792             fail("Should throw IndexOutOfBoundsException!");
793         } catch (IndexOutOfBoundsException e) {
794             // expected
795         }
796 
797         destResult = destOriginal.clone();
798         TextUtils.getChars(mockCharSequence, start, -1, destResult, destOff);
799         for (int i = 0; i < destResult.length; i++) {
800             assertEquals(destOriginal[i], destResult[i]);
801         }
802 
803         // exceptional destOff.
804         destResult = destOriginal.clone();
805         start = 0;
806         end = 2;
807         try {
808             TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MAX_VALUE);
809             fail("Should throw IndexOutOfBoundsException!");
810         } catch (IndexOutOfBoundsException e) {
811             // expect
812         }
813         try {
814             TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MIN_VALUE);
815             fail("Should throw IndexOutOfBoundsException!");
816         } catch (IndexOutOfBoundsException e) {
817             // expect
818         }
819 
820         // exceptional source
821         start = 0;
822         end = 2;
823         destOff =0;
824         try {
825             TextUtils.getChars(null, start, end, destResult, destOff);
826             fail("Should throw NullPointerException!");
827         } catch (NullPointerException e) {
828             // expected
829         }
830 
831         // exceptional destination
832         try {
833             TextUtils.getChars(mockCharSequence, start, end, null, destOff);
834             fail("Should throw NullPointerException!");
835         } catch (NullPointerException e) {
836             // expected
837         }
838     }
839 
840     /**
841      * MockGetChars for test.
842      */
843     private class MockGetChars implements GetChars {
844         private boolean mHasCalledGetChars;
845         private GetCharsParams mGetCharsParams = new GetCharsParams();
846 
847         class GetCharsParams {
848             int start;
849             int end;
850             char[] dest;
851             int destoff;
852         }
853 
hasCalledGetChars()854         public boolean hasCalledGetChars() {
855             return mHasCalledGetChars;
856         }
857 
reset()858         public void reset() {
859             mHasCalledGetChars = false;
860         }
861 
ReadGetCharsParams()862         public GetCharsParams ReadGetCharsParams() {
863             return mGetCharsParams;
864         }
865 
getChars(int start, int end, char[] dest, int destoff)866         public void getChars(int start, int end, char[] dest, int destoff) {
867             mHasCalledGetChars = true;
868             mGetCharsParams.start = start;
869             mGetCharsParams.end = end;
870             mGetCharsParams.dest = dest;
871             mGetCharsParams.destoff = destoff;
872         }
873 
charAt(int arg0)874         public char charAt(int arg0) {
875             return 0;
876         }
877 
length()878         public int length() {
879             return 100;
880         }
881 
subSequence(int arg0, int arg1)882         public CharSequence subSequence(int arg0, int arg1) {
883             return null;
884         }
885     }
886 
887     /**
888      * MockCharSequence for test.
889      */
890     private class MockCharSequence implements CharSequence {
891         private char mText[];
892 
MockCharSequence()893         public MockCharSequence() {
894             this("");
895         }
896 
MockCharSequence(String text)897         public MockCharSequence(String text) {
898             mText = text.toCharArray();
899         }
900 
charAt(int arg0)901         public char charAt(int arg0) {
902             if (arg0 >= 0 && arg0 < mText.length) {
903                 return mText[arg0];
904             }
905             throw new IndexOutOfBoundsException();
906         }
907 
length()908         public int length() {
909             return mText.length;
910         }
911 
subSequence(int arg0, int arg1)912         public CharSequence subSequence(int arg0, int arg1) {
913             return null;
914         }
915     }
916 
testGetOffsetAfter()917     public void testGetOffsetAfter() {
918         // the first '\uD800' is index 9, the second 'uD800' is index 16
919         // the '\uDBFF' is index 26
920         final int POS_FIRST_D800 = 9;       // the position of the first '\uD800'.
921         final int POS_SECOND_D800 = 16;
922         final int POS_FIRST_DBFF = 26;
923         final int SUPPLEMENTARY_CHARACTERS_OFFSET = 2;  // the offset for a supplementary characters
924         final int NORMAL_CHARACTERS_OFFSET = 1;
925         SpannableString text = new SpannableString(
926                 "string to\uD800\uDB00 get \uD800\uDC00 offset \uDBFF\uDFFF after");
927         assertEquals(0 + 1, TextUtils.getOffsetAfter(text, 0));
928         assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length()));
929         assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length() - 1));
930         assertEquals(POS_FIRST_D800 + NORMAL_CHARACTERS_OFFSET,
931                 TextUtils.getOffsetAfter(text, POS_FIRST_D800));
932         assertEquals(POS_SECOND_D800 + SUPPLEMENTARY_CHARACTERS_OFFSET,
933                 TextUtils.getOffsetAfter(text, POS_SECOND_D800));
934         assertEquals(POS_FIRST_DBFF + SUPPLEMENTARY_CHARACTERS_OFFSET,
935                 TextUtils.getOffsetAfter(text, POS_FIRST_DBFF));
936 
937         // the CharSequence string has a span.
938         MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
939         text.setSpan(mockReplacementSpan, POS_FIRST_D800 - 1, text.length() - 1,
940                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
941         assertEquals(text.length() - 1, TextUtils.getOffsetAfter(text, POS_FIRST_D800));
942 
943         try {
944             TextUtils.getOffsetAfter(text, -1);
945             fail("Should throw IndexOutOfBoundsException!");
946         } catch (IndexOutOfBoundsException e) {
947         }
948 
949         try {
950             TextUtils.getOffsetAfter(text, Integer.MAX_VALUE);
951             fail("Should throw IndexOutOfBoundsException!");
952         } catch (IndexOutOfBoundsException e) {
953         }
954 
955         try {
956             TextUtils.getOffsetAfter(null, 0);
957             fail("Should throw NullPointerException!");
958         } catch (NullPointerException e) {
959             // expected
960         }
961     }
962 
963     /**
964      * MockReplacementSpan for test.
965      */
966     private class MockReplacementSpan extends ReplacementSpan {
967         @Override
draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint)968         public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
969                 int y, int bottom, Paint paint) {
970         }
971 
972         @Override
getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm)973         public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
974             return 0;
975         }
976     }
977 
testGetOffsetBefore()978     public void testGetOffsetBefore() {
979         // the first '\uDC00' is index 10, the second 'uDC00' is index 17
980         // the '\uDFFF' is index 27
981         final int POS_FIRST_DC00 = 10;
982         final int POS_SECOND_DC00 = 17;
983         final int POS_FIRST_DFFF = 27;
984         final int SUPPLYMENTARY_CHARACTERS_OFFSET = 2;
985         final int NORMAL_CHARACTERS_OFFSET = 1;
986         SpannableString text = new SpannableString(
987                 "string to\uD700\uDC00 get \uD800\uDC00 offset \uDBFF\uDFFF before");
988         assertEquals(0, TextUtils.getOffsetBefore(text, 0));
989         assertEquals(0, TextUtils.getOffsetBefore(text, 1));
990         assertEquals(text.length() - 1, TextUtils.getOffsetBefore(text, text.length()));
991         assertEquals(POS_FIRST_DC00 + 1 - NORMAL_CHARACTERS_OFFSET,
992                 TextUtils.getOffsetBefore(text, POS_FIRST_DC00 + 1));
993         assertEquals(POS_SECOND_DC00 + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
994                 TextUtils.getOffsetBefore(text, POS_SECOND_DC00 + 1));
995         assertEquals(POS_FIRST_DFFF + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
996                 TextUtils.getOffsetBefore(text, POS_FIRST_DFFF + 1));
997 
998         // the CharSequence string has a span.
999         MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
1000         text.setSpan(mockReplacementSpan, 0, POS_FIRST_DC00 + 1,
1001                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
1002         assertEquals(0, TextUtils.getOffsetBefore(text, POS_FIRST_DC00));
1003 
1004         try {
1005             TextUtils.getOffsetBefore(text, -1);
1006             fail("Should throw IndexOutOfBoundsException!");
1007         } catch (IndexOutOfBoundsException e) {
1008         }
1009 
1010         try {
1011             TextUtils.getOffsetBefore(text, Integer.MAX_VALUE);
1012             fail("Should throw IndexOutOfBoundsException!");
1013         } catch (IndexOutOfBoundsException e) {
1014         }
1015 
1016         try {
1017             TextUtils.getOffsetBefore(null, POS_FIRST_DC00);
1018             fail("Should throw NullPointerException!");
1019         } catch (NullPointerException e) {
1020             // expected
1021         }
1022     }
1023 
testGetReverse()1024     public void testGetReverse() {
1025         String source = "string to be reversed";
1026         assertEquals("gnirts", TextUtils.getReverse(source, 0, "string".length()).toString());
1027         assertEquals("desrever",
1028                 TextUtils.getReverse(source, source.length() - "reversed".length(),
1029                         source.length()).toString());
1030         assertEquals("", TextUtils.getReverse(source, 0, 0).toString());
1031 
1032         // issue 1695243, exception is thrown after the result of some cases
1033         // convert to a string, is this expected?
1034         CharSequence result = TextUtils.getReverse(source, -1, "string".length());
1035         try {
1036             result.toString();
1037             fail("Should throw IndexOutOfBoundsException!");
1038         } catch (IndexOutOfBoundsException e) {
1039         }
1040 
1041         TextUtils.getReverse(source, 0, source.length() + 1);
1042         try {
1043             result.toString();
1044             fail("Should throw IndexOutOfBoundsException!");
1045         } catch (IndexOutOfBoundsException e) {
1046         }
1047 
1048         TextUtils.getReverse(source, "string".length(), 0);
1049         try {
1050             result.toString();
1051             fail("Should throw IndexOutOfBoundsException!");
1052         } catch (IndexOutOfBoundsException e) {
1053         }
1054 
1055         TextUtils.getReverse(source, 0, Integer.MAX_VALUE);
1056         try {
1057             result.toString();
1058             fail("Should throw IndexOutOfBoundsException!");
1059         } catch (IndexOutOfBoundsException e) {
1060         }
1061 
1062         TextUtils.getReverse(source, Integer.MIN_VALUE, "string".length());
1063         try {
1064             result.toString();
1065             fail("Should throw IndexOutOfBoundsException!");
1066         } catch (IndexOutOfBoundsException e) {
1067         }
1068 
1069         TextUtils.getReverse(null, 0, "string".length());
1070         try {
1071             result.toString();
1072             fail("Should throw IndexOutOfBoundsException!");
1073         } catch (IndexOutOfBoundsException e) {
1074             // expected
1075         }
1076     }
1077 
testGetTrimmedLength()1078     public void testGetTrimmedLength() {
1079         assertEquals("normalstring".length(), TextUtils.getTrimmedLength("normalstring"));
1080         assertEquals("normal string".length(), TextUtils.getTrimmedLength("normal string"));
1081         assertEquals("blank before".length(), TextUtils.getTrimmedLength(" \t  blank before"));
1082         assertEquals("blank after".length(), TextUtils.getTrimmedLength("blank after   \n    "));
1083         assertEquals("blank both".length(), TextUtils.getTrimmedLength(" \t   blank both  \n "));
1084 
1085         char[] allTrimmedChars = new char[] {
1086                 '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
1087                 '\u0008', '\u0009', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015',
1088                 '\u0016', '\u0017', '\u0018', '\u0019', '\u0020'
1089         };
1090         assertEquals(0, TextUtils.getTrimmedLength(String.valueOf(allTrimmedChars)));
1091 
1092         try {
1093             TextUtils.getTrimmedLength(null);
1094             fail("Should throw NullPointerException!");
1095         } catch (NullPointerException e) {
1096             // expected
1097         }
1098     }
1099 
testHtmlEncode()1100     public void testHtmlEncode() {
1101         assertEquals("&lt;_html_&gt;\\ &amp;&quot;&#39;string&#39;&quot;",
1102                 TextUtils.htmlEncode("<_html_>\\ &\"'string'\""));
1103 
1104          try {
1105              TextUtils.htmlEncode(null);
1106              fail("Should throw NullPointerException!");
1107          } catch (NullPointerException e) {
1108              // expected
1109          }
1110     }
1111 
testIndexOf1()1112     public void testIndexOf1() {
1113         String searchString = "string to be searched";
1114         final int INDEX_OF_FIRST_R = 2;     // first occurrence of 'r'
1115         final int INDEX_OF_FIRST_T = 1;
1116         final int INDEX_OF_FIRST_D = searchString.length() - 1;
1117 
1118         assertEquals(INDEX_OF_FIRST_T, TextUtils.indexOf(searchString, 't'));
1119         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r'));
1120         assertEquals(INDEX_OF_FIRST_D, TextUtils.indexOf(searchString, 'd'));
1121         assertEquals(-1, TextUtils.indexOf(searchString, 'f'));
1122 
1123         StringBuffer stringBuffer = new StringBuffer(searchString);
1124         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuffer, 'r'));
1125 
1126         StringBuilder stringBuilder = new StringBuilder(searchString);
1127         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuilder, 'r'));
1128 
1129         MockGetChars mockGetChars = new MockGetChars();
1130         assertFalse(mockGetChars.hasCalledGetChars());
1131         TextUtils.indexOf(mockGetChars, 'r');
1132         assertTrue(mockGetChars.hasCalledGetChars());
1133 
1134         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1135         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(mockCharSequence, 'r'));
1136     }
1137 
testIndexOf2()1138     public void testIndexOf2() {
1139         String searchString = "string to be searched";
1140         final int INDEX_OF_FIRST_R = 2;
1141         final int INDEX_OF_SECOND_R = 16;
1142 
1143         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', 0));
1144         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r', INDEX_OF_FIRST_R + 1));
1145         assertEquals(-1, TextUtils.indexOf(searchString, 'r', searchString.length()));
1146         assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE));
1147         assertEquals(-1, TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE));
1148 
1149         StringBuffer stringBuffer = new StringBuffer(searchString);
1150         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r', INDEX_OF_FIRST_R + 1));
1151         try {
1152             TextUtils.indexOf(stringBuffer, 'r', Integer.MIN_VALUE);
1153             fail("Should throw IndexOutOfBoundsException!");
1154         } catch (IndexOutOfBoundsException e) {
1155             // expect
1156         }
1157         assertEquals(-1, TextUtils.indexOf(stringBuffer, 'r', Integer.MAX_VALUE));
1158 
1159         StringBuilder stringBuilder = new StringBuilder(searchString);
1160         assertEquals(INDEX_OF_SECOND_R,
1161                 TextUtils.indexOf(stringBuilder, 'r', INDEX_OF_FIRST_R + 1));
1162 
1163         MockGetChars mockGetChars = new MockGetChars();
1164         TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1);
1165         assertTrue(mockGetChars.hasCalledGetChars());
1166 
1167         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1168         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
1169                 INDEX_OF_FIRST_R + 1));
1170     }
1171 
testIndexOf3()1172     public void testIndexOf3() {
1173         String searchString = "string to be searched";
1174         final int INDEX_OF_FIRST_R = 2;
1175         final int INDEX_OF_SECOND_R = 16;
1176 
1177         assertEquals(INDEX_OF_FIRST_R,
1178                 TextUtils.indexOf(searchString, 'r', 0, searchString.length()));
1179         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r',
1180                 INDEX_OF_FIRST_R + 1, searchString.length()));
1181         assertEquals(-1, TextUtils.indexOf(searchString, 'r',
1182                 INDEX_OF_FIRST_R + 1, INDEX_OF_SECOND_R));
1183 
1184         try {
1185             TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R);
1186             fail("Should throw IndexOutOfBoundsException!");
1187         } catch (IndexOutOfBoundsException e) {
1188             // expect
1189         }
1190         assertEquals(-1,
1191                 TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE, INDEX_OF_SECOND_R));
1192         assertEquals(-1, TextUtils.indexOf(searchString, 'r', 0, Integer.MIN_VALUE));
1193         try {
1194             TextUtils.indexOf(searchString, 'r', 0, Integer.MAX_VALUE);
1195             fail("Should throw IndexOutOfBoundsException!");
1196         } catch (IndexOutOfBoundsException e) {
1197             // expect
1198         }
1199 
1200         StringBuffer stringBuffer = new StringBuffer(searchString);
1201         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r',
1202                 INDEX_OF_FIRST_R + 1, searchString.length()));
1203 
1204         StringBuilder stringBuilder = new StringBuilder(searchString);
1205         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuilder, 'r',
1206                 INDEX_OF_FIRST_R + 1, searchString.length()));
1207 
1208         MockGetChars mockGetChars = new MockGetChars();
1209         TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1, searchString.length());
1210         assertTrue(mockGetChars.hasCalledGetChars());
1211 
1212         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1213         assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
1214                 INDEX_OF_FIRST_R + 1, searchString.length()));
1215     }
1216 
testIndexOf4()1217     public void testIndexOf4() {
1218         String searchString = "string to be searched by string";
1219         final int SEARCH_INDEX = 13;
1220 
1221         assertEquals(0, TextUtils.indexOf(searchString, "string"));
1222         assertEquals(SEARCH_INDEX, TextUtils.indexOf(searchString, "search"));
1223         assertEquals(-1, TextUtils.indexOf(searchString, "tobe"));
1224         assertEquals(0, TextUtils.indexOf(searchString, ""));
1225 
1226         StringBuffer stringBuffer = new StringBuffer(searchString);
1227         assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuffer, "search"));
1228 
1229         StringBuilder stringBuilder = new StringBuilder(searchString);
1230         assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuilder, "search"));
1231 
1232         MockGetChars mockGetChars = new MockGetChars();
1233         TextUtils.indexOf(mockGetChars, "search");
1234         assertTrue(mockGetChars.hasCalledGetChars());
1235 
1236         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1237         assertEquals(SEARCH_INDEX, TextUtils.indexOf(mockCharSequence, "search"));
1238     }
1239 
testIndexOf5()1240     public void testIndexOf5() {
1241         String searchString = "string to be searched by string";
1242         final int INDEX_OF_FIRST_STRING = 0;
1243         final int INDEX_OF_SECOND_STRING = 25;
1244 
1245         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0));
1246         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
1247                 INDEX_OF_FIRST_STRING + 1));
1248         assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_SECOND_STRING + 1));
1249         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
1250                 Integer.MIN_VALUE));
1251         assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE));
1252 
1253         assertEquals(1, TextUtils.indexOf(searchString, "", 1));
1254         assertEquals(Integer.MAX_VALUE, TextUtils.indexOf(searchString, "", Integer.MAX_VALUE));
1255 
1256         assertEquals(0, TextUtils.indexOf(searchString, searchString, 0));
1257         assertEquals(-1, TextUtils.indexOf(searchString, searchString + "longer needle", 0));
1258 
1259         StringBuffer stringBuffer = new StringBuffer(searchString);
1260         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
1261                 INDEX_OF_FIRST_STRING + 1));
1262         try {
1263             TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE);
1264             fail("Should throw IndexOutOfBoundsException!");
1265         } catch (IndexOutOfBoundsException e) {
1266             // expect
1267         }
1268         assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE));
1269 
1270         StringBuilder stringBuilder = new StringBuilder(searchString);
1271         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
1272                 INDEX_OF_FIRST_STRING + 1));
1273 
1274         MockGetChars mockGetChars = new MockGetChars();
1275         assertFalse(mockGetChars.hasCalledGetChars());
1276         TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1);
1277         assertTrue(mockGetChars.hasCalledGetChars());
1278 
1279         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1280         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
1281                 INDEX_OF_FIRST_STRING + 1));
1282     }
1283 
testIndexOf6()1284     public void testIndexOf6() {
1285         String searchString = "string to be searched by string";
1286         final int INDEX_OF_FIRST_STRING = 0;
1287         final int INDEX_OF_SECOND_STRING = 25;
1288 
1289         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0,
1290                 searchString.length()));
1291         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
1292                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
1293         assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_FIRST_STRING + 1,
1294                 INDEX_OF_SECOND_STRING - 1));
1295         assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
1296                 Integer.MIN_VALUE, INDEX_OF_SECOND_STRING - 1));
1297         assertEquals(-1, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE,
1298                 INDEX_OF_SECOND_STRING - 1));
1299 
1300         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
1301                 INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
1302         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
1303                 INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
1304 
1305         StringBuffer stringBuffer = new StringBuffer(searchString);
1306         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
1307                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
1308         try {
1309             TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE,
1310                     INDEX_OF_SECOND_STRING - 1);
1311             fail("Should throw IndexOutOfBoundsException!");
1312         } catch (IndexOutOfBoundsException e) {
1313             // expect
1314         }
1315         assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE,
1316                 searchString.length()));
1317         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
1318                 "string", INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
1319         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
1320                 "string", INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
1321 
1322         StringBuilder stringBuilder = new StringBuilder(searchString);
1323         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
1324                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
1325 
1326         MockGetChars mockGetChars = new MockGetChars();
1327         TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1, searchString.length());
1328         assertTrue(mockGetChars.hasCalledGetChars());
1329 
1330         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1331         assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
1332                 INDEX_OF_FIRST_STRING + 1, searchString.length()));
1333     }
1334 
testIsDigitsOnly()1335     public void testIsDigitsOnly() {
1336         assertFalse(TextUtils.isDigitsOnly("no digit"));
1337         assertFalse(TextUtils.isDigitsOnly("character and 56 digits"));
1338         assertTrue(TextUtils.isDigitsOnly("0123456789"));
1339         assertFalse(TextUtils.isDigitsOnly("1234 56789"));
1340 
1341         try {
1342             TextUtils.isDigitsOnly(null);
1343             fail("Should throw NullPointerException!");
1344         } catch (NullPointerException e) {
1345             // issue 1695243, not clear what is supposed result if the CharSequence is null.
1346         }
1347     }
1348 
testIsEmpty()1349     public void testIsEmpty() {
1350         assertFalse(TextUtils.isEmpty("not empty"));
1351         assertFalse(TextUtils.isEmpty("    "));
1352         assertTrue(TextUtils.isEmpty(""));
1353         assertTrue(TextUtils.isEmpty(null));
1354     }
1355 
testIsGraphicChar()1356     public void testIsGraphicChar() {
1357         assertTrue(TextUtils.isGraphic('a'));
1358         assertTrue(TextUtils.isGraphic("\uBA00"));
1359 
1360         // LINE_SEPARATOR
1361         assertFalse(TextUtils.isGraphic('\u2028'));
1362 
1363         // PARAGRAPH_SEPARATOR
1364         assertFalse(TextUtils.isGraphic('\u2029'));
1365 
1366         // CONTROL
1367         assertFalse(TextUtils.isGraphic('\u0085'));
1368 
1369         // UNASSIGNED
1370         assertFalse(TextUtils.isGraphic('\u0D00'));
1371 
1372         // SURROGATE
1373         assertFalse(TextUtils.isGraphic('\uD800'));
1374 
1375         // SPACE_SEPARATOR
1376         assertFalse(TextUtils.isGraphic('\u0020'));
1377 
1378         try {
1379             assertFalse(TextUtils.isGraphic((Character) null));
1380             fail("Should throw NullPointerException!");
1381         } catch (NullPointerException e) {
1382             // expected
1383         }
1384     }
1385 
testIsGraphicCharSequence()1386     public void testIsGraphicCharSequence() {
1387         assertTrue(TextUtils.isGraphic("printable characters"));
1388 
1389         assertFalse(TextUtils.isGraphic("\u2028\u2029\u0085\u0D00\uD800\u0020"));
1390 
1391         assertTrue(TextUtils.isGraphic("a\u2028\u2029\u0085\u0D00\uD800\u0020"));
1392 
1393         try {
1394             TextUtils.isGraphic(null);
1395             fail("Should throw NullPointerException!");
1396         } catch (NullPointerException e) {
1397             // expected
1398         }
1399     }
1400 
1401     @SuppressWarnings("unchecked")
testJoin1()1402     public void testJoin1() {
1403         ArrayList<CharSequence> charTokens = new ArrayList<CharSequence>();
1404         charTokens.add("string1");
1405         charTokens.add("string2");
1406         charTokens.add("string3");
1407         assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
1408         assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
1409         assertEquals("string1string2string3", TextUtils.join("", charTokens));
1410 
1411         // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
1412         assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
1413         try {
1414             TextUtils.join("|", (Iterable) null);
1415             fail("Should throw NullPointerException!");
1416         } catch (NullPointerException e) {
1417             // expect
1418         }
1419 
1420         ArrayList<SpannableString> spannableStringTokens = new ArrayList<SpannableString>();
1421         spannableStringTokens.add(new SpannableString("span 1"));
1422         spannableStringTokens.add(new SpannableString("span 2"));
1423         spannableStringTokens.add(new SpannableString("span 3"));
1424         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
1425     }
1426 
testJoin2()1427     public void testJoin2() {
1428         CharSequence[] charTokens = new CharSequence[] { "string1", "string2", "string3" };
1429         assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
1430         assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
1431         assertEquals("string1string2string3", TextUtils.join("", charTokens));
1432 
1433         // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
1434         assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
1435         try {
1436             TextUtils.join("|", (Object[]) null);
1437             fail("Should throw NullPointerException!");
1438         } catch (NullPointerException e) {
1439             // expect
1440         }
1441 
1442         SpannableString[] spannableStringTokens = new SpannableString[] {
1443                 new SpannableString("span 1"),
1444                 new SpannableString("span 2"),
1445                 new SpannableString("span 3") };
1446         assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
1447     }
1448 
testLastIndexOf1()1449     public void testLastIndexOf1() {
1450         String searchString = "string to be searched";
1451         final int INDEX_OF_LAST_R = 16;
1452         final int INDEX_OF_LAST_T = 7;
1453         final int INDEX_OF_LAST_D = searchString.length() - 1;
1454 
1455         assertEquals(INDEX_OF_LAST_T, TextUtils.lastIndexOf(searchString, 't'));
1456         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(searchString, 'r'));
1457         assertEquals(INDEX_OF_LAST_D, TextUtils.lastIndexOf(searchString, 'd'));
1458         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'f'));
1459 
1460         StringBuffer stringBuffer = new StringBuffer(searchString);
1461         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuffer, 'r'));
1462 
1463         StringBuilder stringBuilder = new StringBuilder(searchString);
1464         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuilder, 'r'));
1465 
1466         MockGetChars mockGetChars = new MockGetChars();
1467         TextUtils.lastIndexOf(mockGetChars, 'r');
1468         assertTrue(mockGetChars.hasCalledGetChars());
1469 
1470         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1471         assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(mockCharSequence, 'r'));
1472     }
1473 
testLastIndexOf2()1474     public void testLastIndexOf2() {
1475         String searchString = "string to be searched";
1476         final int INDEX_OF_FIRST_R = 2;
1477         final int INDEX_OF_SECOND_R = 16;
1478 
1479         assertEquals(INDEX_OF_SECOND_R,
1480                 TextUtils.lastIndexOf(searchString, 'r', searchString.length()));
1481         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0));
1482         assertEquals(INDEX_OF_FIRST_R,
1483                 TextUtils.lastIndexOf(searchString, 'r', INDEX_OF_FIRST_R));
1484         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE));
1485         assertEquals(INDEX_OF_SECOND_R,
1486                 TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE));
1487 
1488         StringBuffer stringBuffer = new StringBuffer(searchString);
1489         assertEquals(INDEX_OF_FIRST_R,
1490                 TextUtils.lastIndexOf(stringBuffer, 'r', INDEX_OF_FIRST_R));
1491         assertEquals(-1, TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MIN_VALUE));
1492         assertEquals(INDEX_OF_SECOND_R,
1493                 TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MAX_VALUE));
1494 
1495         StringBuilder stringBuilder = new StringBuilder(searchString);
1496         assertEquals(INDEX_OF_FIRST_R,
1497                 TextUtils.lastIndexOf(stringBuilder, 'r', INDEX_OF_FIRST_R));
1498 
1499         MockGetChars mockGetChars = new MockGetChars();
1500         TextUtils.lastIndexOf(mockGetChars, 'r', INDEX_OF_FIRST_R);
1501         assertTrue(mockGetChars.hasCalledGetChars());
1502 
1503         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1504         assertEquals(INDEX_OF_FIRST_R,
1505                 TextUtils.lastIndexOf(mockCharSequence, 'r', INDEX_OF_FIRST_R));
1506     }
1507 
testLastIndexOf3()1508     public void testLastIndexOf3() {
1509         String searchString = "string to be searched";
1510         final int INDEX_OF_FIRST_R = 2;
1511         final int INDEX_OF_SECOND_R = 16;
1512 
1513         assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
1514                 searchString.length()));
1515         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(searchString, 'r', 0,
1516                 INDEX_OF_SECOND_R - 1));
1517         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, INDEX_OF_FIRST_R - 1));
1518 
1519         try {
1520             TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R - 1);
1521             fail("Should throw IndexOutOfBoundsException!");
1522         } catch (IndexOutOfBoundsException e) {
1523             // expect
1524         }
1525         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE,
1526                 INDEX_OF_SECOND_R - 1));
1527         assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, Integer.MIN_VALUE));
1528         assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
1529                 Integer.MAX_VALUE));
1530 
1531         StringBuffer stringBuffer = new StringBuffer(searchString);
1532         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuffer, 'r', 0,
1533                 INDEX_OF_SECOND_R - 1));
1534 
1535         StringBuilder stringBuilder = new StringBuilder(searchString);
1536         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuilder, 'r', 0,
1537                 INDEX_OF_SECOND_R - 1));
1538 
1539         MockGetChars mockGetChars = new MockGetChars();
1540         TextUtils.lastIndexOf(mockGetChars, 'r', 0, INDEX_OF_SECOND_R - 1);
1541         assertTrue(mockGetChars.hasCalledGetChars());
1542 
1543         MockCharSequence mockCharSequence = new MockCharSequence(searchString);
1544         assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(mockCharSequence, 'r', 0,
1545                 INDEX_OF_SECOND_R - 1));
1546     }
1547 
testRegionMatches()1548     public void testRegionMatches() {
1549         assertFalse(TextUtils.regionMatches("one", 0, "two", 0, "one".length()));
1550         assertTrue(TextUtils.regionMatches("one", 0, "one", 0, "one".length()));
1551         try {
1552             TextUtils.regionMatches("one", 0, "one", 0, "one".length() + 1);
1553             fail("Should throw IndexOutOfBoundsException!");
1554         } catch (IndexOutOfBoundsException e) {
1555         }
1556 
1557         String one = "Hello Android, hello World!";
1558         String two = "Hello World";
1559         // match "Hello"
1560         assertTrue(TextUtils.regionMatches(one, 0, two, 0, "Hello".length()));
1561 
1562         // match "Hello A" and "Hello W"
1563         assertFalse(TextUtils.regionMatches(one, 0, two, 0, "Hello A".length()));
1564 
1565         // match "World"
1566         assertTrue(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
1567                 two, "Hello ".length(), "World".length()));
1568         assertFalse(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
1569                 two, 0, "World".length()));
1570 
1571         try {
1572             TextUtils.regionMatches(one, Integer.MIN_VALUE, two, 0, "Hello".length());
1573             fail("Should throw IndexOutOfBoundsException!");
1574         } catch (IndexOutOfBoundsException e) {
1575         }
1576         try {
1577             TextUtils.regionMatches(one, Integer.MAX_VALUE, two, 0, "Hello".length());
1578             fail("Should throw IndexOutOfBoundsException!");
1579         } catch (IndexOutOfBoundsException e) {
1580         }
1581 
1582         try {
1583             TextUtils.regionMatches(one, 0, two, Integer.MIN_VALUE, "Hello".length());
1584             fail("Should throw IndexOutOfBoundsException!");
1585         } catch (IndexOutOfBoundsException e) {
1586         }
1587         try {
1588             TextUtils.regionMatches(one, 0, two, Integer.MAX_VALUE, "Hello".length());
1589             fail("Should throw IndexOutOfBoundsException!");
1590         } catch (IndexOutOfBoundsException e) {
1591         }
1592 
1593         try {
1594             TextUtils.regionMatches(one, 0, two, 0, Integer.MIN_VALUE);
1595             fail("Should throw IndexOutOfBoundsException!");
1596         } catch (IndexOutOfBoundsException e) {
1597         }
1598         try {
1599             TextUtils.regionMatches(one, 0, two, 0, Integer.MAX_VALUE);
1600             fail("Should throw IndexOutOfBoundsException!");
1601         } catch (IndexOutOfBoundsException e) {
1602         }
1603 
1604         try {
1605             TextUtils.regionMatches(null, 0, two, 0, "Hello".length());
1606             fail("Should throw NullPointerException!");
1607         } catch (NullPointerException e) {
1608             // expect
1609         }
1610         try {
1611             TextUtils.regionMatches(one, 0, null, 0, "Hello".length());
1612             fail("Should throw NullPointerException!");
1613         } catch (NullPointerException e) {
1614             // expect
1615         }
1616     }
1617 
testReplace()1618     public void testReplace() {
1619         String template = "this is a string to be as the template for replacement";
1620 
1621         String sources[] = new String[] { "string" };
1622         CharSequence destinations[] = new CharSequence[] { "text" };
1623         SpannableStringBuilder replacedString = (SpannableStringBuilder) TextUtils.replace(template,
1624                 sources, destinations);
1625         assertEquals("this is a text to be as the template for replacement",
1626                 replacedString.toString());
1627 
1628         sources = new String[] {"is", "the", "for replacement"};
1629         destinations = new CharSequence[] {"was", "", "to be replaced"};
1630         replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
1631         assertEquals("thwas is a string to be as  template to be replaced",
1632                 replacedString.toString());
1633 
1634         sources = new String[] {"is", "for replacement"};
1635         destinations = new CharSequence[] {"was", "", "to be replaced"};
1636         replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
1637         assertEquals("thwas is a string to be as the template ", replacedString.toString());
1638 
1639         sources = new String[] {"is", "the", "for replacement"};
1640         destinations = new CharSequence[] {"was", "to be replaced"};
1641         try {
1642             TextUtils.replace(template, sources, destinations);
1643             fail("Should throw ArrayIndexOutOfBoundsException!");
1644         } catch (ArrayIndexOutOfBoundsException e) {
1645             // expected
1646         }
1647 
1648         try {
1649             TextUtils.replace(null, sources, destinations);
1650             fail("Should throw NullPointerException!");
1651         } catch (NullPointerException e) {
1652             // expected
1653         }
1654         try {
1655             TextUtils.replace(template, null, destinations);
1656             fail("Should throw NullPointerException!");
1657         } catch (NullPointerException e) {
1658             // expected
1659         }
1660         try {
1661             TextUtils.replace(template, sources, null);
1662             fail("Should throw NullPointerException!");
1663         } catch (NullPointerException e) {
1664             // expected
1665         }
1666     }
1667 
testSplitPattern()1668     public void testSplitPattern() {
1669         String testString = "abccbadecdebz";
1670         assertEquals(calculateCharsCount(testString, "c") + 1,
1671                 TextUtils.split(testString, Pattern.compile("c")).length);
1672         assertEquals(calculateCharsCount(testString, "a") + 1,
1673                 TextUtils.split(testString, Pattern.compile("a")).length);
1674         assertEquals(calculateCharsCount(testString, "z") + 1,
1675                 TextUtils.split(testString, Pattern.compile("z")).length);
1676         assertEquals(calculateCharsCount(testString, "de") + 1,
1677                 TextUtils.split(testString, Pattern.compile("de")).length);
1678         int totalCount = 1 + calculateCharsCount(testString, "a")
1679                 + calculateCharsCount(testString, "b") + calculateCharsCount(testString, "c");
1680         assertEquals(totalCount,
1681                 TextUtils.split(testString, Pattern.compile("[a-c]")).length);
1682         assertEquals(0, TextUtils.split("", Pattern.compile("a")).length);
1683         // issue 1695243, not clear what is supposed result if the pattern string is empty.
1684         assertEquals(testString.length() + 2,
1685                 TextUtils.split(testString, Pattern.compile("")).length);
1686 
1687         try {
1688             TextUtils.split(null, Pattern.compile("a"));
1689             fail("Should throw NullPointerException!");
1690         } catch (NullPointerException e) {
1691             // expect
1692         }
1693         try {
1694             TextUtils.split("abccbadecdebz", (Pattern) null);
1695             fail("Should throw NullPointerException!");
1696         } catch (NullPointerException e) {
1697             // expect
1698         }
1699     }
1700 
1701     /*
1702      * return the appearance count of searched chars in text.
1703      */
calculateCharsCount(CharSequence text, CharSequence searches)1704     private int calculateCharsCount(CharSequence text, CharSequence searches) {
1705         int count = 0;
1706         int start = TextUtils.indexOf(text, searches, 0);
1707 
1708         while (start != -1) {
1709             count++;
1710             start = TextUtils.indexOf(text, searches, start + 1);
1711         }
1712         return count;
1713     }
1714 
testSplitString()1715     public void testSplitString() {
1716         String testString = "abccbadecdebz";
1717         assertEquals(calculateCharsCount(testString, "c") + 1,
1718                 TextUtils.split("abccbadecdebz", "c").length);
1719         assertEquals(calculateCharsCount(testString, "a") + 1,
1720                 TextUtils.split("abccbadecdebz", "a").length);
1721         assertEquals(calculateCharsCount(testString, "z") + 1,
1722                 TextUtils.split("abccbadecdebz", "z").length);
1723         assertEquals(calculateCharsCount(testString, "de") + 1,
1724                 TextUtils.split("abccbadecdebz", "de").length);
1725         assertEquals(0, TextUtils.split("", "a").length);
1726         // issue 1695243, not clear what is supposed result if the pattern string is empty.
1727         assertEquals(testString.length() + 2,
1728                 TextUtils.split("abccbadecdebz", "").length);
1729 
1730         try {
1731             TextUtils.split(null, "a");
1732             fail("Should throw NullPointerException!");
1733         } catch (NullPointerException e) {
1734             // expect
1735         }
1736         try {
1737             TextUtils.split("abccbadecdebz", (String) null);
1738             fail("Should throw NullPointerException!");
1739         } catch (NullPointerException e) {
1740             // expect
1741         }
1742     }
1743 
testStringOrSpannedString()1744     public void testStringOrSpannedString() {
1745         assertNull(TextUtils.stringOrSpannedString(null));
1746 
1747         SpannedString spannedString = new SpannedString("Spanned String");
1748         assertSame(spannedString, TextUtils.stringOrSpannedString(spannedString));
1749 
1750         SpannableString spannableString = new SpannableString("Spannable String");
1751         assertEquals("Spannable String",
1752                 TextUtils.stringOrSpannedString(spannableString).toString());
1753         assertEquals(SpannedString.class,
1754                 TextUtils.stringOrSpannedString(spannableString).getClass());
1755 
1756         StringBuffer stringBuffer = new StringBuffer("String Buffer");
1757         assertEquals("String Buffer",
1758                 TextUtils.stringOrSpannedString(stringBuffer).toString());
1759         assertEquals(String.class,
1760                 TextUtils.stringOrSpannedString(stringBuffer).getClass());
1761     }
1762 
testSubString()1763     public void testSubString() {
1764         String string = "String";
1765         assertSame(string, TextUtils.substring(string, 0, string.length()));
1766         assertEquals("Strin", TextUtils.substring(string, 0, string.length() - 1));
1767         assertEquals("", TextUtils.substring(string, 1, 1));
1768 
1769         try {
1770             TextUtils.substring(string, string.length(), 0);
1771             fail("Should throw IndexOutOfBoundsException!");
1772         } catch (IndexOutOfBoundsException e) {
1773             // expected
1774         }
1775 
1776         try {
1777             TextUtils.substring(string, -1, string.length());
1778             fail("Should throw IndexOutOfBoundsException!");
1779         } catch (IndexOutOfBoundsException e) {
1780             // expected
1781         }
1782 
1783         try {
1784             TextUtils.substring(string, Integer.MAX_VALUE, string.length());
1785             fail("Should throw IndexOutOfBoundsException!");
1786         } catch (IndexOutOfBoundsException e) {
1787             // expected
1788         }
1789 
1790         try {
1791             TextUtils.substring(string, 0, -1);
1792             fail("Should throw IndexOutOfBoundsException!");
1793         } catch (IndexOutOfBoundsException e) {
1794             // expected
1795         }
1796 
1797         try {
1798             TextUtils.substring(string, 0, Integer.MAX_VALUE);
1799             fail("Should throw IndexOutOfBoundsException!");
1800         } catch (IndexOutOfBoundsException e) {
1801             // expected
1802         }
1803 
1804         try {
1805             TextUtils.substring(null, 0, string.length());
1806             fail("Should throw NullPointerException!");
1807         } catch (NullPointerException e) {
1808             // expected
1809         }
1810 
1811         StringBuffer stringBuffer = new StringBuffer("String Buffer");
1812         assertEquals("Strin", TextUtils.substring(stringBuffer, 0, string.length() - 1));
1813         assertEquals("", TextUtils.substring(stringBuffer, 1, 1));
1814 
1815         MockGetChars mockGetChars = new MockGetChars();
1816         TextUtils.substring(mockGetChars, 0, string.length());
1817         assertTrue(mockGetChars.hasCalledGetChars());
1818     }
1819 
testWriteToParcel()1820     public void testWriteToParcel() {
1821         Parcelable.Creator<CharSequence> creator = TextUtils.CHAR_SEQUENCE_CREATOR;
1822         String string = "String";
1823         Parcel p = Parcel.obtain();
1824         try {
1825             TextUtils.writeToParcel(string, p, 0);
1826             p.setDataPosition(0);
1827             assertEquals(string, creator.createFromParcel(p).toString());
1828         } finally {
1829             p.recycle();
1830         }
1831 
1832         p = Parcel.obtain();
1833         try {
1834             TextUtils.writeToParcel(null, p, 0);
1835             p.setDataPosition(0);
1836             assertNull(creator.createFromParcel(p));
1837         } finally {
1838             p.recycle();
1839         }
1840 
1841         SpannableString spannableString = new SpannableString("Spannable String");
1842         int urlSpanStart = spannableString.length() >> 1;
1843         int urlSpanEnd = spannableString.length();
1844         p = Parcel.obtain();
1845         try {
1846             URLSpan urlSpan = new URLSpan("URL Span");
1847             spannableString.setSpan(urlSpan, urlSpanStart, urlSpanEnd,
1848                     Spanned.SPAN_INCLUSIVE_INCLUSIVE);
1849             TextUtils.writeToParcel(spannableString, p, 0);
1850             p.setDataPosition(0);
1851             SpannableString ret = (SpannableString) creator.createFromParcel(p);
1852             assertEquals("Spannable String", ret.toString());
1853             Object[] spans = ret.getSpans(0, ret.length(), Object.class);
1854             assertEquals(1, spans.length);
1855             assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
1856             assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
1857             assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
1858             assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
1859         } finally {
1860             p.recycle();
1861         }
1862 
1863         p = Parcel.obtain();
1864         try {
1865             ColorStateList colors = new ColorStateList(new int[][] {
1866                     new int[] {android.R.attr.state_focused}, new int[0]},
1867                     new int[] {Color.rgb(0, 255, 0), Color.BLACK});
1868             int textSize = 20;
1869             TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(
1870                     null, Typeface.ITALIC, textSize, colors, null);
1871             int textAppearanceSpanStart = 0;
1872             int textAppearanceSpanEnd = spannableString.length() >> 1;
1873             spannableString.setSpan(textAppearanceSpan, textAppearanceSpanStart,
1874                     textAppearanceSpanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
1875             TextUtils.writeToParcel(spannableString, p, -1);
1876             p.setDataPosition(0);
1877             SpannableString ret = (SpannableString) creator.createFromParcel(p);
1878             assertEquals("Spannable String", ret.toString());
1879             Object[] spans = ret.getSpans(0, ret.length(), Object.class);
1880             assertEquals(2, spans.length);
1881             assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
1882             assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
1883             assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
1884             assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
1885             assertEquals(null, ((TextAppearanceSpan) spans[1]).getFamily());
1886 
1887             assertEquals(Typeface.ITALIC, ((TextAppearanceSpan) spans[1]).getTextStyle());
1888             assertEquals(textSize, ((TextAppearanceSpan) spans[1]).getTextSize());
1889 
1890             assertEquals(colors.toString(), ((TextAppearanceSpan) spans[1]).getTextColor().toString());
1891             assertEquals(null, ((TextAppearanceSpan) spans[1]).getLinkTextColor());
1892             assertEquals(textAppearanceSpanStart, ret.getSpanStart(spans[1]));
1893             assertEquals(textAppearanceSpanEnd, ret.getSpanEnd(spans[1]));
1894             assertEquals(Spanned.SPAN_INCLUSIVE_EXCLUSIVE, ret.getSpanFlags(spans[1]));
1895         } finally {
1896             p.recycle();
1897         }
1898 
1899         try {
1900             TextUtils.writeToParcel(spannableString, null, 0);
1901             fail("Should throw NullPointerException!");
1902         } catch (NullPointerException e) {
1903             // expected
1904         }
1905     }
1906 
testGetCapsMode()1907     public void testGetCapsMode() {
1908         final int CAP_MODE_ALL = TextUtils.CAP_MODE_CHARACTERS
1909                 | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES;
1910         final int CAP_MODE_CHARACTERS_AND_WORD =
1911                 TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS;
1912         String testString = "Start. Sentence word!No space before\n\t" +
1913                 "Paragraph? (\"\'skip begin\'\"). skip end";
1914 
1915         // CAP_MODE_SENTENCES should be in effect in the whole text.
1916         for (int i = 0; i < testString.length(); i++) {
1917             assertEquals(TextUtils.CAP_MODE_CHARACTERS,
1918                     TextUtils.getCapsMode(testString, i, TextUtils.CAP_MODE_CHARACTERS));
1919         }
1920 
1921         // all modes should be in effect at the start of the text.
1922         assertEquals(TextUtils.CAP_MODE_WORDS,
1923                 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_WORDS));
1924         // issue 1586346
1925         assertEquals(TextUtils.CAP_MODE_WORDS,
1926                 TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_SENTENCES));
1927         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
1928                 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
1929 
1930         // all mode should be in effect at the position after "." or "?" or "!" + " ".
1931         int offset = testString.indexOf("Sentence word!");
1932         assertEquals(TextUtils.CAP_MODE_WORDS,
1933                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1934         assertEquals(TextUtils.CAP_MODE_SENTENCES,
1935                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1936         // issue 1586346
1937         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
1938                 TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
1939 
1940         // CAP_MODE_SENTENCES should NOT be in effect at the position after other words + " ".
1941         offset = testString.indexOf("word!");
1942         assertEquals(TextUtils.CAP_MODE_WORDS,
1943                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1944         assertEquals(0,
1945                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1946         // issue 1586346
1947         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
1948                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
1949 
1950         // if no space after "." or "?" or "!", CAP_MODE_SENTENCES and CAP_MODE_WORDS
1951         // should NOT be in effect.
1952         offset = testString.indexOf("No space before");
1953         assertEquals(0,
1954                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1955         assertEquals(0,
1956                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1957         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
1958                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
1959 
1960         // all mode should be in effect at a beginning of a new paragraph.
1961         offset = testString.indexOf("Paragraph");
1962         assertEquals(TextUtils.CAP_MODE_WORDS,
1963                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1964         // issue 1586346
1965         assertEquals(TextUtils.CAP_MODE_WORDS,
1966                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1967         assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
1968                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
1969 
1970         // some special word which means the start of a sentence should be skipped.
1971         offset = testString.indexOf("skip begin");
1972         assertEquals(TextUtils.CAP_MODE_WORDS,
1973                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1974         assertEquals(TextUtils.CAP_MODE_SENTENCES,
1975                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1976         // issue 1586346
1977         assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
1978                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
1979 
1980         // some special word which means the end of a sentence should be skipped.
1981         offset = testString.indexOf("skip end");
1982         assertEquals(TextUtils.CAP_MODE_WORDS,
1983                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
1984         assertEquals(TextUtils.CAP_MODE_SENTENCES,
1985                 TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
1986         // issue 1586346
1987         assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
1988                 TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
1989     }
1990 
testGetCapsModeException()1991     public void testGetCapsModeException() {
1992         String testString = "Start. Sentence word!No space before\n\t" +
1993                 "Paragraph? (\"\'skip begin\'\"). skip end";
1994 
1995         int offset = testString.indexOf("Sentence word!");
1996         assertEquals(TextUtils.CAP_MODE_CHARACTERS,
1997                 TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_CHARACTERS));
1998 
1999         try {
2000             TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_SENTENCES);
2001             fail("Should throw NullPointerException!");
2002         } catch (NullPointerException e) {
2003             // expected
2004         }
2005 
2006         assertEquals(0, TextUtils.getCapsMode(testString, -1, TextUtils.CAP_MODE_SENTENCES));
2007 
2008         try {
2009             TextUtils.getCapsMode(testString, testString.length() + 1,
2010                     TextUtils.CAP_MODE_SENTENCES);
2011             fail("Should throw IndexOutOfBoundsException!");
2012         } catch (IndexOutOfBoundsException e) {
2013             // expected
2014         }
2015     }
2016 
testDumpSpans()2017     public void testDumpSpans() {
2018         StringBuilder builder = new StringBuilder();
2019         StringBuilderPrinter printer = new StringBuilderPrinter(builder);
2020         CharSequence source = "test dump spans";
2021         String prefix = "prefix";
2022 
2023         assertEquals(0, builder.length());
2024         TextUtils.dumpSpans(source, printer, prefix);
2025         assertTrue(builder.length() > 0);
2026 
2027         builder = new StringBuilder();
2028         printer = new StringBuilderPrinter(builder);
2029         assertEquals(0, builder.length());
2030         SpannableString spanned = new SpannableString(source);
2031         spanned.setSpan(new Object(), 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
2032         TextUtils.dumpSpans(spanned, printer, prefix);
2033         assertTrue(builder.length() > 0);
2034     }
2035 
2036     @TestTargetNew(
2037             level = TestLevel.COMPLETE,
2038             method = "getLayoutDirectionFromLocale",
2039             args = {Locale.class}
2040     )
testGetLayoutDirectionFromLocale()2041     public void testGetLayoutDirectionFromLocale() {
2042         assertEquals(LAYOUT_DIRECTION_LTR,
2043                 TextUtils.getLayoutDirectionFromLocale(null));
2044 
2045         assertEquals(LAYOUT_DIRECTION_LTR,
2046                 TextUtils.getLayoutDirectionFromLocale(Locale.ENGLISH));
2047         assertEquals(LAYOUT_DIRECTION_LTR,
2048                 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA));
2049         assertEquals(LAYOUT_DIRECTION_LTR,
2050                 TextUtils.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
2051         assertEquals(LAYOUT_DIRECTION_LTR,
2052                 TextUtils.getLayoutDirectionFromLocale(Locale.FRANCE));
2053         assertEquals(LAYOUT_DIRECTION_LTR,
2054                 TextUtils.getLayoutDirectionFromLocale(Locale.FRENCH));
2055         assertEquals(LAYOUT_DIRECTION_LTR,
2056                 TextUtils.getLayoutDirectionFromLocale(Locale.GERMAN));
2057         assertEquals(LAYOUT_DIRECTION_LTR,
2058                 TextUtils.getLayoutDirectionFromLocale(Locale.GERMANY));
2059         assertEquals(LAYOUT_DIRECTION_LTR,
2060                 TextUtils.getLayoutDirectionFromLocale(Locale.ITALIAN));
2061         assertEquals(LAYOUT_DIRECTION_LTR,
2062                 TextUtils.getLayoutDirectionFromLocale(Locale.ITALY));
2063         assertEquals(LAYOUT_DIRECTION_LTR,
2064                 TextUtils.getLayoutDirectionFromLocale(Locale.UK));
2065         assertEquals(LAYOUT_DIRECTION_LTR,
2066                 TextUtils.getLayoutDirectionFromLocale(Locale.US));
2067 
2068         assertEquals(LAYOUT_DIRECTION_LTR,
2069                 TextUtils.getLayoutDirectionFromLocale(Locale.ROOT));
2070 
2071         assertEquals(LAYOUT_DIRECTION_LTR,
2072                 TextUtils.getLayoutDirectionFromLocale(Locale.CHINA));
2073         assertEquals(LAYOUT_DIRECTION_LTR,
2074                 TextUtils.getLayoutDirectionFromLocale(Locale.CHINESE));
2075         assertEquals(LAYOUT_DIRECTION_LTR,
2076                 TextUtils.getLayoutDirectionFromLocale(Locale.JAPAN));
2077         assertEquals(LAYOUT_DIRECTION_LTR,
2078                 TextUtils.getLayoutDirectionFromLocale(Locale.JAPANESE));
2079         assertEquals(LAYOUT_DIRECTION_LTR,
2080                 TextUtils.getLayoutDirectionFromLocale(Locale.KOREA));
2081         assertEquals(LAYOUT_DIRECTION_LTR,
2082                 TextUtils.getLayoutDirectionFromLocale(Locale.KOREAN));
2083         assertEquals(LAYOUT_DIRECTION_LTR,
2084                 TextUtils.getLayoutDirectionFromLocale(Locale.PRC));
2085         assertEquals(LAYOUT_DIRECTION_LTR,
2086                 TextUtils.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
2087         assertEquals(LAYOUT_DIRECTION_LTR,
2088                 TextUtils.getLayoutDirectionFromLocale(Locale.TAIWAN));
2089         assertEquals(LAYOUT_DIRECTION_LTR,
2090                 TextUtils.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
2091 
2092         // Some languages always use an RTL script.
2093         for (Locale l : Locale.getAvailableLocales()) {
2094             String languageCode = l.getLanguage();
2095             if (languageCode.equals("ar") ||
2096                     languageCode.equals("fa") ||
2097                     languageCode.equals("iw") ||
2098                     languageCode.equals("he") ||
2099                     languageCode.equals("ps") ||
2100                     languageCode.equals("ur")) {
2101                 int direction = TextUtils.getLayoutDirectionFromLocale(l);
2102                 assertEquals(l.toLanguageTag() + " not RTL: " + direction,
2103                              LAYOUT_DIRECTION_RTL, direction);
2104             }
2105         }
2106 
2107         // Other languages have some cases where they use an RTL script.
2108         String[] tags = {
2109             "pa-Arab",
2110             "pa-Arab-PK",
2111             "ps",
2112             "ps-AF",
2113             "uz-Arab",
2114             "uz-Arab-AF",
2115         };
2116         for (String tag : tags) {
2117             Locale l = Locale.forLanguageTag(tag);
2118             int direction = TextUtils.getLayoutDirectionFromLocale(l);
2119             assertEquals(l.toLanguageTag() + " not RTL: " + direction,
2120                          LAYOUT_DIRECTION_RTL, direction);
2121         }
2122 
2123         // Locale without a real language
2124         Locale locale = Locale.forLanguageTag("zz");
2125         assertEquals(LAYOUT_DIRECTION_LTR,
2126                 TextUtils.getLayoutDirectionFromLocale(locale));
2127     }
2128 }
2129