OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / cts / tests / tests / text / src / android / text / cts / TextUtilsTest.java
diff --git a/cts/tests/tests/text/src/android/text/cts/TextUtilsTest.java b/cts/tests/tests/text/src/android/text/cts/TextUtilsTest.java
new file mode 100644 (file)
index 0000000..04f9366
--- /dev/null
@@ -0,0 +1,2291 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.ToBeFixed;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.Paint.FontMetricsInt;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+import android.text.GetChars;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.TextUtils.EllipsizeCallback;
+import android.text.TextUtils.TruncateAt;
+import android.text.style.BackgroundColorSpan;
+import android.text.style.ReplacementSpan;
+import android.text.style.TextAppearanceSpan;
+import android.text.style.URLSpan;
+import android.util.StringBuilderPrinter;
+
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+
+/**
+ * Test {@link TextUtils}.
+ */
+@TestTargetClass(TextUtils.class)
+public class TextUtilsTest extends AndroidTestCase {
+    private static String mEllipsis;
+    private int mStart;
+    private int mEnd;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mEllipsis = getEllipsis();
+        resetRange();
+    }
+
+    private void resetRange() {
+        mStart = -1;
+        mEnd = -1;
+    }
+
+    /**
+     * Get the ellipsis from system.
+     * @return the string of ellipsis.
+     */
+    private String getEllipsis() {
+        String text = "xxxxx";
+        TextPaint p = new TextPaint();
+        float width = p.measureText(text.substring(1));
+        String re = TextUtils.ellipsize(text, p, width, TruncateAt.START).toString();
+        return re.substring(0, re.indexOf("x"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "commaEllipsize",
+        args = {CharSequence.class, TextPaint.class, float.class, String.class, String.class}
+    )
+    @ToBeFixed(bug = "1688347 ", explanation = "The javadoc for commaEllipsize() " +
+            "does not discuss any of the corner cases")
+    public void testCommaEllipsize() {
+        TextPaint p = new TextPaint();
+        String text = "long, string, to, truncate";
+
+        float textWidth = p.measureText("long, 3 plus");
+        // avail is shorter than text width for only one item plus the appropriate ellipsis.
+        // issue 1688347, the expected result for this case does not be described
+        // in the javadoc of commaEllipsize().
+        assertEquals("",
+                TextUtils.commaEllipsize(text, p, textWidth - 1, "plus 1", "%d plus").toString());
+        // avail is long enough for only one item plus the appropriate ellipsis.
+        assertEquals("long, 3 plus",
+                TextUtils.commaEllipsize(text, p, textWidth, "plus 1", "%d plus").toString());
+
+        // avail is long enough for two item plus the appropriate ellipsis.
+        textWidth = p.measureText("long, string, 2 more");
+        assertEquals("long, string, 2 more",
+                TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
+
+        // avail is long enough for the whole sentence.
+        textWidth = p.measureText("long, string, to, truncate");
+        assertEquals("long, string, to, truncate",
+                TextUtils.commaEllipsize(text, p, textWidth, "more 1", "%d more").toString());
+
+        // the sentence is extended, avail is NOT long enough for the whole sentence.
+        assertEquals("long, string, to, more 1", TextUtils.commaEllipsize(
+                text + "-extended", p, textWidth, "more 1", "%d more").toString());
+
+        // exceptional value
+        assertEquals("", TextUtils.commaEllipsize(text, p, -1f, "plus 1", "%d plus").toString());
+
+        assertEquals(text, TextUtils.commaEllipsize(
+                text, p, Float.MAX_VALUE, "more 1", "%d more").toString());
+
+        assertEquals("long, string, to, null", TextUtils.commaEllipsize(
+                text + "-extended", p, textWidth, null, "%d more").toString());
+
+        try {
+            TextUtils.commaEllipsize(null, p, textWidth, "plus 1", "%d plus");
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // issue 1688347, not clear what is supposed to happen if the text to truncate is null.
+        }
+
+        try {
+            TextUtils.commaEllipsize(text, null, textWidth, "plus 1", "%d plus");
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // issue 1688347, not clear what is supposed to happen if TextPaint is null.
+        }
+
+        try {
+            TextUtils.commaEllipsize(text, p, textWidth, "plus 1", null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // issue 1688347, not clear what is supposed to happen
+            // if the string for "%d more" in the current locale is null.
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "concat",
+        args = {CharSequence[].class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for concat() is incomplete." +
+            "1. doesn't explain @param and @return" +
+            "2. doesn't describe the expected result when parameter is empty" +
+            "3. doesn't discuss the case that parameter is expectional.")
+    public void testConcat() {
+        // issue 1695243
+        // the javadoc for concat() doesn't describe the expected result when parameter is empty.
+        assertEquals("", TextUtils.concat().toString());
+
+        assertEquals("first", TextUtils.concat("first").toString());
+
+        assertEquals("first, second", TextUtils.concat("first", ", ", "second").toString());
+
+        SpannableString string1 = new SpannableString("first");
+        SpannableString string2 = new SpannableString("second");
+        final String url = "www.test_url.com";
+        URLSpan urlSpan = new URLSpan(url);
+        string1.setSpan(urlSpan, 0, string1.length() - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
+        string2.setSpan(bgColorSpan, 0, string2.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        final String comma = ", ";
+        Spanned strResult = (Spanned) TextUtils.concat(string1, comma, string2);
+        assertEquals(string1.toString() + comma + string2.toString(), strResult.toString());
+        Object spans[] = strResult.getSpans(0, strResult.length(), Object.class);
+        assertEquals(2, spans.length);
+        assertTrue(spans[0] instanceof URLSpan);
+        assertEquals(url, ((URLSpan) spans[0]).getURL());
+        assertTrue(spans[1] instanceof BackgroundColorSpan);
+        assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
+        assertEquals(0, strResult.getSpanStart(urlSpan));
+        assertEquals(string1.length() - 1, strResult.getSpanEnd(urlSpan));
+        assertEquals(string1.length() + comma.length(), strResult.getSpanStart(bgColorSpan));
+        assertEquals(strResult.length() - 1, strResult.getSpanEnd(bgColorSpan));
+
+        assertEquals(string1, TextUtils.concat(string1));
+
+        // issue 1695243, the javadoc for concat() doesn't describe
+        // the expected result when parameters are null.
+        assertEquals(null, TextUtils.concat((CharSequence) null));
+
+        try {
+            TextUtils.concat((CharSequence[]) null);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "copySpansFrom",
+        args = {Spanned.class, int.class, int.class, Class.class, Spannable.class, int.class}
+    )
+    @ToBeFixed(bug = "1688347", explanation = "the javadoc for copySpansFrom() does not exist.")
+    public void testCopySpansFrom() {
+        Object[] spans;
+        String text = "content";
+        SpannableString source1 = new SpannableString(text);
+        int midPos = source1.length() / 2;
+        final String url = "www.test_url.com";
+        URLSpan urlSpan = new URLSpan(url);
+        source1.setSpan(urlSpan, 0, midPos, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
+        source1.setSpan(bgColorSpan, midPos - 1,
+                source1.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        // normal test
+        SpannableString dest1 = new SpannableString(text);
+        TextUtils.copySpansFrom(source1, 0, source1.length(), Object.class, dest1, 0);
+        spans = dest1.getSpans(0, dest1.length(), Object.class);
+        assertEquals(2, spans.length);
+        assertTrue(spans[0] instanceof URLSpan);
+        assertEquals(url, ((URLSpan) spans[0]).getURL());
+        assertTrue(spans[1] instanceof BackgroundColorSpan);
+        assertEquals(Color.GREEN, ((BackgroundColorSpan) spans[1]).getBackgroundColor());
+        assertEquals(0, dest1.getSpanStart(urlSpan));
+        assertEquals(midPos, dest1.getSpanEnd(urlSpan));
+        assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, dest1.getSpanFlags(urlSpan));
+        assertEquals(midPos - 1, dest1.getSpanStart(bgColorSpan));
+        assertEquals(source1.length() - 1, dest1.getSpanEnd(bgColorSpan));
+        assertEquals(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, dest1.getSpanFlags(bgColorSpan));
+
+        SpannableString source2 = new SpannableString(text);
+        source2.setSpan(urlSpan, 0, source2.length() - 1, Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
+        SpannableString dest2 = new SpannableString(text);
+        TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest2, 0);
+        spans = dest2.getSpans(0, dest2.length(), Object.class);
+        assertEquals(1, spans.length);
+        assertTrue(spans[0] instanceof URLSpan);
+        assertEquals(url, ((URLSpan) spans[0]).getURL());
+        assertEquals(0, dest2.getSpanStart(urlSpan));
+        assertEquals(source2.length() - 1, dest2.getSpanEnd(urlSpan));
+        assertEquals(Spanned.SPAN_EXCLUSIVE_INCLUSIVE, dest2.getSpanFlags(urlSpan));
+
+        SpannableString dest3 = new SpannableString(text);
+        TextUtils.copySpansFrom(source2, 0, source2.length(), BackgroundColorSpan.class, dest3, 0);
+        spans = dest3.getSpans(0, dest3.length(), Object.class);
+        assertEquals(0, spans.length);
+        TextUtils.copySpansFrom(source2, 0, source2.length(), URLSpan.class, dest3, 0);
+        spans = dest3.getSpans(0, dest3.length(), Object.class);
+        assertEquals(1, spans.length);
+
+        SpannableString dest4 = new SpannableString("short");
+        try {
+            TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest4, 0);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+        TextUtils.copySpansFrom(source2, 0, dest4.length(), Object.class, dest4, 0);
+        spans = dest4.getSpans(0, dest4.length(), Object.class);
+        assertEquals(1, spans.length);
+        assertEquals(0, dest4.getSpanStart(spans[0]));
+        // issue 1688347, not clear the expected result when 'start ~ end' only
+        // covered a part of the span.
+        assertEquals(dest4.length(), dest4.getSpanEnd(spans[0]));
+
+        SpannableString dest5 = new SpannableString("longer content");
+        TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 0);
+        spans = dest5.getSpans(0, 1, Object.class);
+        assertEquals(1, spans.length);
+
+        dest5 = new SpannableString("longer content");
+        TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest5, 2);
+        spans = dest5.getSpans(0, 1, Object.class);
+        assertEquals(0, spans.length);
+        spans = dest5.getSpans(2, dest5.length(), Object.class);
+        assertEquals(1, spans.length);
+        try {
+            TextUtils.copySpansFrom(source2, 0, source2.length(),
+                    Object.class, dest5, dest5.length() - source2.length() + 2);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        // issue 1688347, no javadoc about the expected behavior of the exceptional argument.
+        // exceptional source start
+        SpannableString dest6 = new SpannableString("exceptional test");
+        TextUtils.copySpansFrom(source2, -1, source2.length(), Object.class, dest6, 0);
+        spans = dest6.getSpans(0, dest6.length(), Object.class);
+        assertEquals(1, spans.length);
+        dest6 = new SpannableString("exceptional test");
+        TextUtils.copySpansFrom(source2, Integer.MAX_VALUE, source2.length() - 1,
+                    Object.class, dest6, 0);
+        spans = dest6.getSpans(0, dest6.length(), Object.class);
+        assertEquals(0, spans.length);
+
+        // exceptional source end
+        dest6 = new SpannableString("exceptional test");
+        TextUtils.copySpansFrom(source2, 0, -1, Object.class, dest6, 0);
+        spans = dest6.getSpans(0, dest6.length(), Object.class);
+        assertEquals(0, spans.length);
+        TextUtils.copySpansFrom(source2, 0, Integer.MAX_VALUE, Object.class, dest6, 0);
+        spans = dest6.getSpans(0, dest6.length(), Object.class);
+        assertEquals(1, spans.length);
+
+        // exceptional class kind
+        dest6 = new SpannableString("exceptional test");
+        TextUtils.copySpansFrom(source2, 0, source2.length(), null, dest6, 0);
+        spans = dest6.getSpans(0, dest6.length(), Object.class);
+        assertEquals(1, spans.length);
+
+        // exceptional destination offset
+        dest6 = new SpannableString("exceptional test");
+        try {
+            TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, dest6, -1);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        try {
+            TextUtils.copySpansFrom(source2, 0, source2.length(),
+                    Object.class, dest6, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+
+        // exceptional source
+        try {
+            TextUtils.copySpansFrom(null, 0, source2.length(), Object.class, dest6, 0);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expect
+        }
+
+        // exceptional destination
+        try {
+            TextUtils.copySpansFrom(source2, 0, source2.length(), Object.class, null, 0);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expect
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "ellipsize",
+        args = {CharSequence.class, TextPaint.class, float.class, TruncateAt.class}
+    )
+    @ToBeFixed(bug = "1688347", explanation = "" +
+            "1. the javadoc for ellipsize() is incomplete." +
+            "   - doesn't explain @param and @return" +
+            "   - doesn't describe expected behavior if user pass an exceptional argument." +
+            "2. ellipsize() is not defined for TruncateAt.MARQUEE. " +
+            "   In the code it looks like this does the same as MIDDLE. " +
+            "   In other methods, MARQUEE is equivalent to END, except for the first line.")
+    public void testEllipsize() {
+        TextPaint p = new TextPaint();
+        
+        // turn off kerning. with kerning enabled, different methods of measuring the same text
+        // produce different results.
+        p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
+        
+        CharSequence text = "long string to truncate";
+
+        float textWidth = p.measureText(mEllipsis + "uncate");
+        assertEquals(mEllipsis + "uncate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
+
+        textWidth = p.measureText("long str" + mEllipsis);
+        assertEquals("long str" + mEllipsis,
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
+
+        textWidth = p.measureText("long" + mEllipsis + "ate");
+        assertEquals("long" + mEllipsis + "ate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
+
+        // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
+        // In the code it looks like this does the same as MIDDLE.
+        // In other methods, MARQUEE is equivalent to END, except for the first line.
+        assertEquals("long" + mEllipsis + "ate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE).toString());
+
+        textWidth = p.measureText(mEllipsis);
+        assertEquals(mEllipsis, TextUtils.ellipsize(text, p, textWidth, TruncateAt.END).toString());
+        assertEquals("", TextUtils.ellipsize(text, p, textWidth - 1, TruncateAt.END).toString());
+        assertEquals("", TextUtils.ellipsize(text, p, -1f, TruncateAt.END).toString());
+        assertEquals(text,
+                TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END).toString());
+
+        assertEquals(mEllipsis,
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.START).toString());
+        assertEquals(mEllipsis,
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE).toString());
+
+        try {
+            TextUtils.ellipsize(text, null, textWidth, TruncateAt.MIDDLE);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.ellipsize(null, p, textWidth, TruncateAt.MIDDLE);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "ellipsize",
+        args = {CharSequence.class, TextPaint.class, float.class, TruncateAt.class,
+                boolean.class, EllipsizeCallback.class}
+    )
+    @ToBeFixed(bug = "1688347", explanation = "" +
+            "1. the javadoc for ellipsize() is incomplete." +
+            "   - doesn't explain @param and @return" +
+            "   - doesn't describe expected behavior if user pass an exceptional argument." +
+            "2. ellipsize() is not defined for TruncateAt.MARQUEE. " +
+            "   In the code it looks like this does the same as MIDDLE. " +
+            "   In other methods, MARQUEE is equivalent to END, except for the first line.")
+    public void testEllipsizeCallback() {
+        TextPaint p = new TextPaint();
+
+        // turn off kerning. with kerning enabled, different methods of measuring the same text
+        // produce different results.
+        p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
+        
+        TextUtils.EllipsizeCallback callback = new TextUtils.EllipsizeCallback() {
+            public void ellipsized(final int start, final int end) {
+                mStart = start;
+                mEnd = end;
+            }
+        };
+
+        String text = "long string to truncate";
+
+        // TruncateAt.START, does not specify preserveLength
+        resetRange();
+        float textWidth = p.measureText(mEllipsis + "uncate");
+        assertEquals(mEllipsis + "uncate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, false,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length() - "uncate".length(), mEnd);
+
+        // TruncateAt.START, specify preserveLength
+        resetRange();
+        int ellipsisNum = text.length() - "uncate".length();
+        assertEquals(getBlankString(true, ellipsisNum) + "uncate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.START, true,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length() - "uncate".length(), mEnd);
+
+        // TruncateAt.END, specify preserveLength
+        resetRange();
+        textWidth = p.measureText("long str" + mEllipsis);
+        ellipsisNum = text.length() - "long str".length();
+        assertEquals("long str" + getBlankString(true, ellipsisNum),
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
+        assertEquals("long str".length(), mStart);
+        assertEquals(text.length(), mEnd);
+
+        // TruncateAt.MIDDLE, specify preserveLength
+        resetRange();
+        textWidth = p.measureText("long" + mEllipsis + "ate");
+        ellipsisNum = text.length() - "long".length() - "ate".length();
+        assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
+                        callback).toString());
+        assertEquals("long".length(), mStart);
+        assertEquals(text.length() - "ate".length(), mEnd);
+
+        // TruncateAt.MIDDLE, specify preserveLength, but does not specify callback.
+        resetRange();
+        assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MIDDLE, true,
+                        null).toString());
+        assertEquals(-1, mStart);
+        assertEquals(-1, mEnd);
+
+        // TruncateAt.MARQUEE, specify preserveLength
+        // issue 1688347, ellipsize() is not defined for TruncateAt.MARQUEE.
+        // In the code it looks like this does the same as MIDDLE.
+        // In other methods, MARQUEE is equivalent to END, except for the first line.
+        resetRange();
+        textWidth = p.measureText("long" + mEllipsis + "ate");
+        ellipsisNum = text.length() - "long".length() - "ate".length();
+        assertEquals("long" + getBlankString(true, ellipsisNum) + "ate",
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.MARQUEE, true,
+                        callback).toString());
+        assertEquals("long".length(), mStart);
+        assertEquals(text.length() - "ate".length(), mEnd);
+
+        // avail is not long enough for ELLIPSIS, and preserveLength is specified.
+        resetRange();
+        textWidth = p.measureText(mEllipsis);
+        assertEquals(getBlankString(false, text.length()),
+                TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, true,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length(), mEnd);
+
+        // avail is not long enough for ELLIPSIS, and preserveLength doesn't be specified.
+        resetRange();
+        assertEquals("",
+                TextUtils.ellipsize(text, p, textWidth - 1f, TruncateAt.END, false,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length(), mEnd);
+
+        // avail is long enough for ELLIPSIS, and preserveLength is specified.
+        resetRange();
+        assertEquals(getBlankString(true, text.length()),
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, true, callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length(), mEnd);
+
+        // avail is long enough for ELLIPSIS, and preserveLength doesn't be specified.
+        resetRange();
+        assertEquals(mEllipsis,
+                TextUtils.ellipsize(text, p, textWidth, TruncateAt.END, false,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(text.length(), mEnd);
+
+        // avail is long enough for the whole sentence.
+        resetRange();
+        assertEquals(text,
+                TextUtils.ellipsize(text, p, Float.MAX_VALUE, TruncateAt.END, true,
+                        callback).toString());
+        assertEquals(0, mStart);
+        assertEquals(0, mEnd);
+
+        textWidth = p.measureText("long str" + mEllipsis);
+        try {
+            TextUtils.ellipsize(text, null, textWidth, TruncateAt.END, true, callback);
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.ellipsize(null, p, textWidth, TruncateAt.END, true, callback);
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Get a blank string which is filled up by '\uFEFF'.
+     *
+     * @param isNeedStart - boolean whether need to start with char '\u2026' in the string.
+     * @param len - int length of string.
+     * @return a blank string which is filled up by '\uFEFF'.
+     */
+    private String getBlankString(boolean isNeedStart, int len) {
+        StringBuilder buf = new StringBuilder();
+
+        int i = 0;
+        if (isNeedStart) {
+            buf.append('\u2026');
+            i++;
+        }
+        for (; i < len; i++) {
+            buf.append('\uFEFF');
+        }
+
+        return buf.toString();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "equals",
+        args = {CharSequence.class, CharSequence.class}
+    )
+    public void testEquals() {
+        // compare with itself.
+        // String is a subclass of CharSequence and overrides equals().
+        String string = "same object";
+        assertTrue(TextUtils.equals(string, string));
+
+        // SpannableString is a subclass of CharSequence and does NOT override equals().
+        SpannableString spanString = new SpannableString("same object");
+        final String url = "www.test_url.com";
+        spanString.setSpan(new URLSpan(url), 0, spanString.length(),
+                Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        assertTrue(TextUtils.equals(spanString, spanString));
+
+        // compare with other objects which have same content.
+        assertTrue(TextUtils.equals("different object", "different object"));
+
+        SpannableString urlSpanString = new SpannableString("same content");
+        SpannableString bgColorSpanString = new SpannableString(
+                "same content");
+        URLSpan urlSpan = new URLSpan(url);
+        urlSpanString.setSpan(urlSpan, 0, urlSpanString.length(),
+                Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        BackgroundColorSpan bgColorSpan = new BackgroundColorSpan(Color.GREEN);
+        bgColorSpanString.setSpan(bgColorSpan, 0, bgColorSpanString.length(),
+                Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+        assertTrue(TextUtils.equals(bgColorSpanString, urlSpanString));
+
+        // compare with other objects which have different content.
+        assertFalse(TextUtils.equals("different content A", "different content B"));
+        assertFalse(TextUtils.equals(spanString, urlSpanString));
+        assertFalse(TextUtils.equals(spanString, bgColorSpanString));
+
+        // compare with null
+        assertTrue(TextUtils.equals(null, null));
+        assertFalse(TextUtils.equals(spanString, null));
+        assertFalse(TextUtils.equals(null, string));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "expandTemplate",
+        args = {CharSequence.class, CharSequence[].class}
+    )
+    @ToBeFixed(bug = "1695243", explanation =
+            "the javadoc for expandTemplate() is incomplete." +
+            "1. not clear what is supposed to happen if template or values is null." +
+            "2. doesn't discuss the case that ^0 in template string.")
+    public void testExpandTemplate() {
+        // ^1 at the start of template string.
+        assertEquals("value1 template to be expanded",
+                TextUtils.expandTemplate("^1 template to be expanded", "value1").toString());
+        // ^1 at the end of template string.
+        assertEquals("template to be expanded value1",
+                TextUtils.expandTemplate("template to be expanded ^1", "value1").toString());
+        // ^1 in the middle of template string.
+        assertEquals("template value1 to be expanded",
+                TextUtils.expandTemplate("template ^1 to be expanded", "value1").toString());
+        // ^1 followed by a '0'
+        assertEquals("template value10 to be expanded",
+                TextUtils.expandTemplate("template ^10 to be expanded", "value1").toString());
+        // ^1 followed by a 'a'
+        assertEquals("template value1a to be expanded",
+                TextUtils.expandTemplate("template ^1a to be expanded", "value1").toString());
+        // no ^1
+        assertEquals("template ^a to be expanded",
+                TextUtils.expandTemplate("template ^a to be expanded", "value1").toString());
+        assertEquals("template to be expanded",
+                TextUtils.expandTemplate("template to be expanded", "value1").toString());
+        // two consecutive ^ in the input to produce a single ^ in the output.
+        assertEquals("template ^ to be expanded",
+                TextUtils.expandTemplate("template ^^ to be expanded", "value1").toString());
+        // two ^ with a space in the middle.
+        assertEquals("template ^ ^ to be expanded",
+                TextUtils.expandTemplate("template ^ ^ to be expanded", "value1").toString());
+        // ^1 follow a '^'
+        assertEquals("template ^1 to be expanded",
+                TextUtils.expandTemplate("template ^^1 to be expanded", "value1").toString());
+        // ^1 followed by a '^'
+        assertEquals("template value1^ to be expanded",
+                TextUtils.expandTemplate("template ^1^ to be expanded", "value1").toString());
+
+        // 9 replacement values
+        final int MAX_SUPPORTED_VALUES_NUM = 9;
+        CharSequence values[] = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM);
+        String expected = "value1 value2 template value3 value4 to value5 value6" +
+                " be value7 value8 expanded value9";
+        String template = "^1 ^2 template ^3 ^4 to ^5 ^6 be ^7 ^8 expanded ^9";
+        assertEquals(expected, TextUtils.expandTemplate(template, values).toString());
+
+        //  only up to 9 replacement values are supported
+        values = createCharSequenceArray(MAX_SUPPORTED_VALUES_NUM + 1);
+        try {
+            TextUtils.expandTemplate(template, values);
+            fail("Should throw IllegalArgumentException!");
+        } catch (IllegalArgumentException e) {
+            // expect
+        }
+
+        // template string is ^0
+        try {
+            TextUtils.expandTemplate("template ^0 to be expanded", "value1");
+        } catch (IllegalArgumentException e) {
+            // issue 1695243, doesn't discuss the case that ^0 in template string.
+        }
+
+        // template string is ^0
+        try {
+            TextUtils.expandTemplate("template ^0 to be expanded");
+        } catch (IllegalArgumentException e) {
+            // issue 1695243, doesn't discuss the case that ^0 in template string.
+        }
+
+        // the template requests 2 values but only 1 is provided
+        try {
+            TextUtils.expandTemplate("template ^2 to be expanded", "value1");
+            fail("Should throw IllegalArgumentException!");
+        } catch (IllegalArgumentException e) {
+            // expect
+        }
+
+        // values is null
+        try {
+            TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence[]) null);
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        // the template requests 2 values but only one null value is provided
+        try {
+            TextUtils.expandTemplate("template ^2 to be expanded", (CharSequence) null);
+            fail("Should throw IllegalArgumentException!");
+        } catch (IllegalArgumentException e) {
+            // expect
+        }
+
+        // the template requests 2 values and 2 values is provided, but all values are null.
+        try {
+            TextUtils.expandTemplate("template ^2 to be expanded",
+                    (CharSequence) null, (CharSequence) null);
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        // the template requests 2 values but no value is provided.
+        try {
+            TextUtils.expandTemplate("template ^2 to be expanded");
+            fail("Should throw IllegalArgumentException!");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // template is null
+        try {
+            TextUtils.expandTemplate(null, "value1");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Create a char sequence array with the specified length
+     * @param len the length of the array
+     * @return The char sequence array with the specified length.
+     * The value of each item is "value[index+1]"
+     */
+    private CharSequence[] createCharSequenceArray(int len) {
+        CharSequence array[] = new CharSequence[len];
+
+        for (int i = 0; i < len; i++) {
+            array[i] = "value" + (i + 1);
+        }
+
+        return array;
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getChars",
+        args = {CharSequence.class, int.class, int.class, char[].class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for getChars() does not exist.")
+    public void testGetChars() {
+        char[] destOriginal = "destination".toCharArray();
+        char[] destResult = destOriginal.clone();
+
+        // check whether GetChars.getChars() is called and with the proper parameters.
+        MockGetChars mockGetChars = new MockGetChars();
+        int start = 1;
+        int end = destResult.length;
+        int destOff = 2;
+        TextUtils.getChars(mockGetChars, start, end, destResult, destOff);
+        assertTrue(mockGetChars.hasCalledGetChars());
+        assertEquals(start, mockGetChars.ReadGetCharsParams().start);
+        assertEquals(end, mockGetChars.ReadGetCharsParams().end);
+        assertEquals(destResult, mockGetChars.ReadGetCharsParams().dest);
+        assertEquals(destOff, mockGetChars.ReadGetCharsParams().destoff);
+
+        // use MockCharSequence to do the test includes corner cases.
+        MockCharSequence mockCharSequence = new MockCharSequence("source string mock");
+        // get chars to place at the beginning of the destination except the latest one char.
+        destResult = destOriginal.clone();
+        start = 0;
+        end = destResult.length - 1;
+        destOff = 0;
+        TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
+        // chars before end are copied from the mockCharSequence.
+        for (int i = 0; i < end - start; i++) {
+            assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
+        }
+        // chars after end doesn't be changed.
+        for (int i = destOff + (end - start); i < destOriginal.length; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+
+        // get chars to place at the end of the destination except the earliest two chars.
+        destResult = destOriginal.clone();
+        start = 0;
+        end = destResult.length - 2;
+        destOff = 2;
+        TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
+        // chars before start doesn't be changed.
+        for (int i = 0; i < destOff; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+        // chars after start are copied from the mockCharSequence.
+        for (int i = 0; i < end - start; i++) {
+            assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
+        }
+
+        // get chars to place at the end of the destination except the earliest two chars
+        // and the latest one word.
+        destResult = destOriginal.clone();
+        start = 1;
+        end = destResult.length - 2;
+        destOff = 0;
+        TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
+        for (int i = 0; i < destOff; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+        for (int i = 0; i < end - start; i++) {
+            assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
+        }
+        for (int i = destOff + (end - start); i < destOriginal.length; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+
+        // get chars to place the whole of the destination
+        destResult = destOriginal.clone();
+        start = 0;
+        end = destResult.length;
+        destOff = 0;
+        TextUtils.getChars(mockCharSequence, start, end, destResult, destOff);
+        for (int i = 0; i < end - start; i++) {
+            assertEquals(mockCharSequence.charAt(start + i), destResult[destOff + i]);
+        }
+
+        // exceptional start.
+        end = 2;
+        destOff = 0;
+        destResult = destOriginal.clone();
+        try {
+            TextUtils.getChars(mockCharSequence, -1, end, destResult, destOff);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        destResult = destOriginal.clone();
+        TextUtils.getChars(mockCharSequence, Integer.MAX_VALUE, end, destResult, destOff);
+        for (int i = 0; i < destResult.length; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+
+        // exceptional end.
+        destResult = destOriginal.clone();
+        start = 0;
+        destOff = 0;
+        try {
+            TextUtils.getChars(mockCharSequence, start, destResult.length + 1, destResult, destOff);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        destResult = destOriginal.clone();
+        TextUtils.getChars(mockCharSequence, start, -1, destResult, destOff);
+        for (int i = 0; i < destResult.length; i++) {
+            assertEquals(destOriginal[i], destResult[i]);
+        }
+
+        // exceptional destOff.
+        destResult = destOriginal.clone();
+        start = 0;
+        end = 2;
+        try {
+            TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        try {
+            TextUtils.getChars(mockCharSequence, start, end, destResult, Integer.MIN_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+
+        // exceptional source
+        start = 0;
+        end = 2;
+        destOff =0;
+        try {
+            TextUtils.getChars(null, start, end, destResult, destOff);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        // exceptional destination
+        try {
+            TextUtils.getChars(mockCharSequence, start, end, null, destOff);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * MockGetChars for test.
+     */
+    private class MockGetChars implements GetChars {
+        private boolean mHasCalledGetChars;
+        private GetCharsParams mGetCharsParams = new GetCharsParams();
+
+        class GetCharsParams {
+            int start;
+            int end;
+            char[] dest;
+            int destoff;
+        }
+
+        public boolean hasCalledGetChars() {
+            return mHasCalledGetChars;
+        }
+
+        public void reset() {
+            mHasCalledGetChars = false;
+        }
+
+        public GetCharsParams ReadGetCharsParams() {
+            return mGetCharsParams;
+        }
+
+        public void getChars(int start, int end, char[] dest, int destoff) {
+            mHasCalledGetChars = true;
+            mGetCharsParams.start = start;
+            mGetCharsParams.end = end;
+            mGetCharsParams.dest = dest;
+            mGetCharsParams.destoff = destoff;
+        }
+
+        public char charAt(int arg0) {
+            return 0;
+        }
+
+        public int length() {
+            return 100;
+        }
+
+        public CharSequence subSequence(int arg0, int arg1) {
+            return null;
+        }
+    }
+
+    /**
+     * MockCharSequence for test.
+     */
+    private class MockCharSequence implements CharSequence {
+        private char mText[];
+
+        public MockCharSequence() {
+            this("");
+        }
+
+        public MockCharSequence(String text) {
+            mText = text.toCharArray();
+        }
+
+        public char charAt(int arg0) {
+            if (arg0 >= 0 && arg0 < mText.length) {
+                return mText[arg0];
+            }
+            throw new IndexOutOfBoundsException();
+        }
+
+        public int length() {
+            return mText.length;
+        }
+
+        public CharSequence subSequence(int arg0, int arg1) {
+            return null;
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getOffsetAfter",
+        args = {CharSequence.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for getOffsetAfter() does not exist.")
+    public void testGetOffsetAfter() {
+        // the first '\uD800' is index 9, the second 'uD800' is index 16
+        // the '\uDBFF' is index 26
+        final int POS_FIRST_D800 = 9;       // the position of the first '\uD800'.
+        final int POS_SECOND_D800 = 16;
+        final int POS_FIRST_DBFF = 26;
+        final int SUPPLEMENTARY_CHARACTERS_OFFSET = 2;  // the offset for a supplementary characters
+        final int NORMAL_CHARACTERS_OFFSET = 1;
+        SpannableString text = new SpannableString(
+                "string to\uD800\uDB00 get \uD800\uDC00 offset \uDBFF\uDFFF after");
+        assertEquals(0 + 1, TextUtils.getOffsetAfter(text, 0));
+        assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length()));
+        assertEquals(text.length(), TextUtils.getOffsetAfter(text, text.length() - 1));
+        assertEquals(POS_FIRST_D800 + NORMAL_CHARACTERS_OFFSET,
+                TextUtils.getOffsetAfter(text, POS_FIRST_D800));
+        assertEquals(POS_SECOND_D800 + SUPPLEMENTARY_CHARACTERS_OFFSET,
+                TextUtils.getOffsetAfter(text, POS_SECOND_D800));
+        assertEquals(POS_FIRST_DBFF + SUPPLEMENTARY_CHARACTERS_OFFSET,
+                TextUtils.getOffsetAfter(text, POS_FIRST_DBFF));
+
+        // the CharSequence string has a span.
+        MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
+        text.setSpan(mockReplacementSpan, POS_FIRST_D800 - 1, text.length() - 1,
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        assertEquals(text.length() - 1, TextUtils.getOffsetAfter(text, POS_FIRST_D800));
+
+        try {
+            TextUtils.getOffsetAfter(text, -1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.getOffsetAfter(text, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.getOffsetAfter(null, 0);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    /**
+     * MockReplacementSpan for test.
+     */
+    private class MockReplacementSpan extends ReplacementSpan {
+        @Override
+        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
+                int y, int bottom, Paint paint) {
+        }
+
+        @Override
+        public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
+            return 0;
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getOffsetBefore",
+        args = {CharSequence.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for getOffsetBefore() does not exist.")
+    public void testGetOffsetBefore() {
+        // the first '\uDC00' is index 10, the second 'uDC00' is index 17
+        // the '\uDFFF' is index 27
+        final int POS_FIRST_DC00 = 10;
+        final int POS_SECOND_DC00 = 17;
+        final int POS_FIRST_DFFF = 27;
+        final int SUPPLYMENTARY_CHARACTERS_OFFSET = 2;
+        final int NORMAL_CHARACTERS_OFFSET = 1;
+        SpannableString text = new SpannableString(
+                "string to\uD700\uDC00 get \uD800\uDC00 offset \uDBFF\uDFFF before");
+        assertEquals(0, TextUtils.getOffsetBefore(text, 0));
+        assertEquals(0, TextUtils.getOffsetBefore(text, 1));
+        assertEquals(text.length() - 1, TextUtils.getOffsetBefore(text, text.length()));
+        assertEquals(POS_FIRST_DC00 + 1 - NORMAL_CHARACTERS_OFFSET,
+                TextUtils.getOffsetBefore(text, POS_FIRST_DC00 + 1));
+        assertEquals(POS_SECOND_DC00 + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
+                TextUtils.getOffsetBefore(text, POS_SECOND_DC00 + 1));
+        assertEquals(POS_FIRST_DFFF + 1 - SUPPLYMENTARY_CHARACTERS_OFFSET,
+                TextUtils.getOffsetBefore(text, POS_FIRST_DFFF + 1));
+
+        // the CharSequence string has a span.
+        MockReplacementSpan mockReplacementSpan = new MockReplacementSpan();
+        text.setSpan(mockReplacementSpan, 0, POS_FIRST_DC00 + 1,
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        assertEquals(0, TextUtils.getOffsetBefore(text, POS_FIRST_DC00));
+
+        try {
+            TextUtils.getOffsetBefore(text, -1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.getOffsetBefore(text, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.getOffsetBefore(null, POS_FIRST_DC00);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getReverse",
+        args = {CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for getReverse() does not exist.")
+    public void testGetReverse() {
+        String source = "string to be reversed";
+        assertEquals("gnirts", TextUtils.getReverse(source, 0, "string".length()).toString());
+        assertEquals("desrever",
+                TextUtils.getReverse(source, source.length() - "reversed".length(),
+                        source.length()).toString());
+        assertEquals("", TextUtils.getReverse(source, 0, 0).toString());
+
+        // issue 1695243, exception is thrown after the result of some cases
+        // convert to a string, is this expected?
+        CharSequence result = TextUtils.getReverse(source, -1, "string".length());
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        TextUtils.getReverse(source, 0, source.length() + 1);
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        TextUtils.getReverse(source, "string".length(), 0);
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        TextUtils.getReverse(source, 0, Integer.MAX_VALUE);
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        TextUtils.getReverse(source, Integer.MIN_VALUE, "string".length());
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        TextUtils.getReverse(null, 0, "string".length());
+        try {
+            result.toString();
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getTrimmedLength",
+        args = {CharSequence.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for getReverse() is incomplete." +
+            "1. doesn't explain @param and @return." +
+            "2. doesn't discuss the case that parameter is expectional.")
+    public void testGetTrimmedLength() {
+        assertEquals("normalstring".length(), TextUtils.getTrimmedLength("normalstring"));
+        assertEquals("normal string".length(), TextUtils.getTrimmedLength("normal string"));
+        assertEquals("blank before".length(), TextUtils.getTrimmedLength(" \t  blank before"));
+        assertEquals("blank after".length(), TextUtils.getTrimmedLength("blank after   \n    "));
+        assertEquals("blank both".length(), TextUtils.getTrimmedLength(" \t   blank both  \n "));
+
+        char[] allTrimmedChars = new char[] {
+                '\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005', '\u0006', '\u0007',
+                '\u0008', '\u0009', '\u0010', '\u0011', '\u0012', '\u0013', '\u0014', '\u0015',
+                '\u0016', '\u0017', '\u0018', '\u0019', '\u0020'
+        };
+        assertEquals(0, TextUtils.getTrimmedLength(String.valueOf(allTrimmedChars)));
+
+        try {
+            TextUtils.getTrimmedLength(null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "htmlEncode",
+        args = {String.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for htmlEncode() is incomplete." +
+            "1. doesn't discuss the case that parameter is expectional.")
+    public void testHtmlEncode() {
+        assertEquals("&lt;_html_&gt;\\ &amp;&quot;&apos;string&apos;&quot;",
+                TextUtils.htmlEncode("<_html_>\\ &\"'string'\""));
+
+         try {
+             TextUtils.htmlEncode(null);
+             fail("Should throw NullPointerException!");
+         } catch (NullPointerException e) {
+             // expected
+         }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, char.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf1() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_FIRST_R = 2;     // first occurrence of 'r'
+        final int INDEX_OF_FIRST_T = 1;
+        final int INDEX_OF_FIRST_D = searchString.length() - 1;
+
+        assertEquals(INDEX_OF_FIRST_T, TextUtils.indexOf(searchString, 't'));
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r'));
+        assertEquals(INDEX_OF_FIRST_D, TextUtils.indexOf(searchString, 'd'));
+        assertEquals(-1, TextUtils.indexOf(searchString, 'f'));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuffer, 'r'));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(stringBuilder, 'r'));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        assertFalse(mockGetChars.hasCalledGetChars());
+        TextUtils.indexOf(mockGetChars, 'r');
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(mockCharSequence, 'r'));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, char.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf2() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_FIRST_R = 2;
+        final int INDEX_OF_SECOND_R = 16;
+
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', 0));
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r', INDEX_OF_FIRST_R + 1));
+        assertEquals(-1, TextUtils.indexOf(searchString, 'r', searchString.length()));
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE));
+        assertEquals(2, TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r', INDEX_OF_FIRST_R + 1));
+        try {
+            TextUtils.indexOf(stringBuffer, 'r', Integer.MIN_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        assertEquals(-1, TextUtils.indexOf(stringBuffer, 'r', Integer.MAX_VALUE));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_SECOND_R,
+                TextUtils.indexOf(stringBuilder, 'r', INDEX_OF_FIRST_R + 1));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1);
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
+                INDEX_OF_FIRST_R + 1));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, char.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf3() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_FIRST_R = 2;
+        final int INDEX_OF_SECOND_R = 16;
+
+        assertEquals(INDEX_OF_FIRST_R,
+                TextUtils.indexOf(searchString, 'r', 0, searchString.length()));
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(searchString, 'r',
+                INDEX_OF_FIRST_R + 1, searchString.length()));
+        assertEquals(-1, TextUtils.indexOf(searchString, 'r',
+                INDEX_OF_FIRST_R + 1, INDEX_OF_SECOND_R));
+
+        try {
+            TextUtils.indexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        assertEquals(-1,
+                TextUtils.indexOf(searchString, 'r', Integer.MAX_VALUE, INDEX_OF_SECOND_R));
+        assertEquals(-1, TextUtils.indexOf(searchString, 'r', 0, Integer.MIN_VALUE));
+        try {
+            TextUtils.indexOf(searchString, 'r', 0, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuffer, 'r',
+                INDEX_OF_FIRST_R + 1, searchString.length()));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(stringBuilder, 'r',
+                INDEX_OF_FIRST_R + 1, searchString.length()));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.indexOf(mockGetChars, 'r', INDEX_OF_FIRST_R + 1, searchString.length());
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.indexOf(mockCharSequence, 'r',
+                INDEX_OF_FIRST_R + 1, searchString.length()));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, CharSequence.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf4() {
+        String searchString = "string to be searched by string";
+        final int SEARCH_INDEX = 13;
+
+        assertEquals(0, TextUtils.indexOf(searchString, "string"));
+        assertEquals(SEARCH_INDEX, TextUtils.indexOf(searchString, "search"));
+        assertEquals(-1, TextUtils.indexOf(searchString, "tobe"));
+        assertEquals(0, TextUtils.indexOf(searchString, ""));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuffer, "search"));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(SEARCH_INDEX, TextUtils.indexOf(stringBuilder, "search"));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.indexOf(mockGetChars, "search");
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(SEARCH_INDEX, TextUtils.indexOf(mockCharSequence, "search"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, CharSequence.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf5() {
+        String searchString = "string to be searched by string";
+        final int INDEX_OF_FIRST_STRING = 0;
+        final int INDEX_OF_SECOND_STRING = 25;
+
+        assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0));
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
+                INDEX_OF_FIRST_STRING + 1));
+        assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_SECOND_STRING + 1));
+        assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
+                Integer.MIN_VALUE));
+        assertEquals(0, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE));
+
+        assertEquals(1, TextUtils.indexOf(searchString, "", 1));
+        assertEquals(Integer.MAX_VALUE, TextUtils.indexOf(searchString, "", Integer.MAX_VALUE));
+
+        assertEquals(0, TextUtils.indexOf(searchString, searchString, 0));
+        assertEquals(-1, TextUtils.indexOf(searchString, searchString + "longer needle", 0));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
+                INDEX_OF_FIRST_STRING + 1));
+        try {
+            TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
+                INDEX_OF_FIRST_STRING + 1));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        assertFalse(mockGetChars.hasCalledGetChars());
+        TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1);
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
+                INDEX_OF_FIRST_STRING + 1));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "indexOf",
+        args = {CharSequence.class, CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for indexOf() does not exist.")
+    public void testIndexOf6() {
+        String searchString = "string to be searched by string";
+        final int INDEX_OF_FIRST_STRING = 0;
+        final int INDEX_OF_SECOND_STRING = 25;
+
+        assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string", 0,
+                searchString.length()));
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
+                INDEX_OF_FIRST_STRING + 1, searchString.length()));
+        assertEquals(-1, TextUtils.indexOf(searchString, "string", INDEX_OF_FIRST_STRING + 1,
+                INDEX_OF_SECOND_STRING - 1));
+        assertEquals(INDEX_OF_FIRST_STRING, TextUtils.indexOf(searchString, "string",
+                Integer.MIN_VALUE, INDEX_OF_SECOND_STRING - 1));
+        assertEquals(0, TextUtils.indexOf(searchString, "string", Integer.MAX_VALUE,
+                INDEX_OF_SECOND_STRING - 1));
+
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
+                INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(searchString, "string",
+                INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer, "string",
+                INDEX_OF_FIRST_STRING + 1, searchString.length()));
+        try {
+            TextUtils.indexOf(stringBuffer, "string", Integer.MIN_VALUE,
+                    INDEX_OF_SECOND_STRING - 1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        assertEquals(-1, TextUtils.indexOf(stringBuffer, "string", Integer.MAX_VALUE,
+                searchString.length()));
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
+                "string", INDEX_OF_FIRST_STRING + 1, Integer.MIN_VALUE));
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuffer,
+                "string", INDEX_OF_FIRST_STRING + 1, Integer.MAX_VALUE));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(stringBuilder, "string",
+                INDEX_OF_FIRST_STRING + 1, searchString.length()));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.indexOf(mockGetChars, "string", INDEX_OF_FIRST_STRING + 1, searchString.length());
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_SECOND_STRING, TextUtils.indexOf(mockCharSequence, "string",
+                INDEX_OF_FIRST_STRING + 1, searchString.length()));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isDigitsOnly",
+        args = {CharSequence.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for isDigitsOnly() is incomplete.")
+    public void testIsDigitsOnly() {
+        assertFalse(TextUtils.isDigitsOnly("no digit"));
+        assertFalse(TextUtils.isDigitsOnly("character and 56 digits"));
+        assertTrue(TextUtils.isDigitsOnly("0123456789"));
+        assertFalse(TextUtils.isDigitsOnly("1234 56789"));
+
+        try {
+            TextUtils.isDigitsOnly(null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // issue 1695243, not clear what is supposed result if the CharSequence is null.
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isEmpty",
+        args = {CharSequence.class}
+    )
+    public void testIsEmpty() {
+        assertFalse(TextUtils.isEmpty("not empty"));
+        assertFalse(TextUtils.isEmpty("    "));
+        assertTrue(TextUtils.isEmpty(""));
+        assertTrue(TextUtils.isEmpty(null));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isGraphic",
+        args = {char.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for isGraphic() is incomplete.")
+    public void testIsGraphicChar() {
+        assertTrue(TextUtils.isGraphic('a'));
+        assertTrue(TextUtils.isGraphic("\uBA00"));
+
+        // LINE_SEPARATOR
+        assertFalse(TextUtils.isGraphic('\u2028'));
+
+        // PARAGRAPH_SEPARATOR
+        assertFalse(TextUtils.isGraphic('\u2029'));
+
+        // CONTROL
+        assertFalse(TextUtils.isGraphic('\u0085'));
+
+        // UNASSIGNED
+        assertFalse(TextUtils.isGraphic('\u0D00'));
+
+        // SURROGATE
+        assertFalse(TextUtils.isGraphic('\uD800'));
+
+        // SPACE_SEPARATOR
+        assertFalse(TextUtils.isGraphic('\u0020'));
+
+        try {
+            assertFalse(TextUtils.isGraphic((Character) null));
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isGraphic",
+        args = {CharSequence.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for isGraphic() is incomplete.")
+    public void testIsGraphicCharSequence() {
+        assertTrue(TextUtils.isGraphic("printable characters"));
+
+        assertFalse(TextUtils.isGraphic("\u2028\u2029\u0085\u0D00\uD800\u0020"));
+
+        assertTrue(TextUtils.isGraphic("a\u2028\u2029\u0085\u0D00\uD800\u0020"));
+
+        try {
+            TextUtils.isGraphic(null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "join",
+        args = {CharSequence.class, Iterable.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for join() is incomplete.")
+    public void testJoin1() {
+        ArrayList<CharSequence> charTokens = new ArrayList<CharSequence>();
+        charTokens.add("string1");
+        charTokens.add("string2");
+        charTokens.add("string3");
+        assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
+        assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
+        assertEquals("string1string2string3", TextUtils.join("", charTokens));
+
+        // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
+        assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
+        try {
+            TextUtils.join("|", (Iterable) null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+
+        ArrayList<SpannableString> spannableStringTokens = new ArrayList<SpannableString>();
+        spannableStringTokens.add(new SpannableString("span 1"));
+        spannableStringTokens.add(new SpannableString("span 2"));
+        spannableStringTokens.add(new SpannableString("span 3"));
+        assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "join",
+        args = {CharSequence.class, Object[].class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for join() is incomplete.")
+    public void testJoin2() {
+        CharSequence[] charTokens = new CharSequence[] { "string1", "string2", "string3" };
+        assertEquals("string1|string2|string3", TextUtils.join("|", charTokens));
+        assertEquals("string1; string2; string3", TextUtils.join("; ", charTokens));
+        assertEquals("string1string2string3", TextUtils.join("", charTokens));
+
+        // issue 1695243, not clear what is supposed result if the delimiter or tokens are null.
+        assertEquals("string1nullstring2nullstring3", TextUtils.join(null, charTokens));
+        try {
+            TextUtils.join("|", (Object[]) null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+
+        SpannableString[] spannableStringTokens = new SpannableString[] {
+                new SpannableString("span 1"),
+                new SpannableString("span 2"),
+                new SpannableString("span 3") };
+        assertEquals("span 1;span 2;span 3", TextUtils.join(";", spannableStringTokens));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "lastIndexOf",
+        args = {CharSequence.class, char.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for lastIndexOf() does not exist.")
+    public void testLastIndexOf1() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_LAST_R = 16;
+        final int INDEX_OF_LAST_T = 7;
+        final int INDEX_OF_LAST_D = searchString.length() - 1;
+
+        assertEquals(INDEX_OF_LAST_T, TextUtils.lastIndexOf(searchString, 't'));
+        assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(searchString, 'r'));
+        assertEquals(INDEX_OF_LAST_D, TextUtils.lastIndexOf(searchString, 'd'));
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'f'));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuffer, 'r'));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(stringBuilder, 'r'));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.lastIndexOf(mockGetChars, 'r');
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_LAST_R, TextUtils.lastIndexOf(mockCharSequence, 'r'));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "lastIndexOf",
+        args = {CharSequence.class, char.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for lastIndexOf() does not exist.")
+    public void testLastIndexOf2() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_FIRST_R = 2;
+        final int INDEX_OF_SECOND_R = 16;
+
+        assertEquals(INDEX_OF_SECOND_R,
+                TextUtils.lastIndexOf(searchString, 'r', searchString.length()));
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0));
+        assertEquals(INDEX_OF_FIRST_R,
+                TextUtils.lastIndexOf(searchString, 'r', INDEX_OF_FIRST_R));
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE));
+        assertEquals(INDEX_OF_SECOND_R,
+                TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_FIRST_R,
+                TextUtils.lastIndexOf(stringBuffer, 'r', INDEX_OF_FIRST_R));
+        assertEquals(-1, TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MIN_VALUE));
+        assertEquals(INDEX_OF_SECOND_R,
+                TextUtils.lastIndexOf(stringBuffer, 'r', Integer.MAX_VALUE));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_FIRST_R,
+                TextUtils.lastIndexOf(stringBuilder, 'r', INDEX_OF_FIRST_R));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.lastIndexOf(mockGetChars, 'r', INDEX_OF_FIRST_R);
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_FIRST_R,
+                TextUtils.lastIndexOf(mockCharSequence, 'r', INDEX_OF_FIRST_R));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "lastIndexOf",
+        args = {CharSequence.class, char.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for lastIndexOf() does not exist.")
+    public void testLastIndexOf3() {
+        String searchString = "string to be searched";
+        final int INDEX_OF_FIRST_R = 2;
+        final int INDEX_OF_SECOND_R = 16;
+
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
+                searchString.length()));
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(searchString, 'r', 0,
+                INDEX_OF_SECOND_R - 1));
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, INDEX_OF_FIRST_R - 1));
+
+        try {
+            TextUtils.lastIndexOf(searchString, 'r', Integer.MIN_VALUE, INDEX_OF_SECOND_R - 1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expect
+        }
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', Integer.MAX_VALUE,
+                INDEX_OF_SECOND_R - 1));
+        assertEquals(-1, TextUtils.lastIndexOf(searchString, 'r', 0, Integer.MIN_VALUE));
+        assertEquals(INDEX_OF_SECOND_R, TextUtils.lastIndexOf(searchString, 'r', 0,
+                Integer.MAX_VALUE));
+
+        StringBuffer stringBuffer = new StringBuffer(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuffer, 'r', 0,
+                INDEX_OF_SECOND_R - 1));
+
+        StringBuilder stringBuilder = new StringBuilder(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(stringBuilder, 'r', 0,
+                INDEX_OF_SECOND_R - 1));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.lastIndexOf(mockGetChars, 'r', 0, INDEX_OF_SECOND_R - 1);
+        assertTrue(mockGetChars.hasCalledGetChars());
+
+        MockCharSequence mockCharSequence = new MockCharSequence(searchString);
+        assertEquals(INDEX_OF_FIRST_R, TextUtils.lastIndexOf(mockCharSequence, 'r', 0,
+                INDEX_OF_SECOND_R - 1));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "regionMatches",
+        args = {CharSequence.class, int.class, CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for regionMatches() does not exist.")
+    public void testRegionMatches() {
+        assertFalse(TextUtils.regionMatches("one", 0, "two", 0, "one".length()));
+        assertTrue(TextUtils.regionMatches("one", 0, "one", 0, "one".length()));
+        try {
+            TextUtils.regionMatches("one", 0, "one", 0, "one".length() + 1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        String one = "Hello Android, hello World!";
+        String two = "Hello World";
+        // match "Hello"
+        assertTrue(TextUtils.regionMatches(one, 0, two, 0, "Hello".length()));
+
+        // match "Hello A" and "Hello W"
+        assertFalse(TextUtils.regionMatches(one, 0, two, 0, "Hello A".length()));
+
+        // match "World"
+        assertTrue(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
+                two, "Hello ".length(), "World".length()));
+        assertFalse(TextUtils.regionMatches(one, "Hello Android, hello ".length(),
+                two, 0, "World".length()));
+
+        try {
+            TextUtils.regionMatches(one, Integer.MIN_VALUE, two, 0, "Hello".length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+        try {
+            TextUtils.regionMatches(one, Integer.MAX_VALUE, two, 0, "Hello".length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.regionMatches(one, 0, two, Integer.MIN_VALUE, "Hello".length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+        try {
+            TextUtils.regionMatches(one, 0, two, Integer.MAX_VALUE, "Hello".length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.regionMatches(one, 0, two, 0, Integer.MIN_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+        try {
+            TextUtils.regionMatches(one, 0, two, 0, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+        }
+
+        try {
+            TextUtils.regionMatches(null, 0, two, 0, "Hello".length());
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+        try {
+            TextUtils.regionMatches(one, 0, null, 0, "Hello".length());
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "replace",
+        args = {CharSequence.class, String[].class, CharSequence[].class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for replace() is incomplete.")
+    public void testReplace() {
+        String template = "this is a string to be as the template for replacement";
+
+        String sources[] = new String[] { "string" };
+        CharSequence destinations[] = new CharSequence[] { "text" };
+        SpannableStringBuilder replacedString = (SpannableStringBuilder) TextUtils.replace(template,
+                sources, destinations);
+        assertEquals("this is a text to be as the template for replacement",
+                replacedString.toString());
+
+        sources = new String[] {"is", "the", "for replacement"};
+        destinations = new CharSequence[] {"was", "", "to be replaced"};
+        replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
+        assertEquals("thwas is a string to be as  template to be replaced",
+                replacedString.toString());
+
+        sources = new String[] {"is", "for replacement"};
+        destinations = new CharSequence[] {"was", "", "to be replaced"};
+        replacedString = (SpannableStringBuilder)TextUtils.replace(template, sources, destinations);
+        assertEquals("thwas is a string to be as the template ", replacedString.toString());
+
+        sources = new String[] {"is", "the", "for replacement"};
+        destinations = new CharSequence[] {"was", "to be replaced"};
+        try {
+            TextUtils.replace(template, sources, destinations);
+            fail("Should throw ArrayIndexOutOfBoundsException!");
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.replace(null, sources, destinations);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            TextUtils.replace(template, null, destinations);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        try {
+            TextUtils.replace(template, sources, null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "split",
+        args = {String.class, Pattern.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for split() is incomplete." +
+            "1. not clear what is supposed result if the pattern string is empty.")
+    public void testSplitPattern() {
+        String testString = "abccbadecdebz";
+        assertEquals(calculateCharsCount(testString, "c") + 1,
+                TextUtils.split(testString, Pattern.compile("c")).length);
+        assertEquals(calculateCharsCount(testString, "a") + 1,
+                TextUtils.split(testString, Pattern.compile("a")).length);
+        assertEquals(calculateCharsCount(testString, "z") + 1,
+                TextUtils.split(testString, Pattern.compile("z")).length);
+        assertEquals(calculateCharsCount(testString, "de") + 1,
+                TextUtils.split(testString, Pattern.compile("de")).length);
+        int totalCount = 1 + calculateCharsCount(testString, "a")
+                + calculateCharsCount(testString, "b") + calculateCharsCount(testString, "c");
+        assertEquals(totalCount,
+                TextUtils.split(testString, Pattern.compile("[a-c]")).length);
+        assertEquals(0, TextUtils.split("", Pattern.compile("a")).length);
+        // issue 1695243, not clear what is supposed result if the pattern string is empty.
+        assertEquals(testString.length() + 2,
+                TextUtils.split(testString, Pattern.compile("")).length);
+
+        try {
+            TextUtils.split(null, Pattern.compile("a"));
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+        try {
+            TextUtils.split("abccbadecdebz", (Pattern) null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+    }
+
+    /*
+     * return the appearance count of searched chars in text.
+     */
+    private int calculateCharsCount(CharSequence text, CharSequence searches) {
+        int count = 0;
+        int start = TextUtils.indexOf(text, searches, 0);
+
+        while (start != -1) {
+            count++;
+            start = TextUtils.indexOf(text, searches, start + 1);
+        }
+        return count;
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "split",
+        args = {String.class, String.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for split() is incomplete." +
+            "1. not clear what is supposed result if the pattern string is empty.")
+    public void testSplitString() {
+        String testString = "abccbadecdebz";
+        assertEquals(calculateCharsCount(testString, "c") + 1,
+                TextUtils.split("abccbadecdebz", "c").length);
+        assertEquals(calculateCharsCount(testString, "a") + 1,
+                TextUtils.split("abccbadecdebz", "a").length);
+        assertEquals(calculateCharsCount(testString, "z") + 1,
+                TextUtils.split("abccbadecdebz", "z").length);
+        assertEquals(calculateCharsCount(testString, "de") + 1,
+                TextUtils.split("abccbadecdebz", "de").length);
+        assertEquals(0, TextUtils.split("", "a").length);
+        // issue 1695243, not clear what is supposed result if the pattern string is empty.
+        assertEquals(testString.length() + 2,
+                TextUtils.split("abccbadecdebz", "").length);
+
+        try {
+            TextUtils.split(null, "a");
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+        try {
+            TextUtils.split("abccbadecdebz", (String) null);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expect
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "stringOrSpannedString",
+        args = {CharSequence.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for" +
+            " stringOrSpannedString() does not exist.")
+    public void testStringOrSpannedString() {
+        assertNull(TextUtils.stringOrSpannedString(null));
+
+        SpannedString spannedString = new SpannedString("Spanned String");
+        assertSame(spannedString, TextUtils.stringOrSpannedString(spannedString));
+
+        SpannableString spannableString = new SpannableString("Spannable String");
+        assertEquals("Spannable String",
+                TextUtils.stringOrSpannedString(spannableString).toString());
+        assertEquals(SpannedString.class,
+                TextUtils.stringOrSpannedString(spannableString).getClass());
+
+        StringBuffer stringBuffer = new StringBuffer("String Buffer");
+        assertEquals("String Buffer",
+                TextUtils.stringOrSpannedString(stringBuffer).toString());
+        assertEquals(String.class,
+                TextUtils.stringOrSpannedString(stringBuffer).getClass());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "substring",
+        args = {CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for substring() is incomplete." +
+            "1. doesn't explain @param and @return" +
+            "2. not clear what is supposed to happen if source is null." +
+            "3. doesn't explain the thrown IndexOutOfBoundsException")
+    public void testSubString() {
+        String string = "String";
+        assertSame(string, TextUtils.substring(string, 0, string.length()));
+        assertEquals("Strin", TextUtils.substring(string, 0, string.length() - 1));
+        assertEquals("", TextUtils.substring(string, 1, 1));
+
+        try {
+            TextUtils.substring(string, string.length(), 0);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.substring(string, -1, string.length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.substring(string, Integer.MAX_VALUE, string.length());
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.substring(string, 0, -1);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.substring(string, 0, Integer.MAX_VALUE);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            TextUtils.substring(null, 0, string.length());
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        StringBuffer stringBuffer = new StringBuffer("String Buffer");
+        assertEquals("Strin", TextUtils.substring(stringBuffer, 0, string.length() - 1));
+        assertEquals("", TextUtils.substring(stringBuffer, 1, 1));
+
+        MockGetChars mockGetChars = new MockGetChars();
+        TextUtils.substring(mockGetChars, 0, string.length());
+        assertTrue(mockGetChars.hasCalledGetChars());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "writeToParcel",
+        args = {CharSequence.class, Parcel.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for writeToParcel() is incomplete." +
+            "1. doesn't explain @param and @return" +
+            "2. not clear is it the supposed result when the CharSequence is null.")
+    public void testWriteToParcel() {
+        Parcel p = Parcel.obtain();
+
+        Parcelable.Creator<CharSequence> creator = TextUtils.CHAR_SEQUENCE_CREATOR;
+
+        String string = "String";
+        TextUtils.writeToParcel(string, p, 0);
+        p.setDataPosition(0);
+        assertEquals(string, creator.createFromParcel(p).toString());
+        p.recycle();
+
+        p = Parcel.obtain();
+        TextUtils.writeToParcel(null, p, 0);
+        p.setDataPosition(0);
+        assertNull(creator.createFromParcel(p));
+        p.recycle();
+
+        p = Parcel.obtain();
+        SpannableString spannableString = new SpannableString("Spannable String");
+        URLSpan urlSpan = new URLSpan("URL Span");
+        int urlSpanStart = spannableString.length() >> 1;
+        int urlSpanEnd = spannableString.length();
+        spannableString.setSpan(urlSpan, urlSpanStart, urlSpanEnd,
+                Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        TextUtils.writeToParcel(spannableString, p, 0);
+        p.setDataPosition(0);
+        SpannableString ret = (SpannableString) creator.createFromParcel(p);
+        assertEquals("Spannable String", ret.toString());
+        Object[] spans = ret.getSpans(0, ret.length(), Object.class);
+        assertEquals(1, spans.length);
+        assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
+        assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
+        assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
+        assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
+        p.recycle();
+
+        p = Parcel.obtain();
+        ColorStateList colors = new ColorStateList(new int[][] {
+                new int[] {android.R.attr.state_focused}, new int[0]},
+                new int[] {Color.rgb(0, 255, 0), Color.BLACK});
+        int textSize = 20;
+        TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(
+                null, Typeface.ITALIC, textSize, colors, null);
+        int textAppearanceSpanStart = 0;
+        int textAppearanceSpanEnd = spannableString.length() >> 1;
+        spannableString.setSpan(textAppearanceSpan, textAppearanceSpanStart,
+                textAppearanceSpanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        TextUtils.writeToParcel(spannableString, p, -1);
+        p.setDataPosition(0);
+        ret = (SpannableString) creator.createFromParcel(p);
+        assertEquals("Spannable String", ret.toString());
+        spans = ret.getSpans(0, ret.length(), Object.class);
+        assertEquals(2, spans.length);
+        assertEquals("URL Span", ((URLSpan) spans[0]).getURL());
+        assertEquals(urlSpanStart, ret.getSpanStart(spans[0]));
+        assertEquals(urlSpanEnd, ret.getSpanEnd(spans[0]));
+        assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, ret.getSpanFlags(spans[0]));
+        assertEquals(null, ((TextAppearanceSpan) spans[1]).getFamily());
+
+        assertEquals(Typeface.ITALIC, ((TextAppearanceSpan) spans[1]).getTextStyle());
+        assertEquals(textSize, ((TextAppearanceSpan) spans[1]).getTextSize());
+
+        assertEquals(colors.toString(), ((TextAppearanceSpan) spans[1]).getTextColor().toString());
+        assertEquals(null, ((TextAppearanceSpan) spans[1]).getLinkTextColor());
+        assertEquals(textAppearanceSpanStart, ret.getSpanStart(spans[1]));
+        assertEquals(textAppearanceSpanEnd, ret.getSpanEnd(spans[1]));
+        assertEquals(Spanned.SPAN_INCLUSIVE_EXCLUSIVE, ret.getSpanFlags(spans[1]));
+        p.recycle();
+
+        try {
+            TextUtils.writeToParcel(spannableString, null, 0);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getCapsMode",
+        args = {CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1586346", explanation = "return cap mode which is NOT set in reqModes")
+    public void testGetCapsMode() {
+        final int CAP_MODE_ALL = TextUtils.CAP_MODE_CHARACTERS
+                | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES;
+        final int CAP_MODE_CHARACTERS_AND_WORD =
+                TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS;
+        String testString = "Start. Sentence word!No space before\n\t" +
+                "Paragraph? (\"\'skip begin\'\"). skip end";
+
+        // CAP_MODE_SENTENCES should be in effect in the whole text.
+        for (int i = 0; i < testString.length(); i++) {
+            assertEquals(TextUtils.CAP_MODE_CHARACTERS,
+                    TextUtils.getCapsMode(testString, i, TextUtils.CAP_MODE_CHARACTERS));
+        }
+
+        // all modes should be in effect at the start of the text.
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_WORDS));
+        // issue 1586346
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, 0, TextUtils.CAP_MODE_SENTENCES));
+        assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
+                TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
+
+        // all mode should be in effect at the position after "." or "?" or "!" + " ".
+        int offset = testString.indexOf("Sentence word!");
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        assertEquals(TextUtils.CAP_MODE_SENTENCES,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        // issue 1586346
+        assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
+                TextUtils.getCapsMode(testString, 0, CAP_MODE_ALL));
+
+        // CAP_MODE_SENTENCES should NOT be in effect at the position after other words + " ".
+        offset = testString.indexOf("word!");
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        assertEquals(0,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        // issue 1586346
+        assertEquals(TextUtils.CAP_MODE_CHARACTERS,
+                TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
+
+        // if no space after "." or "?" or "!", CAP_MODE_SENTENCES and CAP_MODE_WORDS
+        // should NOT be in effect.
+        offset = testString.indexOf("No space before");
+        assertEquals(0,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        assertEquals(0,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        assertEquals(TextUtils.CAP_MODE_CHARACTERS,
+                TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
+
+        // all mode should be in effect at a beginning of a new paragraph.
+        offset = testString.indexOf("Paragraph");
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        // issue 1586346
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        assertEquals(CAP_MODE_CHARACTERS_AND_WORD,
+                TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
+
+        // some special word which means the start of a sentence should be skipped.
+        offset = testString.indexOf("skip begin");
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        assertEquals(TextUtils.CAP_MODE_SENTENCES,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        // issue 1586346
+        assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
+                TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
+
+        // some special word which means the end of a sentence should be skipped.
+        offset = testString.indexOf("skip end");
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_WORDS));
+        assertEquals(TextUtils.CAP_MODE_SENTENCES,
+                TextUtils.getCapsMode(testString, offset, TextUtils.CAP_MODE_SENTENCES));
+        // issue 1586346
+        assertEquals(TextUtils.CAP_MODE_SENTENCES | TextUtils.CAP_MODE_CHARACTERS,
+                TextUtils.getCapsMode(testString, offset, CAP_MODE_ALL));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getCapsMode",
+        args = {CharSequence.class, int.class, int.class}
+    )
+    @ToBeFixed(bug = "1695243", explanation = "the javadoc for substring() is incomplete." +
+            "1. doesn't describe the expected result when parameter is exceptional.")
+    public void testGetCapsModeException() {
+        String testString = "Start. Sentence word!No space before\n\t" +
+                "Paragraph? (\"\'skip begin\'\"). skip end";
+
+        int offset = testString.indexOf("Sentence word!");
+        assertEquals(TextUtils.CAP_MODE_CHARACTERS,
+                TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_CHARACTERS));
+
+        try {
+            TextUtils.getCapsMode(null, offset, TextUtils.CAP_MODE_SENTENCES);
+            fail("Should throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+
+        assertEquals(0, TextUtils.getCapsMode(testString, -1, TextUtils.CAP_MODE_SENTENCES));
+
+        try {
+            TextUtils.getCapsMode(testString, testString.length() + 1,
+                    TextUtils.CAP_MODE_SENTENCES);
+            fail("Should throw IndexOutOfBoundsException!");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "dumpSpans",
+        args = {java.lang.CharSequence.class, android.util.Printer.class, java.lang.String.class}
+    )
+    public void testDumpSpans() {
+        StringBuilder builder = new StringBuilder();
+        StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+        CharSequence source = "test dump spans";
+        String prefix = "prefix";
+
+        assertEquals(0, builder.length());
+        TextUtils.dumpSpans(source, printer, prefix);
+        assertTrue(builder.length() > 0);
+
+        builder = new StringBuilder();
+        printer = new StringBuilderPrinter(builder);
+        assertEquals(0, builder.length());
+        SpannableString spanned = new SpannableString(source);
+        spanned.setSpan(new Object(), 0, source.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+        TextUtils.dumpSpans(spanned, printer, prefix);
+        assertTrue(builder.length() > 0);
+    }
+}