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