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