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.preference.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.AdditionalMatchers.or; 24 import static org.mockito.Matchers.any; 25 import static org.mockito.Matchers.anyBoolean; 26 import static org.mockito.Matchers.anyFloat; 27 import static org.mockito.Matchers.anyInt; 28 import static org.mockito.Matchers.anyLong; 29 import static org.mockito.Matchers.anyString; 30 import static org.mockito.Matchers.eq; 31 import static org.mockito.Matchers.isNull; 32 import static org.mockito.Matchers.nullable; 33 import static org.mockito.Mockito.atLeast; 34 import static org.mockito.Mockito.atLeastOnce; 35 import static org.mockito.Mockito.mock; 36 import static org.mockito.Mockito.verify; 37 import static org.mockito.Mockito.verifyNoMoreInteractions; 38 import static org.mockito.Mockito.verifyZeroInteractions; 39 import static org.mockito.Mockito.when; 40 41 import android.content.Context; 42 import android.content.SharedPreferences; 43 import android.preference.CheckBoxPreference; 44 import android.preference.Preference; 45 import android.preference.PreferenceDataStore; 46 import android.preference.PreferenceManager; 47 import android.preference.PreferenceScreen; 48 49 import androidx.test.filters.SmallTest; 50 import androidx.test.rule.ActivityTestRule; 51 import androidx.test.runner.AndroidJUnit4; 52 53 import org.junit.Before; 54 import org.junit.Rule; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 58 import java.util.HashSet; 59 import java.util.Set; 60 61 @SmallTest 62 @RunWith(AndroidJUnit4.class) 63 public class PreferenceDataStoreTest { 64 65 private PreferenceFragmentActivity mActivity; 66 private PreferenceWrapper mPreference; 67 private PreferenceDataStore mDataStore; 68 private PreferenceScreen mScreen; 69 private PreferenceManager mManager; 70 private SharedPreferences mSharedPref; 71 72 private static final String KEY = "TestPrefKey"; 73 private static final String TEST_STR = "Test"; 74 private static final String TEST_DEFAULT_STR = "TestDefault"; 75 private static final String TEST_WRONG_STR = "TestFromSharedPref"; 76 77 @Rule 78 public ActivityTestRule<PreferenceFragmentActivity> mActivityRule = 79 new ActivityTestRule<>(PreferenceFragmentActivity.class); 80 81 82 @Before 83 public void setup() { 84 mActivity = mActivityRule.getActivity(); 85 mPreference = new PreferenceWrapper(mActivity); 86 mPreference.setKey(KEY); 87 88 // Assign the Preference to the PreferenceFragment. 89 mScreen = mActivity.prefFragment.getPreferenceManager().createPreferenceScreen(mActivity); 90 mManager = mScreen.getPreferenceManager(); 91 mSharedPref = mManager.getSharedPreferences(); 92 93 mDataStore = mock(PreferenceDataStore.class); 94 95 // Make sure that the key is not present in SharedPreferences to ensure test correctness. 96 mManager.getSharedPreferences().edit().remove(KEY).commit(); 97 } 98 99 @Test 100 public void testThatDataStoreIsNullByDefault() { 101 Preference preference = new Preference(mActivity); 102 mScreen.addPreference(preference); 103 104 assertNull(preference.getPreferenceDataStore()); 105 assertNotNull(preference.getSharedPreferences()); 106 107 assertNull(mManager.getPreferenceDataStore()); 108 assertNotNull(mManager.getSharedPreferences()); 109 } 110 111 @Test 112 public void testSetGetOnPreference() { 113 Preference preference = new Preference(mActivity); 114 preference.setPreferenceDataStore(mDataStore); 115 116 assertEquals(mDataStore, preference.getPreferenceDataStore()); 117 assertNull(preference.getSharedPreferences()); 118 } 119 120 @Test 121 public void testSetGetOnPreferenceManager() { 122 mManager.setPreferenceDataStore(mDataStore); 123 124 assertEquals(mDataStore, mManager.getPreferenceDataStore()); 125 assertNull(mManager.getSharedPreferences()); 126 } 127 128 @Test 129 public void testSetOnPreferenceManagerGetOnPreference() { 130 Preference preference = new Preference(mActivity); 131 mScreen.addPreference(preference); 132 mManager.setPreferenceDataStore(mDataStore); 133 134 assertEquals(mDataStore, preference.getPreferenceDataStore()); 135 assertNull(preference.getSharedPreferences()); 136 } 137 138 @Test 139 public void testDataStoresHierarchy() { 140 mPreference.setPreferenceDataStore(mDataStore); 141 PreferenceDataStore secondaryDataStore = mock(PreferenceDataStore.class); 142 mScreen.addPreference(mPreference); 143 mManager.setPreferenceDataStore(secondaryDataStore); 144 mPreference.putString(TEST_STR); 145 146 // Check that the Preference returns the correct data store. 147 assertEquals(mDataStore, mPreference.getPreferenceDataStore()); 148 149 // Check that the secondary data store assigned to the manager was NOT used. 150 verifyZeroInteractions(secondaryDataStore); 151 152 // Check that the primary data store assigned directly to the preference was used. 153 verify(mDataStore, atLeastOnce()).getString(eq(KEY), any()); 154 } 155 156 @Test 157 public void testPutStringWithDataStoreOnPref() { 158 mPreference.setPreferenceDataStore(mDataStore); 159 mScreen.addPreference(mPreference); 160 putStringTestCommon(); 161 } 162 163 @Test 164 public void testPutStringWithDataStoreOnMgr() { 165 mManager.setPreferenceDataStore(mDataStore); 166 mScreen.addPreference(mPreference); 167 putStringTestCommon(); 168 } 169 170 private void putStringTestCommon() { 171 mPreference.putString(TEST_STR); 172 173 verify(mDataStore, atLeast(0)).getString(eq(KEY), nullable(String.class)); 174 verify(mDataStore, atLeastOnce()).putString(eq(KEY), anyString()); 175 verifyNoMoreInteractions(mDataStore); 176 177 // Test that the value was NOT propagated to SharedPreferences. 178 assertNull(mSharedPref.getString(KEY, null)); 179 } 180 181 @Test 182 public void testGetStringWithDataStoreOnPref() { 183 mPreference.setPreferenceDataStore(mDataStore); 184 mScreen.addPreference(mPreference); 185 mPreference.getString(TEST_STR); 186 verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR)); 187 } 188 189 @Test 190 public void testGetStringWithDataStoreOnMgr() { 191 mManager.setPreferenceDataStore(mDataStore); 192 mScreen.addPreference(mPreference); 193 mPreference.getString(TEST_STR); 194 verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR)); 195 } 196 197 /** 198 * This test makes sure that when a default value is set to a preference that has a data store 199 * assigned that the default value is correctly propagated to 200 * {@link Preference#onSetInitialValue(boolean, Object)} instead of passing a value from 201 * {@link android.content.SharedPreferences}. We have this test only for String because the 202 * implementation is not dependent on value type so this coverage should be fine. 203 */ 204 @Test 205 public void testDefaultStringValue() { 206 mPreference.setPreferenceDataStore(mDataStore); 207 mPreference.setDefaultValue(TEST_DEFAULT_STR); 208 mSharedPref.edit().putString(KEY, TEST_WRONG_STR).commit(); 209 mScreen.addPreference(mPreference); 210 mSharedPref.edit().remove(KEY).commit(); 211 assertEquals(TEST_DEFAULT_STR, mPreference.defaultValue); 212 } 213 214 /** 215 * Test that the initial value is taken from the data store (before the preference gets assigned 216 * to the preference hierarchy). 217 */ 218 @Test 219 public void testInitialValueIsFromDataStoreOnPreference() { 220 when(mDataStore.getBoolean(anyString(), anyBoolean())).thenReturn(true); 221 222 CheckBoxPreference pref = new CheckBoxPreference(mActivityRule.getActivity()); 223 pref.setKey("CheckboxTestPref"); 224 pref.setPreferenceDataStore(mDataStore); 225 226 mScreen.addPreference(pref); 227 228 assertTrue(pref.isChecked()); 229 } 230 231 /** 232 * Test that the initial value is taken from the data store (before the preference gets assigned 233 * to the preference hierarchy). 234 */ 235 @Test 236 public void testInitialValueIsFromDataStoreOnPreferenceManager() { 237 when(mDataStore.getBoolean(anyString(), anyBoolean())).thenReturn(true); 238 mManager.setPreferenceDataStore(mDataStore); 239 240 CheckBoxPreference pref = new CheckBoxPreference(mActivityRule.getActivity()); 241 pref.setKey("CheckboxTestPref"); 242 243 mScreen.addPreference(pref); 244 245 assertTrue(pref.isChecked()); 246 } 247 248 @Test 249 public void testPutStringSetWithDataStoreOnPref() { 250 mPreference.setPreferenceDataStore(mDataStore); 251 mScreen.addPreference(mPreference); 252 putStringSetTestCommon(); 253 } 254 255 @Test 256 public void testPutStringSetWithDataStoreOnMgr() { 257 mManager.setPreferenceDataStore(mDataStore); 258 mScreen.addPreference(mPreference); 259 putStringSetTestCommon(); 260 } 261 262 private void putStringSetTestCommon() { 263 Set<String> testSet = new HashSet<>(); 264 testSet.add(TEST_STR); 265 mPreference.putStringSet(testSet); 266 267 verify(mDataStore, atLeast(0)).getStringSet(eq(KEY), or(isNull(Set.class), any())); 268 verify(mDataStore, atLeastOnce()).putStringSet(eq(KEY), or(isNull(Set.class), any())); 269 verifyNoMoreInteractions(mDataStore); 270 271 // Test that the value was NOT propagated to SharedPreferences. 272 assertNull(mSharedPref.getStringSet(KEY, null)); 273 } 274 275 @Test 276 public void testGetStringSetWithDataStoreOnPref() { 277 mPreference.setPreferenceDataStore(mDataStore); 278 mScreen.addPreference(mPreference); 279 Set<String> testSet = new HashSet<>(); 280 mPreference.getStringSet(testSet); 281 verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet)); 282 } 283 284 @Test 285 public void testGetStringSetWithDataStoreOnMgr() { 286 mManager.setPreferenceDataStore(mDataStore); 287 mScreen.addPreference(mPreference); 288 Set<String> testSet = new HashSet<>(); 289 mPreference.getStringSet(testSet); 290 verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet)); 291 } 292 293 @Test 294 public void testPutIntWithDataStoreOnPref() { 295 mPreference.setPreferenceDataStore(mDataStore); 296 mScreen.addPreference(mPreference); 297 putIntTestCommon(); 298 } 299 300 @Test 301 public void testPutIntWithDataStoreOnMgr() { 302 mManager.setPreferenceDataStore(mDataStore); 303 mScreen.addPreference(mPreference); 304 putIntTestCommon(); 305 } 306 307 private void putIntTestCommon() { 308 mPreference.putInt(1); 309 310 verify(mDataStore, atLeast(0)).getInt(eq(KEY), anyInt()); 311 verify(mDataStore, atLeastOnce()).putInt(eq(KEY), anyInt()); 312 verifyNoMoreInteractions(mDataStore); 313 314 // Test that the value was NOT propagated to SharedPreferences. 315 assertEquals(-1, mSharedPref.getInt(KEY, -1)); 316 } 317 318 @Test 319 public void testGetIntWithDataStoreOnPref() { 320 mPreference.setPreferenceDataStore(mDataStore); 321 mScreen.addPreference(mPreference); 322 mPreference.getInt(1); 323 verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1)); 324 } 325 326 @Test 327 public void testGetIntWithDataStoreOnMgr() { 328 mManager.setPreferenceDataStore(mDataStore); 329 mScreen.addPreference(mPreference); 330 mPreference.getInt(1); 331 verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1)); 332 } 333 334 @Test 335 public void testPutLongWithDataStoreOnPref() { 336 mPreference.setPreferenceDataStore(mDataStore); 337 mScreen.addPreference(mPreference); 338 putLongTestCommon(); 339 } 340 341 @Test 342 public void testPutLongWithDataStoreOnMgr() { 343 mManager.setPreferenceDataStore(mDataStore); 344 mScreen.addPreference(mPreference); 345 putLongTestCommon(); 346 } 347 348 private void putLongTestCommon() { 349 mPreference.putLong(1L); 350 351 verify(mDataStore, atLeast(0)).getLong(eq(KEY), anyLong()); 352 verify(mDataStore, atLeastOnce()).putLong(eq(KEY), anyLong()); 353 verifyNoMoreInteractions(mDataStore); 354 355 // Test that the value was NOT propagated to SharedPreferences. 356 assertEquals(-1, mSharedPref.getLong(KEY, -1L)); 357 } 358 359 @Test 360 public void testGetLongWithDataStoreOnPref() { 361 mPreference.setPreferenceDataStore(mDataStore); 362 mScreen.addPreference(mPreference); 363 mPreference.getLong(1L); 364 verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L)); 365 } 366 367 @Test 368 public void testGetLongWithDataStoreOnMgr() { 369 mManager.setPreferenceDataStore(mDataStore); 370 mScreen.addPreference(mPreference); 371 mPreference.getLong(1L); 372 verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L)); 373 } 374 375 @Test 376 public void testPutFloatWithDataStoreOnPref() { 377 mPreference.setPreferenceDataStore(mDataStore); 378 mScreen.addPreference(mPreference); 379 putFloatTestCommon(); 380 } 381 382 @Test 383 public void testPutFloatWithDataStoreOnMgr() { 384 mManager.setPreferenceDataStore(mDataStore); 385 mScreen.addPreference(mPreference); 386 putFloatTestCommon(); 387 } 388 389 private void putFloatTestCommon() { 390 mPreference.putFloat(1f); 391 392 verify(mDataStore, atLeast(0)).getFloat(eq(KEY), anyFloat()); 393 verify(mDataStore, atLeastOnce()).putFloat(eq(KEY), anyFloat()); 394 verifyNoMoreInteractions(mDataStore); 395 396 // Test that the value was NOT propagated to SharedPreferences. 397 assertEquals(-1, mSharedPref.getFloat(KEY, -1f), 0.1f /* epsilon */); 398 } 399 400 @Test 401 public void testGetFloatWithDataStoreOnPref() { 402 mPreference.setPreferenceDataStore(mDataStore); 403 mScreen.addPreference(mPreference); 404 mPreference.getFloat(1f); 405 verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f)); 406 } 407 408 @Test 409 public void testGetFloatWithDataStoreOnMgr() { 410 mManager.setPreferenceDataStore(mDataStore); 411 mScreen.addPreference(mPreference); 412 mPreference.getFloat(1f); 413 verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f)); 414 } 415 416 @Test 417 public void testPutBooleanWithDataStoreOnPref() { 418 mPreference.setPreferenceDataStore(mDataStore); 419 mScreen.addPreference(mPreference); 420 putBooleanTestCommon(); 421 } 422 423 @Test 424 public void testPutBooleanWithDataStoreOnMgr() { 425 mManager.setPreferenceDataStore(mDataStore); 426 mScreen.addPreference(mPreference); 427 putBooleanTestCommon(); 428 } 429 430 private void putBooleanTestCommon() { 431 mPreference.putBoolean(true); 432 433 verify(mDataStore, atLeast(0)).getBoolean(eq(KEY), anyBoolean()); 434 verify(mDataStore, atLeastOnce()).putBoolean(eq(KEY), anyBoolean()); 435 verifyNoMoreInteractions(mDataStore); 436 437 // Test that the value was NOT propagated to SharedPreferences. 438 assertEquals(false, mSharedPref.getBoolean(KEY, false)); 439 } 440 441 @Test 442 public void testGetBooleanWithDataStoreOnPref() { 443 mPreference.setPreferenceDataStore(mDataStore); 444 mScreen.addPreference(mPreference); 445 mPreference.getBoolean(true); 446 verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true)); 447 } 448 449 @Test 450 public void testGetBooleanWithDataStoreOnMgr() { 451 mManager.setPreferenceDataStore(mDataStore); 452 mScreen.addPreference(mPreference); 453 mPreference.getBoolean(true); 454 verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true)); 455 } 456 457 /** 458 * When {@link PreferenceDataStore} is NOT assigned, the getter for SharedPreferences should not 459 * return null. 460 */ 461 @Test 462 public void testSharedPrefNotNullIfNoDS() { 463 mScreen.addPreference(mPreference); 464 assertNotNull(mPreference.getSharedPreferences()); 465 assertNotNull(mPreference.getEditor()); 466 } 467 468 /** 469 * When {@link PreferenceDataStore} is NOT assigned, the getter for SharedPreferences must not 470 * return null for PreferenceManager. 471 */ 472 @Test 473 public void testSharedPrefNotNullIfNoDSMgr() { 474 assertNotNull(mManager.getSharedPreferences()); 475 } 476 477 /** 478 * When {@link PreferenceDataStore} is assigned, the getter for SharedPreferences has to return 479 * null. 480 */ 481 @Test 482 public void testSharedPrefNullIfWithDS() { 483 mScreen.addPreference(mPreference); 484 mPreference.setPreferenceDataStore(mDataStore); 485 assertNull(mPreference.getSharedPreferences()); 486 assertNull(mPreference.getEditor()); 487 } 488 489 /** 490 * When {@link PreferenceDataStore} is assigned, the getter for SharedPreferences has to return 491 * null for PreferenceManager. 492 */ 493 @Test 494 public void testSharedPrefNullIfWithDSMgr() { 495 mManager.setPreferenceDataStore(mDataStore); 496 assertNull(mManager.getSharedPreferences()); 497 } 498 499 /** 500 * Wrapper to allow to easily call protected methods. 501 */ 502 private static class PreferenceWrapper extends Preference { 503 504 public Object defaultValue; 505 506 PreferenceWrapper(Context context) { 507 super(context); 508 } 509 510 void putString(String value) { 511 persistString(value); 512 } 513 514 String getString(String defaultValue) { 515 return getPersistedString(defaultValue); 516 } 517 518 void putStringSet(Set<String> values) { 519 persistStringSet(values); 520 } 521 522 Set<String> getStringSet(Set<String> defaultValues) { 523 return getPersistedStringSet(defaultValues); 524 } 525 526 void putInt(int value) { 527 persistInt(value); 528 } 529 530 int getInt(int defaultValue) { 531 return getPersistedInt(defaultValue); 532 } 533 534 void putLong(long value) { 535 persistLong(value); 536 } 537 538 long getLong(long defaultValue) { 539 return getPersistedLong(defaultValue); 540 } 541 542 void putFloat(float value) { 543 persistFloat(value); 544 } 545 546 float getFloat(float defaultValue) { 547 return getPersistedFloat(defaultValue); 548 } 549 550 void putBoolean(boolean value) { 551 persistBoolean(value); 552 } 553 554 boolean getBoolean(boolean defaultValue) { 555 return getPersistedBoolean(defaultValue); 556 } 557 558 @Override 559 protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { 560 this.defaultValue = defaultValue; 561 super.onSetInitialValue(restorePersistedValue, defaultValue); 562 } 563 } 564 565 } 566