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 setUp()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 MockConnection(final CharSequence text, final int cursorPosition)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 MockConnection(String textBefore, String textAfter, ExtractedText extractedText)88 public MockConnection(String textBefore, String textAfter, ExtractedText extractedText) { 89 super(null, false); 90 mTextBefore = textBefore; 91 mTextAfter = textAfter; 92 mExtractedText = extractedText; 93 } 94 cursorPos()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 getTextBeforeCursor(int n, int flags)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 getTextAfterCursor(int n, int flags)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 getExtractedText(ExtractedTextRequest request, int flags)120 public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) { 121 return mExtractedText; 122 } 123 124 @Override beginBatchEdit()125 public boolean beginBatchEdit() { 126 return true; 127 } 128 129 @Override endBatchEdit()130 public boolean endBatchEdit() { 131 return true; 132 } 133 134 @Override finishComposingText()135 public boolean finishComposingText() { 136 return true; 137 } 138 } 139 140 static class MockInputMethodService extends InputMethodService { 141 private MockConnection mMockConnection; setInputConnection(final MockConnection mockConnection)142 public void setInputConnection(final MockConnection mockConnection) { 143 mMockConnection = mockConnection; 144 } cursorPos()145 public int cursorPos() { 146 return mMockConnection.cursorPos(); 147 } 148 @Override getCurrentInputConnection()149 public InputConnection getCurrentInputConnection() { 150 return mMockConnection; 151 } 152 } 153 154 /************************** Tests ************************/ 155 156 /** 157 * Test for getting previous word (for bigram suggestions) 158 */ testGetPreviousWord()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 testGetWordRangeAtCursor()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 */ testGetSuggestionSpansAtWord()285 public void testGetSuggestionSpansAtWord() { 286 helpTestGetSuggestionSpansAtWord(10); 287 helpTestGetSuggestionSpansAtWord(12); 288 helpTestGetSuggestionSpansAtWord(15); 289 helpTestGetSuggestionSpansAtWord(16); 290 } 291 helpTestGetSuggestionSpansAtWord(final int cursorPos)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 testCursorTouchingWord()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