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