1 /*
2  * 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.lifecycle.testing
18 
19 import androidx.lifecycle.Lifecycle
20 import androidx.lifecycle.LifecycleOwner
21 import androidx.lifecycle.LifecycleRegistry
22 import kotlin.jvm.JvmOverloads
23 import kotlinx.coroutines.CoroutineDispatcher
24 import kotlinx.coroutines.Dispatchers
25 import kotlinx.coroutines.runBlocking
26 import kotlinx.coroutines.withContext
27 
28 /**
29  * Create a [LifecycleOwner] that allows changing the state via the [handleLifecycleEvent] method or
30  * [currentState] property.
31  *
32  * Under the hood, this uses a [LifecycleRegistry]. However, it uses
33  * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate] as the default
34  * [coroutineDispatcher] to ensure that all mutations to the [current state][currentState] are run
35  * on that dispatcher, no matter what thread you mutate the state from.
36  *
37  * @param initialState The initial [Lifecycle.State].
38  * @param coroutineDispatcher A [CoroutineDispatcher] to use when dispatching work from this class.
39  */
40 public class TestLifecycleOwner
41 @JvmOverloads
42 constructor(
43     initialState: Lifecycle.State = Lifecycle.State.STARTED,
44     private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate
45 ) : LifecycleOwner {
46     // it is in test artifact
47     private val lifecycleRegistry =
<lambda>null48         LifecycleRegistry.createUnsafe(this).apply { currentState = initialState }
49 
50     override val lifecycle: LifecycleRegistry
51         get() = lifecycleRegistry
52 
53     /**
54      * Update the [currentState] by moving it to the state directly after the given [event]. This is
55      * safe to mutate on any thread, but will block that thread during execution.
56      */
handleLifecycleEventnull57     public fun handleLifecycleEvent(event: Lifecycle.Event) {
58         runBlocking(coroutineDispatcher) { lifecycleRegistry.handleLifecycleEvent(event) }
59     }
60 
61     /**
62      * The current [Lifecycle.State] of this owner. This is safe to call on any thread but is
63      * thread-blocking and should not be called from within a coroutine (use [setCurrentState]
64      * instead).
65      */
66     public var currentState: Lifecycle.State
<lambda>null67         get() = runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState }
68         set(value) {
<lambda>null69             runBlocking(coroutineDispatcher) { lifecycleRegistry.currentState = value }
70         }
71 
72     /**
73      * Updates the [currentState]. This suspending function is safe to call on any thread and will
74      * not block that thread. If the state should be updated from outside of a suspending function,
75      * use [currentState] property syntax instead.
76      */
setCurrentStatenull77     public suspend fun setCurrentState(state: Lifecycle.State) {
78         withContext(coroutineDispatcher) { lifecycleRegistry.currentState = state }
79     }
80 
81     /** Get the number of observers. */
82     public val observerCount: Int
83         get() = lifecycleRegistry.observerCount
84 }
85