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 static android.test.MoreAsserts.assertNotEqual;
20 
21 import android.test.suitebuilder.annotation.LargeTest;
22 import android.text.TextUtils;
23 import android.view.inputmethod.BaseInputConnection;
24 
25 import com.android.inputmethod.latin.common.Constants;
26 import com.android.inputmethod.latin.define.DecoderSpecificConstants;
27 import com.android.inputmethod.latin.settings.Settings;
28 
29 @LargeTest
30 public class InputLogicTests extends InputTestsBase {
31 
32     private boolean mNextWordPrediction;
33 
34     @Override
setUp()35     public void setUp() throws Exception {
36         super.setUp();
37         mNextWordPrediction = getBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, true);
38     }
39 
40     @Override
tearDown()41     public void tearDown() throws Exception {
42         setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mNextWordPrediction, true);
43         super.tearDown();
44     }
45 
testTypeWord()46     public void testTypeWord() {
47         final String WORD_TO_TYPE = "abcd";
48         type(WORD_TO_TYPE);
49         assertEquals("type word", WORD_TO_TYPE, mEditText.getText().toString());
50     }
51 
testPickSuggestionThenBackspace()52     public void testPickSuggestionThenBackspace() {
53         final String WORD_TO_TYPE = "this";
54         final String EXPECTED_RESULT = "thi";
55         type(WORD_TO_TYPE);
56         pickSuggestionManually(WORD_TO_TYPE);
57         sendUpdateForCursorMoveTo(WORD_TO_TYPE.length());
58         type(Constants.CODE_DELETE);
59         assertEquals("press suggestion then backspace", EXPECTED_RESULT,
60                 mEditText.getText().toString());
61     }
62 
testPickAutoCorrectionThenBackspace()63     public void testPickAutoCorrectionThenBackspace() {
64         final String WORD_TO_TYPE = "tgis";
65         final String WORD_TO_PICK = "this";
66         final String EXPECTED_RESULT = "thi";
67         type(WORD_TO_TYPE);
68         // Choose the auto-correction. For "tgis", the auto-correction should be "this".
69         pickSuggestionManually(WORD_TO_PICK);
70         sendUpdateForCursorMoveTo(WORD_TO_TYPE.length());
71         assertEquals("pick typed word over auto-correction then backspace", WORD_TO_PICK,
72                 mEditText.getText().toString());
73         type(Constants.CODE_DELETE);
74         assertEquals("pick typed word over auto-correction then backspace", EXPECTED_RESULT,
75                 mEditText.getText().toString());
76     }
77 
testPickTypedWordOverAutoCorrectionThenBackspace()78     public void testPickTypedWordOverAutoCorrectionThenBackspace() {
79         final String WORD_TO_TYPE = "tgis";
80         final String EXPECTED_RESULT = "tgi";
81         type(WORD_TO_TYPE);
82         // Choose the typed word.
83         pickSuggestionManually(WORD_TO_TYPE);
84         sendUpdateForCursorMoveTo(WORD_TO_TYPE.length());
85         assertEquals("pick typed word over auto-correction then backspace", WORD_TO_TYPE,
86                 mEditText.getText().toString());
87         type(Constants.CODE_DELETE);
88         assertEquals("pick typed word over auto-correction then backspace", EXPECTED_RESULT,
89                 mEditText.getText().toString());
90     }
91 
testPickDifferentSuggestionThenBackspace()92     public void testPickDifferentSuggestionThenBackspace() {
93         final String WORD_TO_TYPE = "tgis";
94         final String WORD_TO_PICK = "thus";
95         final String EXPECTED_RESULT = "thu";
96         type(WORD_TO_TYPE);
97         // Choose the second suggestion, which should be "thus" when "tgis" is typed.
98         pickSuggestionManually(WORD_TO_PICK);
99         sendUpdateForCursorMoveTo(WORD_TO_TYPE.length());
100         assertEquals("pick different suggestion then backspace", WORD_TO_PICK,
101                 mEditText.getText().toString());
102         type(Constants.CODE_DELETE);
103         assertEquals("pick different suggestion then backspace", EXPECTED_RESULT,
104                 mEditText.getText().toString());
105     }
106 
testDeleteSelection()107     public void testDeleteSelection() {
108         final String STRING_TO_TYPE = "some text delete me some text";
109         final int typedLength = STRING_TO_TYPE.length();
110         final int SELECTION_START = 10;
111         final int SELECTION_END = 19;
112         final String EXPECTED_RESULT = "some text  some text";
113         type(STRING_TO_TYPE);
114         // Don't use the sendUpdateForCursorMove* family of methods here because they
115         // don't handle selections.
116         // Send once to simulate the cursor actually responding to the move caused by typing.
117         // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor
118         // move with a move triggered by LatinIME inputting stuff.
119         mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
120         mInputConnection.setSelection(SELECTION_START, SELECTION_END);
121         // And now we simulate the user actually selecting some text.
122         mLatinIME.onUpdateSelection(typedLength, typedLength,
123                 SELECTION_START, SELECTION_END, -1, -1);
124         type(Constants.CODE_DELETE);
125         assertEquals("delete selection", EXPECTED_RESULT, mEditText.getText().toString());
126     }
127 
testDeleteSelectionTwice()128     public void testDeleteSelectionTwice() {
129         final String STRING_TO_TYPE = "some text delete me some text";
130         final int typedLength = STRING_TO_TYPE.length();
131         final int SELECTION_START = 10;
132         final int SELECTION_END = 19;
133         final String EXPECTED_RESULT = "some text some text";
134         type(STRING_TO_TYPE);
135         // Don't use the sendUpdateForCursorMove* family of methods here because they
136         // don't handle selections.
137         // Send once to simulate the cursor actually responding to the move caused by typing.
138         // This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor
139         // move with a move triggered by LatinIME inputting stuff.
140         mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
141         mInputConnection.setSelection(SELECTION_START, SELECTION_END);
142         // And now we simulate the user actually selecting some text.
143         mLatinIME.onUpdateSelection(typedLength, typedLength,
144                 SELECTION_START, SELECTION_END, -1, -1);
145         type(Constants.CODE_DELETE);
146         type(Constants.CODE_DELETE);
147         assertEquals("delete selection twice", EXPECTED_RESULT, mEditText.getText().toString());
148     }
149 
testAutoCorrect()150     public void testAutoCorrect() {
151         final String STRING_TO_TYPE = "tgis ";
152         final String EXPECTED_RESULT = "this ";
153         type(STRING_TO_TYPE);
154         assertEquals("simple auto-correct", EXPECTED_RESULT, mEditText.getText().toString());
155     }
156 
testAutoCorrectWithQuote()157     public void testAutoCorrectWithQuote() {
158         final String STRING_TO_TYPE = "didn' ";
159         final String EXPECTED_RESULT = "didn't ";
160         type(STRING_TO_TYPE);
161         assertEquals("auto-correct with quote", EXPECTED_RESULT, mEditText.getText().toString());
162     }
163 
testAutoCorrectWithPeriod()164     public void testAutoCorrectWithPeriod() {
165         final String STRING_TO_TYPE = "tgis.";
166         final String EXPECTED_RESULT = "this.";
167         type(STRING_TO_TYPE);
168         assertEquals("auto-correct with period", EXPECTED_RESULT, mEditText.getText().toString());
169     }
170 
testAutoCorrectWithPeriodThenRevert()171     public void testAutoCorrectWithPeriodThenRevert() {
172         final String STRING_TO_TYPE = "tgis.";
173         final String EXPECTED_RESULT = "tgis.";
174         type(STRING_TO_TYPE);
175         sendUpdateForCursorMoveTo(STRING_TO_TYPE.length());
176         type(Constants.CODE_DELETE);
177         assertEquals("auto-correct with period then revert", EXPECTED_RESULT,
178                 mEditText.getText().toString());
179     }
180 
testAutoCorrectWithSpaceThenRevert()181     public void testAutoCorrectWithSpaceThenRevert() {
182         // Backspacing to cancel the "tgis"->"this" autocorrection should result in
183         // a "phantom space": if the user presses space immediately after,
184         // only one space will be inserted in total.
185         final String STRING_TO_TYPE = "tgis ";
186         final String EXPECTED_RESULT = "tgis";
187         type(STRING_TO_TYPE);
188         sendUpdateForCursorMoveTo(STRING_TO_TYPE.length());
189         type(Constants.CODE_DELETE);
190         assertEquals("auto-correct with space then revert", EXPECTED_RESULT,
191                 mEditText.getText().toString());
192     }
193 
testAutoCorrectWithSpaceThenRevertThenTypeMore()194     public void testAutoCorrectWithSpaceThenRevertThenTypeMore() {
195         final String STRING_TO_TYPE_FIRST = "tgis ";
196         final String STRING_TO_TYPE_SECOND = "a";
197         final String EXPECTED_RESULT = "tgis a";
198         type(STRING_TO_TYPE_FIRST);
199         sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length());
200         type(Constants.CODE_DELETE);
201 
202         type(STRING_TO_TYPE_SECOND);
203         sendUpdateForCursorMoveTo(STRING_TO_TYPE_FIRST.length() - 1
204                 + STRING_TO_TYPE_SECOND.length());
205         assertEquals("auto-correct with space then revert then type more", EXPECTED_RESULT,
206                 mEditText.getText().toString());
207     }
208 
testAutoCorrectToSelfDoesNotRevert()209     public void testAutoCorrectToSelfDoesNotRevert() {
210         final String STRING_TO_TYPE = "this ";
211         final String EXPECTED_RESULT = "this";
212         type(STRING_TO_TYPE);
213         sendUpdateForCursorMoveTo(STRING_TO_TYPE.length());
214         type(Constants.CODE_DELETE);
215         assertEquals("auto-correct with space does not revert", EXPECTED_RESULT,
216                 mEditText.getText().toString());
217     }
218 
testDoubleSpace()219     public void testDoubleSpace() {
220         // U+1F607 is an emoji
221         final String[] STRINGS_TO_TYPE =
222                 new String[] { "this   ", "a+  ", "\u1F607  ", "..  ", ")  ", "(  ", "%  " };
223         final String[] EXPECTED_RESULTS =
224                 new String[] { "this.  ", "a+. ", "\u1F607. ", "..  ", "). ", "(  ", "%. " };
225         verifyDoubleSpace(STRINGS_TO_TYPE, EXPECTED_RESULTS);
226     }
227 
testDoubleSpaceHindi()228     public void testDoubleSpaceHindi() {
229         changeLanguage("hi");
230         // U+1F607 is an emoji
231         final String[] STRINGS_TO_TYPE =
232                 new String[] { "this   ", "a+  ", "\u1F607  ", "||  ", ")  ", "(  ", "%  " };
233         final String[] EXPECTED_RESULTS =
234                 new String[] { "this|  ", "a+| ", "\u1F607| ", "||  ", ")| ", "(  ", "%| " };
235         verifyDoubleSpace(STRINGS_TO_TYPE, EXPECTED_RESULTS);
236     }
237 
verifyDoubleSpace(String[] stringsToType, String[] expectedResults)238     private void verifyDoubleSpace(String[] stringsToType, String[] expectedResults) {
239         // Set default pref just in case
240         setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true);
241         for (int i = 0; i < stringsToType.length; ++i) {
242             mEditText.setText("");
243             type(stringsToType[i]);
244             assertEquals("double space processing", expectedResults[i],
245                     mEditText.getText().toString());
246         }
247     }
248 
testCancelDoubleSpaceEnglish()249     public void testCancelDoubleSpaceEnglish() {
250         final String STRING_TO_TYPE = "this  ";
251         final String EXPECTED_RESULT = "this ";
252         type(STRING_TO_TYPE);
253         type(Constants.CODE_DELETE);
254         assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
255     }
256 
testCancelDoubleSpaceHindi()257     public void testCancelDoubleSpaceHindi() {
258         changeLanguage("hi");
259         final String STRING_TO_TYPE = "this  ";
260         final String EXPECTED_RESULT = "this ";
261         type(STRING_TO_TYPE);
262         type(Constants.CODE_DELETE);
263         assertEquals("double space make a period", EXPECTED_RESULT, mEditText.getText().toString());
264     }
265 
testDoubleSpacePeriodWithSettings(final boolean expectsPeriod, final Object... settingsKeysValues)266     private void testDoubleSpacePeriodWithSettings(final boolean expectsPeriod,
267             final Object... settingsKeysValues) {
268         final Object[] oldSettings = new Object[settingsKeysValues.length / 2];
269         final String STRING_WITHOUT_PERIOD = "this  ";
270         final String STRING_WITH_PERIOD = "this. ";
271         final String EXPECTED_RESULT = expectsPeriod ? STRING_WITH_PERIOD : STRING_WITHOUT_PERIOD;
272         try {
273             for (int i = 0; i < settingsKeysValues.length; i += 2) {
274                 if (settingsKeysValues[i + 1] instanceof String) {
275                     oldSettings[i / 2] = setStringPreference((String)settingsKeysValues[i],
276                             (String)settingsKeysValues[i + 1], "0");
277                 } else {
278                     oldSettings[i / 2] = setBooleanPreference((String)settingsKeysValues[i],
279                             (Boolean)settingsKeysValues[i + 1], false);
280                 }
281             }
282             mLatinIME.loadSettings();
283             mEditText.setText("");
284             type(STRING_WITHOUT_PERIOD);
285             assertEquals("double-space-to-period with specific settings "
286                     + TextUtils.join(" ", settingsKeysValues),
287                     EXPECTED_RESULT, mEditText.getText().toString());
288         } finally {
289             // Restore old settings
290             for (int i = 0; i < settingsKeysValues.length; i += 2) {
291                 if (null == oldSettings[i / 2]) {
292                     break;
293                 } if (oldSettings[i / 2] instanceof String) {
294                     setStringPreference((String)settingsKeysValues[i], (String)oldSettings[i / 2],
295                             "");
296                 } else {
297                     setBooleanPreference((String)settingsKeysValues[i], (Boolean)oldSettings[i / 2],
298                             false);
299                 }
300             }
301         }
302     }
303 
testDoubleSpacePeriod()304     public void testDoubleSpacePeriod() {
305         // Reset settings to default, else these tests will go flaky.
306         setBooleanPreference(Settings.PREF_SHOW_SUGGESTIONS, true, true);
307         setBooleanPreference(Settings.PREF_AUTO_CORRECTION, true, true);
308         setBooleanPreference(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true, true);
309         testDoubleSpacePeriodWithSettings(true);
310         // "Suggestion visibility" to off
311         testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS, false);
312         // "Suggestion visibility" to on
313         testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS, true);
314 
315         // "Double-space period" to "off"
316         testDoubleSpacePeriodWithSettings(false, Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false);
317 
318         // "Auto-correction" to "off"
319         testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION, false);
320         // "Auto-correction" to "on"
321         testDoubleSpacePeriodWithSettings(true, Settings.PREF_AUTO_CORRECTION, true);
322 
323         // "Suggestion visibility" to "always hide" and "Auto-correction" to "off"
324         testDoubleSpacePeriodWithSettings(true, Settings.PREF_SHOW_SUGGESTIONS, false,
325                 Settings.PREF_AUTO_CORRECTION, false);
326         // "Suggestion visibility" to "always hide" and "Auto-correction" to "off"
327         testDoubleSpacePeriodWithSettings(false, Settings.PREF_SHOW_SUGGESTIONS, false,
328                 Settings.PREF_AUTO_CORRECTION, false,
329                 Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, false);
330     }
331 
testBackspaceAtStartAfterAutocorrect()332     public void testBackspaceAtStartAfterAutocorrect() {
333         final String STRING_TO_TYPE = "tgis ";
334         final int typedLength = STRING_TO_TYPE.length();
335         final String EXPECTED_RESULT = "this ";
336         final int NEW_CURSOR_POSITION = 0;
337         type(STRING_TO_TYPE);
338         sendUpdateForCursorMoveTo(typedLength);
339         mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
340         sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION);
341         type(Constants.CODE_DELETE);
342         assertEquals("auto correct then move cursor to start of line then backspace",
343                 EXPECTED_RESULT, mEditText.getText().toString());
344     }
345 
testAutoCorrectThenMoveCursorThenBackspace()346     public void testAutoCorrectThenMoveCursorThenBackspace() {
347         final String STRING_TO_TYPE = "and tgis ";
348         final int typedLength = STRING_TO_TYPE.length();
349         final String EXPECTED_RESULT = "andthis ";
350         final int NEW_CURSOR_POSITION = STRING_TO_TYPE.indexOf('t');
351         type(STRING_TO_TYPE);
352         sendUpdateForCursorMoveTo(typedLength);
353         mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
354         sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION);
355         type(Constants.CODE_DELETE);
356         assertEquals("auto correct then move cursor then backspace",
357                 EXPECTED_RESULT, mEditText.getText().toString());
358     }
359 
testNoSpaceAfterManualPick()360     public void testNoSpaceAfterManualPick() {
361         final String WORD_TO_TYPE = "this";
362         final String EXPECTED_RESULT = WORD_TO_TYPE;
363         type(WORD_TO_TYPE);
364         pickSuggestionManually(WORD_TO_TYPE);
365         assertEquals("no space after manual pick", EXPECTED_RESULT,
366                 mEditText.getText().toString());
367     }
368 
testManualPickThenType()369     public void testManualPickThenType() {
370         final String WORD1_TO_TYPE = "this";
371         final String WORD2_TO_TYPE = "is";
372         final String EXPECTED_RESULT = "this is";
373         type(WORD1_TO_TYPE);
374         pickSuggestionManually(WORD1_TO_TYPE);
375         type(WORD2_TO_TYPE);
376         assertEquals("manual pick then type", EXPECTED_RESULT, mEditText.getText().toString());
377     }
378 
testManualPickThenSeparator()379     public void testManualPickThenSeparator() {
380         final String WORD1_TO_TYPE = "this";
381         final String WORD2_TO_TYPE = "!";
382         final String EXPECTED_RESULT = "this!";
383         type(WORD1_TO_TYPE);
384         pickSuggestionManually(WORD1_TO_TYPE);
385         type(WORD2_TO_TYPE);
386         assertEquals("manual pick then separator", EXPECTED_RESULT, mEditText.getText().toString());
387     }
388 
389     // This test matches testClusteringPunctuationForFrench.
390     // In some non-English languages, ! and ? are clustering punctuation signs.
testClusteringPunctuation()391     public void testClusteringPunctuation() {
392         final String WORD1_TO_TYPE = "test";
393         final String WORD2_TO_TYPE = "!!?!:!";
394         final String EXPECTED_RESULT = "test!!?!:!";
395         type(WORD1_TO_TYPE);
396         pickSuggestionManually(WORD1_TO_TYPE);
397         type(WORD2_TO_TYPE);
398         assertEquals("clustering punctuation", EXPECTED_RESULT, mEditText.getText().toString());
399     }
400 
testManualPickThenStripperThenPick()401     public void testManualPickThenStripperThenPick() {
402         final String WORD_TO_TYPE = "this";
403         final String STRIPPER = "\n";
404         final String EXPECTED_RESULT = "this\nthis";
405         type(WORD_TO_TYPE);
406         pickSuggestionManually(WORD_TO_TYPE);
407         type(STRIPPER);
408         type(WORD_TO_TYPE);
409         pickSuggestionManually(WORD_TO_TYPE);
410         assertEquals("manual pick then \\n then manual pick", EXPECTED_RESULT,
411                 mEditText.getText().toString());
412     }
413 
testManualPickThenSpaceThenType()414     public void testManualPickThenSpaceThenType() {
415         final String WORD1_TO_TYPE = "this";
416         final String WORD2_TO_TYPE = " is";
417         final String EXPECTED_RESULT = "this is";
418         type(WORD1_TO_TYPE);
419         pickSuggestionManually(WORD1_TO_TYPE);
420         type(WORD2_TO_TYPE);
421         assertEquals("manual pick then space then type", EXPECTED_RESULT,
422                 mEditText.getText().toString());
423     }
424 
testManualPickThenManualPick()425     public void testManualPickThenManualPick() {
426         final String WORD1_TO_TYPE = "this";
427         final String WORD2_TO_PICK = "is";
428         final String EXPECTED_RESULT = "this is";
429         type(WORD1_TO_TYPE);
430         pickSuggestionManually(WORD1_TO_TYPE);
431         // Here we fake picking a word through bigram prediction.
432         pickSuggestionManually(WORD2_TO_PICK);
433         assertEquals("manual pick then manual pick", EXPECTED_RESULT,
434                 mEditText.getText().toString());
435     }
436 
testDeleteWholeComposingWord()437     public void testDeleteWholeComposingWord() {
438         final String WORD_TO_TYPE = "this";
439         type(WORD_TO_TYPE);
440         for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
441             type(Constants.CODE_DELETE);
442         }
443         assertEquals("delete whole composing word", "", mEditText.getText().toString());
444     }
445 
testResumeSuggestionOnBackspace()446     public void testResumeSuggestionOnBackspace() {
447         final String STRING_TO_TYPE = "and this ";
448         final int typedLength = STRING_TO_TYPE.length();
449         type(STRING_TO_TYPE);
450         assertEquals("resume suggestion on backspace", -1,
451                 BaseInputConnection.getComposingSpanStart(mEditText.getText()));
452         assertEquals("resume suggestion on backspace", -1,
453                 BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
454         sendUpdateForCursorMoveTo(typedLength);
455         type(Constants.CODE_DELETE);
456         assertEquals("resume suggestion on backspace", 4,
457                 BaseInputConnection.getComposingSpanStart(mEditText.getText()));
458         assertEquals("resume suggestion on backspace", 8,
459                 BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
460     }
461 
helperTestComposing(final String wordToType, final boolean shouldBeComposing)462     private void helperTestComposing(final String wordToType, final boolean shouldBeComposing) {
463         mEditText.setText("");
464         type(wordToType);
465         assertEquals("start composing inside text", shouldBeComposing ? 0 : -1,
466                 BaseInputConnection.getComposingSpanStart(mEditText.getText()));
467         assertEquals("start composing inside text", shouldBeComposing ? wordToType.length() : -1,
468                 BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
469     }
470 
testStartComposing()471     public void testStartComposing() {
472         // Should start composing on a letter
473         helperTestComposing("a", true);
474         type("  "); // To reset the composing state
475         // Should not start composing on quote
476         helperTestComposing("'", false);
477         type("  ");
478         helperTestComposing("'-", false);
479         type("  ");
480         // Should not start composing on dash
481         helperTestComposing("-", false);
482         type("  ");
483         helperTestComposing("-'", false);
484         type("  ");
485         helperTestComposing("a-", true);
486         type("  ");
487         helperTestComposing("a'", true);
488     }
489 
490     // TODO: Add some tests for non-BMP characters
491 
testAutoCorrectByUserHistory()492     public void testAutoCorrectByUserHistory() {
493         type("qpmz");
494         type(Constants.CODE_SPACE);
495 
496         int startIndex = mEditText.getText().length();
497         type("qpmx");
498         type(Constants.CODE_SPACE);
499         int endIndex = mEditText.getText().length();
500         assertEquals("auto-corrected by user history",
501                 "qpmz ", mEditText.getText().subSequence(startIndex, endIndex).toString());
502     }
503 
testPredictionsAfterSpace()504     public void testPredictionsAfterSpace() {
505         final String WORD_TO_TYPE = "Barack ";
506         type(WORD_TO_TYPE);
507         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
508         runMessages();
509         // Test the first prediction is displayed
510         final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
511         assertEquals("predictions after space", "Obama",
512                 suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
513     }
514 
testPredictionsWithDoubleSpaceToPeriod()515     public void testPredictionsWithDoubleSpaceToPeriod() {
516         mLatinIME.clearPersonalizedDictionariesForTest();
517         final String WORD_TO_TYPE = "Barack  ";
518         type(WORD_TO_TYPE);
519         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
520         runMessages();
521 
522         type(Constants.CODE_DELETE);
523         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
524         runMessages();
525 
526         SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
527         suggestedWords = mLatinIME.getSuggestedWordsForTest();
528         assertEquals("predictions after cancel double-space-to-period", "Obama",
529                 mLatinIME.getSuggestedWordsForTest().getWord(0));
530     }
531 
testPredictionsAfterManualPick()532     public void testPredictionsAfterManualPick() {
533         final String WORD_TO_TYPE = "Barack";
534         type(WORD_TO_TYPE);
535         // Choose the auto-correction. For "Barack", the auto-correction should be "Barack".
536         pickSuggestionManually(WORD_TO_TYPE);
537         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
538         runMessages();
539         // Test the first prediction is displayed
540         final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
541         assertEquals("predictions after manual pick", "Obama",
542                 suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
543     }
544 
testPredictionsAfterPeriod()545     public void testPredictionsAfterPeriod() {
546         mLatinIME.clearPersonalizedDictionariesForTest();
547         final String WORD_TO_TYPE = "Barack. ";
548         type(WORD_TO_TYPE);
549         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
550         runMessages();
551 
552         SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
553         assertFalse(mLatinIME.getSuggestedWordsForTest().isEmpty());
554     }
555 
testPredictionsAfterRecorrection()556     public void testPredictionsAfterRecorrection() {
557         final String PREFIX = "A ";
558         final String WORD_TO_TYPE = "Barack";
559         final String FIRST_NON_TYPED_SUGGESTION = "Barrack";
560         final int endOfPrefix = PREFIX.length();
561         final int endOfWord = endOfPrefix + WORD_TO_TYPE.length();
562         final int endOfSuggestion = endOfPrefix + FIRST_NON_TYPED_SUGGESTION.length();
563         final int indexForManualCursor = endOfPrefix + 3; // +3 because it's after "Bar" in "Barack"
564         type(PREFIX);
565         sendUpdateForCursorMoveTo(endOfPrefix);
566         type(WORD_TO_TYPE);
567         pickSuggestionManually(FIRST_NON_TYPED_SUGGESTION);
568         sendUpdateForCursorMoveTo(endOfSuggestion);
569         runMessages();
570         type(" ");
571         sendUpdateForCursorMoveBy(1);
572         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
573         runMessages();
574         // Simulate a manual cursor move
575         mInputConnection.setSelection(indexForManualCursor, indexForManualCursor);
576         sendUpdateForCursorMoveTo(indexForManualCursor);
577         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
578         runMessages();
579         pickSuggestionManually(WORD_TO_TYPE);
580         sendUpdateForCursorMoveTo(endOfWord);
581         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
582         runMessages();
583         // Test the first prediction is displayed
584         final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
585         assertEquals("predictions after recorrection", "Obama",
586                 suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
587     }
588 
testComposingMultipleBackspace()589     public void testComposingMultipleBackspace() {
590         final String WORD_TO_TYPE = "radklro";
591         final int TIMES_TO_TYPE = 3;
592         final int TIMES_TO_BACKSPACE = 8;
593         type(WORD_TO_TYPE);
594         type(Constants.CODE_DELETE);
595         type(Constants.CODE_DELETE);
596         type(Constants.CODE_DELETE);
597         type(WORD_TO_TYPE);
598         type(Constants.CODE_DELETE);
599         type(Constants.CODE_DELETE);
600         type(WORD_TO_TYPE);
601         type(Constants.CODE_DELETE);
602         type(Constants.CODE_DELETE);
603         type(Constants.CODE_DELETE);
604         assertEquals("composing with multiple backspace",
605                 WORD_TO_TYPE.length() * TIMES_TO_TYPE - TIMES_TO_BACKSPACE,
606                 mEditText.getText().length());
607     }
608 
testManySingleQuotes()609     public void testManySingleQuotes() {
610         final String WORD_TO_AUTOCORRECT = "i";
611         final String WORD_AUTOCORRECTED = "I";
612         final String QUOTES = "''''''''''''''''''''";
613         final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
614         final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
615         type(WORD_TO_TYPE);
616         assertEquals("auto-correct with many trailing single quotes", EXPECTED_RESULT,
617                 mEditText.getText().toString());
618     }
619 
testManySingleQuotesOneByOne()620     public void testManySingleQuotesOneByOne() {
621         final String WORD_TO_AUTOCORRECT = "i";
622         final String WORD_AUTOCORRECTED = "I";
623         final String QUOTES = "''''''''''''''''''''";
624         final String WORD_TO_TYPE = WORD_TO_AUTOCORRECT + QUOTES + " ";
625         final String EXPECTED_RESULT = WORD_AUTOCORRECTED + QUOTES + " ";
626 
627         for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
628             type(WORD_TO_TYPE.substring(i, i+1));
629             sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
630             runMessages();
631         }
632         assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT,
633                 mEditText.getText().toString());
634     }
635 
testTypingSingleQuotesOneByOne()636     public void testTypingSingleQuotesOneByOne() {
637         final String WORD_TO_TYPE = "it's ";
638         final String EXPECTED_RESULT = WORD_TO_TYPE;
639         for (int i = 0; i < WORD_TO_TYPE.length(); ++i) {
640             type(WORD_TO_TYPE.substring(i, i+1));
641             sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS);
642             runMessages();
643         }
644         assertEquals("type words letter by letter", EXPECTED_RESULT,
645                 mEditText.getText().toString());
646     }
647 
testBasicGesture()648     public void testBasicGesture() {
649         gesture("this");
650         assertEquals("this", mEditText.getText().toString());
651     }
652 
testGestureGesture()653     public void testGestureGesture() {
654         gesture("got");
655         gesture("milk");
656         assertEquals("got milk", mEditText.getText().toString());
657     }
658 
testGestureBackspaceGestureAgain()659     public void testGestureBackspaceGestureAgain() {
660         gesture("this");
661         type(Constants.CODE_DELETE);
662         assertEquals("gesture then backspace", "", mEditText.getText().toString());
663         gesture("this");
664         if (DecoderSpecificConstants.SHOULD_REMOVE_PREVIOUSLY_REJECTED_SUGGESTION) {
665             assertNotEqual("this", mEditText.getText().toString());
666         } else {
667             assertEquals("this", mEditText.getText().toString());
668         }
669     }
670 
typeOrGestureWordAndPutCursorInside(final boolean gesture, final String word, final int startPos)671     private void typeOrGestureWordAndPutCursorInside(final boolean gesture, final String word,
672             final int startPos) {
673         final int END_OF_WORD = startPos + word.length();
674         final int NEW_CURSOR_POSITION = startPos + word.length() / 2;
675         if (gesture) {
676             gesture(word);
677         } else {
678             type(word);
679         }
680         sendUpdateForCursorMoveTo(END_OF_WORD);
681         runMessages();
682         sendUpdateForCursorMoveTo(NEW_CURSOR_POSITION);
683         sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS);
684         runMessages();
685     }
686 
typeWordAndPutCursorInside(final String word, final int startPos)687     private void typeWordAndPutCursorInside(final String word, final int startPos) {
688         typeOrGestureWordAndPutCursorInside(false /* gesture */, word, startPos);
689     }
690 
gestureWordAndPutCursorInside(final String word, final int startPos)691     private void gestureWordAndPutCursorInside(final String word, final int startPos) {
692         typeOrGestureWordAndPutCursorInside(true /* gesture */, word, startPos);
693     }
694 
ensureComposingSpanPos(final String message, final int from, final int to)695     private void ensureComposingSpanPos(final String message, final int from, final int to) {
696         assertEquals(message, from, BaseInputConnection.getComposingSpanStart(mEditText.getText()));
697         assertEquals(message, to, BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
698     }
699 
testTypeWithinComposing()700     public void testTypeWithinComposing() {
701         final String WORD_TO_TYPE = "something";
702         final String EXPECTED_RESULT = "some thing";
703         typeWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */);
704         type(" ");
705         ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1);
706         assertEquals("space in the middle of a composing word", EXPECTED_RESULT,
707                 mEditText.getText().toString());
708         int cursorPos = sendUpdateForCursorMoveToEndOfLine();
709         runMessages();
710         type(" ");
711         assertEquals("mbo", "some thing ", mEditText.getText().toString());
712         typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */);
713         type(Constants.CODE_DELETE);
714         ensureComposingSpanPos("delete while in the middle of a word cancels composition", -1, -1);
715     }
716 
testTypeWithinGestureComposing()717     public void testTypeWithinGestureComposing() {
718         final String WORD_TO_TYPE = "something";
719         final String EXPECTED_RESULT = "some thing";
720         gestureWordAndPutCursorInside(WORD_TO_TYPE, 0 /* startPos */);
721         type(" ");
722         ensureComposingSpanPos("space while in the middle of a word cancels composition", -1, -1);
723         assertEquals("space in the middle of a composing word", EXPECTED_RESULT,
724                 mEditText.getText().toString());
725         int cursorPos = sendUpdateForCursorMoveToEndOfLine();
726         runMessages();
727         type(" ");
728         typeWordAndPutCursorInside(WORD_TO_TYPE, cursorPos + 1 /* startPos */);
729         type(Constants.CODE_DELETE);
730         sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS);
731         ensureComposingSpanPos("delete while in the middle of a word cancels composition", -1, -1);
732     }
733 
testManualPickThenSeparatorForFrench()734     public void testManualPickThenSeparatorForFrench() {
735         final String WORD1_TO_TYPE = "test";
736         final String WORD2_TO_TYPE = "!";
737         final String EXPECTED_RESULT = "test !";
738         changeLanguage("fr");
739         type(WORD1_TO_TYPE);
740         pickSuggestionManually(WORD1_TO_TYPE);
741         type(WORD2_TO_TYPE);
742         assertEquals("manual pick then separator for French", EXPECTED_RESULT,
743                 mEditText.getText().toString());
744     }
745 
testClusteringPunctuationForFrench()746     public void testClusteringPunctuationForFrench() {
747         final String WORD1_TO_TYPE = "test";
748         final String WORD2_TO_TYPE = "!!?!:!";
749         // In English, the expected result would be "test!!?!:!"
750         final String EXPECTED_RESULT = "test !!?! : !";
751         changeLanguage("fr");
752         type(WORD1_TO_TYPE);
753         pickSuggestionManually(WORD1_TO_TYPE);
754         type(WORD2_TO_TYPE);
755         assertEquals("clustering punctuation for French", EXPECTED_RESULT,
756                 mEditText.getText().toString());
757     }
758 
testWordThenSpaceThenPunctuationFromStripTwice()759     public void testWordThenSpaceThenPunctuationFromStripTwice() {
760         setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, false, true);
761 
762         final String WORD_TO_TYPE = "test ";
763         final String PUNCTUATION_FROM_STRIP = "!";
764         final String EXPECTED_RESULT = "test!! ";
765         type(WORD_TO_TYPE);
766         sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS);
767         runMessages();
768         assertTrue("type word then type space should display punctuation strip",
769                 mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions());
770         pickSuggestionManually(PUNCTUATION_FROM_STRIP);
771         pickSuggestionManually(PUNCTUATION_FROM_STRIP);
772         assertEquals(EXPECTED_RESULT, mEditText.getText().toString());
773     }
774 
testWordThenSpaceDisplaysPredictions()775     public void testWordThenSpaceDisplaysPredictions() {
776         final String WORD_TO_TYPE = "Barack ";
777         final String EXPECTED_RESULT = "Obama";
778         type(WORD_TO_TYPE);
779         sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS);
780         runMessages();
781         final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest();
782         assertEquals("type word then type space yields predictions for French",
783                 EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
784     }
785 }
786