1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.latin;
18
19import static android.test.MoreAsserts.assertNotEqual;
20
21import android.test.suitebuilder.annotation.LargeTest;
22import android.text.TextUtils;
23import android.view.inputmethod.BaseInputConnection;
24
25import com.android.inputmethod.latin.common.Constants;
26import com.android.inputmethod.latin.define.DecoderSpecificConstants;
27import com.android.inputmethod.latin.settings.Settings;
28
29@LargeTest
30public class InputLogicTests extends InputTestsBase {
31
32    private boolean mNextWordPrediction;
33
34    @Override
35    public void setUp() throws Exception {
36        super.setUp();
37        mNextWordPrediction = getBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, true);
38    }
39
40    @Override
41    public void tearDown() throws Exception {
42        setBooleanPreference(Settings.PREF_BIGRAM_PREDICTIONS, mNextWordPrediction, true);
43        super.tearDown();
44    }
45
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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.
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
648    public void testBasicGesture() {
649        gesture("this");
650        assertEquals("this", mEditText.getText().toString());
651    }
652
653    public void testGestureGesture() {
654        gesture("got");
655        gesture("milk");
656        assertEquals("got milk", mEditText.getText().toString());
657    }
658
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
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
687    private void typeWordAndPutCursorInside(final String word, final int startPos) {
688        typeOrGestureWordAndPutCursorInside(false /* gesture */, word, startPos);
689    }
690
691    private void gestureWordAndPutCursorInside(final String word, final int startPos) {
692        typeOrGestureWordAndPutCursorInside(true /* gesture */, word, startPos);
693    }
694
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
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
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
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
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
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
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