xref: /aosp_15_r20/cts/tests/tests/widget/src/android/widget/cts/SearchViewTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2016 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 org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertTrue;
22 import static org.mockito.Mockito.anyString;
23 import static org.mockito.Mockito.atLeastOnce;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.never;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyNoMoreInteractions;
29 import static org.mockito.Mockito.when;
30 
31 import android.Manifest;
32 import android.app.Activity;
33 import android.app.Instrumentation;
34 import android.content.res.Resources;
35 import android.text.InputType;
36 import android.text.TextUtils;
37 import android.view.KeyEvent;
38 import android.view.inputmethod.EditorInfo;
39 import android.widget.SearchView;
40 
41 import androidx.test.InstrumentationRegistry;
42 import androidx.test.annotation.UiThreadTest;
43 import androidx.test.filters.MediumTest;
44 import androidx.test.rule.ActivityTestRule;
45 import androidx.test.runner.AndroidJUnit4;
46 
47 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
48 import com.android.compatibility.common.util.CtsKeyEventUtil;
49 import com.android.compatibility.common.util.PollingCheck;
50 import com.android.compatibility.common.util.WidgetTestUtils;
51 
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 /**
58  * Test {@link SearchView}.
59  */
60 @MediumTest
61 @RunWith(AndroidJUnit4.class)
62 public class SearchViewTest {
63     private Activity mActivity;
64     private SearchView mSearchView;
65     private Instrumentation mInstrumentation;
66     private CtsKeyEventUtil mCtsKeyEventUtil;
67 
68     @Rule(order = 0)
69     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
70             androidx.test.platform.app.InstrumentationRegistry
71                     .getInstrumentation().getUiAutomation(),
72             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
73 
74     @Rule(order = 1)
75     public ActivityTestRule<SearchViewCtsActivity> mActivityRule =
76             new ActivityTestRule<>(SearchViewCtsActivity.class);
77 
78     @Before
setup()79     public void setup() {
80         mInstrumentation = InstrumentationRegistry.getInstrumentation();
81         mCtsKeyEventUtil = new CtsKeyEventUtil(mInstrumentation.getTargetContext());
82         mActivity = mActivityRule.getActivity();
83         mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
84 
85         PollingCheck.waitFor(() -> mActivity.hasWindowFocus());
86     }
87 
88     @UiThreadTest
89     @Test
testConstructor()90     public void testConstructor() {
91         new SearchView(mActivity);
92 
93         new SearchView(mActivity, null);
94 
95         new SearchView(mActivity, null, android.R.attr.searchViewStyle);
96 
97         new SearchView(mActivity, null, 0, android.R.style.Widget_Material_SearchView);
98 
99         new SearchView(mActivity, null, 0, android.R.style.Widget_Material_Light_SearchView);
100     }
101 
102     @UiThreadTest
103     @Test
testAttributesFromXml()104     public void testAttributesFromXml() {
105         SearchView searchViewWithAttributes =
106                 (SearchView) mActivity.findViewById(R.id.search_view_with_defaults);
107         assertEquals(mActivity.getString(R.string.search_query_hint),
108                 searchViewWithAttributes.getQueryHint());
109         assertFalse(searchViewWithAttributes.isIconfiedByDefault());
110         assertEquals(EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS | EditorInfo.TYPE_CLASS_TEXT,
111                 searchViewWithAttributes.getInputType());
112         assertEquals(EditorInfo.IME_ACTION_DONE, searchViewWithAttributes.getImeOptions());
113         assertEquals(mActivity.getResources().getDimensionPixelSize(R.dimen.search_view_max_width),
114                 searchViewWithAttributes.getMaxWidth());
115     }
116 
117     @UiThreadTest
118     @Test
testAccessIconified()119     public void testAccessIconified() {
120         mSearchView.setIconified(true);
121         assertTrue(mSearchView.isIconified());
122 
123         mSearchView.setIconified(false);
124         assertFalse(mSearchView.isIconified());
125     }
126 
127     @UiThreadTest
128     @Test
testAccessIconifiedByDefault()129     public void testAccessIconifiedByDefault() {
130         mSearchView.setIconifiedByDefault(true);
131         assertTrue(mSearchView.isIconfiedByDefault());
132 
133         mSearchView.setIconifiedByDefault(false);
134         assertFalse(mSearchView.isIconfiedByDefault());
135     }
136 
137     @Test
testDenyIconifyingNonInconifiableView()138     public void testDenyIconifyingNonInconifiableView() throws Throwable {
139         mActivityRule.runOnUiThread(() -> {
140             mSearchView.setIconifiedByDefault(false);
141             mSearchView.setIconified(false);
142         });
143 
144         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
145                 () -> mSearchView.setIconified(true), true);
146 
147         // Since our search view is marked with iconifiedByDefault=false, call to setIconified
148         // with true us going to be ignored, as detailed in the class-level documentation of
149         // SearchView.
150         assertFalse(mSearchView.isIconified());
151     }
152 
153     @Test
testDenyIconifyingInconifiableView()154     public void testDenyIconifyingInconifiableView() throws Throwable {
155         mActivityRule.runOnUiThread(() -> {
156             mSearchView.setIconifiedByDefault(true);
157             mSearchView.setIconified(false);
158         });
159 
160         final SearchView.OnCloseListener mockDenyCloseListener =
161                 mock(SearchView.OnCloseListener.class);
162         when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
163         mSearchView.setOnCloseListener(mockDenyCloseListener);
164 
165         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
166                 () -> mSearchView.setIconified(true), true);
167 
168         // Our mock listener is configured to return true from its onClose, thereby preventing
169         // the iconify request to be completed. Check that the listener was called and that the
170         // search view is not iconified.
171         verify(mockDenyCloseListener, times(1)).onClose();
172         assertFalse(mSearchView.isIconified());
173     }
174 
175     @Test
testAllowIconifyingInconifiableView()176     public void testAllowIconifyingInconifiableView() throws Throwable {
177         mActivityRule.runOnUiThread(() -> {
178             mSearchView.setIconifiedByDefault(true);
179             mSearchView.setIconified(false);
180         });
181 
182         final SearchView.OnCloseListener mockAllowCloseListener =
183                 mock(SearchView.OnCloseListener.class);
184         when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
185         mSearchView.setOnCloseListener(mockAllowCloseListener);
186 
187         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
188                 () -> mSearchView.setIconified(true), true);
189 
190         // Our mock listener is configured to return false from its onClose, thereby allowing
191         // the iconify request to be completed. Check that the listener was called and that the
192         // search view is not iconified.
193         verify(mockAllowCloseListener, times(1)).onClose();
194         assertTrue(mSearchView.isIconified());
195     }
196 
197     @Test
testAccessMaxWidth()198     public void testAccessMaxWidth() throws Throwable {
199         final Resources res = mActivity.getResources();
200         final int maxWidth1 = res.getDimensionPixelSize(R.dimen.search_view_max_width);
201         final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
202 
203         // Set search view to not be iconified before running max-width tests
204         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
205                 () -> {
206                     mSearchView.setIconified(false);
207                     mSearchView.setMaxWidth(maxWidth1);
208                 }, true);
209         assertEquals(maxWidth1, mSearchView.getMaxWidth());
210         assertTrue(mSearchView.getWidth() <= maxWidth1);
211 
212         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, mSearchView,
213                 () -> mSearchView.setMaxWidth(maxWidth2), true);
214         assertEquals(maxWidth2, mSearchView.getMaxWidth());
215         assertTrue(mSearchView.getWidth() <= maxWidth2);
216     }
217 
218     @UiThreadTest
219     @Test
testAccessQuery()220     public void testAccessQuery() {
221         mSearchView.setIconified(false);
222 
223         final SearchView.OnQueryTextListener mockQueryTextListener =
224                 mock(SearchView.OnQueryTextListener.class);
225         when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
226         mSearchView.setOnQueryTextListener(mockQueryTextListener);
227 
228         mSearchView.setQuery("alpha", false);
229         assertTrue(TextUtils.equals("alpha", mSearchView.getQuery()));
230         // Since we passed false as the second parameter to setQuery, our query text listener
231         // should have been invoked only with text change
232         verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
233         verify(mockQueryTextListener, never()).onQueryTextSubmit(anyString());
234 
235         mSearchView.setQuery("beta", true);
236         assertTrue(TextUtils.equals("beta", mSearchView.getQuery()));
237         // Since we passed true as the second parameter to setQuery, our query text listener
238         // should have been invoked on both callbacks
239         verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
240         verify(mockQueryTextListener, times(1)).onQueryTextSubmit("beta");
241 
242         mSearchView.setQuery("gamma", true);
243         assertTrue(TextUtils.equals("gamma", mSearchView.getQuery()));
244         // Since we passed true as the second parameter to setQuery, our query text listener
245         // should have been invoked on both callbacks
246         verify(mockQueryTextListener, times(1)).onQueryTextChange("gamma");
247         verify(mockQueryTextListener, times(1)).onQueryTextSubmit("gamma");
248 
249         verifyNoMoreInteractions(mockQueryTextListener);
250     }
251 
252     @UiThreadTest
253     @Test
testAccessQueryHint()254     public void testAccessQueryHint() {
255         mSearchView.setQueryHint("hint 1");
256         assertTrue(TextUtils.equals("hint 1", mSearchView.getQueryHint()));
257 
258         mSearchView.setQueryHint("hint 2");
259         assertTrue(TextUtils.equals("hint 2", mSearchView.getQueryHint()));
260     }
261 
262     @UiThreadTest
263     @Test
testAccessInputType()264     public void testAccessInputType() {
265         mSearchView.setInputType(InputType.TYPE_CLASS_NUMBER
266                 | InputType.TYPE_NUMBER_FLAG_DECIMAL
267                 | InputType.TYPE_NUMBER_FLAG_SIGNED);
268         assertEquals(InputType.TYPE_CLASS_NUMBER
269                 | InputType.TYPE_NUMBER_FLAG_DECIMAL
270                 | InputType.TYPE_NUMBER_FLAG_SIGNED, mSearchView.getInputType());
271 
272         mSearchView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
273         assertEquals(InputType.TYPE_CLASS_TEXT
274                 | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mSearchView.getInputType());
275 
276         mSearchView.setInputType(InputType.TYPE_CLASS_PHONE);
277         assertEquals(InputType.TYPE_CLASS_PHONE, mSearchView.getInputType());
278     }
279 
280     @UiThreadTest
281     @Test
testAccessImeOptions()282     public void testAccessImeOptions() {
283         mSearchView.setImeOptions(EditorInfo.IME_ACTION_GO);
284         assertEquals(EditorInfo.IME_ACTION_GO, mSearchView.getImeOptions());
285 
286         mSearchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
287         assertEquals(EditorInfo.IME_ACTION_DONE, mSearchView.getImeOptions());
288 
289         mSearchView.setImeOptions(EditorInfo.IME_NULL);
290         assertEquals(EditorInfo.IME_NULL, mSearchView.getImeOptions());
291     }
292 
293     @Test
testEnterKey()294     public void testEnterKey() throws Throwable {
295         mActivityRule.runOnUiThread(() -> {
296             mSearchView.setIconifiedByDefault(false);
297             mSearchView.setIconified(false);
298         });
299 
300         final SearchView.OnQueryTextListener mockQueryTextListener =
301                 mock(SearchView.OnQueryTextListener.class);
302         when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
303         mSearchView.setOnQueryTextListener(mockQueryTextListener);
304 
305         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
306             mSearchView.setQuery("alpha", false);
307             mSearchView.requestFocus();
308         });
309         mInstrumentation.waitForIdleSync();
310 
311         mCtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mSearchView, KeyEvent.KEYCODE_ENTER);
312 
313         verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
314         verify(mockQueryTextListener, atLeastOnce()).onQueryTextSubmit("alpha");
315 
316         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mSearchView, () -> {
317             mSearchView.setQuery("beta", false);
318             mSearchView.requestFocus();
319         });
320         mInstrumentation.waitForIdleSync();
321 
322         mCtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mSearchView,
323                 KeyEvent.KEYCODE_NUMPAD_ENTER);
324 
325         verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
326         verify(mockQueryTextListener, atLeastOnce()).onQueryTextSubmit("beta");
327 
328         verifyNoMoreInteractions(mockQueryTextListener);
329     }
330 
331 }
332