Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc7502b2 authored by Steve Elliott's avatar Steve Elliott
Browse files

SysUITestModule utilities

Introduce a convenience interface SysUITestComponent<UnderTest>, that
can be extended by TestComponents that include SysUITestModule.
Extending this interface gives you a convenient runTest {..} method that
gives the passed lambda access to all of the fields declared on the
component, as well as common TestScope methods.

Flag: NA
Test: atest SystemUITests
Change-Id: Ife5d4d8942a11df8c6aea233f0150a53be1d2368
parent 1c99b7d9
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -24,11 +24,21 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.FakeBroadcastDispatcher
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import dagger.Binds
import dagger.Module
import dagger.Provides
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest

@Module(
    includes =
@@ -64,3 +74,35 @@ interface SysUITestModule {
            test.fakeBroadcastDispatcher
    }
}

interface SysUITestComponent<out T> {
    val testScope: TestScope
    val underTest: T
}

@OptIn(ExperimentalCoroutinesApi::class)
fun <T : SysUITestComponent<*>> T.runTest(block: suspend T.() -> Unit): Unit =
    testScope.runTest {
        // Access underTest immediately to force Dagger to instantiate it prior to the test running
        underTest
        runCurrent()
        block()
    }

@OptIn(ExperimentalCoroutinesApi::class)
fun SysUITestComponent<*>.runCurrent() = testScope.runCurrent()

fun <T> SysUITestComponent<*>.collectLastValue(
    flow: Flow<T>,
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
) = testScope.collectLastValue(flow, context, start)

fun <T> SysUITestComponent<*>.collectValues(
    flow: Flow<T>,
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
) = testScope.collectValues(flow, context, start)

val SysUITestComponent<*>.backgroundScope
    get() = testScope.backgroundScope
+31 −42
Original line number Diff line number Diff line
@@ -17,7 +17,10 @@
package com.android.systemui.biometrics

import androidx.test.filters.SmallTest
import com.android.SysUITestComponent
import com.android.SysUITestModule
import com.android.runCurrent
import com.android.runTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FakeFeatureFlagsClassicModule
@@ -26,10 +29,6 @@ import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.domain.UserDomainLayerModule
import dagger.BindsInstance
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -38,9 +37,29 @@ import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations

