1 /*
2  * 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.viewmodel.testing.internal
18 
19 import androidx.lifecycle.DEFAULT_ARGS_KEY
20 import androidx.lifecycle.Lifecycle
21 import androidx.lifecycle.LifecycleOwner
22 import androidx.lifecycle.LifecycleRegistry
23 import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY
24 import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY
25 import androidx.lifecycle.ViewModel
26 import androidx.lifecycle.ViewModelStore
27 import androidx.lifecycle.ViewModelStoreOwner
28 import androidx.lifecycle.enableSavedStateHandles
29 import androidx.lifecycle.viewmodel.CreationExtras
30 import androidx.lifecycle.viewmodel.MutableCreationExtras
31 import androidx.lifecycle.viewmodel.testing.ViewModelScenario
32 import androidx.savedstate.SavedState
33 import androidx.savedstate.SavedStateRegistryController
34 import androidx.savedstate.SavedStateRegistryOwner
35 import androidx.savedstate.savedState
36 
37 /**
38  * An internal implementation of [ViewModelStoreOwner], [LifecycleOwner], and
39  * [SavedStateRegistryOwner] used by [ViewModelScenario] as its default owner.
40  *
41  * This class provides the necessary infrastructure to manage a [ViewModel]'s lifecycle, saved
42  * state, and associated registry for testing purposes.
43  */
44 private class ViewModelScenarioOwner :
45     ViewModelStoreOwner, LifecycleOwner, SavedStateRegistryOwner {
46 
47     override val viewModelStore = ViewModelStore()
48 
49     val lifecycleRegistry = LifecycleRegistry.createUnsafe(owner = this)
50     override val lifecycle: Lifecycle = lifecycleRegistry
51 
52     val savedStateRegistryController = SavedStateRegistryController.create(owner = this)
53     override val savedStateRegistry = savedStateRegistryController.savedStateRegistry
54 }
55 
56 /**
57  * Creates a configured instance of [CreationExtras] for use in a [ViewModelScenario].
58  *
59  * This method sets up the necessary components to simulate and manage a [ViewModel]'s lifecycle and
60  * saved state in a test environment.
61  *
62  * It initializes required keys for state restoration and lifecycle management.
63  */
createScenarioExtrasnull64 internal fun createScenarioExtras(
65     initialExtras: CreationExtras = MutableCreationExtras(),
66     restoredState: SavedState = savedState(),
67     defaultArgs: SavedState = initialExtras[DEFAULT_ARGS_KEY] ?: savedState(),
68 ): CreationExtras {
69     val registryOwner = initialExtras[SAVED_STATE_REGISTRY_OWNER_KEY]
70     require(registryOwner == null || registryOwner is ViewModelScenarioOwner) {
71         "'SAVED_STATE_REGISTRY_OWNER_KEY' must be null or default, but was $registryOwner."
72     }
73 
74     val storeOwner = initialExtras[VIEW_MODEL_STORE_OWNER_KEY]
75     require(storeOwner == null || storeOwner is ViewModelScenarioOwner) {
76         "'VIEW_MODEL_STORE_OWNER_KEY' must be null or default, but was $storeOwner."
77     }
78 
79     val owner =
80         ViewModelScenarioOwner().apply {
81             savedStateRegistryController.performAttach()
82             enableSavedStateHandles()
83             savedStateRegistryController.performRestore(savedState = restoredState)
84             lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
85         }
86 
87     return MutableCreationExtras(initialExtras).apply {
88         this[SAVED_STATE_REGISTRY_OWNER_KEY] = owner
89         this[VIEW_MODEL_STORE_OWNER_KEY] = owner
90         this[DEFAULT_ARGS_KEY] = defaultArgs
91     }
92 }
93 
94 /**
95  * Saves the current state of [CreationExtras] associated with a [ViewModelScenario].
96  *
97  * This method captures the state of the [ViewModelScenarioOwner] from the
98  * [SAVED_STATE_REGISTRY_OWNER_KEY] and encodes it using platform-specific serialization.
99  */
saveScenarioExtrasnull100 internal fun saveScenarioExtras(extras: CreationExtras): SavedState {
101     val owner = extras[SAVED_STATE_REGISTRY_OWNER_KEY] as ViewModelScenarioOwner
102 
103     val outState = savedState()
104     owner.savedStateRegistryController.performSave(outState)
105 
106     return platformEncodeDecode(outState)
107 }
108 
109 /**
110  * Encodes and decodes a [SavedState] using platform-specific algorithms. It ensures platform
111  * requirements during tests.
112  */
platformEncodeDecodenull113 internal expect fun platformEncodeDecode(savedState: SavedState): SavedState
114