RichInputConnectionAndTextRangeTests.java revision bb843eb223ce0f8fb1088ed3393a4165123ddb1f
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
17package com.android.inputmethod.latin;
18
19import android.content.res.Resources;
20import android.inputmethodservice.InputMethodService;
21import android.os.Parcel;
22import android.test.AndroidTestCase;
23import android.test.MoreAsserts;
24import android.test.suitebuilder.annotation.SmallTest;
25import android.text.SpannableString;
26import android.text.TextUtils;
27import android.text.style.SuggestionSpan;
28import android.view.inputmethod.ExtractedText;
29import android.view.inputmethod.ExtractedTextRequest;
30import android.view.inputmethod.InputConnection;
31import android.view.inputmethod.InputConnectionWrapper;
32
33import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
34import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
35import com.android.inputmethod.latin.utils.PrevWordsInfoUtils;
36import com.android.inputmethod.latin.utils.RunInLocale;
37import com.android.inputmethod.latin.utils.ScriptUtils;
38import com.android.inputmethod.latin.utils.StringUtils;
39import com.android.inputmethod.latin.utils.TextRange;
40
41import java.util.Locale;
42
43@SmallTest
44public 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    private 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(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
162                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
163        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
164                "abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
165        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
166                "abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
167
168        assertFalse(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
169                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
170        assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
171                "abc", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
172
173        // For n-gram
174        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
175                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
176        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
177                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[1].mWord, "abc");
178        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
179                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[1],
180                WordInfo.BEGINNING_OF_SENTENCE);
181
182        // The following tests reflect the current behavior of the function
183        // RichInputConnection#getNthPreviousWord.
184        // TODO: However at this time, the code does never go
185        // into such a path, so it should be safe to change the behavior of
186        // this function if needed - especially since it does not seem very
187        // logical. These tests are just there to catch any unintentional
188        // changes in the behavior of the RichInputConnection#getPreviousWord method.
189        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
190                "abc def ", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
191        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
192                "abc def.", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
193        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
194                "abc def .", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "def");
195        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
196                "abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
197
198        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
199                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
200        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
201                "abc def ", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
202        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
203                "abc 'def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "'def");
204        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
205                "abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
206        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
207                "abc def .", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
208        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
209                "abc, def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
210        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
211                "abc? def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
212        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
213                "abc! def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
214        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
215                "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO);
216    }
217
218    /**
219     * Test logic in getting the word range at the cursor.
220     */
221    private static final int[] SPACE = { Constants.CODE_SPACE };
222    static final int[] TAB = { Constants.CODE_TAB };
223    private static final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t");
224    // A character that needs surrogate pair to represent its code point (U+2008A).
225    private static final String SUPPLEMENTARY_CHAR = "\uD840\uDC8A";
226    private static final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお
227    private static final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και
228
229    public void testGetWordRangeAtCursor() {
230        ExtractedText et = new ExtractedText();
231        final MockInputMethodService mockInputMethodService = new MockInputMethodService();
232        final RichInputConnection ic = new RichInputConnection(mockInputMethodService);
233        mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et));
234        et.startOffset = 0;
235        et.selectionStart = 7;
236        TextRange r;
237
238        ic.beginBatchEdit();
239        // basic case
240        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
241        assertTrue(TextUtils.equals("word", r.mWord));
242
243        // tab character instead of space
244        mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et));
245        ic.beginBatchEdit();
246        r = ic.getWordRangeAtCursor(TAB, ScriptUtils.SCRIPT_LATIN);
247        ic.endBatchEdit();
248        assertTrue(TextUtils.equals("word", r.mWord));
249
250        // splitting on supplementary character
251        mockInputMethodService.setInputConnection(
252                new MockConnection("one word" + SUPPLEMENTARY_CHAR + "wo", "rd", et));
253        ic.beginBatchEdit();
254        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
255                ScriptUtils.SCRIPT_LATIN);
256        ic.endBatchEdit();
257        assertTrue(TextUtils.equals("word", r.mWord));
258
259        // split on chars outside the specified script
260        mockInputMethodService.setInputConnection(
261                new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et));
262        ic.beginBatchEdit();
263        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
264                ScriptUtils.SCRIPT_LATIN);
265        ic.endBatchEdit();
266        assertTrue(TextUtils.equals("word", r.mWord));
267
268        // likewise for greek
269        mockInputMethodService.setInputConnection(
270                new MockConnection("text" + GREEK_WORD, "text", et));
271        ic.beginBatchEdit();
272        r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR),
273                ScriptUtils.SCRIPT_GREEK);
274        ic.endBatchEdit();
275        assertTrue(TextUtils.equals(GREEK_WORD, r.mWord));
276    }
277
278    /**
279     * Test logic in getting the word range at the cursor.
280     */
281    public void testGetSuggestionSpansAtWord() {
282        helpTestGetSuggestionSpansAtWord(10);
283        helpTestGetSuggestionSpansAtWord(12);
284        helpTestGetSuggestionSpansAtWord(15);
285        helpTestGetSuggestionSpansAtWord(16);
286    }
287
288    private void helpTestGetSuggestionSpansAtWord(final int cursorPos) {
289        final MockInputMethodService mockInputMethodService = new MockInputMethodService();
290        final RichInputConnection ic = new RichInputConnection(mockInputMethodService);
291
292        final String[] SUGGESTIONS1 = { "swing", "strong" };
293        final String[] SUGGESTIONS2 = { "storing", "strung" };
294
295        // Test the usual case.
296        SpannableString text = new SpannableString("This is a string for test");
297        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
298                10 /* start */, 16 /* end */, 0 /* flags */);
299        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
300        TextRange r;
301        SuggestionSpan[] suggestions;
302
303        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
304        suggestions = r.getSuggestionSpansAtWord();
305        assertEquals(suggestions.length, 1);
306        MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
307
308        // Test the case with 2 suggestion spans in the same place.
309        text = new SpannableString("This is a string for test");
310        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
311                10 /* start */, 16 /* end */, 0 /* flags */);
312        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
313                10 /* start */, 16 /* end */, 0 /* flags */);
314        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
315        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
316        suggestions = r.getSuggestionSpansAtWord();
317        assertEquals(suggestions.length, 2);
318        MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
319        MoreAsserts.assertEquals(suggestions[1].getSuggestions(), SUGGESTIONS2);
320
321        // Test a case with overlapping spans, 2nd extending past the start of the word
322        text = new SpannableString("This is a string for test");
323        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
324                10 /* start */, 16 /* end */, 0 /* flags */);
325        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
326                5 /* start */, 16 /* end */, 0 /* flags */);
327        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
328        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
329        suggestions = r.getSuggestionSpansAtWord();
330        assertEquals(suggestions.length, 1);
331        MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
332
333        // Test a case with overlapping spans, 2nd extending past the end of the word
334        text = new SpannableString("This is a string for test");
335        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
336                10 /* start */, 16 /* end */, 0 /* flags */);
337        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
338                10 /* start */, 20 /* end */, 0 /* flags */);
339        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
340        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
341        suggestions = r.getSuggestionSpansAtWord();
342        assertEquals(suggestions.length, 1);
343        MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
344
345        // Test a case with overlapping spans, 2nd extending past both ends of the word
346        text = new SpannableString("This is a string for test");
347        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
348                10 /* start */, 16 /* end */, 0 /* flags */);
349        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
350                5 /* start */, 20 /* end */, 0 /* flags */);
351        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
352        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
353        suggestions = r.getSuggestionSpansAtWord();
354        assertEquals(suggestions.length, 1);
355        MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1);
356
357        // Test a case with overlapping spans, none right on the word
358        text = new SpannableString("This is a string for test");
359        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */),
360                5 /* start */, 16 /* end */, 0 /* flags */);
361        text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */),
362                5 /* start */, 20 /* end */, 0 /* flags */);
363        mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos));
364        r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN);
365        suggestions = r.getSuggestionSpansAtWord();
366        assertEquals(suggestions.length, 0);
367    }
368
369    public void testCursorTouchingWord() {
370        final MockInputMethodService ims = new MockInputMethodService();
371        final RichInputConnection ic = new RichInputConnection(ims);
372        final SpacingAndPunctuations sap = mSpacingAndPunctuations;
373
374        ims.setInputConnection(new MockConnection("users", 5));
375        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
376        assertTrue(ic.isCursorTouchingWord(sap));
377
378        ims.setInputConnection(new MockConnection("users'", 5));
379        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
380        assertTrue(ic.isCursorTouchingWord(sap));
381
382        ims.setInputConnection(new MockConnection("users'", 6));
383        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
384        assertTrue(ic.isCursorTouchingWord(sap));
385
386        ims.setInputConnection(new MockConnection("'users'", 6));
387        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
388        assertTrue(ic.isCursorTouchingWord(sap));
389
390        ims.setInputConnection(new MockConnection("'users'", 7));
391        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
392        assertTrue(ic.isCursorTouchingWord(sap));
393
394        ims.setInputConnection(new MockConnection("users '", 6));
395        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
396        assertFalse(ic.isCursorTouchingWord(sap));
397
398        ims.setInputConnection(new MockConnection("users '", 7));
399        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
400        assertFalse(ic.isCursorTouchingWord(sap));
401
402        ims.setInputConnection(new MockConnection("re-", 3));
403        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
404        assertTrue(ic.isCursorTouchingWord(sap));
405
406        ims.setInputConnection(new MockConnection("re--", 4));
407        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
408        assertFalse(ic.isCursorTouchingWord(sap));
409
410        ims.setInputConnection(new MockConnection("-", 1));
411        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
412        assertFalse(ic.isCursorTouchingWord(sap));
413
414        ims.setInputConnection(new MockConnection("--", 2));
415        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
416        assertFalse(ic.isCursorTouchingWord(sap));
417
418        ims.setInputConnection(new MockConnection(" -", 2));
419        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
420        assertFalse(ic.isCursorTouchingWord(sap));
421
422        ims.setInputConnection(new MockConnection(" --", 3));
423        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
424        assertFalse(ic.isCursorTouchingWord(sap));
425
426        ims.setInputConnection(new MockConnection(" users '", 1));
427        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
428        assertTrue(ic.isCursorTouchingWord(sap));
429
430        ims.setInputConnection(new MockConnection(" users '", 3));
431        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
432        assertTrue(ic.isCursorTouchingWord(sap));
433
434        ims.setInputConnection(new MockConnection(" users '", 7));
435        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
436        assertFalse(ic.isCursorTouchingWord(sap));
437
438        ims.setInputConnection(new MockConnection(" users are", 7));
439        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
440        assertTrue(ic.isCursorTouchingWord(sap));
441
442        ims.setInputConnection(new MockConnection(" users 'are", 7));
443        ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true);
444        assertFalse(ic.isCursorTouchingWord(sap));
445    }
446}
447