@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

    @SysUISingleton
    @Component(
        modules =
            [
                SysUITestModule::class,
                UserDomainLayerModule::class,
            ]
    )
    interface TestComponent : SysUITestComponent<AuthDialogPanelInteractionDetector> {

        val shadeRepository: FakeShadeRepository

        @Component.Factory
        interface Factory {
            fun create(
                @BindsInstance test: SysuiTestCase,
                featureFlags: FakeFeatureFlagsClassicModule,
            ): TestComponent
        }
    }

    private val testComponent: TestComponent =
        DaggerAuthDialogPanelInteractionDetectorTest_TestComponent.factory()
            .create(
@@ -52,11 +71,9 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {
                    },
            )

    @Mock private lateinit var action: Runnable
    private val detector: AuthDialogPanelInteractionDetector = testComponent.underTest

    private val testScope = testComponent.testScope
    private val shadeRepository = testComponent.shadeRepository
    private val detector = testComponent.detector
    @Mock private lateinit var action: Runnable

    @Before
    fun setUp() {
@@ -65,7 +82,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

    @Test
    fun enableDetector_expand_shouldRunAction() =
        testScope.runTest {
        testComponent.runTest {
            // GIVEN shade is closed and detector is enabled
            shadeRepository.setLegacyShadeExpansion(0f)
            detector.enable(action)
@@ -82,7 +99,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

    @Test
    fun enableDetector_shadeExpandImmediate_shouldNotPostRunnable() =
        testScope.runTest {
        testComponent.runTest {
            // GIVEN shade is closed and detector is enabled
            shadeRepository.setLegacyShadeExpansion(0f)
            detector.enable(action)
@@ -94,14 +111,11 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

            // THEN action not run
            verifyZeroInteractions(action)

            // Clean up job
            detector.disable()
        }

    @Test
    fun disableDetector_shouldNotPostRunnable() =
        testScope.runTest {
        testComponent.runTest {
            // GIVEN shade is closed and detector is enabled
            shadeRepository.setLegacyShadeExpansion(0f)
            detector.enable(action)
@@ -109,6 +123,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

            // WHEN detector is disabled and shade opens
            detector.disable()
            runCurrent()
            shadeRepository.setLegacyShadeTracking(true)
            shadeRepository.setLegacyShadeExpansion(.5f)
            runCurrent()
@@ -119,7 +134,7 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

    @Test
    fun enableDetector_beginCollapse_shouldNotPostRunnable() =
        testScope.runTest {
        testComponent.runTest {
            // GIVEN shade is open and detector is enabled
            shadeRepository.setLegacyShadeExpansion(1f)
            detector.enable(action)
@@ -131,31 +146,5 @@ class AuthDialogPanelInteractionDetectorTest : SysuiTestCase() {

            // THEN action not run
            verifyZeroInteractions(action)

            // Clean up job
            detector.disable()
        }

    @SysUISingleton
    @Component(
        modules =
            [
                SysUITestModule::class,
                UserDomainLayerModule::class,
            ]
    )
    interface TestComponent {

        val detector: AuthDialogPanelInteractionDetector
        val shadeRepository: FakeShadeRepository
        val testScope: TestScope

        @Component.Factory
        interface Factory {
            fun create(
                @BindsInstance test: SysuiTestCase,
                featureFlags: FakeFeatureFlagsClassicModule,
            ): TestComponent
        }
        }
}
+33 −34
Original line number Diff line number Diff line
@@ -21,8 +21,12 @@ package com.android.systemui.keyguard.ui.viewmodel

import android.view.View
import androidx.test.filters.SmallTest
import com.android.SysUITestComponent
import com.android.SysUITestModule
import com.android.TestMocksModule
import com.android.collectLastValue
import com.android.runCurrent
import com.android.runTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
@@ -319,12 +323,10 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Component(modules = [SysUITestModule::class])
    @SysUISingleton
    interface TestComponent {
        val underTest: KeyguardRootViewModel
    interface TestComponent : SysUITestComponent<KeyguardRootViewModel> {
        val deviceEntryRepository: FakeDeviceEntryRepository
        val notifsKeyguardRepository: FakeNotificationsKeyguardViewStateRepository
        val repository: FakeKeyguardRepository
        val testScope: TestScope
        val transitionRepository: FakeKeyguardTransitionRepository

        @Component.Factory
@@ -356,28 +358,25 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {
                    TestMocksModule(
                        dozeParameters = dozeParams,
                        screenOffAnimationController = screenOffAnimController,
                    ),
            )
            )
            .run {
            .runTest {
                reset(clockController)
                underTest.clockControllerProvider = Provider { clockController }
                testScope.runTest {
                    runCurrent()
                block()
            }
            }

    @Test
    fun iconContainer_isNotVisible_notOnKeyguard_dontShowAodIconsWhenShade() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        transitionRepository.sendTransitionSteps(
            from = KeyguardState.OFF,
            to = KeyguardState.GONE,
            testScope,
        )
        whenever(screenOffAnimController.shouldShowAodIconsWhenShade()).thenReturn(false)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isFalse()
        assertThat(isVisible?.isAnimating).isFalse()
@@ -385,33 +384,33 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Test
    fun iconContainer_isVisible_bypassEnabled() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        deviceEntryRepository.setBypassEnabled(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isTrue()
    }

    @Test
    fun iconContainer_isNotVisible_pulseExpanding_notBypassing() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(true)
        deviceEntryRepository.setBypassEnabled(false)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isEqualTo(false)
    }

    @Test
    fun iconContainer_isVisible_notifsFullyHidden_bypassEnabled() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(false)
        deviceEntryRepository.setBypassEnabled(true)
        notifsKeyguardRepository.setNotificationsFullyHidden(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isTrue()
        assertThat(isVisible?.isAnimating).isTrue()
@@ -419,13 +418,13 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Test
    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_aodDisabled() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(false)
        deviceEntryRepository.setBypassEnabled(false)
        whenever(dozeParams.alwaysOn).thenReturn(false)
        notifsKeyguardRepository.setNotificationsFullyHidden(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isTrue()
        assertThat(isVisible?.isAnimating).isFalse()
@@ -433,14 +432,14 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Test
    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled_displayNeedsBlanking() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(false)
        deviceEntryRepository.setBypassEnabled(false)
        whenever(dozeParams.alwaysOn).thenReturn(true)
        whenever(dozeParams.displayNeedsBlanking).thenReturn(true)
        notifsKeyguardRepository.setNotificationsFullyHidden(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isTrue()
        assertThat(isVisible?.isAnimating).isFalse()
@@ -448,14 +447,14 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Test
    fun iconContainer_isVisible_notifsFullyHidden_bypassDisabled() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(false)
        deviceEntryRepository.setBypassEnabled(false)
        whenever(dozeParams.alwaysOn).thenReturn(true)
        whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
        notifsKeyguardRepository.setNotificationsFullyHidden(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.value).isTrue()
        assertThat(isVisible?.isAnimating).isTrue()
@@ -463,18 +462,18 @@ class KeyguardRootViewModelTestWithFakes : SysuiTestCase() {

    @Test
    fun isIconContainerVisible_stopAnimation() = runTest {
        val isVisible by testScope.collectLastValue(underTest.isNotifIconContainerVisible)
        testScope.runCurrent()
        val isVisible by collectLastValue(underTest.isNotifIconContainerVisible)
        runCurrent()
        notifsKeyguardRepository.setPulseExpanding(false)
        deviceEntryRepository.setBypassEnabled(false)
        whenever(dozeParams.alwaysOn).thenReturn(true)
        whenever(dozeParams.displayNeedsBlanking).thenReturn(false)
        notifsKeyguardRepository.setNotificationsFullyHidden(true)
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.isAnimating).isEqualTo(true)
        isVisible?.stopAnimating()
        testScope.runCurrent()
        runCurrent()

        assertThat(isVisible?.isAnimating).isEqualTo(false)
    }
+96 −130

File changed.

Preview size limit exceeded, changes collapsed.

+36 −50
Original line number Diff line number Diff line
@@ -13,14 +13,16 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.notification.data.repository

import androidx.test.filters.SmallTest
import com.android.SysUITestComponent
import com.android.SysUITestModule
import com.android.collectLastValue
import com.android.runCurrent
import com.android.runTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator
import com.android.systemui.util.mockito.whenever
@@ -28,24 +30,33 @@ import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.mockito.Mockito.verify

@SmallTest
class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {

    @SysUISingleton
    @Component(modules = [SysUITestModule::class])
    interface TestComponent : SysUITestComponent<NotificationsKeyguardViewStateRepositoryImpl> {

        val mockWakeUpCoordinator: NotificationWakeUpCoordinator

        @Component.Factory
        interface Factory {
            fun create(
                @BindsInstance test: SysuiTestCase,
            ): TestComponent
        }
    }

    private val testComponent: TestComponent =
        DaggerNotificationsKeyguardViewStateRepositoryTest_TestComponent.factory()
            .create(test = this)

    @Test
    fun areNotifsFullyHidden_reflectsWakeUpCoordinator() =
        with(testComponent) {
            testScope.runTest {
        testComponent.runTest {
            whenever(mockWakeUpCoordinator.notificationsFullyHidden).thenReturn(false)
            val notifsFullyHidden by collectLastValue(underTest.areNotificationsFullyHidden)
            runCurrent()
@@ -58,12 +69,10 @@ class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {

            assertThat(notifsFullyHidden).isTrue()
        }
        }

    @Test
    fun isPulseExpanding_reflectsWakeUpCoordinator() =
        with(testComponent) {
            testScope.runTest {
        testComponent.runTest {
            whenever(mockWakeUpCoordinator.isPulseExpanding()).thenReturn(false)
            val isPulseExpanding by collectLastValue(underTest.isPulseExpanding)
            runCurrent()
@@ -77,26 +86,3 @@ class NotificationsKeyguardViewStateRepositoryTest : SysuiTestCase() {
            assertThat(isPulseExpanding).isTrue()
        }
}

    @SysUISingleton
    @Component(
        modules =
            [
                SysUITestModule::class,
            ]
    )
    interface TestComponent {

        val underTest: NotificationsKeyguardViewStateRepositoryImpl

        val mockWakeUpCoordinator: NotificationWakeUpCoordinator
        val testScope: TestScope

        @Component.Factory
        interface Factory {
            fun create(
                @BindsInstance test: SysuiTestCase,
            ): TestComponent
        }
    }
}
Loading