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