SearchFragmentTest.java revision 5035f8c5590a8eea7bf372a325a4aa0ee1e96149
1/* 2 * Copyright (C) 2017 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 18package com.android.settings.search; 19 20import android.app.LoaderManager; 21import android.content.Context; 22import android.content.Intent; 23import android.content.Loader; 24import android.os.Bundle; 25import android.util.Pair; 26import android.view.View; 27 28import com.android.internal.logging.nano.MetricsProto; 29import com.android.settings.R; 30import com.android.settings.SettingsActivity; 31import com.android.settings.testutils.SettingsRobolectricTestRunner; 32import com.android.settings.TestConfig; 33import com.android.settings.testutils.DatabaseTestUtils; 34import com.android.settings.testutils.FakeFeatureFactory; 35import com.android.settings.testutils.shadow.SettingsShadowResources; 36 37import org.junit.After; 38import org.junit.Before; 39import org.junit.Test; 40import org.junit.runner.RunWith; 41import org.mockito.Answers; 42import org.mockito.ArgumentCaptor; 43import org.mockito.ArgumentMatcher; 44import org.mockito.Captor; 45import org.mockito.Mock; 46import org.mockito.MockitoAnnotations; 47import org.robolectric.Robolectric; 48import org.robolectric.RuntimeEnvironment; 49import org.robolectric.annotation.Config; 50import org.robolectric.util.ActivityController; 51import org.robolectric.util.ReflectionHelpers; 52 53import java.util.Set; 54 55import static com.google.common.truth.Truth.assertThat; 56import static org.mockito.ArgumentMatchers.nullable; 57import static org.mockito.Matchers.any; 58import static org.mockito.Matchers.anyInt; 59import static org.mockito.Matchers.anyString; 60import static org.mockito.Matchers.argThat; 61import static org.mockito.Matchers.eq; 62import static org.mockito.Mockito.mock; 63import static org.mockito.Mockito.never; 64import static org.mockito.Mockito.spy; 65import static org.mockito.Mockito.times; 66import static org.mockito.Mockito.verify; 67import static org.mockito.Mockito.when; 68 69@RunWith(SettingsRobolectricTestRunner.class) 70@Config(manifest = TestConfig.MANIFEST_PATH, 71 sdk = TestConfig.SDK_VERSION, 72 shadows = { 73 SettingsShadowResources.class, 74 SettingsShadowResources.SettingsShadowTheme.class, 75 }) 76public class SearchFragmentTest { 77 78 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 79 private Context mContext; 80 @Mock 81 private DatabaseResultLoader mDatabaseResultLoader; 82 @Mock 83 private InstalledAppResultLoader mInstalledAppResultLoader; 84 @Mock 85 private AccessibilityServiceResultLoader mAccessibilityServiceResultLoader; 86 87 @Mock 88 private SavedQueryLoader mSavedQueryLoader; 89 @Mock 90 private SavedQueryController mSavedQueryController; 91 @Mock 92 private SearchResultsAdapter mSearchResultsAdapter; 93 @Captor 94 private ArgumentCaptor<String> mQueryCaptor = ArgumentCaptor.forClass(String.class); 95 96 private FakeFeatureFactory mFeatureFactory; 97 98 @Before 99 public void setUp() { 100 MockitoAnnotations.initMocks(this); 101 102 mFeatureFactory = FakeFeatureFactory.setupForTest(mContext); 103 } 104 105 @After 106 public void tearDown() { 107 DatabaseTestUtils.clearDb(RuntimeEnvironment.application); 108 } 109 110 @Test 111 public void screenRotate_shouldPersistQuery() { 112 when(mFeatureFactory.searchFeatureProvider 113 .getDatabaseSearchLoader(any(Context.class), anyString())) 114 .thenReturn(mDatabaseResultLoader); 115 when(mFeatureFactory.searchFeatureProvider 116 .getInstalledAppSearchLoader(any(Context.class), anyString())) 117 .thenReturn(mInstalledAppResultLoader); 118 when(mFeatureFactory.searchFeatureProvider 119 .getAccessibilityServiceResultLoader(any(Context.class), anyString())) 120 .thenReturn(mAccessibilityServiceResultLoader); 121 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 122 .thenReturn(mSavedQueryLoader); 123 124 final Bundle bundle = new Bundle(); 125 final String testQuery = "test"; 126 ActivityController<SearchActivity> activityController = 127 Robolectric.buildActivity(SearchActivity.class); 128 activityController.setup(); 129 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 130 .findFragmentById(R.id.main_content); 131 132 ReflectionHelpers.setField(fragment, "mShowingSavedQuery", false); 133 fragment.mQuery = testQuery; 134 135 activityController.saveInstanceState(bundle).pause().stop().destroy(); 136 137 activityController = Robolectric.buildActivity(SearchActivity.class); 138 activityController.setup(bundle); 139 140 assertThat(fragment.mQuery).isEqualTo(testQuery); 141 } 142 143 @Test 144 public void screenRotateEmptyString_ShouldNotCrash() { 145 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 146 .thenReturn(mSavedQueryLoader); 147 148 final Bundle bundle = new Bundle(); 149 ActivityController<SearchActivity> activityController = 150 Robolectric.buildActivity(SearchActivity.class); 151 activityController.setup(); 152 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 153 .findFragmentById(R.id.main_content); 154 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 155 .thenReturn(true); 156 157 fragment.mQuery = ""; 158 159 activityController.saveInstanceState(bundle).pause().stop().destroy(); 160 161 activityController = Robolectric.buildActivity(SearchActivity.class); 162 activityController.setup(bundle); 163 164 verify(mFeatureFactory.searchFeatureProvider, never()) 165 .getDatabaseSearchLoader(any(Context.class), anyString()); 166 verify(mFeatureFactory.searchFeatureProvider, never()) 167 .getInstalledAppSearchLoader(any(Context.class), anyString()); 168 } 169 170 @Test 171 public void queryTextChange_shouldTriggerLoaderAndInitializeSearch() { 172 when(mFeatureFactory.searchFeatureProvider 173 .getDatabaseSearchLoader(any(Context.class), anyString())) 174 .thenReturn(mDatabaseResultLoader); 175 when(mFeatureFactory.searchFeatureProvider 176 .getInstalledAppSearchLoader(any(Context.class), anyString())) 177 .thenReturn(mInstalledAppResultLoader); 178 when(mFeatureFactory.searchFeatureProvider 179 .getAccessibilityServiceResultLoader(any(Context.class), anyString())) 180 .thenReturn(mAccessibilityServiceResultLoader); 181 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 182 .thenReturn(mSavedQueryLoader); 183 184 final String testQuery = "test"; 185 ActivityController<SearchActivity> activityController = 186 Robolectric.buildActivity(SearchActivity.class); 187 activityController.setup(); 188 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 189 .findFragmentById(R.id.main_content); 190 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 191 .thenReturn(true); 192 193 ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter); 194 fragment.onQueryTextChange(testQuery); 195 activityController.get().onBackPressed(); 196 197 activityController.pause().stop().destroy(); 198 199 verify(mFeatureFactory.metricsFeatureProvider, never()).action( 200 any(Context.class), 201 eq(MetricsProto.MetricsEvent.ACTION_LEAVE_SEARCH_RESULT_WITHOUT_QUERY)); 202 verify(mFeatureFactory.metricsFeatureProvider).histogram( 203 any(Context.class), eq(SearchFragment.RESULT_CLICK_COUNT), eq(0)); 204 verify(mFeatureFactory.searchFeatureProvider) 205 .getDatabaseSearchLoader(any(Context.class), anyString()); 206 verify(mFeatureFactory.searchFeatureProvider) 207 .getInstalledAppSearchLoader(any(Context.class), anyString()); 208 verify(mSearchResultsAdapter).initializeSearch(mQueryCaptor.capture()); 209 assertThat(mQueryCaptor.getValue()).isEqualTo(testQuery); 210 } 211 212 @Test 213 public void onSearchResultsDisplayed_noResult_shouldShowNoResultView() { 214 ActivityController<SearchActivity> activityController = 215 Robolectric.buildActivity(SearchActivity.class); 216 activityController.setup(); 217 SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager() 218 .findFragmentById(R.id.main_content)); 219 fragment.onSearchResultsDisplayed(0 /* count */); 220 221 assertThat(fragment.mNoResultsView.getVisibility()).isEqualTo(View.VISIBLE); 222 verify(mFeatureFactory.metricsFeatureProvider).visible( 223 any(Context.class), 224 anyInt(), 225 eq(MetricsProto.MetricsEvent.SETTINGS_SEARCH_NO_RESULT)); 226 } 227 228 @Test 229 public void queryTextChangeToEmpty_shouldLoadSavedQueryAndNotInitializeSearch() { 230 when(mFeatureFactory.searchFeatureProvider 231 .getDatabaseSearchLoader(any(Context.class), anyString())) 232 .thenReturn(mDatabaseResultLoader); 233 when(mFeatureFactory.searchFeatureProvider 234 .getInstalledAppSearchLoader(any(Context.class), anyString())) 235 .thenReturn(mInstalledAppResultLoader); 236 when(mFeatureFactory.searchFeatureProvider 237 .getAccessibilityServiceResultLoader(any(Context.class), anyString())) 238 .thenReturn(mAccessibilityServiceResultLoader); 239 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 240 .thenReturn(mSavedQueryLoader); 241 ActivityController<SearchActivity> activityController = 242 Robolectric.buildActivity(SearchActivity.class); 243 activityController.setup(); 244 SearchFragment fragment = spy((SearchFragment) activityController.get().getFragmentManager() 245 .findFragmentById(R.id.main_content)); 246 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 247 .thenReturn(true); 248 ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController); 249 ReflectionHelpers.setField(fragment, "mSearchAdapter", mSearchResultsAdapter); 250 fragment.mQuery = "123"; 251 252 fragment.onQueryTextChange(""); 253 254 verify(mFeatureFactory.searchFeatureProvider, never()) 255 .getDatabaseSearchLoader(any(Context.class), anyString()); 256 verify(mFeatureFactory.searchFeatureProvider, never()) 257 .getInstalledAppSearchLoader(any(Context.class), anyString()); 258 verify(mSavedQueryController).loadSavedQueries(); 259 verify(mSearchResultsAdapter, never()).initializeSearch(anyString()); 260 } 261 262 @Test 263 public void updateIndex_TriggerOnCreate() { 264 when(mFeatureFactory.searchFeatureProvider 265 .getDatabaseSearchLoader(any(Context.class), anyString())) 266 .thenReturn(mDatabaseResultLoader); 267 when(mFeatureFactory.searchFeatureProvider 268 .getInstalledAppSearchLoader(any(Context.class), anyString())) 269 .thenReturn(mInstalledAppResultLoader); 270 when(mFeatureFactory.searchFeatureProvider 271 .getAccessibilityServiceResultLoader(any(Context.class), anyString())) 272 .thenReturn(mAccessibilityServiceResultLoader); 273 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 274 .thenReturn(mSavedQueryLoader); 275 276 ActivityController<SearchActivity> activityController = 277 Robolectric.buildActivity(SearchActivity.class); 278 activityController.setup(); 279 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 280 .findFragmentById(R.id.main_content); 281 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 282 .thenReturn(true); 283 284 fragment.onAttach(null); 285 verify(mFeatureFactory.searchFeatureProvider).updateIndexAsync(any(Context.class), 286 any(IndexingCallback.class)); 287 } 288 289 @Test 290 public void syncLoaders_MergeWhenAllLoadersDone() { 291 when(mFeatureFactory.searchFeatureProvider 292 .getDatabaseSearchLoader(any(Context.class), anyString())) 293 .thenReturn(new MockDBLoader(RuntimeEnvironment.application)); 294 when(mFeatureFactory.searchFeatureProvider 295 .getInstalledAppSearchLoader(any(Context.class), anyString())) 296 .thenReturn(new MockAppLoader(RuntimeEnvironment.application)); 297 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 298 .thenReturn(mSavedQueryLoader); 299 300 ActivityController<SearchActivity> activityController = 301 Robolectric.buildActivity(SearchActivity.class); 302 activityController.setup(); 303 304 SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() 305 .findFragmentById(R.id.main_content)); 306 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 307 .thenReturn(true); 308 309 fragment.onQueryTextChange("non-empty"); 310 311 Robolectric.flushForegroundThreadScheduler(); 312 313 verify(fragment, times(2)).onLoadFinished(any(Loader.class), any(Set.class)); 314 } 315 316 @Test 317 public void whenNoQuery_HideFeedbackIsCalled() { 318 when(mFeatureFactory.searchFeatureProvider 319 .getDatabaseSearchLoader(any(Context.class), anyString())) 320 .thenReturn(new MockDBLoader(RuntimeEnvironment.application)); 321 when(mFeatureFactory.searchFeatureProvider 322 .getInstalledAppSearchLoader(any(Context.class), anyString())) 323 .thenReturn(new MockAppLoader(RuntimeEnvironment.application)); 324 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 325 .thenReturn(mSavedQueryLoader); 326 327 ActivityController<SearchActivity> activityController = 328 Robolectric.buildActivity(SearchActivity.class); 329 activityController.setup(); 330 SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() 331 .findFragmentById(R.id.main_content)); 332 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 333 .thenReturn(true); 334 when(fragment.getLoaderManager()).thenReturn(mock(LoaderManager.class)); 335 336 fragment.onQueryTextChange(""); 337 Robolectric.flushForegroundThreadScheduler(); 338 339 verify(mFeatureFactory.searchFeatureProvider).hideFeedbackButton(); 340 } 341 342 @Test 343 public void onLoadFinished_ShowsFeedback() { 344 when(mFeatureFactory.searchFeatureProvider 345 .getDatabaseSearchLoader(any(Context.class), anyString())) 346 .thenReturn(new MockDBLoader(RuntimeEnvironment.application)); 347 when(mFeatureFactory.searchFeatureProvider 348 .getInstalledAppSearchLoader(any(Context.class), anyString())) 349 .thenReturn(new MockAppLoader(RuntimeEnvironment.application)); 350 when(mFeatureFactory.searchFeatureProvider 351 .getAccessibilityServiceResultLoader(any(Context.class), anyString())) 352 .thenReturn(new MockAccessiblityLoader(RuntimeEnvironment.application)); 353 when(mFeatureFactory.searchFeatureProvider.getSavedQueryLoader(any(Context.class))) 354 .thenReturn(mSavedQueryLoader); 355 ActivityController<SearchActivity> activityController = 356 Robolectric.buildActivity(SearchActivity.class); 357 activityController.setup(); 358 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 359 .findFragmentById(R.id.main_content); 360 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 361 .thenReturn(true); 362 363 fragment.onQueryTextChange("non-empty"); 364 Robolectric.flushForegroundThreadScheduler(); 365 366 verify(mFeatureFactory.searchFeatureProvider).showFeedbackButton(any(SearchFragment.class), 367 any(View.class)); 368 } 369 370 @Test 371 public void preIndexingFinished_isIndexingFinishedFlag_isFalse() { 372 ActivityController<SearchActivity> activityController = 373 Robolectric.buildActivity(SearchActivity.class); 374 activityController.setup(); 375 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 376 .findFragmentById(R.id.main_content); 377 378 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 379 .thenReturn(false); 380 } 381 382 @Test 383 public void onIndexingFinished_notShowingSavedQuery_initLoaders() { 384 ActivityController<SearchActivity> activityController = 385 Robolectric.buildActivity(SearchActivity.class); 386 activityController.setup(); 387 SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() 388 .findFragmentById(R.id.main_content)); 389 final LoaderManager loaderManager = mock(LoaderManager.class); 390 when(fragment.getLoaderManager()).thenReturn(loaderManager); 391 fragment.mShowingSavedQuery = false; 392 fragment.mQuery = null; 393 394 fragment.onIndexingFinished(); 395 396 verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_DATABASE), 397 eq(null), any(LoaderManager.LoaderCallbacks.class)); 398 verify(loaderManager).initLoader(eq(SearchFragment.LOADER_ID_INSTALLED_APPS), 399 eq(null), any(LoaderManager.LoaderCallbacks.class)); 400 } 401 402 @Test 403 public void onIndexingFinished_showingSavedQuery_loadsSavedQueries() { 404 ActivityController<SearchActivity> activityController = 405 Robolectric.buildActivity(SearchActivity.class); 406 activityController.setup(); 407 SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() 408 .findFragmentById(R.id.main_content)); 409 fragment.mShowingSavedQuery = true; 410 ReflectionHelpers.setField(fragment, "mSavedQueryController", mSavedQueryController); 411 412 fragment.onIndexingFinished(); 413 414 verify(fragment.mSavedQueryController).loadSavedQueries(); 415 } 416 417 @Test 418 public void onIndexingFinished_noActivity_shouldNotCrash() { 419 ActivityController<SearchActivity> activityController = 420 Robolectric.buildActivity(SearchActivity.class); 421 activityController.setup(); 422 SearchFragment fragment = (SearchFragment) spy(activityController.get().getFragmentManager() 423 .findFragmentById(R.id.main_content)); 424 when(mFeatureFactory.searchFeatureProvider.isIndexingComplete(any(Context.class))) 425 .thenReturn(true); 426 fragment.mQuery = "bright"; 427 ReflectionHelpers.setField(fragment, "mLoaderManager", null); 428 ReflectionHelpers.setField(fragment, "mHost", null); 429 430 fragment.onIndexingFinished(); 431 // no crash 432 } 433 434 @Test 435 public void onSearchResultClicked_shouldLogResultMeta() { 436 SearchFragment fragment = new SearchFragment(); 437 ReflectionHelpers.setField(fragment, "mMetricsFeatureProvider", 438 mFeatureFactory.metricsFeatureProvider); 439 ReflectionHelpers.setField(fragment, "mSearchFeatureProvider", 440 mFeatureFactory.searchFeatureProvider); 441 ReflectionHelpers.setField(fragment, "mSearchAdapter", mock(SearchResultsAdapter.class)); 442 fragment.mSavedQueryController = mock(SavedQueryController.class); 443 444 // Should log result name, result count, clicked rank, etc. 445 final SearchViewHolder resultViewHolder = mock(SearchViewHolder.class); 446 when(resultViewHolder.getClickActionMetricName()) 447 .thenReturn(MetricsProto.MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT); 448 ResultPayload payLoad = new ResultPayload( 449 (new Intent()).putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, "test_setting")); 450 SearchResult searchResult = new SearchResult.Builder() 451 .setStableId(payLoad.hashCode()) 452 .setPayload(payLoad) 453 .setTitle("setting_title") 454 .build(); 455 fragment.onSearchResultClicked(resultViewHolder, searchResult); 456 457 verify(mFeatureFactory.metricsFeatureProvider).action( 458 nullable(Context.class), 459 eq(MetricsProto.MetricsEvent.ACTION_CLICK_SETTINGS_SEARCH_RESULT), 460 eq("test_setting"), 461 argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_COUNT)), 462 argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_RESULT_RANK)), 463 argThat(pairMatches(MetricsProto.MetricsEvent 464 .FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE)), 465 argThat(pairMatches(MetricsProto.MetricsEvent.FIELD_SETTINGS_SEARCH_QUERY_LENGTH))); 466 467 verify(mFeatureFactory.searchFeatureProvider).searchResultClicked(nullable(Context.class), 468 nullable(String.class), eq(searchResult)); 469 } 470 471 @Test 472 public void onResume_shouldCallSearchRankingWarmupIfSmartSearchRankingEnabled(){ 473 when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class))) 474 .thenReturn(true); 475 476 ActivityController<SearchActivity> activityController = 477 Robolectric.buildActivity(SearchActivity.class); 478 activityController.setup(); 479 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 480 .findFragmentById(R.id.main_content); 481 482 verify(mFeatureFactory.searchFeatureProvider) 483 .searchRankingWarmup(any(Context.class)); 484 } 485 486 @Test 487 public void onResume_shouldNotCallSearchRankingWarmupIfSmartSearchRankingDisabled(){ 488 when(mFeatureFactory.searchFeatureProvider.isSmartSearchRankingEnabled(any(Context.class))) 489 .thenReturn(false); 490 491 ActivityController<SearchActivity> activityController = 492 Robolectric.buildActivity(SearchActivity.class); 493 activityController.setup(); 494 SearchFragment fragment = (SearchFragment) activityController.get().getFragmentManager() 495 .findFragmentById(R.id.main_content); 496 497 verify(mFeatureFactory.searchFeatureProvider, never()) 498 .searchRankingWarmup(any(Context.class)); 499 } 500 501 private ArgumentMatcher<Pair<Integer, Object>> pairMatches(int tag) { 502 return pair -> pair.first == tag; 503 } 504} 505