Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view.inputmethod.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotNull;
     22 import static org.junit.Assert.assertNull;
     23 import static org.junit.Assert.assertTrue;
     24 
     25 import android.content.ClipDescription;
     26 import android.net.Uri;
     27 import android.os.Bundle;
     28 import android.support.test.InstrumentationRegistry;
     29 import android.support.test.filters.MediumTest;
     30 import android.support.test.runner.AndroidJUnit4;
     31 import android.text.Editable;
     32 import android.text.Selection;
     33 import android.text.Spannable;
     34 import android.text.SpannableString;
     35 import android.text.Spanned;
     36 import android.text.TextUtils;
     37 import android.view.View;
     38 import android.view.inputmethod.BaseInputConnection;
     39 import android.view.inputmethod.CompletionInfo;
     40 import android.view.inputmethod.ExtractedTextRequest;
     41 import android.view.inputmethod.InputContentInfo;
     42 import android.view.inputmethod.InputMethodManager;
     43 import android.view.inputmethod.cts.util.InputConnectionTestUtils;
     44 
     45 import org.junit.Test;
     46 import org.junit.runner.RunWith;
     47 
     48 @MediumTest
     49 @RunWith(AndroidJUnit4.class)
     50 public class BaseInputConnectionTest {
     51 
     52     private static BaseInputConnection createBaseInputConnection() {
     53         final View view = new View(InstrumentationRegistry.getTargetContext());
     54         return new BaseInputConnection(view, true);
     55     }
     56 
     57     @Test
     58     public void testDefaultMethods() {
     59         // These methods are default to return fixed result.
     60         final BaseInputConnection connection = createBaseInputConnection();
     61 
     62         assertFalse(connection.beginBatchEdit());
     63         assertFalse(connection.endBatchEdit());
     64 
     65         // only fit for test default implementation of commitCompletion.
     66         int completionId = 1;
     67         String completionString = "commitCompletion test";
     68         assertFalse(connection.commitCompletion(new CompletionInfo(completionId,
     69                 0, completionString)));
     70 
     71         assertNull(connection.getExtractedText(new ExtractedTextRequest(), 0));
     72 
     73         // only fit for test default implementation of performEditorAction.
     74         int actionCode = 1;
     75         int actionId = 2;
     76         String action = "android.intent.action.MAIN";
     77         assertTrue(connection.performEditorAction(actionCode));
     78         assertFalse(connection.performContextMenuAction(actionId));
     79         assertFalse(connection.performPrivateCommand(action, new Bundle()));
     80     }
     81 
     82     @Test
     83     public void testOpComposingSpans() {
     84         Spannable text = new SpannableString("Test ComposingSpans");
     85         BaseInputConnection.setComposingSpans(text);
     86         assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1);
     87         assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1);
     88         BaseInputConnection.removeComposingSpans(text);
     89         assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1);
     90         assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1);
     91     }
     92 
     93     /**
     94      * getEditable: Return the target of edit operations. The default implementation
     95      *              returns its own fake editable that is just used for composing text.
     96      * clearMetaKeyStates: Default implementation uses
     97      *              MetaKeyKeyListener#clearMetaKeyState(long, int) to clear the state.
     98      *              BugId:1738511
     99      * commitText: Default implementation replaces any existing composing text with the given
    100      *             text.
    101      * deleteSurroundingText: The default implementation performs the deletion around the current
    102      *              selection position of the editable text.
    103      * getCursorCapsMode: The default implementation uses TextUtils.getCapsMode to get the
    104      *                  cursor caps mode for the current selection position in the editable text.
    105      *                  TextUtils.getCapsMode is tested fully in TextUtilsTest#testGetCapsMode.
    106      * getTextBeforeCursor, getTextAfterCursor: The default implementation performs the deletion
    107      *                          around the current selection position of the editable text.
    108      * setSelection: changes the selection position in the current editable text.
    109      */
    110     @Test
    111     public void testOpTextMethods() {
    112         final BaseInputConnection connection = createBaseInputConnection();
    113 
    114         // return is an default Editable instance with empty source
    115         final Editable text = connection.getEditable();
    116         assertNotNull(text);
    117         assertEquals(0, text.length());
    118 
    119         // Test commitText, not dummy mode
    120         CharSequence str = "TestCommit ";
    121         Editable inputText = Editable.Factory.getInstance().newEditable(str);
    122         connection.commitText(inputText, inputText.length());
    123         final Editable text2 = connection.getEditable();
    124         int strLength = str.length();
    125         assertEquals(strLength, text2.length());
    126         assertEquals(str.toString(), text2.toString());
    127         assertEquals(TextUtils.CAP_MODE_WORDS,
    128                 connection.getCursorCapsMode(TextUtils.CAP_MODE_WORDS));
    129         int offLength = 3;
    130         CharSequence expected = str.subSequence(strLength - offLength, strLength);
    131         assertEquals(expected.toString(), connection.getTextBeforeCursor(offLength,
    132                 BaseInputConnection.GET_TEXT_WITH_STYLES).toString());
    133         connection.setSelection(0, 0);
    134         expected = str.subSequence(0, offLength);
    135         assertEquals(expected.toString(), connection.getTextAfterCursor(offLength,
    136                 BaseInputConnection.GET_TEXT_WITH_STYLES).toString());
    137 
    138         // Test deleteSurroundingText
    139         int end = text2.length();
    140         connection.setSelection(end, end);
    141         // Delete the ending space
    142         assertTrue(connection.deleteSurroundingText(1, 2));
    143         Editable text3 = connection.getEditable();
    144         assertEquals(strLength - 1, text3.length());
    145         String expectedDelString = "TestCommit";
    146         assertEquals(expectedDelString, text3.toString());
    147     }
    148 
    149     /**
    150      * finishComposingText: The default implementation removes the composing state from the
    151      *                      current editable text.
    152      * setComposingText: The default implementation places the given text into the editable,
    153      *                  replacing any existing composing text
    154      */
    155     @Test
    156     public void testFinishComposingText() {
    157         final BaseInputConnection connection = createBaseInputConnection();
    158         CharSequence str = "TestFinish";
    159         Editable inputText = Editable.Factory.getInstance().newEditable(str);
    160         connection.commitText(inputText, inputText.length());
    161         final Editable text = connection.getEditable();
    162         // Test finishComposingText, not dummy mode
    163         BaseInputConnection.setComposingSpans(text);
    164         assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1);
    165         assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1);
    166         connection.finishComposingText();
    167         assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1);
    168         assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1);
    169     }
    170 
    171     /**
    172      * Updates InputMethodManager with the current fullscreen mode.
    173      */
    174     @Test
    175     public void testReportFullscreenMode() {
    176         final InputMethodManager imm = InstrumentationRegistry.getInstrumentation()
    177                 .getTargetContext().getSystemService(InputMethodManager.class);
    178         final BaseInputConnection connection = createBaseInputConnection();
    179         connection.reportFullscreenMode(false);
    180         assertFalse(imm.isFullscreenMode());
    181         connection.reportFullscreenMode(true);
    182         // Only IMEs are allowed to report full-screen mode.  Calling this method from the
    183         // application should have no effect.
    184         assertFalse(imm.isFullscreenMode());
    185     }
    186 
    187     /**
    188      * An utility method to create an instance of {@link BaseInputConnection} in the full editor
    189      * mode with an initial text and selection range.
    190      *
    191      * @param source the initial text.
    192      * @return {@link BaseInputConnection} instantiated in the full editor mode with {@code source}
    193      *         and selection range from {@code selectionStart} to {@code selectionEnd}
    194      */
    195     private static BaseInputConnection createConnectionWithSelection(CharSequence source) {
    196         final int selectionStart = Selection.getSelectionStart(source);
    197         final int selectionEnd = Selection.getSelectionEnd(source);
    198         final Editable editable = Editable.Factory.getInstance().newEditable(source);
    199         Selection.setSelection(editable, selectionStart, selectionEnd);
    200         final View view = new View(InstrumentationRegistry.getTargetContext());
    201         return new BaseInputConnection(view, true) {
    202             @Override
    203             public Editable getEditable() {
    204                 return editable;
    205             }
    206         };
    207     }
    208 
    209     private static void verifyDeleteSurroundingTextMain(final String initialState,
    210             final int deleteBefore, final int deleteAfter, final String expectedState) {
    211         final CharSequence source = InputConnectionTestUtils.formatString(initialState);
    212         final BaseInputConnection ic = createConnectionWithSelection(source);
    213         ic.deleteSurroundingText(deleteBefore, deleteAfter);
    214 
    215         final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState);
    216         final int expectedSelectionStart = Selection.getSelectionStart(expectedString);
    217         final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString);
    218 
    219         // It is sufficient to check the surrounding text up to source.length() characters, because
    220         // InputConnection.deleteSurroundingText() is not supposed to increase the text length.
    221         final int retrievalLength = source.length();
    222         if (expectedSelectionStart == 0) {
    223             assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0)));
    224         } else {
    225             assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(),
    226                     ic.getTextBeforeCursor(retrievalLength, 0).toString());
    227         }
    228         if (expectedSelectionStart == expectedSelectionEnd) {
    229             assertTrue(TextUtils.isEmpty(ic.getSelectedText(0)));  // null is allowed.
    230         } else {
    231             assertEquals(expectedString.subSequence(expectedSelectionStart,
    232                     expectedSelectionEnd).toString(), ic.getSelectedText(0).toString());
    233         }
    234         if (expectedSelectionEnd == expectedString.length()) {
    235             assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0)));
    236         } else {
    237             assertEquals(expectedString.subSequence(expectedSelectionEnd,
    238                     expectedString.length()).toString(),
    239                     ic.getTextAfterCursor(retrievalLength, 0).toString());
    240         }
    241     }
    242 
    243     /**
    244      * Tests {@link BaseInputConnection#deleteSurroundingText(int, int)} comprehensively.
    245      */
    246     @Test
    247     public void testDeleteSurroundingText() {
    248         verifyDeleteSurroundingTextMain("012[]3456789", 0, 0, "012[]3456789");
    249         verifyDeleteSurroundingTextMain("012[]3456789", -1, -1, "012[]3456789");
    250         verifyDeleteSurroundingTextMain("012[]3456789", 1, 2, "01[]56789");
    251         verifyDeleteSurroundingTextMain("012[]3456789", 10, 1, "[]456789");
    252         verifyDeleteSurroundingTextMain("012[]3456789", 1, 10, "01[]");
    253         verifyDeleteSurroundingTextMain("[]0123456789", 3, 3, "[]3456789");
    254         verifyDeleteSurroundingTextMain("0123456789[]", 3, 3, "0123456[]");
    255         verifyDeleteSurroundingTextMain("012[345]6789", 0, 0, "012[345]6789");
    256         verifyDeleteSurroundingTextMain("012[345]6789", -1, -1, "012[345]6789");
    257         verifyDeleteSurroundingTextMain("012[345]6789", 1, 2, "01[345]89");
    258         verifyDeleteSurroundingTextMain("012[345]6789", 10, 1, "[345]789");
    259         verifyDeleteSurroundingTextMain("012[345]6789", 1, 10, "01[345]");
    260         verifyDeleteSurroundingTextMain("[012]3456789", 3, 3, "[012]6789");
    261         verifyDeleteSurroundingTextMain("0123456[789]", 3, 3, "0123[789]");
    262         verifyDeleteSurroundingTextMain("[0123456789]", 0, 0, "[0123456789]");
    263         verifyDeleteSurroundingTextMain("[0123456789]", 1, 1, "[0123456789]");
    264 
    265         // Surrogate characters do not have any special meanings.  Validating the character sequence
    266         // is beyond the goal of this API.
    267         verifyDeleteSurroundingTextMain("0<>[]3456789", 1, 0, "0<[]3456789");
    268         verifyDeleteSurroundingTextMain("0<>[]3456789", 2, 0, "0[]3456789");
    269         verifyDeleteSurroundingTextMain("0<>[]3456789", 3, 0, "[]3456789");
    270         verifyDeleteSurroundingTextMain("012[]<>56789", 0, 1, "012[]>56789");
    271         verifyDeleteSurroundingTextMain("012[]<>56789", 0, 2, "012[]56789");
    272         verifyDeleteSurroundingTextMain("012[]<>56789", 0, 3, "012[]6789");
    273         verifyDeleteSurroundingTextMain("0<<[]3456789", 1, 0, "0<[]3456789");
    274         verifyDeleteSurroundingTextMain("0<<[]3456789", 2, 0, "0[]3456789");
    275         verifyDeleteSurroundingTextMain("0<<[]3456789", 3, 0, "[]3456789");
    276         verifyDeleteSurroundingTextMain("012[]<<56789", 0, 1, "012[]<56789");
    277         verifyDeleteSurroundingTextMain("012[]<<56789", 0, 2, "012[]56789");
    278         verifyDeleteSurroundingTextMain("012[]<<56789", 0, 3, "012[]6789");
    279         verifyDeleteSurroundingTextMain("0>>[]3456789", 1, 0, "0>[]3456789");
    280         verifyDeleteSurroundingTextMain("0>>[]3456789", 2, 0, "0[]3456789");
    281         verifyDeleteSurroundingTextMain("0>>[]3456789", 3, 0, "[]3456789");
    282         verifyDeleteSurroundingTextMain("012[]>>56789", 0, 1, "012[]>56789");
    283         verifyDeleteSurroundingTextMain("012[]>>56789", 0, 2, "012[]56789");
    284         verifyDeleteSurroundingTextMain("012[]>>56789", 0, 3, "012[]6789");
    285     }
    286 
    287     private static void verifyDeleteSurroundingTextInCodePointsMain(String initialState,
    288             int deleteBeforeInCodePoints, int deleteAfterInCodePoints, String expectedState) {
    289         final CharSequence source = InputConnectionTestUtils.formatString(initialState);
    290         final BaseInputConnection ic = createConnectionWithSelection(source);
    291         ic.deleteSurroundingTextInCodePoints(deleteBeforeInCodePoints, deleteAfterInCodePoints);
    292 
    293         final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState);
    294         final int expectedSelectionStart = Selection.getSelectionStart(expectedString);
    295         final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString);
    296 
    297         // It is sufficient to check the surrounding text up to source.length() characters, because
    298         // InputConnection.deleteSurroundingTextInCodePoints() is not supposed to increase the text
    299         // length.
    300         final int retrievalLength = source.length();
    301         if (expectedSelectionStart == 0) {
    302             assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0)));
    303         } else {
    304             assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(),
    305                     ic.getTextBeforeCursor(retrievalLength, 0).toString());
    306         }
    307         if (expectedSelectionStart == expectedSelectionEnd) {
    308             assertTrue(TextUtils.isEmpty(ic.getSelectedText(0)));  // null is allowed.
    309         } else {
    310             assertEquals(expectedString.subSequence(expectedSelectionStart,
    311                     expectedSelectionEnd).toString(), ic.getSelectedText(0).toString());
    312         }
    313         if (expectedSelectionEnd == expectedString.length()) {
    314             assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0)));
    315         } else {
    316             assertEquals(expectedString.subSequence(expectedSelectionEnd,
    317                     expectedString.length()).toString(),
    318                     ic.getTextAfterCursor(retrievalLength, 0).toString());
    319         }
    320     }
    321 
    322     /**
    323      * Tests {@link BaseInputConnection#deleteSurroundingTextInCodePoints(int, int)}
    324      * comprehensively.
    325      */
    326     @Test
    327     public void testDeleteSurroundingTextInCodePoints() {
    328         verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 0, 0, "012[]3456789");
    329         verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", -1, -1, "012[]3456789");
    330         verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 2, "01[]56789");
    331         verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 10, 1, "[]456789");
    332         verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 10, "01[]");
    333         verifyDeleteSurroundingTextInCodePointsMain("[]0123456789", 3, 3, "[]3456789");
    334         verifyDeleteSurroundingTextInCodePointsMain("0123456789[]", 3, 3, "0123456[]");
    335         verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 0, 0, "012[345]6789");
    336         verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", -1, -1, "012[345]6789");
    337         verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 2, "01[345]89");
    338         verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 10, 1, "[345]789");
    339         verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 10, "01[345]");
    340         verifyDeleteSurroundingTextInCodePointsMain("[012]3456789", 3, 3, "[012]6789");
    341         verifyDeleteSurroundingTextInCodePointsMain("0123456[789]", 3, 3, "0123[789]");
    342         verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 0, 0, "[0123456789]");
    343         verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 1, 1, "[0123456789]");
    344 
    345         verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 1, 0, "0[]3456789");
    346         verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 2, 0, "[]3456789");
    347         verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 3, 0, "[]3456789");
    348         verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 1, "012[]56789");
    349         verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 2, "012[]6789");
    350         verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 3, "012[]789");
    351 
    352         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 0, "[]<><><><><>");
    353         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1, "[]<><><><>");
    354         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 2, "[]<><><>");
    355         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 3, "[]<><>");
    356         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 4, "[]<>");
    357         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 5, "[]");
    358         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 6, "[]");
    359         verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1000, "[]");
    360         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 0, 0, "<><><><><>[]");
    361         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1, 0, "<><><><>[]");
    362         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 2, 0, "<><><>[]");
    363         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 3, 0, "<><>[]");
    364         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 4, 0, "<>[]");
    365         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 5, 0, "[]");
    366         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 6, 0, "[]");
    367         verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1000, 0, "[]");
    368 
    369         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 0, "0<<[]3456789");
    370         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 0, "0<<[]3456789");
    371         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 0, "0<<[]3456789");
    372         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 1, "012[]<<56789");
    373         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 2, "012[]<<56789");
    374         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 3, "012[]<<56789");
    375         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 0, "0>>[]3456789");
    376         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 0, "0>>[]3456789");
    377         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 0, "0>>[]3456789");
    378         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 1, "012[]>>56789");
    379         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 2, "012[]>>56789");
    380         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 3, "012[]>>56789");
    381         verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 0, "01<[]>456789");
    382         verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 0, 1, "01<[]>456789");
    383         verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 1, 0, "<1[]3456789");
    384         verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 2, 0, "<[]3456789");
    385         verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 3, 0, "<12[]3456789");
    386         verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 1, 0, "<[]3456789");
    387         verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 2, 0, "<<>[]3456789");
    388         verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 3, 0, "<<>[]3456789");
    389         verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 1, "012[]4>6789");
    390         verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 2, "012[]>6789");
    391         verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 3, "012[]34>6789");
    392         verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 1, "012[]>6789");
    393         verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 2, "012[]<>>6789");
    394         verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 3, "012[]<>>6789");
    395 
    396         // Atomicity test.
    397         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 1, "0<<[]3456789");
    398         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 1, "0<<[]3456789");
    399         verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 1, "0<<[]3456789");
    400         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 1, "012[]<<56789");
    401         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 2, "012[]<<56789");
    402         verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 3, "012[]<<56789");
    403         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 1, "0>>[]3456789");
    404         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 1, "0>>[]3456789");
    405         verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 1, "0>>[]3456789");
    406         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 1, "012[]>>56789");
    407         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 2, "012[]>>56789");
    408         verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 3, "012[]>>56789");
    409         verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 1, "01<[]>456789");
    410 
    411         // Do not verify the character sequences in the selected region.
    412         verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 0, "0[><]456789");
    413         verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 0, 1, "01[><]56789");
    414         verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 1, "0[><]56789");
    415     }
    416 
    417     @Test
    418     public void testCloseConnection() {
    419         final BaseInputConnection connection = createBaseInputConnection();
    420 
    421         final CharSequence source = "0123456789";
    422         connection.commitText(source, source.length());
    423         connection.setComposingRegion(2, 5);
    424         final Editable text = connection.getEditable();
    425         assertEquals(2, BaseInputConnection.getComposingSpanStart(text));
    426         assertEquals(5, BaseInputConnection.getComposingSpanEnd(text));
    427 
    428         // BaseInputConnection#closeConnection() must clear the on-going composition.
    429         connection.closeConnection();
    430         assertEquals(-1, BaseInputConnection.getComposingSpanStart(text));
    431         assertEquals(-1, BaseInputConnection.getComposingSpanEnd(text));
    432     }
    433 
    434     @Test
    435     public void testGetHandler() {
    436         final BaseInputConnection connection = createBaseInputConnection();
    437 
    438         // BaseInputConnection must not implement getHandler().
    439         assertNull(connection.getHandler());
    440     }
    441 
    442     @Test
    443     public void testCommitContent() {
    444         final BaseInputConnection connection = createBaseInputConnection();
    445 
    446         final InputContentInfo inputContentInfo = new InputContentInfo(
    447                 Uri.parse("content://com.example/path"),
    448                 new ClipDescription("sample content", new String[]{"image/png"}),
    449                 Uri.parse("https://example.com"));
    450         // The default implementation should do nothing and just return false.
    451         assertFalse(connection.commitContent(inputContentInfo, 0 /* flags */, null /* opts */));
    452     }
    453 
    454     @Test
    455     public void testGetSelectedText_wrongSelection() {
    456         final BaseInputConnection connection = createBaseInputConnection();
    457         Editable editable = connection.getEditable();
    458         editable.append("hello");
    459         editable.setSpan(Selection.SELECTION_START, 4, 4, Spanned.SPAN_POINT_POINT);
    460         editable.removeSpan(Selection.SELECTION_END);
    461 
    462         // Should not crash.
    463         connection.getSelectedText(0);
    464     }
    465 }
    466