• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 package com.android.browser.autocomplete;
17 
18 import com.android.browser.autocomplete.SuggestedTextController.TextOwner;
19 
20 import android.graphics.Color;
21 import android.os.Parcelable;
22 import android.test.AndroidTestCase;
23 import android.test.suitebuilder.annotation.SmallTest;
24 import android.text.Editable;
25 import android.text.Selection;
26 import android.text.Spannable;
27 import android.text.SpannableStringBuilder;
28 import android.text.TextWatcher;
29 import android.view.AbsSavedState;
30 
31 /**
32  * Test cases for {@link SuggestedTextController}.
33  */
34 @SmallTest
35 public class SuggestedTextControllerTest extends AndroidTestCase {
36 
37     // these two must have a common prefix (but not be identical):
38     private static final String RUBY_MURRAY = "ruby murray";
39     private static final String RUBY_TUESDAY = "ruby tuesday";
40     private static final String EXTRA_USER_TEXT = " curry";
41     // no common prefix with the top two above:
42     private static final String TOD_SLOAN = "tod sloan";
43 
44     private SuggestedTextController mController;
45     private SpannableStringBuilder mString;
46 
47     private SuggestedTextController m2ndController;
48     private SpannableStringBuilder m2ndString;
49 
50     @Override
setUp()51     public void setUp() throws Exception {
52         super.setUp();
53         mString = new SpannableStringBuilder();
54         Selection.setSelection(mString, 0); // position cursor
55         mController = new SuggestedTextController(new BufferTextOwner(mString), Color.GRAY);
56         checkInvariant();
57     }
58 
create2ndController()59     private void create2ndController() {
60         m2ndString = new SpannableStringBuilder();
61         Selection.setSelection(m2ndString, 0); // position cursor
62         m2ndController = new SuggestedTextController(new BufferTextOwner(m2ndString), Color.GRAY);
63         check2ndInvariant();
64     }
65 
cursorPos(Spannable string)66     private int cursorPos(Spannable string) {
67         int selStart = Selection.getSelectionStart(string);
68         int selEnd = Selection.getSelectionEnd(string);
69         assertEquals("Selection has non-zero length", selStart, selEnd);
70         return selEnd;
71     }
72 
cursorPos()73     private int cursorPos() {
74         return cursorPos(mString);
75     }
76 
insertAtCursor(String text)77     private void insertAtCursor(String text) {
78         mString.insert(cursorPos(), text);
79         checkInvariant();
80     }
81 
insertAtCursor(char ch)82     private void insertAtCursor(char ch) {
83         insertAtCursor(Character.toString(ch));
84     }
85 
insertAt2ndCursor(String text)86     private void insertAt2ndCursor(String text) {
87         m2ndString.insert(cursorPos(m2ndString), text);
88         check2ndInvariant();
89     }
90 
insertAt2ndCursor(char ch)91     private void insertAt2ndCursor(char ch) {
92         insertAt2ndCursor(Character.toString(ch));
93     }
94 
deleteBeforeCursor(int count)95     private void deleteBeforeCursor(int count) {
96         int pos = cursorPos();
97         count = Math.min(pos, count);
98         mString.delete(pos - count, pos);
99         checkInvariant();
100     }
101 
replaceSelection(String withThis)102     private void replaceSelection(String withThis) {
103         mString.replace(Selection.getSelectionStart(mString),
104                 Selection.getSelectionEnd(mString), withThis);
105         checkInvariant();
106     }
107 
setSuggested(String suggested)108     private void setSuggested(String suggested) {
109         mController.setSuggestedText(suggested);
110         checkInvariant();
111     }
112 
set2ndSuggested(String suggested)113     private void set2ndSuggested(String suggested) {
114         m2ndController.setSuggestedText(suggested);
115         check2ndInvariant();
116     }
117 
checkInvariant()118     private void checkInvariant() {
119         mController.checkInvariant(mString);
120     }
121 
check2ndInvariant()122     private void check2ndInvariant() {
123         m2ndController.checkInvariant(m2ndString);
124     }
125 
assertUserEntered(String expected, SuggestedTextController controller)126     private void assertUserEntered(String expected, SuggestedTextController controller) {
127         assertEquals("User entered text not as expected", expected, controller.getUserText());
128     }
129 
assertUserEntered(String expected)130     private void assertUserEntered(String expected) {
131         assertUserEntered(expected, mController);
132     }
133 
assertBuffer(String expected, Editable string)134     private void assertBuffer(String expected, Editable string) {
135         assertEquals("Buffer contents not as expected", expected, string.toString());
136     }
137 
assertBuffer(String expected)138     private void assertBuffer(String expected) {
139         assertBuffer(expected, mString);
140     }
141 
assertCursorPos(int where, Spannable string)142     private void assertCursorPos(int where, Spannable string) {
143         assertEquals("Cursor not at expected position", where, cursorPos(string));
144     }
145 
assertCursorPos(int where)146     private void assertCursorPos(int where) {
147         assertCursorPos(where, mString);
148     }
149 
commonPrefix(String a, String b)150     private static final String commonPrefix(String a, String b) {
151         int pos = 0;
152         while (a.charAt(pos) == b.charAt(pos)) {
153             pos++;
154         }
155         assertTrue("No common prefix between '" + a + "' and '" + b + "'", pos > 0);
156         return a.substring(0, pos);
157     }
158 
testTypeNoSuggested()159     public void testTypeNoSuggested() {
160         for (int i = 0; i < RUBY_MURRAY.length(); ++i) {
161             assertCursorPos(i);
162             assertUserEntered(RUBY_MURRAY.substring(0, i));
163             assertBuffer(RUBY_MURRAY.substring(0, i));
164             insertAtCursor(RUBY_MURRAY.substring(i, i + 1));
165         }
166     }
167 
testTypeSuggested()168     public void testTypeSuggested() {
169         setSuggested(RUBY_MURRAY);
170         assertCursorPos(0);
171         assertBuffer(RUBY_MURRAY);
172         for (int i = 0; i < RUBY_MURRAY.length(); ++i) {
173             assertCursorPos(i);
174             assertUserEntered(RUBY_MURRAY.substring(0, i));
175             assertBuffer(RUBY_MURRAY);
176             insertAtCursor(RUBY_MURRAY.charAt(i));
177         }
178     }
179 
testSetSuggestedAfterTextEntry()180     public void testSetSuggestedAfterTextEntry() {
181         final int count = RUBY_MURRAY.length() / 2;
182         for (int i = 0; i < count; ++i) {
183             assertCursorPos(i);
184             assertUserEntered(RUBY_MURRAY.substring(0, i));
185             insertAtCursor(RUBY_MURRAY.substring(i, i + 1));
186         }
187         setSuggested(RUBY_MURRAY);
188         assertUserEntered(RUBY_MURRAY.substring(0, count));
189         assertBuffer(RUBY_MURRAY);
190     }
191 
testTypeSuggestedUpperCase()192     public void testTypeSuggestedUpperCase() {
193         setSuggested(RUBY_MURRAY);
194         assertCursorPos(0);
195         for (int i = 0; i < RUBY_MURRAY.length(); ++i) {
196             assertCursorPos(i);
197             assertUserEntered(RUBY_MURRAY.substring(0, i).toUpperCase());
198             assertTrue("Buffer doesn't contain suggested text",
199                     RUBY_MURRAY.equalsIgnoreCase(mString.toString()));
200             insertAtCursor(Character.toUpperCase(RUBY_MURRAY.charAt(i)));
201         }
202     }
203 
testChangeSuggestedText()204     public void testChangeSuggestedText() {
205         String pref = commonPrefix(RUBY_MURRAY, RUBY_TUESDAY);
206         setSuggested(RUBY_MURRAY);
207         insertAtCursor(pref);
208         assertBuffer(RUBY_MURRAY);
209         assertUserEntered(pref);
210         setSuggested(RUBY_TUESDAY);
211         assertBuffer(RUBY_TUESDAY);
212         assertUserEntered(pref);
213     }
214 
testTypeNonSuggested()215     public void testTypeNonSuggested() {
216         setSuggested(RUBY_MURRAY);
217         insertAtCursor(RUBY_MURRAY.charAt(0));
218         assertBuffer(RUBY_MURRAY);
219         insertAtCursor('x');
220         assertBuffer("rx");
221     }
222 
testTypeNonSuggestedThenNewSuggestion()223     public void testTypeNonSuggestedThenNewSuggestion() {
224         final String pref = commonPrefix(RUBY_MURRAY, RUBY_TUESDAY);
225         setSuggested(RUBY_MURRAY);
226         assertCursorPos(0);
227         insertAtCursor(pref);
228         assertCursorPos(pref.length());
229         assertUserEntered(pref);
230         insertAtCursor(RUBY_TUESDAY.charAt(pref.length()));
231         assertBuffer(RUBY_TUESDAY.substring(0, pref.length() + 1));
232         setSuggested(RUBY_TUESDAY);
233         assertBuffer(RUBY_TUESDAY);
234     }
235 
testChangeSuggestedToNonUserEntered()236     public void testChangeSuggestedToNonUserEntered() {
237         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
238         setSuggested(RUBY_MURRAY);
239         insertAtCursor(half);
240         setSuggested(TOD_SLOAN);
241         assertUserEntered(half);
242         assertBuffer(half);
243     }
244 
testChangeSuggestedToUserEntered()245     public void testChangeSuggestedToUserEntered() {
246         setSuggested(RUBY_MURRAY);
247         insertAtCursor(TOD_SLOAN);
248         setSuggested(TOD_SLOAN);
249         assertUserEntered(TOD_SLOAN);
250         assertBuffer(TOD_SLOAN);
251     }
252 
testChangeSuggestedToEmpty()253     public void testChangeSuggestedToEmpty() {
254         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
255         setSuggested(RUBY_MURRAY);
256         insertAtCursor(half);
257         setSuggested(null);
258         assertUserEntered(half);
259         assertBuffer(half);
260     }
261 
testChangeSuggestedToEmptyFromUserEntered()262     public void testChangeSuggestedToEmptyFromUserEntered() {
263         setSuggested(RUBY_MURRAY);
264         insertAtCursor(RUBY_MURRAY);
265         setSuggested(null);
266         assertUserEntered(RUBY_MURRAY);
267         assertBuffer(RUBY_MURRAY);
268     }
269 
typeNonSuggestedThenDelete()270     public void typeNonSuggestedThenDelete() {
271         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
272         assertCursorPos(0);
273         insertAtCursor(half);
274         assertCursorPos(half.length());
275         setSuggested(RUBY_MURRAY);
276         insertAtCursor('x');
277         assertBuffer(half + "x");
278         deleteBeforeCursor(1);
279         assertUserEntered(half);
280         assertBuffer(RUBY_MURRAY);
281     }
282 
testDeleteMultipleFromSuggested()283     public void testDeleteMultipleFromSuggested() {
284         final String twoThirds = RUBY_MURRAY.substring(0, (RUBY_MURRAY.length() * 2) / 3);
285         setSuggested(RUBY_MURRAY);
286         insertAtCursor(twoThirds);
287         assertCursorPos(twoThirds.length());
288         // select some of the text just entered:
289         Selection.setSelection(mString, RUBY_MURRAY.length() / 3, twoThirds.length());
290         // and delete it:
291         replaceSelection("");
292         assertCursorPos(RUBY_MURRAY.length() / 3);
293         assertUserEntered(RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 3));
294         assertBuffer(RUBY_MURRAY);
295     }
296 
testDeleteMultipleToFormSuggested()297     public void testDeleteMultipleToFormSuggested() {
298         final String pref = commonPrefix(RUBY_TUESDAY, RUBY_MURRAY);
299         final int extra = (RUBY_TUESDAY.length() - pref.length()) / 2;
300         setSuggested(RUBY_MURRAY);
301         insertAtCursor(RUBY_TUESDAY.substring(0, pref.length() + extra));
302         assertCursorPos(pref.length() + extra);
303         // select and delete extra characters, leaving just prefix
304         Selection.setSelection(mString, pref.length(), pref.length() + extra);
305         replaceSelection("");
306         assertCursorPos(pref.length());
307         assertBuffer(RUBY_MURRAY);
308         assertUserEntered(pref);
309     }
310 
testBackspaceWithinUserTextFromSuggested()311     public void testBackspaceWithinUserTextFromSuggested() {
312         StringBuffer half = new StringBuffer(RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2));
313         insertAtCursor(half.toString());
314         int backSpaceFrom = half.length() / 2;
315         Selection.setSelection(mString, backSpaceFrom);
316         deleteBeforeCursor(1);
317         assertCursorPos(backSpaceFrom - 1);
318         half.delete(backSpaceFrom - 1, backSpaceFrom);
319         assertUserEntered(half.toString());
320         assertBuffer(half.toString());
321     }
322 
testInsertWithinUserTextToFormSuggested()323     public void testInsertWithinUserTextToFormSuggested() {
324         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
325         StringBuffer initial = new StringBuffer(half);
326         int pos = initial.length() / 2;
327         char toInsert = initial.charAt(pos);
328         initial.delete(pos, pos + 1);
329         insertAtCursor(initial.toString());
330         setSuggested(RUBY_MURRAY);
331         assertUserEntered(initial.toString());
332         assertBuffer(initial.toString());
333         Selection.setSelection(mString, pos);
334         insertAtCursor(toInsert);
335         assertCursorPos(pos + 1);
336         assertUserEntered(half);
337         assertBuffer(RUBY_MURRAY);
338     }
339 
testEnterTextBeyondSuggested()340     public void testEnterTextBeyondSuggested() {
341         setSuggested(RUBY_MURRAY);
342         int i = RUBY_MURRAY.length() / 2;
343         insertAtCursor(RUBY_MURRAY.substring(0, i));
344         String query = RUBY_MURRAY + EXTRA_USER_TEXT;
345         for (; i < query.length(); ++i) {
346             assertUserEntered(query.substring(0, i));
347             if (i <= RUBY_MURRAY.length()) {
348                 assertBuffer(RUBY_MURRAY);
349             }
350             insertAtCursor(query.charAt(i));
351         }
352         assertUserEntered(query);
353     }
354 
testDeleteFromLongerThanSuggested()355     public void testDeleteFromLongerThanSuggested() {
356         setSuggested(RUBY_MURRAY);
357         final String entered = RUBY_MURRAY + EXTRA_USER_TEXT;
358         insertAtCursor(entered);
359         for (int i = entered.length(); i > (RUBY_MURRAY.length() / 2); --i) {
360             assertCursorPos(i);
361             assertUserEntered(entered.substring(0, i));
362             if (i <= RUBY_MURRAY.length()) {
363                 assertBuffer(RUBY_MURRAY);
364             }
365             deleteBeforeCursor(1);
366         }
367     }
368 
testReplaceWithShorterToFormSuggested()369     public void testReplaceWithShorterToFormSuggested() {
370         final String pref = commonPrefix(RUBY_TUESDAY, RUBY_MURRAY);
371         final int extra = (RUBY_TUESDAY.length() - pref.length()) / 2;
372         setSuggested(RUBY_MURRAY);
373         insertAtCursor(RUBY_TUESDAY.substring(0, pref.length() + extra));
374         assertCursorPos(pref.length() + extra);
375         // select and replace extra characters, to match suggested
376         Selection.setSelection(mString, pref.length(), pref.length() + extra);
377         replaceSelection(RUBY_MURRAY.substring(pref.length(), pref.length() + extra - 1));
378         assertBuffer(RUBY_MURRAY);
379         assertUserEntered(RUBY_MURRAY.substring(0, pref.length() + extra - 1));
380     }
381 
testReplaceWithSameLengthToFormSuggested()382     public void testReplaceWithSameLengthToFormSuggested() {
383         final String pref = commonPrefix(RUBY_TUESDAY, RUBY_MURRAY);
384         final int extra = (RUBY_TUESDAY.length() - pref.length()) / 2;
385         setSuggested(RUBY_MURRAY);
386         insertAtCursor(RUBY_TUESDAY.substring(0, pref.length() + extra));
387         assertCursorPos(pref.length() + extra);
388         // select and replace extra characters, to match suggested
389         Selection.setSelection(mString, pref.length(), pref.length() + extra);
390         replaceSelection(RUBY_MURRAY.substring(pref.length(), pref.length() + extra));
391         assertBuffer(RUBY_MURRAY);
392         assertUserEntered(RUBY_MURRAY.substring(0, pref.length() + extra));
393     }
394 
testReplaceWithLongerToFormSuggested()395     public void testReplaceWithLongerToFormSuggested() {
396         final String pref = commonPrefix(RUBY_TUESDAY, RUBY_MURRAY);
397         final int extra = (RUBY_TUESDAY.length() - pref.length()) / 2;
398         setSuggested(RUBY_MURRAY);
399         insertAtCursor(RUBY_TUESDAY.substring(0, pref.length() + extra));
400         assertCursorPos(pref.length() + extra);
401         // select and replace extra characters, to match suggested
402         Selection.setSelection(mString, pref.length(), pref.length() + extra);
403         replaceSelection(RUBY_MURRAY.substring(pref.length(), pref.length() + extra + 1));
404         assertBuffer(RUBY_MURRAY);
405         assertUserEntered(RUBY_MURRAY.substring(0, pref.length() + extra + 1));
406     }
407 
testMoveCursorIntoSuggested()408     public void testMoveCursorIntoSuggested() {
409         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
410         insertAtCursor(half);
411         setSuggested(RUBY_MURRAY);
412         assertCursorPos(half.length());
413         Selection.setSelection(mString, half.length() + 1);
414         checkInvariant();
415         assertUserEntered(RUBY_MURRAY);
416     }
417 
testMoveCursorWithinUserEntered()418     public void testMoveCursorWithinUserEntered() {
419         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
420         insertAtCursor(half);
421         setSuggested(RUBY_MURRAY);
422         assertCursorPos(half.length());
423         Selection.setSelection(mString, half.length() - 1);
424         checkInvariant();
425         assertUserEntered(half);
426     }
427 
testSelectWithinSuggested()428     public void testSelectWithinSuggested() {
429         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
430         insertAtCursor(half);
431         setSuggested(RUBY_MURRAY);
432         assertCursorPos(half.length());
433         Selection.setSelection(mString, half.length() + 1, half.length() + 2);
434         checkInvariant();
435         assertUserEntered(RUBY_MURRAY);
436     }
437 
testSelectStraddlingSuggested()438     public void testSelectStraddlingSuggested() {
439         final String half = RUBY_MURRAY.substring(0, RUBY_MURRAY.length() / 2);
440         insertAtCursor(half);
441         setSuggested(RUBY_MURRAY);
442         assertCursorPos(half.length());
443         Selection.setSelection(mString, half.length() - 1, half.length() + 1);
444         checkInvariant();
445         assertUserEntered(RUBY_MURRAY);
446     }
447 
testSaveAndRestoreNoText()448     public void testSaveAndRestoreNoText() {
449         create2ndController();
450         Parcelable state = mController.saveInstanceState(AbsSavedState.EMPTY_STATE);
451         m2ndController.restoreInstanceState(state);
452         check2ndInvariant();
453         assertBuffer("", m2ndString);
454     }
455 
testSaveAndRestoreWithSuggestedText()456     public void testSaveAndRestoreWithSuggestedText() {
457         create2ndController();
458         setSuggested(TOD_SLOAN);
459         Parcelable state = mController.saveInstanceState(AbsSavedState.EMPTY_STATE);
460         m2ndController.restoreInstanceState(state);
461         check2ndInvariant();
462         assertBuffer(TOD_SLOAN, m2ndString);
463         assertUserEntered("", m2ndController);
464     }
465 
testSaveAndRestoreWithUserEnteredAndSuggestedText()466     public void testSaveAndRestoreWithUserEnteredAndSuggestedText() {
467         final String half = TOD_SLOAN.substring(0, TOD_SLOAN.length() / 2);
468         create2ndController();
469         setSuggested(TOD_SLOAN);
470         insertAtCursor(half);
471         Parcelable state = mController.saveInstanceState(AbsSavedState.EMPTY_STATE);
472         m2ndController.restoreInstanceState(state);
473         check2ndInvariant();
474         assertBuffer(TOD_SLOAN, m2ndString);
475         assertUserEntered(half, m2ndController);
476         assertCursorPos(half.length(), m2ndString);
477     }
478 
testSaveAndRestoreWithNonSuggested()479     public void testSaveAndRestoreWithNonSuggested() {
480         final String half = TOD_SLOAN.substring(0, TOD_SLOAN.length() / 2);
481         create2ndController();
482         setSuggested(RUBY_MURRAY);
483         insertAtCursor(half);
484         Parcelable state = mController.saveInstanceState(AbsSavedState.EMPTY_STATE);
485         m2ndController.restoreInstanceState(state);
486         check2ndInvariant();
487         assertBuffer(half, m2ndString);
488         assertUserEntered(half, m2ndController);
489         assertCursorPos(half.length(), m2ndString);
490     }
491 
testSaveAndRestoreThenTypeSuggested()492     public void testSaveAndRestoreThenTypeSuggested() {
493         final String half = TOD_SLOAN.substring(0, TOD_SLOAN.length() / 2);
494         create2ndController();
495         set2ndSuggested(TOD_SLOAN);
496         insertAt2ndCursor(half);
497         insertAt2ndCursor('x');
498         Parcelable state = m2ndController.saveInstanceState(AbsSavedState.EMPTY_STATE);
499         mController.restoreInstanceState(state);
500         assertCursorPos(half.length() + 1);
501         // delete the x
502         deleteBeforeCursor(1);
503         assertCursorPos(half.length());
504         assertBuffer(TOD_SLOAN);
505         assertUserEntered(half);
506     }
507 
testSuspendAndResumeCursorProcessing()508     public void testSuspendAndResumeCursorProcessing() {
509         final String half = TOD_SLOAN.substring(0, TOD_SLOAN.length() / 2);
510         setSuggested(TOD_SLOAN);
511         insertAtCursor(half);
512         mController.suspendCursorMovementHandling();
513         Selection.setSelection(mString, TOD_SLOAN.length());
514         Selection.setSelection(mString, half.length());
515         mController.resumeCursorMovementHandlingAndApplyChanges();
516         assertCursorPos(half.length());
517         assertUserEntered(half);
518         assertBuffer(TOD_SLOAN);
519     }
520 
521     private static class BufferTextOwner implements TextOwner {
522 
523         private final Editable mBuffer;
524 
BufferTextOwner(Editable buffer)525         public BufferTextOwner(Editable buffer) {
526             mBuffer = buffer;
527         }
528 
addTextChangedListener(TextWatcher watcher)529         public void addTextChangedListener(TextWatcher watcher) {
530             mBuffer.setSpan(watcher , 0, mBuffer.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
531         }
532 
removeTextChangedListener(TextWatcher watcher)533         public void removeTextChangedListener(TextWatcher watcher) {
534             mBuffer.removeSpan(watcher);
535         }
536 
getText()537         public Editable getText() {
538             return mBuffer;
539         }
540 
setText(String text)541         public void setText(String text) {
542             mBuffer.replace(0, mBuffer.length(), text);
543         }
544 
545     }
546 
547 }
548