1 /*
2  * Copyright (C) 2008 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 android.widget.cts;
18 
19 import static android.widget.ListPopupWindow.INPUT_METHOD_FROM_FOCUSABLE;
20 import static android.widget.ListPopupWindow.INPUT_METHOD_NEEDED;
21 import static android.widget.ListPopupWindow.INPUT_METHOD_NOT_NEEDED;
22 
23 import static com.android.compatibility.common.util.WidgetTestUtils.sameCharSequence;
24 
25 import static com.google.common.truth.Truth.assertThat;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertNotSame;
31 import static org.junit.Assert.assertNull;
32 import static org.junit.Assert.assertSame;
33 import static org.junit.Assert.assertTrue;
34 import static org.mockito.Mockito.any;
35 import static org.mockito.Mockito.anyInt;
36 import static org.mockito.Mockito.atLeastOnce;
37 import static org.mockito.Mockito.eq;
38 import static org.mockito.Mockito.mock;
39 import static org.mockito.Mockito.never;
40 import static org.mockito.Mockito.spy;
41 import static org.mockito.Mockito.times;
42 import static org.mockito.Mockito.verify;
43 import static org.mockito.Mockito.verifyNoMoreInteractions;
44 import static org.mockito.Mockito.verifyZeroInteractions;
45 
46 import android.Manifest;
47 import android.app.Activity;
48 import android.app.Instrumentation;
49 import android.app.UiModeManager;
50 import android.content.Context;
51 import android.content.res.Configuration;
52 import android.content.res.Resources;
53 import android.graphics.Color;
54 import android.graphics.Rect;
55 import android.graphics.drawable.Drawable;
56 import android.os.SystemClock;
57 import android.text.Editable;
58 import android.text.TextWatcher;
59 import android.util.AttributeSet;
60 import android.util.Xml;
61 import android.view.KeyCharacterMap;
62 import android.view.KeyEvent;
63 import android.view.View;
64 import android.view.ViewGroup;
65 import android.widget.AdapterView;
66 import android.widget.ArrayAdapter;
67 import android.widget.AutoCompleteTextView;
68 import android.widget.AutoCompleteTextView.Validator;
69 import android.widget.Filter;
70 import android.widget.Filterable;
71 import android.widget.cts.util.TestUtils;
72 
73 import androidx.test.InstrumentationRegistry;
74 import androidx.test.annotation.UiThreadTest;
75 import androidx.test.filters.MediumTest;
76 import androidx.test.rule.ActivityTestRule;
77 import androidx.test.runner.AndroidJUnit4;
78 
79 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
80 import com.android.compatibility.common.util.PollingCheck;
81 import com.android.compatibility.common.util.WidgetTestUtils;
82 import com.android.compatibility.common.util.WindowUtil;
83 
84 import com.google.common.collect.ImmutableList;
85 
86 import org.junit.Before;
87 import org.junit.Rule;
88 import org.junit.Test;
89 import org.junit.runner.RunWith;
90 import org.xmlpull.v1.XmlPullParser;
91 
92 import java.util.ArrayList;
93 import java.util.List;
94 
95 @MediumTest
96 @RunWith(AndroidJUnit4.class)
97 public class AutoCompleteTextViewTest {
98     private final static String[] WORDS =
99             new String[] { "testOne", "testTwo", "testThree", "testFour" };
100     private final static String STRING_TEST = "To be tested";
101     private final static String STRING_VALIDATED = "String Validated";
102     private final static String STRING_CHECK = "To be checked";
103 
104     private Activity mActivity;
105     private Instrumentation mInstrumentation;
106     private AutoCompleteTextView mAutoCompleteTextView;
107     private MockAutoCompleteTextView mMockAutoCompleteTextView;
108     private boolean mNumeric = false;
109     private ArrayAdapter<String> mAdapter;
110 
111     @Rule(order = 0)
112     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
113             androidx.test.platform.app.InstrumentationRegistry
114                     .getInstrumentation().getUiAutomation(),
115             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
116 
117     @Rule(order = 1)
118     public ActivityTestRule<AutoCompleteCtsActivity> mActivityRule =
119             new ActivityTestRule<>(AutoCompleteCtsActivity.class);
120 
121     private final Validator mValidator = new Validator() {
122         public CharSequence fixText(CharSequence invalidText) {
123             return STRING_VALIDATED;
124         }
125 
126         public boolean isValid(CharSequence text) {
127             return false;
128         }
129     };
130 
131     protected class MyTextWatcher implements TextWatcher {
132         private CharSequence mExpectedAfter;
133 
MyTextWatcher(CharSequence expectedAfter)134         public MyTextWatcher(CharSequence expectedAfter) {
135             mExpectedAfter = expectedAfter;
136         }
137 
138         @Override
onTextChanged(CharSequence s, int start, int before, int count)139         public void onTextChanged(CharSequence s, int start, int before, int count) {
140             assertEquals(mExpectedAfter.toString(), s.toString());
141             // This watcher is expected to be notified in the middle of completion
142             assertTrue(mAutoCompleteTextView.isPerformingCompletion());
143         }
144 
145         @Override
beforeTextChanged(CharSequence s, int start, int count, int after)146         public void beforeTextChanged(CharSequence s, int start, int count, int after) {
147         }
148 
149         @Override
afterTextChanged(Editable s)150         public void afterTextChanged(Editable s) {
151         }
152     }
153 
154     @Before
setup()155     public void setup() {
156         mActivity = mActivityRule.getActivity();
157         WindowUtil.waitForFocus(mActivity);
158         mInstrumentation = InstrumentationRegistry.getInstrumentation();
159         mAutoCompleteTextView = mActivity.findViewById(R.id.autocompletetv_edit);
160         mAutoCompleteTextView.setFocusableInTouchMode(true);
161         mMockAutoCompleteTextView = mActivity.findViewById(R.id.autocompletetv_custom);
162         mAdapter = new ArrayAdapter<>(mActivity,
163                 android.R.layout.simple_dropdown_item_1line, WORDS);
164         KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
165         if (keymap.getKeyboardType() == KeyCharacterMap.NUMERIC) {
166             mNumeric = true;
167         }
168     }
169 
isTvMode()170     boolean isTvMode() {
171         UiModeManager uiModeManager = (UiModeManager) mActivity.getSystemService(
172                 Context.UI_MODE_SERVICE);
173         return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION;
174     }
175 
176     @Test
testConstructor()177     public void testConstructor() {
178         XmlPullParser parser;
179 
180         new AutoCompleteTextView(mActivity);
181         new AutoCompleteTextView(mActivity, null);
182         new AutoCompleteTextView(mActivity, null, android.R.attr.autoCompleteTextViewStyle);
183         new AutoCompleteTextView(mActivity, null, 0,
184                 android.R.style.Widget_DeviceDefault_AutoCompleteTextView);
185         new AutoCompleteTextView(mActivity, null, 0,
186                 android.R.style.Widget_DeviceDefault_Light_AutoCompleteTextView);
187         new AutoCompleteTextView(mActivity, null, 0,
188                 android.R.style.Widget_Material_AutoCompleteTextView);
189         new AutoCompleteTextView(mActivity, null, 0,
190                 android.R.style.Widget_Material_Light_AutoCompleteTextView);
191 
192         final Resources.Theme popupTheme = mActivity.getResources().newTheme();
193         popupTheme.applyStyle(android.R.style.Theme_Material, true);
194         new AutoCompleteTextView(mActivity, null, 0,
195                 android.R.style.Widget_Material_Light_AutoCompleteTextView, popupTheme);
196 
197         // new the AutoCompleteTextView instance
198         parser = mActivity.getResources().getXml(R.layout.simple_dropdown_item_1line);
199         AttributeSet attributeSet = Xml.asAttributeSet(parser);
200         new AutoCompleteTextView(mActivity, attributeSet);
201 
202         // new the AutoCompleteTextView instance
203         parser = mActivity.getResources().getXml(R.layout.framelayout_layout);
204         attributeSet = Xml.asAttributeSet(parser);
205         new AutoCompleteTextView(mActivity, attributeSet, 0);
206 
207         // Test for negative style resource ID
208         new AutoCompleteTextView(mActivity, attributeSet, -1);
209         // Test null AttributeSet
210         new AutoCompleteTextView(mActivity, null, -1);
211     }
212 
213     @Test(expected=NullPointerException.class)
testConstructorWithNullContext()214     public void testConstructorWithNullContext() {
215         XmlPullParser parser = mActivity.getResources().getXml(R.layout.simple_dropdown_item_1line);
216         AttributeSet attributeSet = Xml.asAttributeSet(parser);
217         new AutoCompleteTextView(null, attributeSet, 0);
218     }
219 
220     @Test
testEnoughToFilter()221     public void testEnoughToFilter() throws Throwable {
222         mAutoCompleteTextView.setThreshold(3);
223         assertEquals(3, mAutoCompleteTextView.getThreshold());
224 
225         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
226                 () -> mAutoCompleteTextView.setText("TryToTest"));
227         assertTrue(mAutoCompleteTextView.enoughToFilter());
228 
229         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
230                 () -> mAutoCompleteTextView.setText("No"));
231         assertFalse(mAutoCompleteTextView.enoughToFilter());
232     }
233 
234     @UiThreadTest
235     @Test
testAccessAdapter()236     public void testAccessAdapter() {
237         mAutoCompleteTextView.setAdapter(null);
238         assertNull(mAutoCompleteTextView.getAdapter());
239 
240         mAutoCompleteTextView.setAdapter(mAdapter);
241         assertSame(mAdapter, mAutoCompleteTextView.getAdapter());
242 
243         // Re-set adapter to null
244         mAutoCompleteTextView.setAdapter(null);
245         assertNull(mAutoCompleteTextView.getAdapter());
246     }
247 
248     @UiThreadTest
249     @Test
testAccessFilter()250     public void testAccessFilter() {
251         MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
252 
253         // Set Threshold to 4 characters
254         autoCompleteTextView.setThreshold(4);
255 
256         autoCompleteTextView.setAdapter(null);
257         assertNull(autoCompleteTextView.getAdapter());
258         assertNull(autoCompleteTextView.getFilter());
259 
260         Filter filter = mAdapter.getFilter();
261         assertNotNull(filter);
262         autoCompleteTextView.setAdapter(mAdapter);
263         assertSame(mAdapter, autoCompleteTextView.getAdapter());
264         assertSame(filter, autoCompleteTextView.getFilter());
265 
266         // Re-set adapter to null
267         autoCompleteTextView.setAdapter(null);
268         assertNull(autoCompleteTextView.getAdapter());
269         assertNull(autoCompleteTextView.getFilter());
270     }
271 
272     @UiThreadTest
273     @Test
testAccessItemClickListener()274     public void testAccessItemClickListener() {
275         final AdapterView.OnItemClickListener mockItemClickListener =
276                 mock(AdapterView.OnItemClickListener.class);
277 
278         // To ensure null listener
279         mAutoCompleteTextView.setOnItemClickListener(null);
280         assertNull(mAutoCompleteTextView.getItemClickListener());
281         assertNull(mAutoCompleteTextView.getOnItemClickListener());
282 
283         mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
284         assertSame(mockItemClickListener, mAutoCompleteTextView.getItemClickListener());
285         assertSame(mockItemClickListener, mAutoCompleteTextView.getOnItemClickListener());
286         verifyZeroInteractions(mockItemClickListener);
287 
288         // re-clear listener by setOnItemClickListener
289         mAutoCompleteTextView.setOnItemClickListener(null);
290         assertNull(mAutoCompleteTextView.getItemClickListener());
291         assertNull(mAutoCompleteTextView.getOnItemClickListener());
292         verifyZeroInteractions(mockItemClickListener);
293     }
294 
295     @UiThreadTest
296     @Test
testAccessItemSelectedListener()297     public void testAccessItemSelectedListener() {
298         final AdapterView.OnItemSelectedListener mockItemSelectedListener =
299                 mock(AdapterView.OnItemSelectedListener.class);
300 
301         // To ensure null listener
302         mAutoCompleteTextView.setOnItemSelectedListener(null);
303         assertNull(mAutoCompleteTextView.getItemSelectedListener());
304         assertNull(mAutoCompleteTextView.getOnItemSelectedListener());
305 
306         mAutoCompleteTextView.setOnItemSelectedListener(mockItemSelectedListener);
307         assertSame(mockItemSelectedListener, mAutoCompleteTextView.getItemSelectedListener());
308         assertSame(mockItemSelectedListener, mAutoCompleteTextView.getOnItemSelectedListener());
309         verifyZeroInteractions(mockItemSelectedListener);
310 
311         //re-clear listener by setOnItemClickListener
312         mAutoCompleteTextView.setOnItemSelectedListener(null);
313         assertNull(mAutoCompleteTextView.getItemSelectedListener());
314         assertNull(mAutoCompleteTextView.getOnItemSelectedListener());
315         verifyZeroInteractions(mockItemSelectedListener);
316     }
317 
318     @UiThreadTest
319     @Test
testConvertSelectionToString()320     public void testConvertSelectionToString() {
321         MockAutoCompleteTextView autoCompleteTextView = new MockAutoCompleteTextView(mActivity);
322 
323         // Set Threshold to 4 characters
324         autoCompleteTextView.setThreshold(4);
325         autoCompleteTextView.setAdapter(mAdapter);
326         assertNotNull(autoCompleteTextView.getAdapter());
327 
328         assertEquals("", autoCompleteTextView.convertSelectionToString(null));
329         assertEquals(STRING_TEST, autoCompleteTextView.convertSelectionToString(STRING_TEST));
330     }
331 
332     @UiThreadTest
333     @Test
testOnTextChanged()334     public void testOnTextChanged() {
335         final TextWatcher mockTextWatcher = mock(TextWatcher.class);
336         mAutoCompleteTextView.addTextChangedListener(mockTextWatcher);
337         verify(mockTextWatcher, never()).onTextChanged(any(CharSequence.class),
338                 anyInt(), anyInt(), anyInt());
339 
340         mAutoCompleteTextView.setText(STRING_TEST);
341         verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence(STRING_TEST),
342                 eq(0), eq(0), eq(STRING_TEST.length()));
343 
344         // Test replacing text.
345         mAutoCompleteTextView.setText(STRING_CHECK);
346         verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence(STRING_CHECK),
347                 eq(0), eq(STRING_TEST.length()), eq(STRING_CHECK.length()));
348     }
349 
350     @UiThreadTest
351     @Test
testPopupWindow()352     public void testPopupWindow() {
353         final AutoCompleteTextView.OnDismissListener mockDismissListener =
354                 mock(AutoCompleteTextView.OnDismissListener.class);
355         mAutoCompleteTextView.setOnDismissListener(mockDismissListener);
356 
357         assertFalse(mAutoCompleteTextView.isPopupShowing());
358         mAutoCompleteTextView.showDropDown();
359         assertTrue(mAutoCompleteTextView.isPopupShowing());
360         verifyZeroInteractions(mockDismissListener);
361 
362         mAutoCompleteTextView.dismissDropDown();
363         assertFalse(mAutoCompleteTextView.isPopupShowing());
364         verify(mockDismissListener, times(1)).onDismiss();
365 
366         mAutoCompleteTextView.showDropDown();
367         assertTrue(mAutoCompleteTextView.isPopupShowing());
368         verify(mockDismissListener, times(1)).onDismiss();
369 
370         final MockValidator validator = new MockValidator();
371         mAutoCompleteTextView.setValidator(validator);
372         mAutoCompleteTextView.requestFocus();
373         mAutoCompleteTextView.showDropDown();
374         mAutoCompleteTextView.setText(STRING_TEST);
375         assertEquals(STRING_TEST, mAutoCompleteTextView.getText().toString());
376 
377         // clearFocus will trigger onFocusChanged, and onFocusChanged will validate the text.
378         mAutoCompleteTextView.clearFocus();
379         assertFalse(mAutoCompleteTextView.isPopupShowing());
380         assertEquals(STRING_VALIDATED, mAutoCompleteTextView.getText().toString());
381         verify(mockDismissListener, times(2)).onDismiss();
382 
383         verifyNoMoreInteractions(mockDismissListener);
384     }
385 
386     @UiThreadTest
387     @Test
testDropDownMetrics()388     public void testDropDownMetrics() {
389         mAutoCompleteTextView.setAdapter(mAdapter);
390 
391         final Resources res = mActivity.getResources();
392         final int dropDownWidth =
393                 res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_width);
394         final int dropDownHeight =
395                 res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_height);
396         final int dropDownOffsetHorizontal =
397                 res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_offset_h);
398         final int dropDownOffsetVertical =
399                 res.getDimensionPixelSize(R.dimen.autocomplete_textview_dropdown_offset_v);
400 
401         mAutoCompleteTextView.setDropDownWidth(dropDownWidth);
402         mAutoCompleteTextView.setDropDownHeight(dropDownHeight);
403         mAutoCompleteTextView.setDropDownHorizontalOffset(dropDownOffsetHorizontal);
404         mAutoCompleteTextView.setDropDownVerticalOffset(dropDownOffsetVertical);
405 
406         mAutoCompleteTextView.showDropDown();
407 
408         assertEquals(dropDownWidth, mAutoCompleteTextView.getDropDownWidth());
409         assertEquals(dropDownHeight, mAutoCompleteTextView.getDropDownHeight());
410         assertEquals(dropDownOffsetHorizontal, mAutoCompleteTextView.getDropDownHorizontalOffset());
411         assertEquals(dropDownOffsetVertical, mAutoCompleteTextView.getDropDownVerticalOffset());
412     }
413 
414     @Test
testDropDownBackground()415     public void testDropDownBackground() throws Throwable {
416         mActivityRule.runOnUiThread(() -> mAutoCompleteTextView.setAdapter(mAdapter));
417 
418         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
419             mAutoCompleteTextView.setDropDownBackgroundResource(R.drawable.blue_fill);
420             mAutoCompleteTextView.showDropDown();
421         });
422 
423         Drawable dropDownBackground = mAutoCompleteTextView.getDropDownBackground();
424         TestUtils.assertAllPixelsOfColor("Drop down should be blue", dropDownBackground,
425                 dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
426                 false, Color.BLUE, 1, true);
427 
428         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
429             mAutoCompleteTextView.dismissDropDown();
430             mAutoCompleteTextView.setDropDownBackgroundDrawable(
431                     mActivity.getDrawable(R.drawable.yellow_fill));
432             mAutoCompleteTextView.showDropDown();
433         });
434 
435         dropDownBackground = mAutoCompleteTextView.getDropDownBackground();
436         TestUtils.assertAllPixelsOfColor("Drop down should be yellow", dropDownBackground,
437                 dropDownBackground.getBounds().width(), dropDownBackground.getBounds().height(),
438                 false, Color.YELLOW, 1, true);
439     }
440 
441     @Test
refreshAutoCompleteResults_addItem()442     public void refreshAutoCompleteResults_addItem() throws Throwable {
443         List<String> suggestions = new ArrayList<>(ImmutableList.of("testOne", "testTwo"));
444         mAdapter = new ArrayAdapter<String>(
445                 mActivity, android.R.layout.simple_dropdown_item_1line, suggestions);
446 
447         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
448             mAutoCompleteTextView.setAdapter(mAdapter);
449             mAutoCompleteTextView.setText("testT");
450             mAutoCompleteTextView.refreshAutoCompleteResults();
451         });
452 
453         PollingCheck.waitFor(() -> {
454             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
455             return (autoCompleteSuggestions.size() == 1)
456                     && autoCompleteSuggestions.contains("testTwo");
457         });
458 
459         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
460             mAdapter.add("testThree");
461             mAutoCompleteTextView.refreshAutoCompleteResults();
462         });
463 
464         PollingCheck.waitFor(() -> {
465             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
466             return (autoCompleteSuggestions.size() == 2)
467                     && autoCompleteSuggestions.contains("testTwo")
468                     && autoCompleteSuggestions.contains("testThree");
469         });
470     }
471 
472     @Test
refreshAutoCompleteResults_removeItem()473     public void refreshAutoCompleteResults_removeItem() throws Throwable {
474         List<String> suggestions = new ArrayList<>(
475                 ImmutableList.of("testOne", "testTwo", "testThree"));
476         mAdapter = new ArrayAdapter<>(
477                 mActivity, android.R.layout.simple_dropdown_item_1line, suggestions);
478 
479         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
480             mAutoCompleteTextView.setAdapter(mAdapter);
481             mAutoCompleteTextView.setText("testT");
482             mAutoCompleteTextView.refreshAutoCompleteResults();
483         });
484         PollingCheck.waitFor(() -> {
485             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
486             return (autoCompleteSuggestions.size() == 2)
487                     && autoCompleteSuggestions.contains("testTwo")
488                     && autoCompleteSuggestions.contains("testThree");
489         });
490 
491         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
492             mAdapter.remove("testThree");
493             mAutoCompleteTextView.refreshAutoCompleteResults();
494         });
495         PollingCheck.waitFor(() -> {
496             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
497             return (autoCompleteSuggestions.size() == 1)
498                     && autoCompleteSuggestions.contains("testTwo");
499         });
500     }
501 
502     @Test
refreshAutoCompleteResults_threshold_changeText()503     public void refreshAutoCompleteResults_threshold_changeText() throws Throwable {
504         List<String> suggestions = new ArrayList<>(
505                 ImmutableList.of("testOne", "testTwo", "testThree"));
506         mAdapter = new ArrayAdapter<String>(
507                 mActivity, android.R.layout.simple_dropdown_item_1line, suggestions);
508 
509         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
510             mAutoCompleteTextView.setThreshold(3);
511             mAutoCompleteTextView.setAdapter(mAdapter);
512             mAutoCompleteTextView.setText("tes");
513         });
514 
515         // Above Threshold.
516         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
517             mAutoCompleteTextView.setText("test");
518             mAutoCompleteTextView.refreshAutoCompleteResults();
519         });
520         assertThat(mAutoCompleteTextView.isPopupShowing()).isTrue();
521         assertThat(getAutoCompleteSuggestions()).containsExactly("testOne", "testTwo", "testThree");
522 
523         // At Threshold.
524         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
525             mAutoCompleteTextView.setText("tes");
526             mAutoCompleteTextView.refreshAutoCompleteResults();
527         });
528         assertThat(mAutoCompleteTextView.isPopupShowing()).isTrue();
529         assertThat(getAutoCompleteSuggestions()).containsExactly("testOne", "testTwo", "testThree");
530 
531         // Below Threshold.
532         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
533             mAutoCompleteTextView.setText("te");
534             mAutoCompleteTextView.refreshAutoCompleteResults();
535         });
536         assertThat(mAutoCompleteTextView.isPopupShowing()).isFalse();
537     }
538 
539     @Test
refreshAutoCompleteResults_changeThreshold()540     public void refreshAutoCompleteResults_changeThreshold() throws Throwable {
541         List<String> suggestions = new ArrayList<>(
542                 ImmutableList.of("testOne", "testTwo", "testThree"));
543         mAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_dropdown_item_1line,
544                 suggestions);
545 
546         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
547             mAutoCompleteTextView.setAdapter(mAdapter);
548             mAutoCompleteTextView.setText("test");
549         });
550 
551         // Above Threshold.
552         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
553             mAutoCompleteTextView.setThreshold(3);
554             mAutoCompleteTextView.refreshAutoCompleteResults();
555         });
556         assertThat(mAutoCompleteTextView.isPopupShowing()).isTrue();
557         assertThat(getAutoCompleteSuggestions()).containsExactly("testOne", "testTwo", "testThree");
558 
559         // At Threshold.
560         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
561             mAutoCompleteTextView.setThreshold(4);
562             mAutoCompleteTextView.refreshAutoCompleteResults();
563         });
564         assertThat(mAutoCompleteTextView.isPopupShowing()).isTrue();
565         assertThat(getAutoCompleteSuggestions()).containsExactly("testOne", "testTwo", "testThree");
566 
567         // Below Threshold.
568         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
569             mAutoCompleteTextView.setThreshold(5);
570             mAutoCompleteTextView.refreshAutoCompleteResults();
571         });
572         assertThat(mAutoCompleteTextView.isPopupShowing()).isFalse();
573     }
574 
575     @Test
refreshAutoCompleteResults_dropDownAlwaysVisible()576     public void refreshAutoCompleteResults_dropDownAlwaysVisible() throws Throwable {
577         List<String> suggestions = new ArrayList<>(
578                 ImmutableList.of("testOne", "testTwo", "testThree"));
579         mAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_dropdown_item_1line,
580                 suggestions);
581 
582         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
583             mAutoCompleteTextView.setAdapter(mAdapter);
584             mAutoCompleteTextView.setDropDownAlwaysVisible(true);
585             mAutoCompleteTextView.setText("test");
586         });
587 
588         // Below Threshold.
589         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
590             mAutoCompleteTextView.setThreshold(5);
591             mAutoCompleteTextView.refreshAutoCompleteResults();
592         });
593         PollingCheck.waitFor(() -> mAutoCompleteTextView.isPopupShowing());
594     }
595 
596     @Test
refreshAutoCompleteResults_dropDownNotAlwaysVisible()597     public void refreshAutoCompleteResults_dropDownNotAlwaysVisible() throws Throwable {
598         List<String> suggestions = new ArrayList<>(
599                 ImmutableList.of("testOne", "testTwo", "testThree"));
600         mAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_dropdown_item_1line,
601                 suggestions);
602 
603         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
604             mAutoCompleteTextView.setAdapter(mAdapter);
605             mAutoCompleteTextView.setDropDownAlwaysVisible(false);
606             mAutoCompleteTextView.setText("test");
607         });
608 
609         // Below Threshold.
610         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
611             mAutoCompleteTextView.setThreshold(5);
612             mAutoCompleteTextView.refreshAutoCompleteResults();
613         });
614         assertThat(mAutoCompleteTextView.isPopupShowing()).isFalse();
615     }
616 
617     @Test
refreshAutoCompleteResults_popupCanBeUpdated()618     public void refreshAutoCompleteResults_popupCanBeUpdated() throws Throwable {
619         List<String> suggestions = new ArrayList<>(
620                 ImmutableList.of("testOne", "testTwo", "testThree"));
621         mAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_dropdown_item_1line,
622                 suggestions);
623 
624         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
625             mAutoCompleteTextView.setAdapter(mAdapter);
626             mAutoCompleteTextView.setDropDownAlwaysVisible(false);
627             mAutoCompleteTextView.setText("test");
628         });
629 
630         // Below Threshold.
631         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
632             mAutoCompleteTextView.setThreshold(5);
633             mAutoCompleteTextView.refreshAutoCompleteResults();
634         });
635         assertThat(mAutoCompleteTextView.isPopupShowing()).isFalse();
636     }
637 
638     @Test
refreshAutoCompleteResults()639     public void refreshAutoCompleteResults() throws Throwable {
640         List<String> suggestions = new ArrayList<>(ImmutableList.of("testOne", "testTwo"));
641         mAdapter = new ArrayAdapter<>(mActivity, android.R.layout.simple_dropdown_item_1line,
642                 suggestions);
643 
644         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
645             mAutoCompleteTextView.setAdapter(mAdapter);
646             mAutoCompleteTextView.setText("testT");
647             mAutoCompleteTextView.refreshAutoCompleteResults();
648         });
649         PollingCheck.waitFor(() -> {
650             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
651             return (autoCompleteSuggestions.size() == 1)
652                     && autoCompleteSuggestions.contains("testTwo");
653         });
654 
655         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
656             mAdapter.add("testThree");
657             mAutoCompleteTextView.refreshAutoCompleteResults();
658         });
659         PollingCheck.waitFor(() -> {
660             List<Object> autoCompleteSuggestions = getAutoCompleteSuggestions();
661             return (autoCompleteSuggestions.size() == 2)
662                     && autoCompleteSuggestions.contains("testTwo")
663                     && autoCompleteSuggestions.contains("testThree");
664         });
665     }
666 
667     @UiThreadTest
668     @Test
setInputMethodMode_fromFocussable()669     public void setInputMethodMode_fromFocussable() {
670         mAutoCompleteTextView.setInputMethodMode(INPUT_METHOD_FROM_FOCUSABLE);
671         assertThat(mAutoCompleteTextView.getInputMethodMode())
672                 .isEqualTo(INPUT_METHOD_FROM_FOCUSABLE);
673     }
674 
675     @UiThreadTest
676     @Test
setInputMethodMode_Needed()677     public void setInputMethodMode_Needed() {
678         mAutoCompleteTextView.setInputMethodMode(INPUT_METHOD_NEEDED);
679         assertThat(mAutoCompleteTextView.getInputMethodMode()).isEqualTo(INPUT_METHOD_NEEDED);
680     }
681 
682     @UiThreadTest
683     @Test
setInputMethodMode_NotNeeded()684     public void setInputMethodMode_NotNeeded() {
685         mAutoCompleteTextView.setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
686         assertThat(mAutoCompleteTextView.getInputMethodMode()).isEqualTo(INPUT_METHOD_NOT_NEEDED);
687     }
688 
689     @UiThreadTest
690     @Test
testReplaceText()691     public void testReplaceText() {
692         final TextWatcher mockTextWatcher = mock(TextWatcher.class);
693         mMockAutoCompleteTextView.addTextChangedListener(mockTextWatcher);
694         verify(mockTextWatcher, never()).onTextChanged(any(CharSequence.class),
695                 anyInt(), anyInt(), anyInt());
696 
697         mMockAutoCompleteTextView.replaceText("Text");
698         assertEquals("Text", mMockAutoCompleteTextView.getText().toString());
699         verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence("Text"),
700                 eq(0), eq(0), eq("Text".length()));
701 
702         mMockAutoCompleteTextView.replaceText("Another");
703         assertEquals("Another", mMockAutoCompleteTextView.getText().toString());
704         verify(mockTextWatcher, times(1)).onTextChanged(sameCharSequence("Another"),
705                 eq(0), eq("Text".length()), eq("Another".length()));
706     }
707 
708     @UiThreadTest
709     @Test
testSetFrame()710     public void testSetFrame() {
711         assertTrue(mMockAutoCompleteTextView.setFrame(0, 1, 2, 3));
712         assertEquals(0, mMockAutoCompleteTextView.getLeft());
713         assertEquals(1, mMockAutoCompleteTextView.getTop());
714         assertEquals(2, mMockAutoCompleteTextView.getRight());
715         assertEquals(3, mMockAutoCompleteTextView.getBottom());
716 
717         // If the values are the same as old ones, function will return false
718         assertFalse(mMockAutoCompleteTextView.setFrame(0, 1, 2, 3));
719         assertEquals(0, mMockAutoCompleteTextView.getLeft());
720         assertEquals(1, mMockAutoCompleteTextView.getTop());
721         assertEquals(2, mMockAutoCompleteTextView.getRight());
722         assertEquals(3, mMockAutoCompleteTextView.getBottom());
723 
724         // If the values are not the same as old ones, function will return true
725         assertTrue(mMockAutoCompleteTextView.setFrame(2, 3, 4, 5));
726         assertEquals(2, mMockAutoCompleteTextView.getLeft());
727         assertEquals(3, mMockAutoCompleteTextView.getTop());
728         assertEquals(4, mMockAutoCompleteTextView.getRight());
729         assertEquals(5, mMockAutoCompleteTextView.getBottom());
730     }
731 
732     @UiThreadTest
733     @Test
testGetThreshold()734     public void testGetThreshold() {
735         assertEquals(1, mAutoCompleteTextView.getThreshold());
736         mAutoCompleteTextView.setThreshold(3);
737         assertEquals(3, mAutoCompleteTextView.getThreshold());
738 
739         // Test negative value input
740         mAutoCompleteTextView.setThreshold(-5);
741         assertEquals(1, mAutoCompleteTextView.getThreshold());
742 
743         // Test zero
744         mAutoCompleteTextView.setThreshold(0);
745         assertEquals(1, mAutoCompleteTextView.getThreshold());
746     }
747 
748     @UiThreadTest
749     @Test
testAccessValidater()750     public void testAccessValidater() {
751         final MockValidator validator = new MockValidator();
752 
753         assertNull(mAutoCompleteTextView.getValidator());
754         mAutoCompleteTextView.setValidator(validator);
755         assertSame(validator, mAutoCompleteTextView.getValidator());
756 
757         // Set to null
758         mAutoCompleteTextView.setValidator(null);
759         assertNull(mAutoCompleteTextView.getValidator());
760     }
761 
762     @Test
testOnFilterComplete()763     public void testOnFilterComplete() throws Throwable {
764         // Set Threshold to 4 characters
765         mAutoCompleteTextView.setThreshold(4);
766 
767         String testString = "";
768         if (mNumeric) {
769             // "tes" in case of 12-key(NUMERIC) keyboard
770             testString = "8337777";
771         } else {
772             testString = "tes";
773         }
774 
775         // Test the filter if the input string is not long enough to threshold
776         mActivityRule.runOnUiThread(() -> {
777                 mAutoCompleteTextView.setAdapter(mAdapter);
778                 mAutoCompleteTextView.setText("");
779                 mAutoCompleteTextView.requestFocus();
780         });
781         mInstrumentation.sendStringSync(testString);
782 
783         // onFilterComplete will close the popup.
784         PollingCheck.waitFor(() -> !mAutoCompleteTextView.isPopupShowing());
785 
786         if (mNumeric) {
787             // "that" in case of 12-key(NUMERIC) keyboard
788             testString = "84428";
789         } else {
790             testString = "that";
791         }
792         mInstrumentation.sendStringSync(testString);
793         PollingCheck.waitFor(() -> !mAutoCompleteTextView.isPopupShowing());
794 
795         // Test the expected filter matching scene
796         mActivityRule.runOnUiThread(() -> {
797                 mAutoCompleteTextView.setFocusable(true);
798                 mAutoCompleteTextView.requestFocus();
799                 mAutoCompleteTextView.setText("");
800         });
801         if (mNumeric) {
802             // "test" in case of 12-key(NUMERIC) keyboard
803             mInstrumentation.sendStringSync("83377778");
804         } else {
805             mInstrumentation.sendStringSync("test");
806         }
807         assertTrue(mAutoCompleteTextView.hasFocus());
808         assertTrue(mAutoCompleteTextView.hasWindowFocus());
809         PollingCheck.waitFor(() -> mAutoCompleteTextView.isPopupShowing());
810     }
811 
812     @Test
testPerformFiltering()813     public void testPerformFiltering() throws Throwable {
814         if (isTvMode()) {
815             return;
816         }
817         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
818             mAutoCompleteTextView.setAdapter(mAdapter);
819             mAutoCompleteTextView.setValidator(mValidator);
820 
821             mAutoCompleteTextView.setText("test");
822             mAutoCompleteTextView.setFocusable(true);
823             mAutoCompleteTextView.requestFocus();
824             mAutoCompleteTextView.showDropDown();
825         });
826         assertTrue(mAutoCompleteTextView.isPopupShowing());
827 
828         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
829         // KeyBack will close the popup.
830         assertFalse(mAutoCompleteTextView.isPopupShowing());
831 
832         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
833             mAutoCompleteTextView.showDropDown();
834         });
835         assertTrue(mAutoCompleteTextView.isPopupShowing());
836 
837         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ESCAPE);
838         // KeyEscape will also close the popup.
839         assertFalse(mAutoCompleteTextView.isPopupShowing());
840 
841         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
842             mAutoCompleteTextView.dismissDropDown();
843             mAutoCompleteTextView.setText(STRING_TEST);
844         });
845 
846         assertEquals(STRING_TEST, mAutoCompleteTextView.getText().toString());
847         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
848         // If the popup is closed, onKeyDown will invoke performValidation.
849         assertEquals(STRING_VALIDATED, mAutoCompleteTextView.getText().toString());
850 
851         final MockAdapter<String> adapter = new MockAdapter<String>(mActivity,
852                 android.R.layout.simple_dropdown_item_1line, WORDS);
853 
854         // Set Threshold to 4 characters onKeyDown
855         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
856             mAutoCompleteTextView.setAdapter(adapter);
857             mAutoCompleteTextView.requestFocus();
858             mAutoCompleteTextView.setText("");
859         });
860         // Create and get the filter.
861         final MockFilter filter = (MockFilter) adapter.getFilter();
862 
863         // performFiltering will be indirectly invoked by onKeyDown
864         assertNull(filter.getResult());
865         // 12-key support
866         if (mNumeric) {
867             // "numeric" in case of 12-key(NUMERIC) keyboard
868             mInstrumentation.sendStringSync("6688633777444222");
869             PollingCheck.waitFor(() -> "numeric".equals(filter.getResult()));
870         } else {
871             SystemClock.sleep(200);
872             mInstrumentation.sendStringSync(STRING_TEST);
873             PollingCheck.waitFor(() -> STRING_TEST.equals(filter.getResult()));
874         }
875     }
876 
877     @Test
testPerformCompletionWithDPad()878     public void testPerformCompletionWithDPad() throws Throwable {
879         if (isTvMode()) {
880             return;
881         }
882         final AdapterView.OnItemClickListener mockItemClickListener =
883                 mock(AdapterView.OnItemClickListener.class);
884         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
885 
886         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
887             mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
888             mAutoCompleteTextView.setAdapter(mAdapter);
889             mAutoCompleteTextView.requestFocus();
890             mAutoCompleteTextView.showDropDown();
891         });
892         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
893 
894         // Key is ENTER, will invoke completion
895         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
896         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
897         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, null);
898         verify(mockItemClickListener, times(1)).onItemClick(any(AdapterView.class), any(View.class),
899                 eq(0), eq(0L));
900         assertEquals(WORDS[0], mAutoCompleteTextView.getText().toString());
901 
902         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
903                 mAutoCompleteTextView::showDropDown);
904         // Key is NUMPAD_ENTER, will invoke completion
905         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
906         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_NUMPAD_ENTER);
907         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, null);
908         verify(mockItemClickListener, times(2)).onItemClick(any(AdapterView.class), any(View.class),
909                 eq(0), eq(0L));
910         assertEquals(WORDS[0], mAutoCompleteTextView.getText().toString());
911 
912         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
913                 mAutoCompleteTextView::showDropDown);
914         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
915         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
916         verify(mockItemClickListener, times(3)).onItemClick(any(AdapterView.class), any(View.class),
917                 eq(0), eq(0L));
918         assertEquals(WORDS[0], mAutoCompleteTextView.getText().toString());
919         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
920 
921         mActivityRule.runOnUiThread(mAutoCompleteTextView::showDropDown);
922         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
923         // Test normal key code.
924         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_0);
925         verifyNoMoreInteractions(mockItemClickListener);
926         assertNotSame("", mAutoCompleteTextView.getText().toString());
927         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
928 
929         // Test the method on the scene of popup is closed.
930         mActivityRule.runOnUiThread(mAutoCompleteTextView::dismissDropDown);
931 
932         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
933         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
934         verifyNoMoreInteractions(mockItemClickListener);
935         assertNotSame("", mAutoCompleteTextView.getText().toString());
936         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
937     }
938 
939     @Test
testPerformCompletionExplicit()940     public void testPerformCompletionExplicit() throws Throwable {
941         final AdapterView.OnItemClickListener mockItemClickListener =
942                 mock(AdapterView.OnItemClickListener.class);
943         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
944 
945         // Create a custom watcher that checks isPerformingCompletion to return true
946         // in the "middle" of the performCompletion processing. We also spy on this watcher
947         // to make sure that its onTextChanged is invoked.
948         final TextWatcher myTextWatcher = new MyTextWatcher(WORDS[1]);
949         final TextWatcher spyTextWatcher = spy(myTextWatcher);
950         mAutoCompleteTextView.addTextChangedListener(spyTextWatcher);
951 
952         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
953             mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
954             mAutoCompleteTextView.setAdapter(mAdapter);
955             mAutoCompleteTextView.requestFocus();
956             mAutoCompleteTextView.showDropDown();
957         });
958 
959         assertTrue(mAutoCompleteTextView.isPopupShowing());
960         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
961 
962         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
963         mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
964         mActivityRule.runOnUiThread(mAutoCompleteTextView::performCompletion);
965         verify(mockItemClickListener, times(1)).onItemClick(any(AdapterView.class), any(View.class),
966                 eq(1), eq(1L));
967         assertEquals(WORDS[1], mAutoCompleteTextView.getText().toString());
968         assertFalse(mAutoCompleteTextView.isPerformingCompletion());
969         assertFalse(mAutoCompleteTextView.isPopupShowing());
970 
971         verify(spyTextWatcher, atLeastOnce()).onTextChanged(sameCharSequence(WORDS[1]),
972                 eq(0), eq(0), eq(WORDS[1].length()));
973         verifyNoMoreInteractions(mockItemClickListener);
974     }
975 
976     @Test
testSetTextWithCompletion()977     public void testSetTextWithCompletion() throws Throwable {
978         final AdapterView.OnItemClickListener mockItemClickListener =
979                 mock(AdapterView.OnItemClickListener.class);
980 
981         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
982             mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
983             mAutoCompleteTextView.setAdapter(mAdapter);
984         });
985         PollingCheck.waitFor(() -> !mAutoCompleteTextView.isPopupShowing());
986 
987         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
988                 () -> mAutoCompleteTextView.setText("testO", true));
989         PollingCheck.waitFor(() -> mAutoCompleteTextView.isPopupShowing());
990 
991         verifyZeroInteractions(mockItemClickListener);
992     }
993 
994     @Test
testSetTextWithNoCompletion()995     public void testSetTextWithNoCompletion() throws Throwable {
996         final AdapterView.OnItemClickListener mockItemClickListener =
997                 mock(AdapterView.OnItemClickListener.class);
998 
999         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
1000             mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
1001             mAutoCompleteTextView.setAdapter(mAdapter);
1002         });
1003 
1004         assertFalse(mAutoCompleteTextView.isPopupShowing());
1005 
1006         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView,
1007                 () -> mAutoCompleteTextView.setText("testO", false));
1008 
1009         assertFalse(mAutoCompleteTextView.isPopupShowing());
1010         verifyZeroInteractions(mockItemClickListener);
1011     }
1012 
1013     @UiThreadTest
1014     @Test
testPerformValidation()1015     public void testPerformValidation() {
1016         final CharSequence text = "this";
1017 
1018         mAutoCompleteTextView.setValidator(mValidator);
1019         mAutoCompleteTextView.setAdapter((ArrayAdapter<String>) null);
1020         mAutoCompleteTextView.setText(text);
1021         mAutoCompleteTextView.performValidation();
1022 
1023         assertEquals(STRING_VALIDATED, mAutoCompleteTextView.getText().toString());
1024         mAutoCompleteTextView.setValidator(null);
1025     }
1026 
1027     @UiThreadTest
1028     @Test
testAccessCompletionHint()1029     public void testAccessCompletionHint() {
1030         mAutoCompleteTextView.setCompletionHint("TEST HINT");
1031         assertEquals("TEST HINT", mAutoCompleteTextView.getCompletionHint());
1032 
1033         mAutoCompleteTextView.setCompletionHint(null);
1034         assertNull(mAutoCompleteTextView.getCompletionHint());
1035     }
1036 
1037     @Test
testAccessListSelection()1038     public void testAccessListSelection() throws Throwable {
1039         final AdapterView.OnItemClickListener mockItemClickListener =
1040                 mock(AdapterView.OnItemClickListener.class);
1041 
1042         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
1043             mAutoCompleteTextView.setOnItemClickListener(mockItemClickListener);
1044             mAutoCompleteTextView.setAdapter(mAdapter);
1045             mAutoCompleteTextView.requestFocus();
1046             mAutoCompleteTextView.showDropDown();
1047         });
1048 
1049         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mAutoCompleteTextView, () -> {
1050             mAutoCompleteTextView.setListSelection(1);
1051             assertEquals(1, mAutoCompleteTextView.getListSelection());
1052 
1053             mAutoCompleteTextView.setListSelection(2);
1054             assertEquals(2, mAutoCompleteTextView.getListSelection());
1055 
1056             mAutoCompleteTextView.clearListSelection();
1057             assertEquals(2, mAutoCompleteTextView.getListSelection());
1058         });
1059     }
1060 
1061     @UiThreadTest
1062     @Test
testAccessDropDownAnchor()1063     public void testAccessDropDownAnchor() {
1064         mAutoCompleteTextView.setDropDownAnchor(View.NO_ID);
1065         assertEquals(View.NO_ID, mAutoCompleteTextView.getDropDownAnchor());
1066 
1067         mAutoCompleteTextView.setDropDownAnchor(0x5555);
1068         assertEquals(0x5555, mAutoCompleteTextView.getDropDownAnchor());
1069     }
1070 
1071     @UiThreadTest
1072     @Test
testAccessDropDownWidth()1073     public void testAccessDropDownWidth() {
1074         mAutoCompleteTextView.setDropDownWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
1075         assertEquals(ViewGroup.LayoutParams.WRAP_CONTENT, mAutoCompleteTextView.getDropDownWidth());
1076 
1077         mAutoCompleteTextView.setDropDownWidth(ViewGroup.LayoutParams.MATCH_PARENT);
1078         assertEquals(ViewGroup.LayoutParams.MATCH_PARENT, mAutoCompleteTextView.getDropDownWidth());
1079     }
1080 
getAutoCompleteSuggestions()1081     private List<Object> getAutoCompleteSuggestions() {
1082         int count = mAutoCompleteTextView.getAdapter().getCount();
1083         List<Object> autoCompleteSuggestions = new ArrayList<>(count);
1084         for (int index = 0; index < count; index++) {
1085             autoCompleteSuggestions.add(mAutoCompleteTextView.getAdapter().getItem(index));
1086         }
1087         return autoCompleteSuggestions;
1088     }
1089 
1090     private class MockValidator implements AutoCompleteTextView.Validator {
fixText(CharSequence invalidText)1091         public CharSequence fixText(CharSequence invalidText) {
1092             return STRING_VALIDATED;
1093         }
1094 
isValid(CharSequence text)1095         public boolean isValid(CharSequence text) {
1096             return (text == STRING_TEST);
1097         }
1098     }
1099 
1100     public static class MockAutoCompleteTextView extends AutoCompleteTextView {
MockAutoCompleteTextView(Context context)1101         public MockAutoCompleteTextView(Context context) {
1102             super(context);
1103         }
1104 
MockAutoCompleteTextView(Context context, AttributeSet attrs)1105         public MockAutoCompleteTextView(Context context, AttributeSet attrs) {
1106             super(context, attrs);
1107         }
1108 
1109         @Override
convertSelectionToString(Object selectedItem)1110         protected CharSequence convertSelectionToString(Object selectedItem) {
1111             return super.convertSelectionToString(selectedItem);
1112         }
1113 
1114         @Override
getFilter()1115         protected Filter getFilter() {
1116             return super.getFilter();
1117         }
1118 
1119         @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)1120         protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
1121             super.onFocusChanged(focused, direction, previouslyFocusedRect);
1122         }
1123 
1124         @Override
performFiltering(CharSequence text, int keyCode)1125         protected void performFiltering(CharSequence text, int keyCode) {
1126             super.performFiltering(text, keyCode);
1127         }
1128 
1129         @Override
replaceText(CharSequence text)1130         protected void replaceText(CharSequence text) {
1131             super.replaceText(text);
1132         }
1133 
1134         @Override
setFrame(int l, int t, int r, int b)1135         protected boolean setFrame(int l, int t, int r, int b) {
1136             return super.setFrame(l, t, r, b);
1137         }
1138     }
1139 
1140     private static class MockFilter extends Filter {
1141         private String mFilterResult;
1142 
1143         @Override
performFiltering(CharSequence constraint)1144         protected FilterResults performFiltering(CharSequence constraint) {
1145             if (constraint != null) {
1146                 mFilterResult = constraint.toString();
1147             }
1148             return null;
1149         }
1150 
1151         @Override
publishResults(CharSequence constraint, FilterResults results)1152         protected void publishResults(CharSequence constraint, FilterResults results) {
1153         }
1154 
getResult()1155         public String getResult() {
1156             return mFilterResult;
1157         }
1158     }
1159 
1160     private static class MockAdapter<T> extends ArrayAdapter<T> implements Filterable {
1161         private MockFilter mFilter;
1162 
MockAdapter(Context context, int textViewResourceId, T[] objects)1163         public MockAdapter(Context context, int textViewResourceId, T[] objects) {
1164             super(context, textViewResourceId, objects);
1165         }
1166 
1167         @Override
getFilter()1168         public Filter getFilter() {
1169             if (mFilter == null) {
1170                 mFilter = new MockFilter();
1171             }
1172             return mFilter;
1173         }
1174     }
1175 }
1176