1 /* <lambda>null2 * Copyright 2020 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 androidx.datastore.migrations 18 19 import android.content.Context 20 import android.content.SharedPreferences 21 import androidx.datastore.TestingSerializer 22 import androidx.datastore.core.DataMigration 23 import androidx.datastore.core.DataStore 24 import androidx.datastore.core.DataStoreFactory 25 import androidx.datastore.dataStore 26 import androidx.test.core.app.ApplicationProvider 27 import androidx.test.filters.MediumTest 28 import androidx.testutils.assertThrows 29 import com.google.common.truth.Truth.assertThat 30 import java.io.File 31 import kotlinx.coroutines.flow.first 32 import kotlinx.coroutines.runBlocking 33 import kotlinx.coroutines.test.TestScope 34 import kotlinx.coroutines.test.UnconfinedTestDispatcher 35 import kotlinx.coroutines.test.runTest 36 import org.junit.Before 37 import org.junit.Rule 38 import org.junit.Test 39 import org.junit.rules.TemporaryFolder 40 41 private const val sharedPrefsName = "shared_prefs_name" 42 43 private val Context.dsWithSpMigration by 44 dataStore( 45 "ds_with_sp_migration", 46 serializer = TestingSerializer(), 47 produceMigrations = { applicationContext -> 48 listOf( 49 SharedPreferencesMigration(applicationContext, sharedPrefsName) { 50 sharedPreferencesView: SharedPreferencesView, 51 t: Byte -> 52 t.plus(sharedPreferencesView.getInt("integer_key", -1)).toByte() 53 } 54 ) 55 } 56 ) 57 58 @MediumTest 59 @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) 60 class SharedPreferencesMigrationTest { 61 @get:Rule val temporaryFolder = TemporaryFolder() 62 63 private lateinit var context: Context 64 private lateinit var sharedPrefs: SharedPreferences 65 private lateinit var datastoreFile: File 66 67 @Before setUpnull68 fun setUp() { 69 context = ApplicationProvider.getApplicationContext() 70 sharedPrefs = context.getSharedPreferences(sharedPrefsName, Context.MODE_PRIVATE) 71 datastoreFile = temporaryFolder.newFile("test_file.preferences_pb") 72 73 assertThat(sharedPrefs.edit().clear().commit()).isTrue() 74 } 75 76 @Test <lambda>null77 fun testShouldMigrateSkipsMigration() = runTest { 78 val sharedPrefsMigration = 79 SharedPreferencesMigration<Byte>( 80 context = context, 81 sharedPreferencesName = sharedPrefsName, 82 shouldRunMigration = { false } 83 ) { _: SharedPreferencesView, _: Byte -> 84 throw IllegalStateException("Migration should've been skipped.") 85 } 86 87 val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration)) 88 89 // Make sure we aren't running migrate() 90 dataStore.data.first() 91 } 92 93 @Test <lambda>null94 fun testSharedPrefsViewContainsSpecifiedKeys() = runTest { 95 val includedKey = "key1" 96 val includedVal = 1 97 val notMigratedKey = "key2" 98 99 assertThat( 100 sharedPrefs 101 .edit() 102 .putInt(includedKey, includedVal) 103 .putInt(notMigratedKey, 123) 104 .commit() 105 ) 106 .isTrue() 107 108 val sharedPrefsMigration = 109 SharedPreferencesMigration( 110 context = context, 111 sharedPreferencesName = sharedPrefsName, 112 keysToMigrate = setOf(includedKey) 113 ) { prefs: SharedPreferencesView, _: Byte -> 114 assertThat(prefs.getInt(includedKey, -1)).isEqualTo(includedVal) 115 assertThrows<IllegalStateException> { prefs.getInt(notMigratedKey, -1) } 116 assertThat(prefs.getAll()).isEqualTo(mapOf(includedKey to includedVal)) 117 118 99.toByte() 119 } 120 121 val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration)) 122 123 assertThat(dataStore.data.first()).isEqualTo(99) 124 assertThat(sharedPrefs.contains(includedKey)).isFalse() 125 assertThat(sharedPrefs.contains(notMigratedKey)).isTrue() 126 } 127 128 @Test <lambda>null129 fun testSharedPrefsViewWithAllKeysSpecified() = runTest { 130 val key1 = "key1" 131 val val1 = 1 132 val key2 = "key2" 133 val val2 = 2 134 135 assertThat(sharedPrefs.edit().putInt(key1, val1).putInt(key2, val2).commit()).isTrue() 136 137 val sharedPrefsMigration = 138 SharedPreferencesMigration( 139 context = context, 140 sharedPreferencesName = sharedPrefsName 141 ) { prefs: SharedPreferencesView, _: Byte -> 142 assertThat(prefs.getInt(key1, -1)).isEqualTo(val1) 143 assertThat(prefs.getInt(key2, -1)).isEqualTo(val2) 144 145 assertThat(prefs.getAll()).isEqualTo(mapOf(key1 to val1, key2 to val2)) 146 147 99.toByte() 148 } 149 150 val dataStore = getDataStoreWithMigrations(listOf(sharedPrefsMigration)) 151 152 assertThat(dataStore.data.first()).isEqualTo(99) 153 assertThat(sharedPrefs.contains(key1)).isFalse() 154 assertThat(sharedPrefs.contains(key2)).isFalse() 155 } 156 157 @Test <lambda>null158 fun testSharedPrefsViewWithAllKeysSpecified_doesntThrowErrorWhenKeyDoesntExist() = runTest { 159 assertThat(sharedPrefs.edit().putInt("unrelated_key", -123).commit()).isTrue() 160 161 val migration = 162 SharedPreferencesMigration( 163 produceSharedPreferences = { sharedPrefs }, 164 ) { prefs: SharedPreferencesView, _: Byte -> 165 prefs.getInt("this_key_doesnt_exist_yet", 123).toByte() 166 } 167 168 val dataStore = getDataStoreWithMigrations(listOf(migration)) 169 assertThat(dataStore.data.first()).isEqualTo(123) 170 } 171 172 @Test <lambda>null173 fun producedSharedPreferencesIsUsed() = runTest { 174 assertThat(sharedPrefs.edit().putInt("integer_key", 123).commit()).isTrue() 175 176 val migration = 177 SharedPreferencesMigration(produceSharedPreferences = { sharedPrefs }) { 178 prefs: SharedPreferencesView, 179 _: Byte -> 180 assertThat(prefs.getAll().size).isEqualTo(1) 181 assertThat(prefs.getInt("integer_key", 0)).isEqualTo(123) 182 123 183 } 184 185 val dataStore = getDataStoreWithMigrations(listOf(migration)) 186 assertThat(dataStore.data.first()).isEqualTo(123) 187 } 188 189 @Test testWithTopLevelDataStoreDelegatenull190 fun testWithTopLevelDataStoreDelegate() = 191 runBlocking<Unit> { 192 File(context.filesDir, "/datastore").deleteRecursively() 193 assertThat(sharedPrefs.edit().putInt("integer_key", 123).commit()).isTrue() 194 195 assertThat(context.dsWithSpMigration.data.first()).isEqualTo(123) 196 } 197 getDataStoreWithMigrationsnull198 private fun getDataStoreWithMigrations(migrations: List<DataMigration<Byte>>): DataStore<Byte> { 199 return DataStoreFactory.create( 200 serializer = TestingSerializer(), 201 migrations = migrations, 202 scope = TestScope(UnconfinedTestDispatcher()) 203 ) { 204 datastoreFile 205 } 206 } 207 } 208