1 /*
2  * Copyright (C) 2023 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 package com.android.systemui
17 
18 import android.content.ContentResolver
19 import android.content.Context
20 import android.content.res.Resources
21 import android.testing.TestableContext
22 import android.testing.TestableResources
23 import com.android.systemui.broadcast.BroadcastDispatcher
24 import com.android.systemui.broadcast.FakeBroadcastDispatcher
25 import com.android.systemui.coroutines.collectLastValue
26 import com.android.systemui.coroutines.collectValues
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.dagger.qualifiers.Main
30 import com.android.systemui.deviceentry.data.repository.FaceWakeUpTriggersConfigModule
31 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
32 import com.android.systemui.deviceentry.domain.interactor.SystemUIDeviceEntryFaceAuthInteractor
33 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
34 import com.android.systemui.scene.SceneContainerFrameworkModule
35 import com.android.systemui.scene.shared.flag.SceneContainerFlag
36 import com.android.systemui.scene.shared.model.SceneContainerConfig
37 import com.android.systemui.scene.shared.model.SceneDataSource
38 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
39 import com.android.systemui.shade.domain.interactor.BaseShadeInteractor
40 import com.android.systemui.shade.domain.interactor.ShadeInteractor
41 import com.android.systemui.shade.domain.interactor.ShadeInteractorImpl
42 import com.android.systemui.shade.domain.interactor.ShadeInteractorLegacyImpl
43 import com.android.systemui.shade.domain.interactor.ShadeInteractorSceneContainerImpl
44 import dagger.Binds
45 import dagger.Module
46 import dagger.Provides
47 import javax.inject.Provider
48 import kotlin.coroutines.CoroutineContext
49 import kotlin.coroutines.EmptyCoroutineContext
50 import kotlinx.coroutines.CoroutineScope
51 import kotlinx.coroutines.CoroutineStart
52 import kotlinx.coroutines.ExperimentalCoroutinesApi
53 import kotlinx.coroutines.flow.Flow
54 import kotlinx.coroutines.test.TestScope
55 import kotlinx.coroutines.test.runCurrent
56 import kotlinx.coroutines.test.runTest
57 
58 @Module(
59     includes =
60         [
61             TestMocksModule::class,
62             CoroutineTestScopeModule::class,
63             FakeSystemUiModule::class,
64             DefaultBlueprintModule::class,
65             SceneContainerFrameworkModule::class,
66             FaceWakeUpTriggersConfigModule::class,
67         ]
68 )
69 @Deprecated("Use Kosmos instead. See com.android.systemui.kosmos.Kosmos.")
70 interface SysUITestModule {
71 
bindTestableContextnull72     @Binds fun bindTestableContext(sysuiTestableContext: SysuiTestableContext): TestableContext
73     @Binds fun bindContext(testableContext: TestableContext): Context
74     @Binds @Application fun bindAppContext(context: Context): Context
75     @Binds @Application fun bindAppResources(resources: Resources): Resources
76     @Binds @Main fun bindMainResources(resources: Resources): Resources
77     @Binds fun bindBroadcastDispatcher(fake: FakeBroadcastDispatcher): BroadcastDispatcher
78     @Binds @SysUISingleton fun bindsShadeInteractor(sii: ShadeInteractorImpl): ShadeInteractor
79 
80     @Binds
81     @SysUISingleton
82     fun bindSceneDataSource(delegator: SceneDataSourceDelegator): SceneDataSource
83 
84     @Binds
85     fun provideFaceAuthInteractor(
86         sysUIFaceAuthInteractor: SystemUIDeviceEntryFaceAuthInteractor
87     ): DeviceEntryFaceAuthInteractor
88 
89     companion object {
90         @Provides
91         fun provideSysuiTestableContext(test: SysuiTestCase): SysuiTestableContext = test.context
92 
93         @Provides
94         fun provideTestableResources(context: TestableContext): TestableResources =
95             context.getOrCreateTestableResources()
96 
97         @Provides
98         fun provideResources(testableResources: TestableResources): Resources =
99             testableResources.resources
100 
101         @Provides
102         fun provideFakeBroadcastDispatcher(test: SysuiTestCase): FakeBroadcastDispatcher =
103             test.fakeBroadcastDispatcher
104 
105         @Provides
106         fun provideContentResolver(context: Context): ContentResolver = context.contentResolver
107 
108         @Provides
109         fun provideBaseShadeInteractor(
110             sceneContainerOn: Provider<ShadeInteractorSceneContainerImpl>,
111             sceneContainerOff: Provider<ShadeInteractorLegacyImpl>
112         ): BaseShadeInteractor {
113             return if (SceneContainerFlag.isEnabled) {
114                 sceneContainerOn.get()
115             } else {
116                 sceneContainerOff.get()
117             }
118         }
119 
120         @Provides
121         @SysUISingleton
122         fun providesSceneDataSourceDelegator(
123             @Application applicationScope: CoroutineScope,
124             config: SceneContainerConfig,
125         ): SceneDataSourceDelegator {
126             return SceneDataSourceDelegator(applicationScope, config)
127         }
128     }
129 }
130 
131 @Deprecated("Use Kosmos instead. See com.android.systemui.kosmos.Kosmos.")
132 interface SysUITestComponent<out T> {
133     val testScope: TestScope
134     val underTest: T
135 }
136 
137 @OptIn(ExperimentalCoroutinesApi::class)
runTestnull138 fun <T : SysUITestComponent<*>> T.runTest(block: suspend T.() -> Unit): Unit =
139     testScope.runTest {
140         // Access underTest immediately to force Dagger to instantiate it prior to the test running
141         underTest
142         runCurrent()
143         block()
144     }
145 
146 @OptIn(ExperimentalCoroutinesApi::class)
SysUITestComponentnull147 fun SysUITestComponent<*>.runCurrent() = testScope.runCurrent()
148 
149 fun <T> SysUITestComponent<*>.collectLastValue(
150     flow: Flow<T>,
151     context: CoroutineContext = EmptyCoroutineContext,
152     start: CoroutineStart = CoroutineStart.DEFAULT,
153 ) = testScope.collectLastValue(flow, context, start)
154 
155 fun <T> SysUITestComponent<*>.collectValues(
156     flow: Flow<T>,
157     context: CoroutineContext = EmptyCoroutineContext,
158     start: CoroutineStart = CoroutineStart.DEFAULT,
159 ) = testScope.collectValues(flow, context, start)
160 
161 val SysUITestComponent<*>.backgroundScope
162     get() = testScope.backgroundScope
163