Loading packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt +42 −31 Original line number Diff line number Diff line Loading @@ -22,13 +22,12 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import java.time.Instant import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -38,7 +37,7 @@ import org.junit.runner.RunWith class TutorialSchedulerRepositoryTest : SysuiTestCase() { private lateinit var underTest: TutorialSchedulerRepository private val kosmos = Kosmos() private val kosmos = testKosmos() private val testScope = kosmos.testScope @Before Loading @@ -46,45 +45,57 @@ class TutorialSchedulerRepositoryTest : SysuiTestCase() { underTest = TutorialSchedulerRepository( context, testScope.backgroundScope, kosmos.backgroundScope, "TutorialSchedulerRepositoryTest", ) } @After fun clear() { testScope.launch { underTest.clear() } } @Test fun initialState() = testScope.runTest { fun initialState() = runTestAndClear { assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse() assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse() assertThat(underTest.isLaunched(KEYBOARD)).isFalse() assertThat(underTest.isLaunched(TOUCHPAD)).isFalse() assertThat(underTest.isNotified(KEYBOARD)).isFalse() assertThat(underTest.isNotified(TOUCHPAD)).isFalse() assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isFalse() assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse() } @Test fun connectKeyboard() = testScope.runTest { fun connectKeyboard() = runTestAndClear { val now = Instant.now() underTest.updateFirstConnectionTime(KEYBOARD, now) underTest.setFirstConnectionTime(KEYBOARD, now) assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue() assertThat(underTest.firstConnectionTime(KEYBOARD)!!.epochSecond) assertThat(underTest.getFirstConnectionTime(KEYBOARD)!!.epochSecond) .isEqualTo(now.epochSecond) assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse() } @Test fun launchKeyboard() = testScope.runTest { fun launchKeyboard() = runTestAndClear { val now = Instant.now() underTest.updateLaunchTime(KEYBOARD, now) underTest.setScheduledTutorialLaunchTime(KEYBOARD, now) assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isTrue() assertThat(underTest.getScheduledTutorialLaunchTime(KEYBOARD)!!.epochSecond) .isEqualTo(now.epochSecond) assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse() } @Test fun notifyKeyboard() = runTestAndClear { underTest.setNotified(KEYBOARD) assertThat(underTest.isLaunched(KEYBOARD)).isTrue() assertThat(underTest.launchTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond) assertThat(underTest.isLaunched(TOUCHPAD)).isFalse() assertThat(underTest.isNotified(KEYBOARD)).isTrue() assertThat(underTest.isNotified(TOUCHPAD)).isFalse() } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { underTest.clear() } } } packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt +39 −45 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedul import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.settings.userTracker Loading @@ -34,14 +35,9 @@ import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.hours import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test Loading @@ -65,7 +61,6 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val keyboardRepository = FakeKeyboardRepository() private val touchpadRepository = FakeTouchpadRepository() private lateinit var dataStoreScope: CoroutineScope private lateinit var repository: TutorialSchedulerRepository @Mock private lateinit var notificationManager: NotificationManager @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification> Loading @@ -73,11 +68,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { @Before fun setup() { dataStoreScope = CoroutineScope(Dispatchers.Unconfined) repository = TutorialSchedulerRepository( context, dataStoreScope, kosmos.backgroundScope, dataStoreName = "TutorialNotificationCoordinatorTest", ) val interactor = Loading @@ -87,6 +81,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { repository, kosmos.inputDeviceTutorialLogger, kosmos.commandRegistry, testScope.backgroundScope, ) underTest = TutorialNotificationCoordinator( Loading @@ -100,17 +95,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { underTest.start() } @After fun clear() { runBlocking { repository.clear() } dataStoreScope.cancel() } @Test fun showKeyboardNotification() = testScope.runTest { fun showKeyboardNotification() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_tutorial_notification_title, R.string.launch_keyboard_tutorial_notification_content, Loading @@ -118,10 +106,9 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun showTouchpadNotification() = testScope.runTest { fun showTouchpadNotification() = runTestAndClear { touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_touchpad_tutorial_notification_title, R.string.launch_touchpad_tutorial_notification_content, Loading @@ -129,11 +116,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun showKeyboardTouchpadNotification() = testScope.runTest { fun showKeyboardTouchpadNotification() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_touchpad_tutorial_notification_title, R.string.launch_keyboard_touchpad_tutorial_notification_content, Loading @@ -141,13 +127,21 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun doNotShowNotification() = testScope.runTest { advanceTimeBy(LAUNCH_DELAY) fun doNotShowNotification() = runTestAndClear { testScope.advanceTimeBy(LAUNCH_DELAY) verify(notificationManager, never()) .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any()) } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { repository.clear() } } private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) { verify(notificationManager) .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any()) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +56 −66 Original line number Diff line number Diff line Loading @@ -30,16 +30,11 @@ import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.hours import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -52,18 +47,16 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { private lateinit var underTest: TutorialSchedulerInteractor private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var dataStoreScope: CoroutineScope private val keyboardRepository = FakeKeyboardRepository() private val touchpadRepository = FakeTouchpadRepository() private lateinit var schedulerRepository: TutorialSchedulerRepository @Before fun setup() { dataStoreScope = CoroutineScope(Dispatchers.Unconfined) schedulerRepository = TutorialSchedulerRepository( context, dataStoreScope, testScope.backgroundScope, dataStoreName = "TutorialSchedulerInteractorTest", ) underTest = Loading @@ -73,98 +66,95 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { schedulerRepository, kosmos.inputDeviceTutorialLogger, kosmos.commandRegistry, testScope.backgroundScope, ) } @After fun clear() { runBlocking { schedulerRepository.clear() } dataStoreScope.cancel() } @Test fun connectKeyboard_delayElapse_launchForKeyboard() = testScope.runTest { fun connectKeyboard_delayElapse_notifyForKeyboard() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.KEYBOARD) testScope.advanceTimeBy(LAUNCH_DELAY) notifyAndAssert(TutorialType.KEYBOARD) } @Test fun connectBothDevices_delayElapse_launchForBoth() = testScope.runTest { fun connectBothDevices_delayElapse_notifyForBoth() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.BOTH) notifyAndAssert(TutorialType.BOTH) } @Test fun connectBothDevice_delayNotElapse_launchNothing() = testScope.runTest { fun connectBothDevice_delayNotElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) launchAndAssert(TutorialType.NONE) notifyAndAssert(TutorialType.NONE) } @Test fun nothingConnect_delayElapse_launchNothing() = testScope.runTest { fun nothingConnect_delayElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(false) touchpadRepository.setIsAnyTouchpadConnected(false) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.NONE) notifyAndAssert(TutorialType.NONE) } @Test fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() = testScope.runTest { fun connectKeyboard_thenTouchpad_delayElapse_notifyForBoth() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(REMAINING_TIME) testScope.advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.BOTH) notifyAndAssert(TutorialType.BOTH) } @Test fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() = testScope.runTest { fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(false) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.NONE) testScope.advanceTimeBy(REMAINING_TIME) notifyAndAssert(TutorialType.NONE) } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { schedulerRepository.clear() } } private suspend fun launchAndAssert(expectedTutorial: TutorialType) = private fun notifyAndAssert(expectedTutorial: TutorialType) = testScope.backgroundScope.launch { val actualTutorial = underTest.tutorials.first() assertThat(actualTutorial).isEqualTo(expectedTutorial) // TODO: need to update after we move launch into the tutorial when (expectedTutorial) { TutorialType.KEYBOARD -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse() } TutorialType.TOUCHPAD -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue() } TutorialType.BOTH -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue() } TutorialType.NONE -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse() } } } Loading packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt +3 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.education.domain.interactor import android.os.SystemProperties import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.contextualeducation.GestureType Loading Loading @@ -56,7 +57,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.merge import com.android.app.tracing.coroutines.launchTraced as launch /** Allow listening to new contextual education triggered */ @SysUISingleton Loading Loading @@ -278,7 +278,8 @@ constructor( } private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean { val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false val oobeLaunchTime = tutorialRepository.getScheduledTutorialLaunchTime(deviceType) ?: return false return clock .instant() .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds)) Loading packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt→packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt +6 −3 Original line number Diff line number Diff line Loading @@ -20,14 +20,17 @@ import java.time.Instant data class DeviceSchedulerInfo( var launchTime: Instant? = null, var firstConnectionTime: Instant? = null var firstConnectionTime: Instant? = null, var isNotified: Boolean = false, ) { constructor( launchTimeSec: Long?, firstConnectionTimeSec: Long? firstConnectionTimeSec: Long?, isNotified: Boolean = false, ) : this( launchTimeSec?.let { Instant.ofEpochSecond(it) }, firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) } firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) }, isNotified, ) val wasEverConnected: Boolean Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt +42 −31 Original line number Diff line number Diff line Loading @@ -22,13 +22,12 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.KEYBOARD import com.android.systemui.inputdevice.tutorial.data.repository.DeviceType.TOUCHPAD import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedulerRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import java.time.Instant import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -38,7 +37,7 @@ import org.junit.runner.RunWith class TutorialSchedulerRepositoryTest : SysuiTestCase() { private lateinit var underTest: TutorialSchedulerRepository private val kosmos = Kosmos() private val kosmos = testKosmos() private val testScope = kosmos.testScope @Before Loading @@ -46,45 +45,57 @@ class TutorialSchedulerRepositoryTest : SysuiTestCase() { underTest = TutorialSchedulerRepository( context, testScope.backgroundScope, kosmos.backgroundScope, "TutorialSchedulerRepositoryTest", ) } @After fun clear() { testScope.launch { underTest.clear() } } @Test fun initialState() = testScope.runTest { fun initialState() = runTestAndClear { assertThat(underTest.wasEverConnected(KEYBOARD)).isFalse() assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse() assertThat(underTest.isLaunched(KEYBOARD)).isFalse() assertThat(underTest.isLaunched(TOUCHPAD)).isFalse() assertThat(underTest.isNotified(KEYBOARD)).isFalse() assertThat(underTest.isNotified(TOUCHPAD)).isFalse() assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isFalse() assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse() } @Test fun connectKeyboard() = testScope.runTest { fun connectKeyboard() = runTestAndClear { val now = Instant.now() underTest.updateFirstConnectionTime(KEYBOARD, now) underTest.setFirstConnectionTime(KEYBOARD, now) assertThat(underTest.wasEverConnected(KEYBOARD)).isTrue() assertThat(underTest.firstConnectionTime(KEYBOARD)!!.epochSecond) assertThat(underTest.getFirstConnectionTime(KEYBOARD)!!.epochSecond) .isEqualTo(now.epochSecond) assertThat(underTest.wasEverConnected(TOUCHPAD)).isFalse() } @Test fun launchKeyboard() = testScope.runTest { fun launchKeyboard() = runTestAndClear { val now = Instant.now() underTest.updateLaunchTime(KEYBOARD, now) underTest.setScheduledTutorialLaunchTime(KEYBOARD, now) assertThat(underTest.isScheduledTutorialLaunched(KEYBOARD)).isTrue() assertThat(underTest.getScheduledTutorialLaunchTime(KEYBOARD)!!.epochSecond) .isEqualTo(now.epochSecond) assertThat(underTest.isScheduledTutorialLaunched(TOUCHPAD)).isFalse() } @Test fun notifyKeyboard() = runTestAndClear { underTest.setNotified(KEYBOARD) assertThat(underTest.isLaunched(KEYBOARD)).isTrue() assertThat(underTest.launchTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond) assertThat(underTest.isLaunched(TOUCHPAD)).isFalse() assertThat(underTest.isNotified(KEYBOARD)).isTrue() assertThat(underTest.isNotified(TOUCHPAD)).isFalse() } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { underTest.clear() } } }
packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialNotificationCoordinatorTest.kt +39 −45 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.inputdevice.tutorial.data.repository.TutorialSchedul import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.ui.TutorialNotificationCoordinator import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository import com.android.systemui.kosmos.backgroundScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.settings.userTracker Loading @@ -34,14 +35,9 @@ import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.hours import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test Loading @@ -65,7 +61,6 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val keyboardRepository = FakeKeyboardRepository() private val touchpadRepository = FakeTouchpadRepository() private lateinit var dataStoreScope: CoroutineScope private lateinit var repository: TutorialSchedulerRepository @Mock private lateinit var notificationManager: NotificationManager @Captor private lateinit var notificationCaptor: ArgumentCaptor<Notification> Loading @@ -73,11 +68,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { @Before fun setup() { dataStoreScope = CoroutineScope(Dispatchers.Unconfined) repository = TutorialSchedulerRepository( context, dataStoreScope, kosmos.backgroundScope, dataStoreName = "TutorialNotificationCoordinatorTest", ) val interactor = Loading @@ -87,6 +81,7 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { repository, kosmos.inputDeviceTutorialLogger, kosmos.commandRegistry, testScope.backgroundScope, ) underTest = TutorialNotificationCoordinator( Loading @@ -100,17 +95,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { underTest.start() } @After fun clear() { runBlocking { repository.clear() } dataStoreScope.cancel() } @Test fun showKeyboardNotification() = testScope.runTest { fun showKeyboardNotification() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_tutorial_notification_title, R.string.launch_keyboard_tutorial_notification_content, Loading @@ -118,10 +106,9 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun showTouchpadNotification() = testScope.runTest { fun showTouchpadNotification() = runTestAndClear { touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_touchpad_tutorial_notification_title, R.string.launch_touchpad_tutorial_notification_content, Loading @@ -129,11 +116,10 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun showKeyboardTouchpadNotification() = testScope.runTest { fun showKeyboardTouchpadNotification() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) verifyNotification( R.string.launch_keyboard_touchpad_tutorial_notification_title, R.string.launch_keyboard_touchpad_tutorial_notification_content, Loading @@ -141,13 +127,21 @@ class TutorialNotificationCoordinatorTest : SysuiTestCase() { } @Test fun doNotShowNotification() = testScope.runTest { advanceTimeBy(LAUNCH_DELAY) fun doNotShowNotification() = runTestAndClear { testScope.advanceTimeBy(LAUNCH_DELAY) verify(notificationManager, never()) .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), any(), any()) } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { repository.clear() } } private fun verifyNotification(@StringRes titleResId: Int, @StringRes contentResId: Int) { verify(notificationManager) .notifyAsUser(eq(TAG), eq(NOTIFICATION_ID), notificationCaptor.capture(), any()) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/tutorial/domain/interactor/TutorialSchedulerInteractorTest.kt +56 −66 Original line number Diff line number Diff line Loading @@ -30,16 +30,11 @@ import com.android.systemui.testKosmos import com.android.systemui.touchpad.data.repository.FakeTouchpadRepository import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.hours import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith Loading @@ -52,18 +47,16 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { private lateinit var underTest: TutorialSchedulerInteractor private val kosmos = testKosmos() private val testScope = kosmos.testScope private lateinit var dataStoreScope: CoroutineScope private val keyboardRepository = FakeKeyboardRepository() private val touchpadRepository = FakeTouchpadRepository() private lateinit var schedulerRepository: TutorialSchedulerRepository @Before fun setup() { dataStoreScope = CoroutineScope(Dispatchers.Unconfined) schedulerRepository = TutorialSchedulerRepository( context, dataStoreScope, testScope.backgroundScope, dataStoreName = "TutorialSchedulerInteractorTest", ) underTest = Loading @@ -73,98 +66,95 @@ class TutorialSchedulerInteractorTest : SysuiTestCase() { schedulerRepository, kosmos.inputDeviceTutorialLogger, kosmos.commandRegistry, testScope.backgroundScope, ) } @After fun clear() { runBlocking { schedulerRepository.clear() } dataStoreScope.cancel() } @Test fun connectKeyboard_delayElapse_launchForKeyboard() = testScope.runTest { fun connectKeyboard_delayElapse_notifyForKeyboard() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.KEYBOARD) testScope.advanceTimeBy(LAUNCH_DELAY) notifyAndAssert(TutorialType.KEYBOARD) } @Test fun connectBothDevices_delayElapse_launchForBoth() = testScope.runTest { fun connectBothDevices_delayElapse_notifyForBoth() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.BOTH) notifyAndAssert(TutorialType.BOTH) } @Test fun connectBothDevice_delayNotElapse_launchNothing() = testScope.runTest { fun connectBothDevice_delayNotElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) launchAndAssert(TutorialType.NONE) notifyAndAssert(TutorialType.NONE) } @Test fun nothingConnect_delayElapse_launchNothing() = testScope.runTest { fun nothingConnect_delayElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(false) touchpadRepository.setIsAnyTouchpadConnected(false) advanceTimeBy(LAUNCH_DELAY) testScope.advanceTimeBy(LAUNCH_DELAY) launchAndAssert(TutorialType.NONE) notifyAndAssert(TutorialType.NONE) } @Test fun connectKeyboard_thenTouchpad_delayElapse_launchForBoth() = testScope.runTest { fun connectKeyboard_thenTouchpad_delayElapse_notifyForBoth() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) advanceTimeBy(REMAINING_TIME) testScope.advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.BOTH) notifyAndAssert(TutorialType.BOTH) } @Test fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_launchNothing() = testScope.runTest { fun connectKeyboard_thenTouchpad_removeKeyboard_delayElapse_notifyNothing() = runTestAndClear { keyboardRepository.setIsAnyKeyboardConnected(true) advanceTimeBy(A_SHORT_PERIOD_OF_TIME) testScope.advanceTimeBy(A_SHORT_PERIOD_OF_TIME) touchpadRepository.setIsAnyTouchpadConnected(true) keyboardRepository.setIsAnyKeyboardConnected(false) advanceTimeBy(REMAINING_TIME) launchAndAssert(TutorialType.NONE) testScope.advanceTimeBy(REMAINING_TIME) notifyAndAssert(TutorialType.NONE) } private fun runTestAndClear(block: suspend () -> Unit) = testScope.runTest { try { block() } finally { schedulerRepository.clear() } } private suspend fun launchAndAssert(expectedTutorial: TutorialType) = private fun notifyAndAssert(expectedTutorial: TutorialType) = testScope.backgroundScope.launch { val actualTutorial = underTest.tutorials.first() assertThat(actualTutorial).isEqualTo(expectedTutorial) // TODO: need to update after we move launch into the tutorial when (expectedTutorial) { TutorialType.KEYBOARD -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse() } TutorialType.TOUCHPAD -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue() } TutorialType.BOTH -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isTrue() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isTrue() } TutorialType.NONE -> { assertThat(schedulerRepository.isLaunched(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isLaunched(DeviceType.TOUCHPAD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.KEYBOARD)).isFalse() assertThat(schedulerRepository.isNotified(DeviceType.TOUCHPAD)).isFalse() } } } Loading
packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractor.kt +3 −2 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.education.domain.interactor import android.os.SystemProperties import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.contextualeducation.GestureType Loading Loading @@ -56,7 +57,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.merge import com.android.app.tracing.coroutines.launchTraced as launch /** Allow listening to new contextual education triggered */ @SysUISingleton Loading Loading @@ -278,7 +278,8 @@ constructor( } private suspend fun hasInitialDelayElapsed(deviceType: DeviceType): Boolean { val oobeLaunchTime = tutorialRepository.launchTime(deviceType) ?: return false val oobeLaunchTime = tutorialRepository.getScheduledTutorialLaunchTime(deviceType) ?: return false return clock .instant() .isAfter(oobeLaunchTime.plusSeconds(initialDelayDuration.inWholeSeconds)) Loading
packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/TutorialSchedulerInfo.kt→packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt +6 −3 Original line number Diff line number Diff line Loading @@ -20,14 +20,17 @@ import java.time.Instant data class DeviceSchedulerInfo( var launchTime: Instant? = null, var firstConnectionTime: Instant? = null var firstConnectionTime: Instant? = null, var isNotified: Boolean = false, ) { constructor( launchTimeSec: Long?, firstConnectionTimeSec: Long? firstConnectionTimeSec: Long?, isNotified: Boolean = false, ) : this( launchTimeSec?.let { Instant.ofEpochSecond(it) }, firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) } firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) }, isNotified, ) val wasEverConnected: Boolean Loading