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