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.PrevWordsInfo.WordInfo; 34 import com.android.inputmethod.latin.settings.SpacingAndPunctuations; 35 import com.android.inputmethod.latin.utils.PrevWordsInfoUtils; 36 import com.android.inputmethod.latin.utils.RunInLocale; 37 import com.android.inputmethod.latin.utils.ScriptUtils; 38 import com.android.inputmethod.latin.utils.StringUtils; 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 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 public void testGetWordRangeAtCursor() { 219 /** 220 * Test logic in getting the word range at the cursor. 221 */ 222 final SpacingAndPunctuations SPACE = new SpacingAndPunctuations( 223 mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); 224 final SpacingAndPunctuations TAB = new SpacingAndPunctuations( 225 mSpacingAndPunctuations, new int[] { Constants.CODE_TAB }); 226 final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t"); 227 // A character that needs surrogate pair to represent its code point (U+2008A). 228 final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A"; 229 final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations( 230 mSpacingAndPunctuations, StringUtils.toSortedCodePointArray( 231 SUPPLEMENTARY_CHAR_STRING)); 232 final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // 233 final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // 234 235 ExtractedText et = new ExtractedText(); 236 final MockInputMethodService mockInputMethodService = new MockInputMethodService(); 237 final RichInputConnection ic = new RichInputConnection(mockInputMethodService); 238 mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et)); 239 et.startOffset = 0; 240 et.selectionStart = 7; 241 TextRange r; 242 243 ic.beginBatchEdit(); 244 // basic case 245 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 246 assertTrue(TextUtils.equals("word", r.mWord)); 247 248 // tab character instead of space 249 mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et)); 250 ic.beginBatchEdit(); 251 r = ic.getWordRangeAtCursor(TAB, ScriptUtils.SCRIPT_LATIN); 252 ic.endBatchEdit(); 253 assertTrue(TextUtils.equals("word", r.mWord)); 254 255 // splitting on supplementary character 256 mockInputMethodService.setInputConnection( 257 new MockConnection("one word" + SUPPLEMENTARY_CHAR_STRING + "wo", "rd", et)); 258 ic.beginBatchEdit(); 259 r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN); 260 ic.endBatchEdit(); 261 assertTrue(TextUtils.equals("word", r.mWord)); 262 263 // split on chars outside the specified script 264 mockInputMethodService.setInputConnection( 265 new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et)); 266 ic.beginBatchEdit(); 267 r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN); 268 ic.endBatchEdit(); 269 assertTrue(TextUtils.equals("word", r.mWord)); 270 271 // likewise for greek 272 mockInputMethodService.setInputConnection( 273 new MockConnection("text" + GREEK_WORD, "text", et)); 274 ic.beginBatchEdit(); 275 r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_GREEK); 276 ic.endBatchEdit(); 277 assertTrue(TextUtils.equals(GREEK_WORD, r.mWord)); 278 } 279 280 /** 281 * Test logic in getting the word range at the cursor. 282 */ 283 public void testGetSuggestionSpansAtWord() { 284 helpTestGetSuggestionSpansAtWord(10); 285 helpTestGetSuggestionSpansAtWord(12); 286 helpTestGetSuggestionSpansAtWord(15); 287 helpTestGetSuggestionSpansAtWord(16); 288 } 289 290 private void helpTestGetSuggestionSpansAtWord(final int cursorPos) { 291 final SpacingAndPunctuations SPACE = new SpacingAndPunctuations( 292 mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); 293 final MockInputMethodService mockInputMethodService = new MockInputMethodService(); 294 final RichInputConnection ic = new RichInputConnection(mockInputMethodService); 295 296 final String[] SUGGESTIONS1 = { "swing", "strong" }; 297 final String[] SUGGESTIONS2 = { "storing", "strung" }; 298 299 // Test the usual case. 300 SpannableString text = new SpannableString("This is a string for test"); 301 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 302 10 /* start */, 16 /* end */, 0 /* flags */); 303 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 304 TextRange r; 305 SuggestionSpan[] suggestions; 306 307 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 308 suggestions = r.getSuggestionSpansAtWord(); 309 assertEquals(suggestions.length, 1); 310 MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); 311 312 // Test the case with 2 suggestion spans in the same place. 313 text = new SpannableString("This is a string for test"); 314 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 315 10 /* start */, 16 /* end */, 0 /* flags */); 316 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), 317 10 /* start */, 16 /* end */, 0 /* flags */); 318 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 319 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 320 suggestions = r.getSuggestionSpansAtWord(); 321 assertEquals(suggestions.length, 2); 322 MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); 323 MoreAsserts.assertEquals(suggestions[1].getSuggestions(), SUGGESTIONS2); 324 325 // Test a case with overlapping spans, 2nd extending past the start of the word 326 text = new SpannableString("This is a string for test"); 327 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 328 10 /* start */, 16 /* end */, 0 /* flags */); 329 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), 330 5 /* start */, 16 /* end */, 0 /* flags */); 331 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 332 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 333 suggestions = r.getSuggestionSpansAtWord(); 334 assertEquals(suggestions.length, 1); 335 MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); 336 337 // Test a case with overlapping spans, 2nd extending past the end of the word 338 text = new SpannableString("This is a string for test"); 339 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 340 10 /* start */, 16 /* end */, 0 /* flags */); 341 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), 342 10 /* start */, 20 /* end */, 0 /* flags */); 343 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 344 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 345 suggestions = r.getSuggestionSpansAtWord(); 346 assertEquals(suggestions.length, 1); 347 MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); 348 349 // Test a case with overlapping spans, 2nd extending past both ends of the word 350 text = new SpannableString("This is a string for test"); 351 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 352 10 /* start */, 16 /* end */, 0 /* flags */); 353 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), 354 5 /* start */, 20 /* end */, 0 /* flags */); 355 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 356 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 357 suggestions = r.getSuggestionSpansAtWord(); 358 assertEquals(suggestions.length, 1); 359 MoreAsserts.assertEquals(suggestions[0].getSuggestions(), SUGGESTIONS1); 360 361 // Test a case with overlapping spans, none right on the word 362 text = new SpannableString("This is a string for test"); 363 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS1, 0 /* flags */), 364 5 /* start */, 16 /* end */, 0 /* flags */); 365 text.setSpan(new SuggestionSpan(Locale.ENGLISH, SUGGESTIONS2, 0 /* flags */), 366 5 /* start */, 20 /* end */, 0 /* flags */); 367 mockInputMethodService.setInputConnection(new MockConnection(text, cursorPos)); 368 r = ic.getWordRangeAtCursor(SPACE, ScriptUtils.SCRIPT_LATIN); 369 suggestions = r.getSuggestionSpansAtWord(); 370 assertEquals(suggestions.length, 0); 371 } 372 373 public void testCursorTouchingWord() { 374 final MockInputMethodService ims = new MockInputMethodService(); 375 final RichInputConnection ic = new RichInputConnection(ims); 376 final SpacingAndPunctuations sap = mSpacingAndPunctuations; 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'", 5)); 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'", 6)); 391 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 392 assertTrue(ic.isCursorTouchingWord(sap)); 393 394 ims.setInputConnection(new MockConnection("'users'", 7)); 395 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 396 assertTrue(ic.isCursorTouchingWord(sap)); 397 398 ims.setInputConnection(new MockConnection("users '", 6)); 399 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 400 assertFalse(ic.isCursorTouchingWord(sap)); 401 402 ims.setInputConnection(new MockConnection("users '", 7)); 403 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 404 assertFalse(ic.isCursorTouchingWord(sap)); 405 406 ims.setInputConnection(new MockConnection("re-", 3)); 407 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 408 assertTrue(ic.isCursorTouchingWord(sap)); 409 410 ims.setInputConnection(new MockConnection("re--", 4)); 411 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 412 assertFalse(ic.isCursorTouchingWord(sap)); 413 414 ims.setInputConnection(new MockConnection("-", 1)); 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(" -", 2)); 423 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 424 assertFalse(ic.isCursorTouchingWord(sap)); 425 426 ims.setInputConnection(new MockConnection(" --", 3)); 427 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 428 assertFalse(ic.isCursorTouchingWord(sap)); 429 430 ims.setInputConnection(new MockConnection(" users '", 1)); 431 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 432 assertTrue(ic.isCursorTouchingWord(sap)); 433 434 ims.setInputConnection(new MockConnection(" users '", 3)); 435 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 436 assertTrue(ic.isCursorTouchingWord(sap)); 437 438 ims.setInputConnection(new MockConnection(" users '", 7)); 439 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 440 assertFalse(ic.isCursorTouchingWord(sap)); 441 442 ims.setInputConnection(new MockConnection(" users are", 7)); 443 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 444 assertTrue(ic.isCursorTouchingWord(sap)); 445 446 ims.setInputConnection(new MockConnection(" users 'are", 7)); 447 ic.resetCachesUponCursorMoveAndReturnSuccess(ims.cursorPos(), ims.cursorPos(), true); 448 assertFalse(ic.isCursorTouchingWord(sap)); 449 } 450 } 451