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