Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt +12 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { } private val testUserId = 1111 private val secondTestUserId = 1112 // For deleting any test files created after the test @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() Loading @@ -73,11 +74,20 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { assertThat(model?.signalCount).isEqualTo(1) // User is changed. underTest.setUser(1112) underTest.setUser(secondTestUserId) // Assert count is 0 after user is changed. assertThat(model?.signalCount).isEqualTo(0) } @Test fun changeUserIdForNewUser() = testScope.runTest { val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) assertThat(model?.userId).isEqualTo(testUserId) underTest.setUser(secondTestUserId) assertThat(model?.userId).isEqualTo(secondTestUserId) } @Test fun dataChangedOnUpdate() = testScope.runTest { Loading @@ -88,6 +98,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(), lastEducationTime = kosmos.fakeEduClock.instant(), usageSessionStartTime = kosmos.fakeEduClock.instant(), userId = testUserId ) underTest.updateGestureEduModel(BACK) { newModel } val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { .isEqualTo( GestureEduModel( signalCount = 1, usageSessionStartTime = secondSignalReceivedTime usageSessionStartTime = secondSignalReceivedTime, userId = 0 ) ) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +68 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.education.domain.ui.view import android.app.Notification import android.app.NotificationManager import android.content.applicationContext import android.widget.Toast import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -30,27 +32,35 @@ import com.android.systemui.education.ui.view.ContextualEduUiCoordinator import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val interactor = kosmos.contextualEducationInteractor private lateinit var underTest: ContextualEduUiCoordinator @Mock private lateinit var toast: Toast @Mock private lateinit var notificationManager: NotificationManager @get:Rule val mockitoRule = MockitoJUnit.rule() private var toastContent = "" @Before fun setUp() { Loading @@ -60,23 +70,76 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { kosmos.keyboardTouchpadEduInteractor ) underTest = ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast } ContextualEduUiCoordinator( kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, notificationManager ) { content -> toastContent = content toast } underTest.start() kosmos.keyboardTouchpadEduInteractor.start() } @Test @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) fun showToastOnNewEdu() = testScope.runTest { triggerEducation(BACK) runCurrent() verify(toast).show() } private suspend fun triggerEducation(gestureType: GestureType) { @Test fun showNotificationOn2ndEdu() = testScope.runTest { triggerEducation(BACK) triggerEducation(BACK) verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any()) } @Test fun verifyBackEduToastContent() = testScope.runTest { triggerEducation(BACK) assertThat(toastContent).isEqualTo(context.getString(R.string.back_edu_toast_content)) } @Test fun verifyBackEduNotificationContent() = testScope.runTest { val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) triggerEducation(BACK) triggerEducation(BACK) verify(notificationManager) .notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any()) verifyNotificationContent( R.string.back_edu_notification_title, R.string.back_edu_notification_content, notificationCaptor.value ) } private fun verifyNotificationContent( titleResId: Int, contentResId: Int, notification: Notification ) { val expectedContent = context.getString(contentResId) val expectedTitle = context.getString(titleResId) val actualContent = notification.getString(Notification.EXTRA_TEXT) val actualTitle = notification.getString(Notification.EXTRA_TITLE) assertThat(actualContent).isEqualTo(expectedContent) assertThat(actualTitle).isEqualTo(expectedTitle) } private fun Notification.getString(key: String): String = this.extras?.getCharSequence(key).toString() private suspend fun TestScope.triggerEducation(gestureType: GestureType) { for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) { interactor.incrementSignalCount(gestureType) } runCurrent() } } packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt +1 −0 Original line number Diff line number Diff line Loading @@ -28,4 +28,5 @@ data class GestureEduModel( val lastShortcutTriggeredTime: Instant? = null, val usageSessionStartTime: Instant? = null, val lastEducationTime: Instant? = null, val userId: Int ) packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt +5 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.systemui.education.data.model.GestureEduModel import java.time.Instant import javax.inject.Inject import javax.inject.Provider import kotlin.properties.Delegates.notNull import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow Loading Loading @@ -79,6 +80,8 @@ constructor( const val DATASTORE_DIR = "education/USER%s_ContextualEducation" } private var userId by notNull<Int>() private var dataStoreScope: CoroutineScope? = null private val datastore = MutableStateFlow<DataStore<Preferences>?>(null) Loading @@ -89,6 +92,7 @@ constructor( override fun setUser(userId: Int) { dataStoreScope?.cancel() val newDsScope = dataStoreScopeProvider.get() this.userId = userId datastore.value = PreferenceDataStoreFactory.create( produceFile = { Loading Loading @@ -123,6 +127,7 @@ constructor( preferences[getLastEducationTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, userId = userId ) } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryTest.kt +12 −1 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { } private val testUserId = 1111 private val secondTestUserId = 1112 // For deleting any test files created after the test @get:Rule val tmpFolder: TemporaryFolder = TemporaryFolder.builder().assureDeletion().build() Loading @@ -73,11 +74,20 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { assertThat(model?.signalCount).isEqualTo(1) // User is changed. underTest.setUser(1112) underTest.setUser(secondTestUserId) // Assert count is 0 after user is changed. assertThat(model?.signalCount).isEqualTo(0) } @Test fun changeUserIdForNewUser() = testScope.runTest { val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) assertThat(model?.userId).isEqualTo(testUserId) underTest.setUser(secondTestUserId) assertThat(model?.userId).isEqualTo(secondTestUserId) } @Test fun dataChangedOnUpdate() = testScope.runTest { Loading @@ -88,6 +98,7 @@ class ContextualEducationRepositoryTest : SysuiTestCase() { lastShortcutTriggeredTime = kosmos.fakeEduClock.instant(), lastEducationTime = kosmos.fakeEduClock.instant(), usageSessionStartTime = kosmos.fakeEduClock.instant(), userId = testUserId ) underTest.updateGestureEduModel(BACK) { newModel } val model by collectLastValue(underTest.readGestureEduModelFlow(BACK)) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduInteractorTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ class KeyboardTouchpadEduInteractorTest : SysuiTestCase() { .isEqualTo( GestureEduModel( signalCount = 1, usageSessionStartTime = secondSignalReceivedTime usageSessionStartTime = secondSignalReceivedTime, userId = 0 ) ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +68 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.education.domain.ui.view import android.app.Notification import android.app.NotificationManager import android.content.applicationContext import android.widget.Toast import androidx.test.ext.junit.runners.AndroidJUnit4 Loading @@ -30,27 +32,35 @@ import com.android.systemui.education.ui.view.ContextualEduUiCoordinator import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any import org.mockito.kotlin.verify @SmallTest @RunWith(AndroidJUnit4::class) @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val interactor = kosmos.contextualEducationInteractor private lateinit var underTest: ContextualEduUiCoordinator @Mock private lateinit var toast: Toast @Mock private lateinit var notificationManager: NotificationManager @get:Rule val mockitoRule = MockitoJUnit.rule() private var toastContent = "" @Before fun setUp() { Loading @@ -60,23 +70,76 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { kosmos.keyboardTouchpadEduInteractor ) underTest = ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast } ContextualEduUiCoordinator( kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, notificationManager ) { content -> toastContent = content toast } underTest.start() kosmos.keyboardTouchpadEduInteractor.start() } @Test @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class) fun showToastOnNewEdu() = testScope.runTest { triggerEducation(BACK) runCurrent() verify(toast).show() } private suspend fun triggerEducation(gestureType: GestureType) { @Test fun showNotificationOn2ndEdu() = testScope.runTest { triggerEducation(BACK) triggerEducation(BACK) verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any()) } @Test fun verifyBackEduToastContent() = testScope.runTest { triggerEducation(BACK) assertThat(toastContent).isEqualTo(context.getString(R.string.back_edu_toast_content)) } @Test fun verifyBackEduNotificationContent() = testScope.runTest { val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) triggerEducation(BACK) triggerEducation(BACK) verify(notificationManager) .notifyAsUser(any(), anyInt(), notificationCaptor.capture(), any()) verifyNotificationContent( R.string.back_edu_notification_title, R.string.back_edu_notification_content, notificationCaptor.value ) } private fun verifyNotificationContent( titleResId: Int, contentResId: Int, notification: Notification ) { val expectedContent = context.getString(contentResId) val expectedTitle = context.getString(titleResId) val actualContent = notification.getString(Notification.EXTRA_TEXT) val actualTitle = notification.getString(Notification.EXTRA_TITLE) assertThat(actualContent).isEqualTo(expectedContent) assertThat(actualTitle).isEqualTo(expectedTitle) } private fun Notification.getString(key: String): String = this.extras?.getCharSequence(key).toString() private suspend fun TestScope.triggerEducation(gestureType: GestureType) { for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) { interactor.incrementSignalCount(gestureType) } runCurrent() } }
packages/SystemUI/src/com/android/systemui/education/data/model/GestureEduModel.kt +1 −0 Original line number Diff line number Diff line Loading @@ -28,4 +28,5 @@ data class GestureEduModel( val lastShortcutTriggeredTime: Instant? = null, val usageSessionStartTime: Instant? = null, val lastEducationTime: Instant? = null, val userId: Int )
packages/SystemUI/src/com/android/systemui/education/data/repository/UserContextualEducationRepository.kt +5 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.systemui.education.data.model.GestureEduModel import java.time.Instant import javax.inject.Inject import javax.inject.Provider import kotlin.properties.Delegates.notNull import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow Loading Loading @@ -79,6 +80,8 @@ constructor( const val DATASTORE_DIR = "education/USER%s_ContextualEducation" } private var userId by notNull<Int>() private var dataStoreScope: CoroutineScope? = null private val datastore = MutableStateFlow<DataStore<Preferences>?>(null) Loading @@ -89,6 +92,7 @@ constructor( override fun setUser(userId: Int) { dataStoreScope?.cancel() val newDsScope = dataStoreScopeProvider.get() this.userId = userId datastore.value = PreferenceDataStoreFactory.create( produceFile = { Loading Loading @@ -123,6 +127,7 @@ constructor( preferences[getLastEducationTimeKey(gestureType)]?.let { Instant.ofEpochSecond(it) }, userId = userId ) } Loading