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