1 /* 2 * Copyright 2019 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.savedstate 18 19 import android.app.Application 20 import androidx.fragment.app.FragmentActivity 21 import androidx.lifecycle.AndroidViewModel 22 import androidx.lifecycle.SAVED_STATE_REGISTRY_OWNER_KEY 23 import androidx.lifecycle.SavedStateHandle 24 import androidx.lifecycle.SavedStateViewModelFactory 25 import androidx.lifecycle.VIEW_MODEL_STORE_OWNER_KEY 26 import androidx.lifecycle.ViewModel 27 import androidx.lifecycle.ViewModelProvider 28 import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY 29 import androidx.lifecycle.ViewModelStore 30 import androidx.lifecycle.enableSavedStateHandles 31 import androidx.lifecycle.viewmodel.MutableCreationExtras 32 import androidx.test.annotation.UiThreadTest 33 import androidx.test.ext.junit.runners.AndroidJUnit4 34 import androidx.test.filters.SmallTest 35 import com.google.common.truth.Truth.assertThat 36 import org.junit.Assert.fail 37 import org.junit.Rule 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 41 @RunWith(AndroidJUnit4::class) 42 @SmallTest 43 class SavedStateFactoryTest { 44 45 @Suppress("DEPRECATION") 46 @get:Rule 47 var activityRule = androidx.test.rule.ActivityTestRule(MyActivity::class.java) 48 49 @UiThreadTest 50 @Test testLegacyCreateAndroidVMnull51 fun testLegacyCreateAndroidVM() { 52 val savedStateVMFactory = 53 SavedStateViewModelFactory(activityRule.activity.application, activityRule.activity) 54 val vm = ViewModelProvider(ViewModelStore(), savedStateVMFactory) 55 assertThat(vm[MyAndroidViewModel::class.java].handle).isNotNull() 56 assertThat(vm[MyViewModel::class.java].handle).isNotNull() 57 } 58 59 @UiThreadTest 60 @Test testCreateAndroidVMnull61 fun testCreateAndroidVM() { 62 val savedStateVMFactory = SavedStateViewModelFactory() 63 val component = TestComponent() 64 component.enableSavedStateHandles() 65 val extras = component.extras 66 extras[APPLICATION_KEY] = activityRule.activity.application 67 val vm = ViewModelProvider(component.viewModelStore, savedStateVMFactory, extras) 68 assertThat(vm[MyAndroidViewModel::class.java].handle).isNotNull() 69 assertThat(vm[MyViewModel::class.java].handle).isNotNull() 70 } 71 72 @UiThreadTest 73 @Test testCreateAndroidWithStatefulFactoryVMnull74 fun testCreateAndroidWithStatefulFactoryVM() { 75 val savedStateVMFactory = SavedStateViewModelFactory(null, activityRule.activity) 76 val component = TestComponent() 77 component.enableSavedStateHandles() 78 val extras = component.extras 79 extras[APPLICATION_KEY] = activityRule.activity.application 80 val vm = ViewModelProvider(component.viewModelStore, savedStateVMFactory, extras) 81 assertThat(vm[MyAndroidViewModel::class.java].handle).isNotNull() 82 assertThat(vm[MyViewModel::class.java].handle).isNotNull() 83 } 84 85 @UiThreadTest 86 @Test testCreateAndroidVMWrongParameterOrdernull87 fun testCreateAndroidVMWrongParameterOrder() { 88 val savedStateVMFactory = SavedStateViewModelFactory() 89 val component = TestComponent() 90 component.enableSavedStateHandles() 91 val extras = component.extras 92 extras[APPLICATION_KEY] = activityRule.activity.application 93 val vm = ViewModelProvider(component.viewModelStore, savedStateVMFactory, extras) 94 try { 95 assertThat(vm[WrongOrderAndroidViewModel::class.java].handle).isNotNull() 96 fail() 97 } catch (e: UnsupportedOperationException) { 98 assertThat(e) 99 .hasMessageThat() 100 .isEqualTo( 101 "Class WrongOrderAndroidViewModel must have parameters in the proper order: " + 102 "[class android.app.Application, class androidx.lifecycle.SavedStateHandle]" 103 ) 104 } 105 } 106 107 @UiThreadTest 108 @Test testLegacyCreateFailAndroidVMnull109 fun testLegacyCreateFailAndroidVM() { 110 val savedStateVMFactory = SavedStateViewModelFactory(null, activityRule.activity) 111 val vm = ViewModelProvider(ViewModelStore(), savedStateVMFactory) 112 try { 113 vm[MyAndroidViewModel::class.java] 114 fail("Creating an AndroidViewModel should fail when no Application is provided") 115 } catch (e: RuntimeException) { 116 // Exception message varies across platform versions, just make sure it's thrown. 117 } 118 assertThat(vm[MyViewModel::class.java].handle).isNotNull() 119 } 120 121 @UiThreadTest 122 @Test testCreateFailAndroidVMnull123 fun testCreateFailAndroidVM() { 124 val savedStateVMFactory = SavedStateViewModelFactory() 125 val component = TestComponent() 126 component.enableSavedStateHandles() 127 val vm = ViewModelProvider(component.viewModelStore, savedStateVMFactory, component.extras) 128 try { 129 vm[MyAndroidViewModel::class.java] 130 fail("Creating an AndroidViewModel should fail when no Application extras is provided") 131 } catch (e: RuntimeException) {} 132 assertThat(vm[MyViewModel::class.java].handle).isNotNull() 133 } 134 135 @UiThreadTest 136 @Test testLegacyCreateAndroidAbstractVMnull137 fun testLegacyCreateAndroidAbstractVM() { 138 val activity = activityRule.activity 139 val app = activity.application 140 @Suppress("DEPRECATION") 141 val savedStateVMFactory = 142 object : androidx.lifecycle.AbstractSavedStateViewModelFactory(activity, null) { 143 override fun <T : ViewModel> create( 144 key: String, 145 modelClass: Class<T>, 146 handle: SavedStateHandle 147 ): T { 148 return modelClass.cast(MyAndroidViewModel(app, handle))!! 149 } 150 } 151 val vm = ViewModelProvider(ViewModelStore(), savedStateVMFactory) 152 assertThat(vm[MyAndroidViewModel::class.java].handle).isNotNull() 153 } 154 155 @UiThreadTest 156 @Test testLegacyMethodsWithEmptyConstructornull157 fun testLegacyMethodsWithEmptyConstructor() { 158 val factory = SavedStateViewModelFactory() 159 try { 160 factory.create(MyViewModel::class.java) 161 fail() 162 } catch (e: UnsupportedOperationException) {} 163 164 try { 165 factory.create("a", MyViewModel::class.java) 166 fail() 167 } catch (e: UnsupportedOperationException) {} 168 169 @Suppress("DEPRECATION") 170 val absFactory = 171 object : androidx.lifecycle.AbstractSavedStateViewModelFactory() { 172 override fun <T : ViewModel> create( 173 key: String, 174 modelClass: Class<T>, 175 handle: SavedStateHandle 176 ): T = create(modelClass) 177 } 178 try { 179 absFactory.create(MyViewModel::class.java) 180 fail() 181 } catch (e: UnsupportedOperationException) {} 182 } 183 184 internal class MyAndroidViewModel(app: Application, val handle: SavedStateHandle) : 185 AndroidViewModel(app) 186 187 internal class WrongOrderAndroidViewModel(val handle: SavedStateHandle, app: Application) : 188 AndroidViewModel(app) 189 190 internal class MyViewModel(val handle: SavedStateHandle) : ViewModel() 191 192 class MyActivity : FragmentActivity() 193 194 val TestComponent.extras: MutableCreationExtras 195 get() { 196 val extras = MutableCreationExtras() 197 extras[SAVED_STATE_REGISTRY_OWNER_KEY] = this 198 extras[VIEW_MODEL_STORE_OWNER_KEY] = this 199 return extras 200 } 201 } 202