Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2012 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 com.android.inputmethod.latin;
     18 
     19 import android.content.res.Resources;
     20 import android.inputmethodservice.InputMethodService;
     21 import android.os.Parcel;
     22 import android.test.AndroidTestCase;
     23 import android.test.MoreAsserts;
     24 import android.test.suitebuilder.annotation.SmallTest;
     25 import android.text.SpannableString;
     26 import android.text.TextUtils;
     27 import android.text.style.SuggestionSpan;
     28 import android.view.inputmethod.ExtractedText;
     29 import android.view.inputmethod.ExtractedTextRequest;
     30 import android.view.inputmethod.InputConnection;
     31 import android.view.inputmethod.InputConnectionWrapper;
     32 
     33 import com.android.inputmethod.latin.common.Constants;
     34 import com.android.inputmethod.latin.common.StringUtils;
     35 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
     36 import com.android.inputmethod.latin.utils.NgramContextUtils;
     37 import com.android.inputmethod.latin.utils.RunInLocale;
     38 import com.android.inputmethod.latin.utils.ScriptUtils;
     39 import com.android.inputmethod.latin.utils.TextRange;
     40 
     41 import java.util.Locale;
     42 
     43 @SmallTest
     44 public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
     45 
     46     // The following is meant to be a reasonable default for
     47     // the "word_separators" resource.
     48     private SpacingAndPunctuations mSpacingAndPunctuations;
     49 
     50     @Override
     51     protected void setUp() throws Exception {
     52         super.setUp();
     53         final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
     54             @Override
     55             protected SpacingAndPunctuations job(final Resources res) {
     56                 return new SpacingAndPunctuations(res);
     57             }
     58         };
     59         final Resources res = getContext().getResources();
     60         mSpacingAndPunctuations = job.runInLocale(res, Locale.ENGLISH);
     61     }
     62 
     63     private class MockConnection extends InputConnectionWrapper {
     64         final CharSequence mTextBefore;
     65         final CharSequence mTextAfter;
     66         final ExtractedText mExtractedText;
     67 
     68         public MockConnection(final CharSequence text, final int cursorPosition) {
     69             super(null, false);
     70             // Interaction of spans with Parcels is completely non-trivial, but in the actual case
     71             // the CharSequences do go through Parcels because they go through IPC. There
     72             // are some significant differences between the behavior of Spanned objects that
     73             // have and that have not gone through parceling, so it's much easier to simulate
     74             // the environment with Parcels than try to emulate things by hand.
     75             final Parcel p = Parcel.obtain();
     76             TextUtils.writeToParcel(text.subSequence(0, cursorPosition), p, 0 /* flags */);
     77             TextUtils.writeToParcel(text.subSequence(cursorPosition, text.length()), p,
     78                     0 /* flags */);
     79             final byte[] marshalled = p.marshall();
     80             p.unmarshall(marshalled, 0, marshalled.length);
     81             p.setDataPosition(0);
     82             mTextBefore = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
     83             mTextAfter = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(p);
     84             mExtractedText = null;
     85             p.recycle();
     86         }
     87 
     88         public MockConnection(String textBefore, String textAfter, ExtractedText extractedText) {
     89             super(null, false);
     90             mTextBefore = textBefore;
     91             mTextAfter = textAfter;
     92             mExtractedText = extractedText;
     93         }
     94 
     95         public int cursorPos() {
     96             return mTextBefore.length();
     97         }
     98 
     99         /* (non-Javadoc)
    100          * @see android.view.inputmethod.InputConnectionWrapper#getTextBeforeCursor(int, int)
    101          */
    102         @Override
    103         public CharSequence getTextBeforeCursor(int n, int flags) {
    104             return mTextBefore;
    105         }
    106 
    107         /* (non-Javadoc)
    108          * @see android.view.inputmethod.InputConnectionWrapper#getTextAfterCursor(int, int)
    109          */
    110         @Override
    111         public CharSequence getTextAfterCursor(int n, int flags) {
    112             return mTextAfter;
    113         }
    114 
    115         /* (non-Javadoc)
    116          * @see android.view.inputmethod.InputConnectionWrapper#getExtractedText(
    117          *         ExtractedTextRequest, int)
    118          */
    119         @Override
    120         public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
    121             return mExtractedText;
    122         }
    123 
    124         @Override
    125         public boolean beginBatchEdit() {
    126             return true;
    127         }
    128 
    129         @Override
    130         public boolean endBatchEdit() {
    131             return true;
    132         }
    133 
    134         @Override
    135         public boolean finishComposingText() {
    136             return true;
    137         }
    138     }
    139 
    140     static class MockInputMethodService extends InputMethodService {
    141         private MockConnection mMockConnection;
    142         public void setInputConnection(final MockConnection mockConnection) {
    143             mMockConnection = mockConnection;
    144         }
    145         public int cursorPos() {
    146             return mMockConnection.cursorPos();
    147         }
    148         @Override
    149         public InputConnection getCurrentInputConnection() {
    150             return mMockConnection;
    151         }
    152     }
    153 
    154     /************************** Tests ************************/
    155 
    156     /**
    157      * Test for getting previous word (for bigram suggestions)
    158      */
    159     public void testGetPreviousWord() {
    160         // If one of the following cases breaks, the bigram suggestions won't work.
    161         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    162                 "abc def", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
    163         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    164                 "abc", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE);
    165         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    166                 "abc. def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE);
    167 
    168         assertFalse(NgramContextUtils.getNgramContextFromNthPreviousWord(
    169                 "abc def", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
    170         assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord(
    171                 "abc", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
    172 
    173         // For n-gram
    174         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    175                 "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
    176         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    177                 "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(2), "abc");
    178         assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord(
    179                 "abc def", mSpacingAndPunctuations, 2).isNthPrevWordBeginningOfSentence(2));
    180 
    181         // The following tests reflect the current behavior of the function
    182         // RichInputConnection#getNthPreviousWord.
    183         // TODO: However at this time, the code does never go
    184         // into such a path, so it should be safe to change the behavior of
    185         // this function if needed - especially since it does not seem very
    186         // logical. These tests are just there to catch any unintentional
    187         // changes in the behavior of the RichInputConnection#getPreviousWord method.
    188         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    189                 "abc def ", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
    190         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    191                 "abc def.", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
    192         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    193                 "abc def .", mSpacingAndPunctuations, 2).getNthPrevWord(1), "def");
    194         assertTrue(NgramContextUtils.getNgramContextFromNthPreviousWord(
    195                 "abc ", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
    196 
    197         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    198                 "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
    199         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    200                 "abc def ", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
    201         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    202                 "abc 'def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "'def");
    203         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    204                 "abc def.", mSpacingAndPunctuations, 1), NgramContext.BEGINNING_OF_SENTENCE);
    205         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    206                 "abc def .", mSpacingAndPunctuations, 1), NgramContext.BEGINNING_OF_SENTENCE);
    207         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    208                 "abc, def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO);
    209         // question mark is treated as the end of the sentence. Hence, beginning of the
    210         // sentence is expected.
    211         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    212                 "abc? def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE);
    213         // Exclamation mark is treated as the end of the sentence. Hence, beginning of the
    214         // sentence is expected.
    215         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    216                 "abc! def", mSpacingAndPunctuations, 2), NgramContext.BEGINNING_OF_SENTENCE);
    217         assertEquals(NgramContextUtils.getNgramContextFromNthPreviousWord(
    218                 "abc 'def", mSpacingAndPunctuations, 2), NgramContext.EMPTY_PREV_WORDS_INFO);
    219     }
    220 
    221     public void testGetWordRangeAtCursor() {
    222         /**
    223          * Test logic in getting the word range at the cursor.
    224          */
    225         final SpacingAndPunctuations SPACE = new SpacingAndPunctuations(
    226                 mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE });
    227         final SpacingAndPunctuations TAB = new SpacingAndPunctuations(
    228                 mSpacingAndPunctuations, new int[] { Constants.CODE_TAB });
    229         // A character that needs surrogate pair to represent its code point (U+2008A).
    230         final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A";
    231         final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations(
    232                 mSpacingAndPunctuations, StringUtils.toSortedCodePointArray(
    233                         SUPPLEMENTARY_CHAR_STRING));
    234         final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // 
    235         final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // 
    236 
    237         ExtractedText et = new ExtractedText();
    238         final MockInputMethodService mockInputMethodService = new MockInputMethodService();
    239         final RichInputConnection ic = new RichInputConnection(mockInputMethodService);
    240         mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et));
    241         et.startOffset = 0;
    242         et.selectionStart = 7;
    243         TextRange r;
    244 
    245         ic.beginBatchEdit();
    246         // basic case
    247         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    248         assertTrue(TextUtils.equals("word", r.mWord));
    249 
    250         // tab character instead of space
    251         mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et));
    252         ic.beginBatchEdit();
    253         r = ic.getWordRangeAtCursor(TAB, ScriptUtils.SCRIPT_LATIN);
    254         ic.endBatchEdit();
    255         assertTrue(TextUtils.equals("word", r.mWord));
    256 
    257         // splitting on supplementary character
    258         mockInputMethodService.setInputConnection(
    259                 new MockConnection("one word" + SUPPLEMENTARY_CHAR_STRING + "wo", "rd", et));
    260         ic.beginBatchEdit();
    261         r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN);
    262         ic.endBatchEdit();
    263         assertTrue(TextUtils.equals("word", r.mWord));
    264 
    265         // split on chars outside the specified script
    266         mockInputMethodService.setInputConnection(
    267                 new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et));
    268         ic.beginBatchEdit();
    269         r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN);
    270         ic.endBatchEdit();
    271         assertTrue(TextUtils.equals("word", r.mWord));
    272 
    273         // likewise for greek
    274         mockInputMethodService.setInputConnection(
    275                 new MockConnection("text" + GREEK_WORD, "text", et));
    276         ic.beginBatchEdit();
    277         r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_GREEK);
    278         ic.endBatchEdit();
    279         assertTrue(TextUtils.equals(GREEK_WORD, r.mWord));
    280     }
    281 
    282     /**
    283      * Test logic in getting the word range at the cursor.
    284      */
    285     public void testGetSuggestionSpansAtWord() {
    286         helpTestGetSuggestionSpansAtWord(10);
    287         helpTestGetSuggestionSpansAtWord(12);
    288         helpTestGetSuggestionSpansAtWord(15);
    289         helpTestGetSuggestionSpansAtWord(16);
    290     }
    291 
    292     private void helpTestGetSuggestionSpansAtWord(final int cursorPos) {
    293         final SpacingAndPunctuations SPACE = new SpacingAndPunctuations(
    294                 mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE });
    295         final MockInputMethodService mockInputMethodService = new MockInputMethodService();
    296         final RichInputConnection ic = new RichInputConnection(mockInputMethodService);
    297 
    298         final String[] SUGGESTIONS1 = { "swing", "strong" };
    299         final String[] SUGGESTIONS2 = { "storing", "strung" };
    300 
    301         // Test the usual case.
    302         SpannableString text = new SpannableString("This is a string for test");
    303         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    304                 10 /* start */, 16 /* end */, 0 /* flags */);
    305         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    306         TextRange r;
    307         SuggestionSpan[] suggestions;
    308 
    309         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    310         suggestions = r.getSuggestionSpansAtWord();
    311         assertEquals(suggestions.length, 1);
    312         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
    313 
    314         // Test the case with 2 suggestion spans in the same place.
    315         text = new SpannableString("This is a string for test");
    316         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    317                 10 /* start */, 16 /* end */, 0 /* flags */);
    318         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
    319                 10 /* start */, 16 /* end */, 0 /* flags */);
    320         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    321         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    322         suggestions = r.getSuggestionSpansAtWord();
    323         assertEquals(suggestions.length, 2);
    324         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
    325         MoreAsserts.assertEquals(suggestions[1].getSuggestions(), SUGGESTIONS2);
    326 
    327         // Test a case with overlapping spans, 2nd extending past the start of the word
    328         text = new SpannableString("This is a string for test");
    329         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    330                 10 /* start */, 16 /* end */, 0 /* flags */);
    331         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
    332                 5 /* start */, 16 /* end */, 0 /* flags */);
    333         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    334         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    335         suggestions = r.getSuggestionSpansAtWord();
    336         assertEquals(suggestions.length, 1);
    337         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
    338 
    339         // Test a case with overlapping spans, 2nd extending past the end of the word
    340         text = new SpannableString("This is a string for test");
    341         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    342                 10 /* start */, 16 /* end */, 0 /* flags */);
    343         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
    344                 10 /* start */, 20 /* end */, 0 /* flags */);
    345         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    346         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    347         suggestions = r.getSuggestionSpansAtWord();
    348         assertEquals(suggestions.length, 1);
    349         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
    350 
    351         // Test a case with overlapping spans, 2nd extending past both ends of the word
    352         text = new SpannableString("This is a string for test");
    353         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    354                 10 /* start */, 16 /* end */, 0 /* flags */);
    355         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
    356                 5 /* start */, 20 /* end */, 0 /* flags */);
    357         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    358         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    359         suggestions = r.getSuggestionSpansAtWord();
    360         assertEquals(suggestions.length, 1);
    361         MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
    362 
    363         // Test a case with overlapping spans, none right on the word
    364         text = new SpannableString("This is a string for test");
    365         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
    366                 5 /* start */, 16 /* end */, 0 /* flags */);
    367         text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
    368                 5 /* start */, 20 /* end */, 0 /* flags */);
    369         mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
    370         r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
    371         suggestions = r.getSuggestionSpansAtWord();
    372         assertEquals(suggestions.length, 0);
    373     }
    374 
    375     public void testCursorTouchingWord() {
    376         final MockInputMethodService ims = new MockInputMethodService();
    377         final RichInputConnection ic = new RichInputConnection(ims);
    378         final SpacingAndPunctuations sap = mSpacingAndPunctuations;
    379 
    380         ims.setInputConnection(new MockConnection("users", 5));
    381         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    382         assertTrue(ic.isCursorTouchingWord(sap, true /* checkTextAfter */));
    383 
    384         ims.setInputConnection(new MockConnection("users'", 5));
    385         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    386         assertTrue(ic.isCursorTouchingWord(sap, true));
    387 
    388         ims.setInputConnection(new MockConnection("users'", 6));
    389         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    390         assertTrue(ic.isCursorTouchingWord(sap, true));
    391 
    392         ims.setInputConnection(new MockConnection("'users'", 6));
    393         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    394         assertTrue(ic.isCursorTouchingWord(sap, true));
    395 
    396         ims.setInputConnection(new MockConnection("'users'", 7));
    397         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    398         assertTrue(ic.isCursorTouchingWord(sap, true));
    399 
    400         ims.setInputConnection(new MockConnection("users '", 6));
    401         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    402         assertFalse(ic.isCursorTouchingWord(sap, true));
    403 
    404         ims.setInputConnection(new MockConnection("users '", 7));
    405         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    406         assertFalse(ic.isCursorTouchingWord(sap, true));
    407 
    408         ims.setInputConnection(new MockConnection("re-", 3));
    409         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    410         assertTrue(ic.isCursorTouchingWord(sap, true));
    411 
    412         ims.setInputConnection(new MockConnection("re--", 4));
    413         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    414         assertFalse(ic.isCursorTouchingWord(sap, true));
    415 
    416         ims.setInputConnection(new MockConnection("-", 1));
    417         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    418         assertFalse(ic.isCursorTouchingWord(sap, true));
    419 
    420         ims.setInputConnection(new MockConnection("--", 2));
    421         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    422         assertFalse(ic.isCursorTouchingWord(sap, true));
    423 
    424         ims.setInputConnection(new MockConnection(" -", 2));
    425         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    426         assertFalse(ic.isCursorTouchingWord(sap, true));
    427 
    428         ims.setInputConnection(new MockConnection(" --", 3));
    429         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    430         assertFalse(ic.isCursorTouchingWord(sap, true));
    431 
    432         ims.setInputConnection(new MockConnection(" users '", 1));
    433         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    434         assertTrue(ic.isCursorTouchingWord(sap, true));
    435 
    436         ims.setInputConnection(new MockConnection(" users '", 3));
    437         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    438         assertTrue(ic.isCursorTouchingWord(sap, true));
    439 
    440         ims.setInputConnection(new MockConnection(" users '", 7));
    441         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    442         assertFalse(ic.isCursorTouchingWord(sap, true));
    443 
    444         ims.setInputConnection(new MockConnection(" users are", 7));
    445         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    446         assertTrue(ic.isCursorTouchingWord(sap, true));
    447 
    448         ims.setInputConnection(new MockConnection(" users 'are", 7));
    449         ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
    450         assertFalse(ic.isCursorTouchingWord(sap, true));
    451     }
    452 }
    453