1 /*
<lambda>null2  * Copyright (C) 2023 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 package com.android.launcher3
17 
18 import android.content.Context
19 import android.content.SharedPreferences
20 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import androidx.test.platform.app.InstrumentationRegistry
24 import com.android.launcher3.LauncherPrefs.Companion.BOOT_AWARE_PREFS_KEY
25 import com.google.common.truth.Truth.assertThat
26 import java.util.concurrent.CountDownLatch
27 import java.util.concurrent.TimeUnit
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 
31 private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
32 private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
33 private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
34 private val TEST_CONTEXTUAL_ITEM =
35     ContextualItem("4", true, { true }, EncryptionType.ENCRYPTED, Boolean::class.java)
36 
37 private const val TEST_DEFAULT_VALUE = "default"
38 private const val TEST_PREF_KEY = "test_pref_key"
39 
40 private const val WAIT_TIME_IN_SECONDS = 3L
41 
42 @SmallTest
43 @RunWith(AndroidJUnit4::class)
44 class LauncherPrefsTest {
45 
<lambda>null46     private val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext }
<lambda>null47     private val launcherPrefs by lazy { LauncherPrefs.get(context) }
48 
49     @Test
has_keyMissingFromLauncherPrefs_returnsFalsenull50     fun has_keyMissingFromLauncherPrefs_returnsFalse() {
51         assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
52     }
53 
54     @Test
has_keyPresentInLauncherPrefs_returnsTruenull55     fun has_keyPresentInLauncherPrefs_returnsTrue() {
56         with(launcherPrefs) {
57             putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue))
58             assertThat(has(TEST_BOOLEAN_ITEM)).isTrue()
59             remove(TEST_BOOLEAN_ITEM)
60         }
61     }
62 
63     @Test
addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdatesnull64     fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() {
65         val latch = CountDownLatch(1)
66         val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
67 
68         with(launcherPrefs) {
69             putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue))
70             addListener(listener, TEST_STRING_ITEM)
71             putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
72 
73             assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
74             remove(TEST_STRING_ITEM)
75         }
76     }
77 
78     @Test
removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdatesnull79     fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() {
80         val latch = CountDownLatch(1)
81         val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
82 
83         with(launcherPrefs) {
84             addListener(listener, TEST_STRING_ITEM)
85             removeListener(listener, TEST_STRING_ITEM)
86             putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
87 
88             // latch will be still be 1 (and await will return false) if the listener was not called
89             assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
90             remove(TEST_STRING_ITEM)
91         }
92     }
93 
94     @Test
addListenerAndRemoveListener_forMultipleItems_bothWorkProperlynull95     fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() {
96         var latch = CountDownLatch(3)
97         val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
98 
99         with(launcherPrefs) {
100             addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
101             putSync(
102                 TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123),
103                 TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
104                 TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
105             )
106             assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isTrue()
107 
108             removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
109             latch = CountDownLatch(1)
110             putSync(
111                 TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
112                 TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
113                 TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
114             )
115             remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
116 
117             assertThat(latch.await(WAIT_TIME_IN_SECONDS, TimeUnit.SECONDS)).isFalse()
118         }
119     }
120 
121     @Test
get_booleanItemNotInLauncherprefs_returnsDefaultValuenull122     fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() {
123         assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
124     }
125 
126     @Test
get_stringItemNotInLauncherPrefs_returnsDefaultValuenull127     fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() {
128         assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue)
129     }
130 
131     @Test
get_intItemNotInLauncherprefs_returnsDefaultValuenull132     fun get_intItemNotInLauncherprefs_returnsDefaultValue() {
133         assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue)
134     }
135 
136     @Test
put_storesItemInLauncherPrefs_successfullynull137     fun put_storesItemInLauncherPrefs_successfully() {
138         val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
139 
140         with(launcherPrefs) {
141             putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
142             assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue)
143             remove(TEST_BOOLEAN_ITEM)
144         }
145     }
146 
147     @Test
put_storesListOfItemsInLauncherPrefs_successfullynull148     fun put_storesListOfItemsInLauncherPrefs_successfully() {
149         with(launcherPrefs) {
150             putSync(
151                 TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
152                 TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
153                 TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
154             )
155             assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
156             remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
157         }
158     }
159 
160     @Test
remove_deletesItemFromLauncherPrefs_successfullynull161     fun remove_deletesItemFromLauncherPrefs_successfully() {
162         val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
163 
164         with(launcherPrefs) {
165             putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
166             remove(TEST_BOOLEAN_ITEM)
167             assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
168         }
169     }
170 
171     @Test
get_contextualItem_returnsCorrectDefaultnull172     fun get_contextualItem_returnsCorrectDefault() {
173         assertThat(launcherPrefs.get(TEST_CONTEXTUAL_ITEM)).isTrue()
174     }
175 
176     @Test
getItemSharedPrefFile_forNonRestorableItem_isCorrectnull177     fun getItemSharedPrefFile_forNonRestorableItem_isCorrect() {
178         val nonRestorableItem = LauncherPrefs.nonRestorableItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
179         assertThat(nonRestorableItem.sharedPrefFile).isEqualTo(LauncherFiles.DEVICE_PREFERENCES_KEY)
180     }
181 
182     @Test
getItemSharedPrefFile_forBackedUpItem_isCorrectnull183     fun getItemSharedPrefFile_forBackedUpItem_isCorrect() {
184         val backedUpItem = LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
185         assertThat(backedUpItem.sharedPrefFile).isEqualTo(LauncherFiles.SHARED_PREFERENCES_KEY)
186     }
187 
188     @Test
put_bootAwareItem_updatesDeviceProtectedStoragenull189     fun put_bootAwareItem_updatesDeviceProtectedStorage() {
190         val bootAwareItem =
191             LauncherPrefs.backedUpItem(
192                 TEST_PREF_KEY,
193                 TEST_DEFAULT_VALUE,
194                 EncryptionType.DEVICE_PROTECTED
195             )
196 
197         val bootAwarePrefs: SharedPreferences =
198             context
199                 .createDeviceProtectedStorageContext()
200                 .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
201         bootAwarePrefs.edit().remove(bootAwareItem.sharedPrefKey).commit()
202 
203         launcherPrefs.putSync(bootAwareItem.to(bootAwareItem.defaultValue))
204         assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isTrue()
205 
206         launcherPrefs.removeSync(bootAwareItem)
207     }
208 
209     @Test
remove_bootAwareItem_removesFromDeviceProtectedStoragenull210     fun remove_bootAwareItem_removesFromDeviceProtectedStorage() {
211         val bootAwareItem =
212             LauncherPrefs.backedUpItem(
213                 TEST_PREF_KEY,
214                 TEST_DEFAULT_VALUE,
215                 EncryptionType.DEVICE_PROTECTED
216             )
217 
218         val bootAwarePrefs: SharedPreferences =
219             context
220                 .createDeviceProtectedStorageContext()
221                 .getSharedPreferences(BOOT_AWARE_PREFS_KEY, Context.MODE_PRIVATE)
222 
223         bootAwarePrefs
224             .edit()
225             .putString(bootAwareItem.sharedPrefKey, bootAwareItem.defaultValue)
226             .commit()
227 
228         launcherPrefs.removeSync(bootAwareItem)
229         assertThat(bootAwarePrefs.contains(bootAwareItem.sharedPrefKey)).isFalse()
230     }
231 }
232