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 */ 16package com.android.settings.dashboard; 17 18import static com.google.common.truth.Truth.assertThat; 19import static org.mockito.ArgumentMatchers.anyInt; 20import static org.mockito.ArgumentMatchers.eq; 21import static org.mockito.Mockito.any; 22import static org.mockito.Mockito.doReturn; 23import static org.mockito.Mockito.mock; 24import static org.mockito.Mockito.never; 25import static org.mockito.Mockito.reset; 26import static org.mockito.Mockito.spy; 27import static org.mockito.Mockito.times; 28import static org.mockito.Mockito.verify; 29import static org.mockito.Mockito.when; 30 31import android.content.ComponentName; 32import android.content.Context; 33import android.content.Intent; 34import android.content.res.Resources; 35import android.content.res.TypedArray; 36import android.graphics.drawable.Icon; 37import android.os.Bundle; 38import android.support.v7.widget.RecyclerView; 39import android.util.DisplayMetrics; 40import android.util.Pair; 41import android.view.LayoutInflater; 42import android.view.View; 43import android.widget.RelativeLayout; 44 45import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 46import com.android.settings.R; 47import com.android.settings.SettingsActivity; 48import com.android.settings.TestConfig; 49import com.android.settings.dashboard.conditional.Condition; 50import com.android.settings.dashboard.conditional.ConditionAdapter; 51import com.android.settings.dashboard.suggestions.SuggestionAdapter; 52import com.android.settings.testutils.FakeFeatureFactory; 53import com.android.settings.testutils.SettingsRobolectricTestRunner; 54import com.android.settings.testutils.shadow.SettingsShadowResources; 55import com.android.settingslib.drawer.DashboardCategory; 56import com.android.settingslib.drawer.Tile; 57 58import org.junit.Before; 59import org.junit.Test; 60import org.junit.runner.RunWith; 61import org.mockito.Answers; 62import org.mockito.ArgumentCaptor; 63import org.mockito.Captor; 64import org.mockito.Mock; 65import org.mockito.MockitoAnnotations; 66import org.robolectric.RuntimeEnvironment; 67import org.robolectric.annotation.Config; 68 69import java.util.ArrayList; 70import java.util.List; 71 72@RunWith(SettingsRobolectricTestRunner.class) 73@Config(manifest = TestConfig.MANIFEST_PATH, 74 sdk = TestConfig.SDK_VERSION, 75 shadows = { 76 SettingsShadowResources.class, 77 SettingsShadowResources.SettingsShadowTheme.class, 78 }) 79public class DashboardAdapterTest { 80 81 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 82 private SettingsActivity mContext; 83 @Mock 84 private View mView; 85 @Mock 86 private Condition mCondition; 87 @Mock 88 private Resources mResources; 89 @Captor 90 private ArgumentCaptor<Integer> mActionCategoryCaptor = ArgumentCaptor.forClass(Integer.class); 91 @Captor 92 private ArgumentCaptor<String> mActionPackageCaptor = ArgumentCaptor.forClass(String.class); 93 @Captor 94 private ArgumentCaptor<Pair> mTaggedDataCaptor = ArgumentCaptor.forClass(Pair.class); 95 private FakeFeatureFactory mFactory; 96 private DashboardAdapter mDashboardAdapter; 97 private DashboardAdapter.SuggestionAndConditionHeaderHolder mSuggestionHolder; 98 private DashboardData.SuggestionConditionHeaderData mSuggestionHeaderData; 99 private List<Condition> mConditionList; 100 101 @Before 102 public void setUp() { 103 MockitoAnnotations.initMocks(this); 104 FakeFeatureFactory.setupForTest(mContext); 105 mFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext); 106 when(mFactory.dashboardFeatureProvider.shouldTintIcon()).thenReturn(true); 107 when(mFactory.suggestionsFeatureProvider 108 .getSuggestionIdentifier(any(Context.class), any(Tile.class))) 109 .thenAnswer(invocation -> { 110 final Object[] args = invocation.getArguments(); 111 return ((Tile)args[1]).intent.getComponent().getPackageName(); 112 }); 113 114 when(mContext.getResources()).thenReturn(mResources); 115 when(mResources.getQuantityString(any(int.class), any(int.class), any())) 116 .thenReturn(""); 117 118 mConditionList = new ArrayList<>(); 119 mConditionList.add(mCondition); 120 when(mCondition.shouldShow()).thenReturn(true); 121 mDashboardAdapter = new DashboardAdapter(mContext, null, mConditionList, null, null); 122 mSuggestionHeaderData = new DashboardData.SuggestionConditionHeaderData(mConditionList, 1); 123 when(mView.getTag()).thenReturn(mCondition); 124 } 125 126 @Test 127 public void testSuggestionsLogs_NotExpanded() { 128 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 129 130 verify(mFactory.metricsFeatureProvider, times(2)).action( 131 any(Context.class), mActionCategoryCaptor.capture(), 132 mActionPackageCaptor.capture(), 133 mTaggedDataCaptor.capture()); 134 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 135 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 136 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION); 137 assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg2"); 138 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 139 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 140 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 141 } 142 143 @Test 144 public void testSuggestionsLogs_NotExpandedAndPaused() { 145 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 146 147 mDashboardAdapter.onPause(); 148 149 verify(mFactory.metricsFeatureProvider, times(4)).action( 150 any(Context.class), mActionCategoryCaptor.capture(), 151 mActionPackageCaptor.capture(), 152 mTaggedDataCaptor.capture()); 153 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 154 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 155 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 156 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 157 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 158 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 159 "pkg1", "pkg2", "pkg1", "pkg2"); 160 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 161 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 162 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 163 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 164 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 165 } 166 167 @Test 168 public void testSuggestionsLogs_Expanded() { 169 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 170 171 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 172 mSuggestionHolder.itemView.callOnClick(); 173 174 verify(mFactory.metricsFeatureProvider, times(3)).action( 175 any(Context.class), mActionCategoryCaptor.capture(), 176 mActionPackageCaptor.capture(), 177 mTaggedDataCaptor.capture()); 178 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 179 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 180 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 181 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION); 182 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 183 "pkg1", "pkg2", "pkg3"); 184 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 185 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 186 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 187 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 188 } 189 190 @Test 191 public void testSuggestionsLogs_ExpandedAndPaused() { 192 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 193 194 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 195 mSuggestionHolder.itemView.callOnClick(); 196 mDashboardAdapter.onPause(); 197 198 verify(mFactory.metricsFeatureProvider, times(6)).action( 199 any(Context.class), mActionCategoryCaptor.capture(), 200 mActionPackageCaptor.capture(), 201 mTaggedDataCaptor.capture()); 202 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 203 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 204 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 205 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 206 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 207 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 208 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 209 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 210 "pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3"); 211 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 212 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 213 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 214 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 215 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 216 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 217 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 218 } 219 220 @Test 221 public void testSuggestionsLogs_ExpandedAfterPause() { 222 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 223 224 mDashboardAdapter.onPause(); 225 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 226 mSuggestionHolder.itemView.callOnClick(); 227 228 verify(mFactory.metricsFeatureProvider, times(7)).action( 229 any(Context.class), mActionCategoryCaptor.capture(), 230 mActionPackageCaptor.capture(), 231 mTaggedDataCaptor.capture()); 232 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 233 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 234 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 235 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 236 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 237 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 238 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 239 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION); 240 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 241 "pkg1", "pkg2", "pkg1", "pkg2", "pkg1", "pkg2", "pkg3"); 242 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 243 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 244 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 245 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 246 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 247 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 248 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 249 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 250 } 251 252 @Test 253 public void testSuggestionsLogs_ExpandedAfterPauseAndPausedAgain() { 254 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3")); 255 256 mDashboardAdapter.onPause(); 257 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 258 mSuggestionHolder.itemView.callOnClick(); 259 mDashboardAdapter.onPause(); 260 261 verify(mFactory.metricsFeatureProvider, times(10)).action( 262 any(Context.class), mActionCategoryCaptor.capture(), 263 mActionPackageCaptor.capture(), 264 mTaggedDataCaptor.capture()); 265 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 266 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 267 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 268 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 269 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 270 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 271 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 272 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 273 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 274 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 275 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 276 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 277 "pkg1", "pkg2", "pkg1", "pkg2", "pkg1", "pkg2", "pkg3", "pkg1", "pkg2", "pkg3"); 278 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 279 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 280 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 281 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 282 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 283 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 284 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 285 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 286 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 287 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 288 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 289 } 290 291 @Test 292 public void testSuggestionsLogs_ExpandedWithLessThanDefaultShown() { 293 setupSuggestions(makeSuggestions("pkg1")); 294 295 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 296 mSuggestionHolder.itemView.callOnClick(); 297 298 verify(mFactory.metricsFeatureProvider, times(1)).action( 299 any(Context.class), mActionCategoryCaptor.capture(), 300 mActionPackageCaptor.capture(), 301 mTaggedDataCaptor.capture()); 302 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 303 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION); 304 assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1"); 305 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 306 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 307 } 308 309 @Test 310 public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAndPaused() { 311 setupSuggestions(makeSuggestions("pkg1")); 312 313 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 314 mSuggestionHolder.itemView.callOnClick(); 315 mDashboardAdapter.onPause(); 316 317 verify(mFactory.metricsFeatureProvider, times(2)).action( 318 any(Context.class), mActionCategoryCaptor.capture(), 319 mActionPackageCaptor.capture(), 320 mTaggedDataCaptor.capture()); 321 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 322 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 323 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 324 assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1"); 325 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 326 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 327 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 328 } 329 330 @Test 331 public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAfterPause() { 332 setupSuggestions(makeSuggestions("pkg1")); 333 334 mDashboardAdapter.onPause(); 335 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 336 mSuggestionHolder.itemView.callOnClick(); 337 338 verify(mFactory.metricsFeatureProvider, times(3)).action( 339 any(Context.class), mActionCategoryCaptor.capture(), 340 mActionPackageCaptor.capture(), 341 mTaggedDataCaptor.capture()); 342 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 343 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 344 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 345 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION); 346 assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1", "pkg1"); 347 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 348 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 349 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 350 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 351 } 352 353 @Test 354 public void testSuggestionsLogs_ExpandedWithLessThanDefaultShownAfterPauseAndPausedAgain() { 355 setupSuggestions(makeSuggestions("pkg1")); 356 mDashboardAdapter.onPause(); 357 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 358 mSuggestionHolder.itemView.callOnClick(); 359 mDashboardAdapter.onPause(); 360 361 verify(mFactory.metricsFeatureProvider, times(4)).action( 362 any(Context.class), mActionCategoryCaptor.capture(), 363 mActionPackageCaptor.capture(), 364 mTaggedDataCaptor.capture()); 365 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 366 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 367 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION, 368 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 369 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 370 assertThat(mActionPackageCaptor.getAllValues()).containsExactly( 371 "pkg1", "pkg1", "pkg1", "pkg1"); 372 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 373 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 374 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 375 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0), 376 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 0)); 377 } 378 379 @Test 380 public void testSuggestionsLogs_SmartSuggestionEnabled() { 381 when(mFactory.suggestionsFeatureProvider 382 .isSmartSuggestionEnabled(any(Context.class))).thenReturn(true); 383 setupSuggestions(makeSuggestions("pkg1")); 384 385 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 386 mSuggestionHolder.itemView.callOnClick(); 387 mDashboardAdapter.onPause(); 388 389 verify(mFactory.metricsFeatureProvider, times(2)).action( 390 any(Context.class), mActionCategoryCaptor.capture(), 391 mActionPackageCaptor.capture(), 392 mTaggedDataCaptor.capture()); 393 assertThat(mActionCategoryCaptor.getAllValues()).containsExactly( 394 MetricsEvent.ACTION_SHOW_SETTINGS_SUGGESTION, 395 MetricsEvent.ACTION_HIDE_SETTINGS_SUGGESTION); 396 assertThat(mActionPackageCaptor.getAllValues()).containsExactly("pkg1", "pkg1"); 397 assertThat(mTaggedDataCaptor.getAllValues()).containsExactly( 398 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 1), 399 Pair.create(MetricsEvent.FIELD_SETTINGS_SMART_SUGGESTIONS_ENABLED, 1)); 400 } 401 402 @Test 403 public void testSuggestionsLogs_nullSuggestionsList_shouldNotCrash() { 404 setupSuggestions(makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5")); 405 mDashboardAdapter.onBindSuggestionConditionHeader(mSuggestionHolder, mSuggestionHeaderData); 406 407 // set suggestions to null 408 final DashboardData prevData = mDashboardAdapter.mDashboardData; 409 mDashboardAdapter.mDashboardData = new DashboardData.Builder(prevData) 410 .setSuggestions(null) 411 .build(); 412 413 mSuggestionHolder.itemView.callOnClick(); 414 // no crash 415 } 416 417 @Test 418 public void testSuggestionDismissed_notOnlySuggestion_updateSuggestionOnly() { 419 final DashboardAdapter adapter = 420 spy(new DashboardAdapter(mContext, null, null, null, null)); 421 final List<Tile> suggestions = makeSuggestions("pkg1", "pkg2", "pkg3"); 422 adapter.setCategoriesAndSuggestions(null /* category */, suggestions); 423 424 final RecyclerView data = mock(RecyclerView.class); 425 when(data.getResources()).thenReturn(mResources); 426 when(data.getContext()).thenReturn(mContext); 427 when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class)); 428 final View itemView = mock(View.class); 429 when(itemView.findViewById(R.id.data)).thenReturn(data); 430 final DashboardAdapter.SuggestionAndConditionContainerHolder holder = 431 new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView); 432 433 adapter.onBindConditionAndSuggestion( 434 holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION); 435 436 final DashboardData dashboardData = adapter.mDashboardData; 437 reset(adapter); // clear interactions tracking 438 439 final Tile suggestionToRemove = suggestions.get(1); 440 adapter.onSuggestionDismissed(suggestionToRemove); 441 442 assertThat(adapter.mDashboardData).isEqualTo(dashboardData); 443 assertThat(suggestions.size()).isEqualTo(2); 444 assertThat(suggestions.contains(suggestionToRemove)).isFalse(); 445 verify(adapter, never()).notifyDashboardDataChanged(any()); 446 } 447 448 @Test 449 public void testSuggestionDismissed_moreThanTwoSuggestions_defaultMode_shouldNotCrash() { 450 final RecyclerView data = new RecyclerView(RuntimeEnvironment.application); 451 final View itemView = mock(View.class); 452 when(itemView.findViewById(R.id.data)).thenReturn(data); 453 final DashboardAdapter.SuggestionAndConditionContainerHolder holder = 454 new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView); 455 final List<Tile> suggestions = 456 makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4"); 457 final DashboardAdapter adapter = spy(new DashboardAdapter(mContext, null /*savedInstance */, 458 null /* conditions */, null /* suggestionParser */, null /* callback */)); 459 adapter.setCategoriesAndSuggestions(null /* category */, suggestions); 460 adapter.onBindConditionAndSuggestion( 461 holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION); 462 // default mode, only displaying 2 suggestions 463 464 adapter.onSuggestionDismissed(suggestions.get(1)); 465 466 // verify operations that access the lists will not cause ConcurrentModificationException 467 assertThat(holder.data.getAdapter().getItemCount()).isEqualTo(1); 468 adapter.setCategoriesAndSuggestions(null /* category */, suggestions); 469 // should not crash 470 } 471 472 @Test 473 public void testSuggestionDismissed_onlySuggestion_updateDashboardData() { 474 DashboardAdapter adapter = 475 spy(new DashboardAdapter(mContext, null, null, null, null)); 476 final List<Tile> suggestions = makeSuggestions("pkg1"); 477 adapter.setCategoriesAndSuggestions(null /* category */, suggestions); 478 final DashboardData dashboardData = adapter.mDashboardData; 479 reset(adapter); // clear interactions tracking 480 481 adapter.onSuggestionDismissed(suggestions.get(0)); 482 483 assertThat(adapter.mDashboardData).isNotEqualTo(dashboardData); 484 verify(adapter).notifyDashboardDataChanged(any()); 485 } 486 487 @Test 488 public void testSetCategoriesAndSuggestions_iconTinted() { 489 TypedArray mockTypedArray = mock(TypedArray.class); 490 doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class)); 491 doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt()); 492 493 List<Tile> packages = makeSuggestions("pkg1"); 494 Icon mockIcon = mock(Icon.class); 495 packages.get(0).isIconTintable = true; 496 packages.get(0).icon = mockIcon; 497 498 mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages); 499 500 verify(mockIcon).setTint(eq(0x89000000)); 501 } 502 503 @Test 504 public void testSetCategories_iconTinted() { 505 TypedArray mockTypedArray = mock(TypedArray.class); 506 doReturn(mockTypedArray).when(mContext).obtainStyledAttributes(any(int[].class)); 507 doReturn(0x89000000).when(mockTypedArray).getColor(anyInt(), anyInt()); 508 509 final DashboardCategory category = mock(DashboardCategory.class); 510 final List<Tile> tiles = new ArrayList<>(); 511 final Icon mockIcon = mock(Icon.class); 512 final Tile tile = new Tile(); 513 tile.isIconTintable = true; 514 tile.icon = mockIcon; 515 tiles.add(tile); 516 category.tiles = tiles; 517 518 mDashboardAdapter.setCategory(category); 519 520 verify(mockIcon).setTint(eq(0x89000000)); 521 } 522 523 @Test 524 public void testSetCategoriesAndSuggestions_limitSuggestionSize() { 525 List<Tile> packages = 526 makeSuggestions("pkg1", "pkg2", "pkg3", "pkg4", "pkg5", "pkg6", "pkg7"); 527 mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, packages); 528 529 assertThat(mDashboardAdapter.mDashboardData.getSuggestions().size()) 530 .isEqualTo(DashboardAdapter.MAX_SUGGESTION_TO_SHOW); 531 } 532 533 @Test 534 public void testBindConditionAndSuggestion_shouldSetSuggestionAdapterAndNoCrash() { 535 mDashboardAdapter = new DashboardAdapter(mContext, null, null, null, null); 536 final List<Tile> suggestions = makeSuggestions("pkg1"); 537 final DashboardCategory category = mock(DashboardCategory.class); 538 final List<Tile> tiles = new ArrayList<>(); 539 tiles.add(mock(Tile.class)); 540 category.tiles = tiles; 541 542 mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions); 543 544 final RecyclerView data = mock(RecyclerView.class); 545 when(data.getResources()).thenReturn(mResources); 546 when(data.getContext()).thenReturn(mContext); 547 when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class)); 548 final View itemView = mock(View.class); 549 when(itemView.findViewById(R.id.data)).thenReturn(data); 550 final DashboardAdapter.SuggestionAndConditionContainerHolder holder = 551 new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView); 552 553 mDashboardAdapter.onBindConditionAndSuggestion( 554 holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION); 555 556 verify(data).setAdapter(any(SuggestionAdapter.class)); 557 // should not crash 558 } 559 560 @Test 561 public void testBindConditionAndSuggestion_emptySuggestion_shouldSetConditionAdpater() { 562 final Bundle savedInstance = new Bundle(); 563 savedInstance.putInt(DashboardAdapter.STATE_SUGGESTION_CONDITION_MODE, 564 DashboardData.HEADER_MODE_FULLY_EXPANDED); 565 mDashboardAdapter = new DashboardAdapter(mContext, savedInstance, mConditionList, 566 null /* SuggestionParser */, null /* SuggestionDismissController.Callback */); 567 568 final List<Tile> suggestions = new ArrayList<>(); 569 final DashboardCategory category = mock(DashboardCategory.class); 570 final List<Tile> tiles = new ArrayList<>(); 571 tiles.add(mock(Tile.class)); 572 category.tiles = tiles; 573 mDashboardAdapter.setCategoriesAndSuggestions(category, suggestions); 574 575 final RecyclerView data = mock(RecyclerView.class); 576 when(data.getResources()).thenReturn(mResources); 577 when(data.getContext()).thenReturn(mContext); 578 when(mResources.getDisplayMetrics()).thenReturn(mock(DisplayMetrics.class)); 579 final View itemView = mock(View.class); 580 when(itemView.findViewById(R.id.data)).thenReturn(data); 581 final DashboardAdapter.SuggestionAndConditionContainerHolder holder = 582 new DashboardAdapter.SuggestionAndConditionContainerHolder(itemView); 583 584 mDashboardAdapter.onBindConditionAndSuggestion( 585 holder, DashboardAdapter.SUGGESTION_CONDITION_HEADER_POSITION); 586 587 verify(data).setAdapter(any(ConditionAdapter.class)); 588 } 589 590 private List<Tile> makeSuggestions(String... pkgNames) { 591 final List<Tile> suggestions = new ArrayList<>(); 592 for (String pkgName : pkgNames) { 593 Tile suggestion = new Tile(); 594 suggestion.intent = new Intent("action"); 595 suggestion.intent.setComponent(new ComponentName(pkgName, "cls")); 596 suggestions.add(suggestion); 597 } 598 return suggestions; 599 } 600 601 private void setupSuggestions(List<Tile> suggestions) { 602 mDashboardAdapter.setCategoriesAndSuggestions(null /* category */, suggestions); 603 final Context context = RuntimeEnvironment.application; 604 mSuggestionHolder = new DashboardAdapter.SuggestionAndConditionHeaderHolder( 605 LayoutInflater.from(context).inflate( 606 R.layout.suggestion_condition_header, new RelativeLayout(context), true)); 607 } 608} 609