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