Merge changes Ia3b1ae8f,Ie87220a7 into tm-qpr-dev

* changes:
  Clean-up FooterActionsViewModelTest
  Introduce TestScope.collectLastValue(Flow)
This commit is contained in:
Jordan Demeulenaere
2022-12-02 09:30:12 +00:00
committed by Android (Google) Code Review
4 changed files with 64 additions and 54 deletions

View File

@@ -41,6 +41,7 @@ import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.TestCoroutineScheduler
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,7 +56,7 @@ class FooterActionsInteractorTest : SysuiTestCase() {
@Before
fun setUp() {
utils = FooterActionsTestUtils(context, TestableLooper.get(this))
utils = FooterActionsTestUtils(context, TestableLooper.get(this), TestCoroutineScheduler())
}
@Test

View File

@@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qs.FakeFgsManagerController
import com.android.systemui.qs.QSSecurityFooterUtils
import com.android.systemui.qs.footer.FooterActionsTestUtils
@@ -44,12 +45,9 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -62,16 +60,20 @@ import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class FooterActionsViewModelTest : SysuiTestCase() {
private val testScope = TestScope()
private lateinit var utils: FooterActionsTestUtils
private val testDispatcher = UnconfinedTestDispatcher(TestCoroutineScheduler())
@Before
fun setUp() {
utils = FooterActionsTestUtils(context, TestableLooper.get(this))
utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
}
private fun runTest(block: suspend TestScope.() -> Unit) {
testScope.runTest(testBody = block)
}
@Test
fun settingsButton() = runBlockingTest {
fun settingsButton() = runTest {
val underTest = utils.footerActionsViewModel(showPowerButton = false)
val settings = underTest.settings
@@ -87,7 +89,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
fun powerButton() = runBlockingTest {
fun powerButton() = runTest {
// Without power button.
val underTestWithoutPower = utils.footerActionsViewModel(showPowerButton = false)
assertThat(underTestWithoutPower.power).isNull()
@@ -114,7 +116,7 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
@Test
fun userSwitcher() = runBlockingTest {
fun userSwitcher() = runTest {
val picture: Drawable = mock()
val userInfoController = FakeUserInfoController(FakeInfo(picture = picture))
val settings = FakeSettings()
@@ -135,7 +137,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
showPowerButton = false,
footerActionsInteractor =
utils.footerActionsInteractor(
bgDispatcher = testDispatcher,
userSwitcherRepository =
utils.userSwitcherRepository(
userTracker = userTracker,
@@ -143,22 +144,12 @@ class FooterActionsViewModelTest : SysuiTestCase() {
userManager = userManager,
userInfoController = userInfoController,
userSwitcherController = userSwitcherControllerWrapper.controller,
bgDispatcher = testDispatcher,
),
)
)
// Collect the user switcher into currentUserSwitcher.
var currentUserSwitcher: FooterActionsButtonViewModel? = null
val job = launch { underTest.userSwitcher.collect { currentUserSwitcher = it } }
fun currentUserSwitcher(): FooterActionsButtonViewModel? {
// Make sure we finish collecting the current user switcher. This is necessary because
// combined flows launch multiple coroutines in the current scope so we need to make
// sure we process all coroutines triggered by our flow collection before we make
// assertions on the current buttons.
advanceUntilIdle()
return currentUserSwitcher
}
val currentUserSwitcher = collectLastValue(underTest.userSwitcher)
// The user switcher is disabled.
assertThat(currentUserSwitcher()).isNull()
@@ -203,12 +194,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
// in guest mode.
userInfoController.updateInfo { this.picture = mock<UserIconDrawable>() }
assertThat(iconTint()).isNull()
job.cancel()
}
@Test
fun security() = runBlockingTest {
fun security() = runTest {
val securityController = FakeSecurityController()
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
@@ -224,22 +213,15 @@ class FooterActionsViewModelTest : SysuiTestCase() {
footerActionsInteractor =
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
bgDispatcher = testDispatcher,
securityRepository =
utils.securityRepository(
securityController = securityController,
bgDispatcher = testDispatcher,
),
),
)
// Collect the security model into currentSecurity.
var currentSecurity: FooterActionsSecurityButtonViewModel? = null
val job = launch { underTest.security.collect { currentSecurity = it } }
fun currentSecurity(): FooterActionsSecurityButtonViewModel? {
advanceUntilIdle()
return currentSecurity
}
val currentSecurity = collectLastValue(underTest.security)
// By default, we always return a null SecurityButtonConfig.
assertThat(currentSecurity()).isNull()
@@ -270,12 +252,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
security = currentSecurity()
assertThat(security).isNotNull()
assertThat(security!!.onClick).isNull()
job.cancel()
}
@Test
fun foregroundServices() = runBlockingTest {
fun foregroundServices() = runTest {
val securityController = FakeSecurityController()
val fgsManagerController =
FakeFgsManagerController(
@@ -300,21 +280,14 @@ class FooterActionsViewModelTest : SysuiTestCase() {
securityRepository =
utils.securityRepository(
securityController,
bgDispatcher = testDispatcher,
),
foregroundServicesRepository =
utils.foregroundServicesRepository(fgsManagerController),
bgDispatcher = testDispatcher,
),
)
// Collect the security model into currentSecurity.
var currentForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
val job = launch { underTest.foregroundServices.collect { currentForegroundServices = it } }
fun currentForegroundServices(): FooterActionsForegroundServicesButtonViewModel? {
advanceUntilIdle()
return currentForegroundServices
}
val currentForegroundServices = collectLastValue(underTest.foregroundServices)
// We don't show the foreground services button if the number of running packages is not
// > 1.
@@ -356,12 +329,10 @@ class FooterActionsViewModelTest : SysuiTestCase() {
}
securityController.updateState {}
assertThat(currentForegroundServices()?.displayText).isFalse()
job.cancel()
}
@Test
fun observeDeviceMonitoringDialogRequests() = runBlockingTest {
fun observeDeviceMonitoringDialogRequests() = runTest {
val qsSecurityFooterUtils = mock<QSSecurityFooterUtils>()
val broadcastDispatcher = mock<BroadcastDispatcher>()
@@ -390,7 +361,6 @@ class FooterActionsViewModelTest : SysuiTestCase() {
utils.footerActionsInteractor(
qsSecurityFooterUtils = qsSecurityFooterUtils,
broadcastDispatcher = broadcastDispatcher,
bgDispatcher = testDispatcher,
),
)
@@ -415,7 +385,4 @@ class FooterActionsViewModelTest : SysuiTestCase() {
underTest.onVisibilityChangeRequested(visible = true)
assertThat(underTest.isVisible.value).isTrue()
}
private fun runBlockingTest(block: suspend TestScope.() -> Unit) =
runTest(testDispatcher) { block() }
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.coroutines
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
fun <T> TestScope.collectLastValue(
flow: Flow<T>,
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
): () -> T? {
var lastValue: T? = null
backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
return {
runCurrent()
lastValue
}
}

View File

@@ -56,7 +56,8 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
/**
* Util class to create real implementations of the FooterActions repositories, viewModel and
@@ -65,6 +66,7 @@ import kotlinx.coroutines.test.TestCoroutineDispatcher
class FooterActionsTestUtils(
private val context: Context,
private val testableLooper: TestableLooper,
private val scheduler: TestCoroutineScheduler,
) {
/** Enable or disable the user switcher in the settings. */
fun setUserSwitcherEnabled(settings: GlobalSettings, enabled: Boolean, userId: Int) {
@@ -105,7 +107,7 @@ class FooterActionsTestUtils(
foregroundServicesRepository: ForegroundServicesRepository = foregroundServicesRepository(),
userSwitcherRepository: UserSwitcherRepository = userSwitcherRepository(),
broadcastDispatcher: BroadcastDispatcher = mock(),
bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): FooterActionsInteractor {
return FooterActionsInteractorImpl(
activityStarter,
@@ -126,7 +128,7 @@ class FooterActionsTestUtils(
/** Create a [SecurityRepository] to be used in tests. */
fun securityRepository(
securityController: SecurityController = FakeSecurityController(),
bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
): SecurityRepository {
return SecurityRepositoryImpl(
securityController,
@@ -145,7 +147,7 @@ class FooterActionsTestUtils(
fun userSwitcherRepository(
@Application context: Context = this.context.applicationContext,
bgHandler: Handler = Handler(testableLooper.looper),
bgDispatcher: CoroutineDispatcher = TestCoroutineDispatcher(),
bgDispatcher: CoroutineDispatcher = StandardTestDispatcher(scheduler),
userManager: UserManager = mock(),
userTracker: UserTracker = FakeUserTracker(),
userSwitcherController: UserSwitcherController = mock(),