1 /*
<lambda>null2 * Copyright 2024 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.lifecycle.serialization
18
19 import androidx.activity.ComponentActivity
20 import androidx.lifecycle.SavedStateHandle
21 import androidx.lifecycle.ViewModel
22 import androidx.lifecycle.ViewModelProvider
23 import androidx.savedstate.serialization.SavedStateConfiguration
24 import androidx.test.ext.junit.rules.ActivityScenarioRule
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import androidx.test.filters.SmallTest
27 import com.google.common.truth.Truth.assertThat
28 import kotlinx.serialization.KSerializer
29 import kotlinx.serialization.builtins.serializer
30 import kotlinx.serialization.descriptors.PrimitiveKind
31 import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
32 import kotlinx.serialization.descriptors.SerialDescriptor
33 import kotlinx.serialization.descriptors.buildClassSerialDescriptor
34 import kotlinx.serialization.encoding.Decoder
35 import kotlinx.serialization.encoding.Encoder
36 import kotlinx.serialization.modules.SerializersModule
37 import kotlinx.serialization.serializer
38 import org.junit.Rule
39 import org.junit.Test
40 import org.junit.runner.RunWith
41
42 @SmallTest
43 @RunWith(AndroidJUnit4::class)
44 class SavedStateHandleDelegateTest {
45
46 @get:Rule val activityTestRuleScenario = ActivityScenarioRule(ComponentActivity::class.java)
47
48 @Test
49 fun simpleRestore() {
50 activityTestRuleScenario.scenario.onActivity { activity ->
51 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
52 var value by viewModel.savedStateHandle.saved { 1 }
53 assertThat(value).isEqualTo(1)
54 value = 2
55 assertThat(value).isEqualTo(2)
56 }
57 activityTestRuleScenario.scenario.recreate()
58 activityTestRuleScenario.scenario.onActivity { activity ->
59 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
60 var value: Int by
61 viewModel.savedStateHandle.saved { error("Unexpected initializer call") }
62 assertThat(value).isEqualTo(2)
63 }
64 }
65
66 @Test
67 fun explicitKey() {
68 activityTestRuleScenario.scenario.onActivity { activity ->
69 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
70 var value by viewModel.savedStateHandle.saved(key = "foo") { 1 }
71 assertThat(value).isEqualTo(1)
72 value = 2
73 assertThat(value).isEqualTo(2)
74 }
75 activityTestRuleScenario.scenario.recreate()
76 activityTestRuleScenario.scenario.onActivity { activity ->
77 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
78 var value: Int by
79 viewModel.savedStateHandle.saved(key = "foo") {
80 error("Unexpected initializer call")
81 }
82 assertThat(value).isEqualTo(2)
83 }
84 }
85
86 @Test
87 fun explicitSerializer() {
88 activityTestRuleScenario.scenario.onActivity { activity ->
89 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
90 val value by viewModel.savedStateHandle.saved(serializer = Int.serializer()) { 1 }
91 assertThat(value).isEqualTo(1)
92 }
93 activityTestRuleScenario.scenario.recreate()
94 activityTestRuleScenario.scenario.onActivity { activity ->
95 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
96 val value: Int by
97 viewModel.savedStateHandle.saved(serializer = Int.serializer()) {
98 error("Unexpected initializer call")
99 }
100 assertThat(value).isEqualTo(1)
101 }
102 }
103
104 @Test
105 fun explicitConfig() {
106 data class User(val name: String)
107 class UserSerializer : KSerializer<User> {
108 override val descriptor: SerialDescriptor =
109 PrimitiveSerialDescriptor("User", PrimitiveKind.STRING)
110
111 override fun serialize(encoder: Encoder, value: User) {
112 encoder.encodeString(value.name)
113 }
114
115 override fun deserialize(decoder: Decoder): User {
116 return User(decoder.decodeString())
117 }
118 }
119
120 val config = SavedStateConfiguration {
121 serializersModule = SerializersModule { contextual(User::class, UserSerializer()) }
122 }
123
124 val user = User("foo")
125 activityTestRuleScenario.scenario.onActivity { activity ->
126 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
127 val value by viewModel.savedStateHandle.saved(configuration = config) { user }
128 assertThat(value).isEqualTo(user)
129 }
130 activityTestRuleScenario.scenario.recreate()
131 activityTestRuleScenario.scenario.onActivity { activity ->
132 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
133 val value: User by
134 viewModel.savedStateHandle.saved(configuration = config) {
135 error("Unexpected initializer call")
136 }
137 assertThat(value).isEqualTo(user)
138 }
139 }
140
141 @Test
142 fun explicitKeyAndSerializer() {
143 activityTestRuleScenario.scenario.onActivity { activity ->
144 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
145 val value by
146 viewModel.savedStateHandle.saved(key = "foo", serializer = Int.serializer()) { 1 }
147 assertThat(value).isEqualTo(1)
148 }
149 activityTestRuleScenario.scenario.recreate()
150 activityTestRuleScenario.scenario.onActivity { activity ->
151 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
152 val value: Int by
153 viewModel.savedStateHandle.saved(key = "foo", serializer = Int.serializer()) {
154 error("Unexpected initializer call")
155 }
156 assertThat(value).isEqualTo(1)
157 }
158 }
159
160 @Test
161 fun duplicateKeys() {
162 activityTestRuleScenario.scenario.onActivity { activity ->
163 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
164 val serializable by viewModel.savedStateHandle.saved(key = "foo") { 1 }
165 assertThat(serializable).isEqualTo(1)
166 val duplicate by viewModel.savedStateHandle.saved(key = "foo") { 2 }
167 assertThat(duplicate).isEqualTo(2)
168 // The value is from the initializer.
169 assertThat(serializable).isEqualTo(1)
170 }
171 activityTestRuleScenario.scenario.recreate()
172 activityTestRuleScenario.scenario.onActivity { activity ->
173 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
174 val serializable: Int by
175 viewModel.savedStateHandle.saved(key = "foo") {
176 error("Unexpected initializer call")
177 }
178 assertThat(serializable).isEqualTo(2)
179 val duplicate: Int by
180 viewModel.savedStateHandle.saved(key = "foo") {
181 error("Unexpected initializer call")
182 }
183 assertThat(duplicate).isEqualTo(2)
184 assertThat(serializable).isEqualTo(2)
185 }
186 }
187
188 @Test
189 fun emptyValueStaysEmpty() {
190 activityTestRuleScenario.scenario.onActivity { activity ->
191 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
192 var serializable: List<Double> by
193 viewModel.savedStateHandle.saved { listOf(1.0, 2.0, 3.0) }
194 assertThat(serializable).isEqualTo(listOf(1.0, 2.0, 3.0))
195 serializable = emptyList()
196 assertThat(serializable).isEqualTo(emptyList<Double>())
197 }
198 activityTestRuleScenario.scenario.recreate()
199 activityTestRuleScenario.scenario.onActivity { activity ->
200 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
201 var serializable: List<Double> by
202 viewModel.savedStateHandle.saved { error("Unexpected initializer call") }
203 assertThat(serializable).isEqualTo(emptyList<Double>())
204 }
205 }
206
207 @Test
208 fun setBeforeGetShouldNotCallInit() {
209 activityTestRuleScenario.scenario.onActivity { activity ->
210 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
211 var serializable: Int by
212 viewModel.savedStateHandle.saved(serializer = noDeserializeSerializer()) {
213 error("Unexpected initializer call")
214 }
215 serializable = 2
216 assertThat(serializable).isEqualTo(2)
217 }
218 }
219
220 @Test
221 fun setBeforeGetShouldNotLoadFromSavedState() {
222 activityTestRuleScenario.scenario.onActivity { activity ->
223 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
224 var serializable: Int by
225 viewModel.savedStateHandle.saved(serializer = noDeserializeSerializer()) {
226 error("Unexpected initializer call")
227 }
228 serializable = 2
229 }
230 activityTestRuleScenario.scenario.recreate()
231 activityTestRuleScenario.scenario.onActivity { activity ->
232 val viewModel: MyViewModel = ViewModelProvider(activity)[MyViewModel::class]
233 var serializable: Int by
234 viewModel.savedStateHandle.saved(serializer = noDeserializeSerializer()) {
235 error("Unexpected initializer call")
236 }
237 serializable = 3
238 assertThat(serializable).isEqualTo(3)
239 }
240 }
241 }
242
243 class MyViewModel(val savedStateHandle: SavedStateHandle) : ViewModel()
244
noDeserializeSerializernull245 private inline fun <reified T> noDeserializeSerializer(): KSerializer<T> =
246 object : KSerializer<T> {
247 override val descriptor: SerialDescriptor = buildClassSerialDescriptor("NoDeserialize")
248
249 override fun serialize(encoder: Encoder, value: T) {
250 serializer<T>().serialize(encoder, value)
251 }
252
253 override fun deserialize(decoder: Decoder): T {
254 error("Unexpected deserialize call")
255 }
256 }
257