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