Refactor test dagger graph to stub PerDisplayRepository instances.

These are not being used yet in Taskbar, but they are hardcoding
DEFAULT_DISPLAY for the primary ID. In tests, we want the primary to be
considered the ID of the virtual display tests are running on.

This change also moves dagger classes to its own file for organization.

Flag: TEST_ONLY
Bug: 415326979
Test: Taskbar multivalent tests
Change-Id: Ibd0cdf46bc53dbd7a3dd5f34d7171d9a6cdebb38
This commit is contained in:
Brian Isganitis
2025-05-12 15:44:50 -04:00
parent bcf2a81aa3
commit ae80cbfed3
4 changed files with 175 additions and 116 deletions

View File

@@ -31,7 +31,7 @@ import com.android.launcher3.Flags.FLAG_ENABLE_ALT_TAB_KQS_ON_CONNECTED_DISPLAYS
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.rules.DisplayControllerModule
import com.android.launcher3.taskbar.rules.AllTaskbarSandboxModules
import com.android.launcher3.taskbar.rules.MockedRecentsModelHelper
import com.android.launcher3.taskbar.rules.MockedRecentsModelTestRule
import com.android.launcher3.taskbar.rules.SandboxParams
@@ -39,8 +39,6 @@ import com.android.launcher3.taskbar.rules.TaskbarSandboxComponent
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.AllModulesForTest
import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.launcher3.util.TestUtil.getOnUiThread
@@ -60,16 +58,15 @@ import com.android.wm.shell.shared.split.SplitScreenConstants
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
import org.junit.Ignore;
import org.junit.Ignore
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.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -81,9 +78,8 @@ class KeyboardQuickSwitchControllerTest {
private var systemUiProxySpy: SystemUiProxy? = null
private var desktopTaskListener: IDesktopTaskListener? = null
private val mockRecentsModelHelper: MockedRecentsModelHelper = MockedRecentsModelHelper()
private val desktopVisibilityController: DesktopVisibilityController = mock()
private val taskIdCaptor = ArgumentCaptor.forClass(Int::class.java)
private val transitionCaptor = ArgumentCaptor.forClass(RemoteTransition::class.java)
private val taskIdCaptor = argumentCaptor<Int>()
private val transitionCaptor = argumentCaptor<RemoteTransition>()
@get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
@get:Rule(order = 1)
@@ -100,8 +96,7 @@ class KeyboardQuickSwitchControllerTest {
},
builderBase =
DaggerKeyboardQuickSwitchControllerComponent.builder()
.bindRecentsModel(mockRecentsModelHelper.mockRecentsModel)
.bindDesktopVisibilityController(desktopVisibilityController),
.bindRecentsModel(mockRecentsModelHelper.mockRecentsModel),
)
)
@@ -279,15 +274,15 @@ class KeyboardQuickSwitchControllerTest {
triggerAltTabAndLaunchFocusedTask()
val deskIdCaptor = ArgumentCaptor.forClass(Int::class.java)
val deskIdCaptor = argumentCaptor<Int>()
verify(systemUiProxySpy)?.activateDesk(deskIdCaptor.capture(), transitionCaptor.capture())
assertThat(deskIdCaptor.value).isEqualTo(deskId)
assertThat(transitionCaptor.value.remoteTransition)
assertThat(deskIdCaptor.firstValue).isEqualTo(deskId)
assertThat(transitionCaptor.firstValue.remoteTransition)
.isInstanceOf(SlideInRemoteTransition::class.java)
verify(systemUiProxySpy)
?.showDesktopApp(taskIdCaptor.capture(), eq(null), eq(DesktopTaskToFrontReason.ALT_TAB))
assertThat(taskIdCaptor.value).isEqualTo(PREVIOUS_TASK_ID)
assertThat(taskIdCaptor.firstValue).isEqualTo(PREVIOUS_TASK_ID)
}
@Test
@@ -304,8 +299,8 @@ class KeyboardQuickSwitchControllerTest {
eq(DesktopModeTransitionSource.KEYBOARD_SHORTCUT),
transitionCaptor.capture(),
)
assertThat(taskIdCaptor.value).isEqualTo(PREVIOUS_TASK_ID)
assertThat(transitionCaptor.value.remoteTransition)
assertThat(taskIdCaptor.firstValue).isEqualTo(PREVIOUS_TASK_ID)
assertThat(transitionCaptor.firstValue.remoteTransition)
.isInstanceOf(SlideInRemoteTransition::class.java)
}
@@ -328,7 +323,8 @@ class KeyboardQuickSwitchControllerTest {
DesktopTask(deskId, DEFAULT_DISPLAY, taskIds.map { createTask(it) })
private fun enableDesktopMode() {
whenever(desktopVisibilityController.isInDesktopMode(anyInt())).thenReturn(true)
whenever(DesktopVisibilityController.INSTANCE[context].isInDesktopMode(any()))
.thenReturn(true)
}
/*
@@ -374,20 +370,13 @@ class KeyboardQuickSwitchControllerTest {
/** KeyboardQuickSwitchControllerComponent used to bind the RecentsModel. */
@LauncherAppSingleton
@Component(
modules = [AllModulesForTest::class, FakePrefsModule::class, DisplayControllerModule::class]
)
@Component(modules = [AllTaskbarSandboxModules::class])
interface KeyboardQuickSwitchControllerComponent : TaskbarSandboxComponent {
@Component.Builder
interface Builder : TaskbarSandboxComponent.Builder {
@BindsInstance fun bindRecentsModel(model: RecentsModel): Builder
@BindsInstance
fun bindDesktopVisibilityController(
desktopVisibilityController: DesktopVisibilityController
): Builder
override fun build(): KeyboardQuickSwitchControllerComponent
}
}

View File

@@ -41,8 +41,7 @@ import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.TaskbarViewTestUtil.createHotseatItems
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController
import com.android.launcher3.taskbar.rules.DesktopVisibilityControllerModule
import com.android.launcher3.taskbar.rules.DisplayControllerModule
import com.android.launcher3.taskbar.rules.AllTaskbarSandboxModules
import com.android.launcher3.taskbar.rules.MockedRecentsModelHelper
import com.android.launcher3.taskbar.rules.MockedRecentsModelTestRule
import com.android.launcher3.taskbar.rules.SandboxParams
@@ -54,8 +53,6 @@ import com.android.launcher3.taskbar.rules.TaskbarSandboxComponent
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.AllModulesForTest
import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.launcher3.util.Preconditions.assertNotNull
@@ -725,15 +722,7 @@ class TaskbarOverflowTest {
/** TaskbarOverflowComponent used to bind the RecentsModel. */
@LauncherAppSingleton
@Component(
modules =
[
AllModulesForTest::class,
FakePrefsModule::class,
DisplayControllerModule::class,
DesktopVisibilityControllerModule::class,
]
)
@Component(modules = [AllTaskbarSandboxModules::class])
interface TaskbarOverflowComponent : TaskbarSandboxComponent {
@Component.Builder

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2025 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.launcher3.taskbar.rules
import android.content.Context
import com.android.app.displaylib.PerDisplayRepository
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.concurrent.ExecutorsModule
import com.android.launcher3.dagger.ApiWrapperModule
import com.android.launcher3.dagger.AppModule
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.BasePerDisplayModule
import com.android.launcher3.dagger.DisplayContext
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.dagger.LauncherConcurrencyModule
import com.android.launcher3.dagger.StaticObjectModule
import com.android.launcher3.dagger.WidgetModule
import com.android.launcher3.dagger.WindowContext
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.dagger.LauncherExecutorsModule
import com.android.launcher3.util.window.WindowManagerProxy
import com.android.quickstep.FallbackWindowInterface
import com.android.quickstep.RecentsAnimationDeviceState
import com.android.quickstep.RotationTouchHelper
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskAnimationManager
import com.android.quickstep.fallback.window.RecentsWindowManager
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
import javax.inject.Inject
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
@LauncherAppSingleton
@Component(modules = [AllTaskbarSandboxModules::class])
interface TaskbarSandboxComponent : LauncherAppComponent {
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
@BindsInstance fun bindSettingsCache(settingsCache: SettingsCache): Builder
override fun build(): TaskbarSandboxComponent
}
}
@Module(
includes =
[
ApiWrapperModule::class,
StaticObjectModule::class,
WidgetModule::class,
AppModule::class,
BasePerDisplayModule::class,
LauncherConcurrencyModule::class,
ExecutorsModule::class,
LauncherExecutorsModule::class,
FakePrefsModule::class,
DisplayControllerModule::class,
TaskbarSandboxWmProxyModule::class,
TaskbarPerDisplayReposModule::class,
DesktopVisibilityControllerModule::class,
]
)
interface AllTaskbarSandboxModules
@Module
abstract class DisplayControllerModule {
@Binds abstract fun bindDisplayController(controller: DisplayControllerSpy): DisplayController
}
/** A wrapper over display controller which allows modifying the underlying info */
@LauncherAppSingleton
class DisplayControllerSpy
@Inject
constructor(
@ApplicationContext context: Context,
wmProxy: WindowManagerProxy,
prefs: LauncherPrefs,
lifecycle: DaggerSingletonTracker,
) : DisplayController(context, wmProxy, prefs, lifecycle) {
var infoModifier: ((Info) -> Info)? = null
override fun getInfo(): Info = infoModifier?.invoke(super.getInfo()) ?: super.getInfo()
}
@Module
object DesktopVisibilityControllerModule {
@JvmStatic
@Provides
@LauncherAppSingleton
fun provideDesktopVisibilityController(
@ApplicationContext context: Context,
systemUiProxy: SystemUiProxy,
lifecycleTracker: DaggerSingletonTracker,
): DesktopVisibilityController {
return spy(DesktopVisibilityController(context, systemUiProxy, lifecycleTracker))
}
}
@Module
object TaskbarPerDisplayReposModule {
@Provides
@LauncherAppSingleton
fun provideRecentsAnimationDeviceStateRepo():
PerDisplayRepository<RecentsAnimationDeviceState> = mock()
@Provides
@LauncherAppSingleton
fun provideTaskAnimationManagerRepo(): PerDisplayRepository<TaskAnimationManager> = mock()
@Provides
@LauncherAppSingleton
fun provideRotationTouchHandlerRepo(): PerDisplayRepository<RotationTouchHelper> = mock()
@Provides
@LauncherAppSingleton
fun provideFallbackWindowInterfaceRepo(): PerDisplayRepository<FallbackWindowInterface> = mock()
@Provides
@LauncherAppSingleton
fun provideRecentsWindowManagerRepo(): PerDisplayRepository<RecentsWindowManager> = mock()
@Provides
@LauncherAppSingleton
@DisplayContext
fun provideDisplayContext(): PerDisplayRepository<Context> = mock()
@Provides
@LauncherAppSingleton
@WindowContext
fun provideWindowContext(): PerDisplayRepository<Context> = mock()
}

View File

@@ -22,32 +22,14 @@ import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.util.AllModulesMinusWMProxy
import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.FakePrefsModule
import com.android.launcher3.util.SandboxApplication
import com.android.launcher3.util.SettingsCache
import com.android.launcher3.util.SettingsCacheSandbox
import com.android.launcher3.util.window.WindowManagerProxy
import com.android.quickstep.SystemUiProxy
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
import dagger.Module
import dagger.Provides
import javax.inject.Inject
import org.junit.rules.ExternalResource
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.mockito.kotlin.spy
/**
* [SandboxApplication] for running Taskbar tests.
@@ -117,64 +99,6 @@ private constructor(
}
}
/** A wrapper over display controller which allows modifying the underlying info */
@LauncherAppSingleton
class DisplayControllerSpy
@Inject
constructor(
@ApplicationContext context: Context,
wmProxy: WindowManagerProxy,
prefs: LauncherPrefs,
lifecycle: DaggerSingletonTracker,
) : DisplayController(context, wmProxy, prefs, lifecycle) {
var infoModifier: ((Info) -> Info)? = null
override fun getInfo(): Info = infoModifier?.invoke(super.getInfo()) ?: super.getInfo()
}
@Module
abstract class DisplayControllerModule {
@Binds abstract fun bindDisplayController(controller: DisplayControllerSpy): DisplayController
}
@Module
object DesktopVisibilityControllerModule {
@JvmStatic
@Provides
@LauncherAppSingleton
fun provideDesktopVisibilityController(
@ApplicationContext context: Context,
systemUiProxy: SystemUiProxy,
lifecycleTracker: DaggerSingletonTracker,
): DesktopVisibilityController {
return spy(DesktopVisibilityController(context, systemUiProxy, lifecycleTracker))
}
}
@LauncherAppSingleton
@Component(
modules =
[
AllModulesMinusWMProxy::class,
FakePrefsModule::class,
DisplayControllerModule::class,
TaskbarSandboxModule::class,
DesktopVisibilityControllerModule::class,
]
)
interface TaskbarSandboxComponent : LauncherAppComponent {
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
@BindsInstance fun bindSettingsCache(settingsCache: SettingsCache): Builder
override fun build(): TaskbarSandboxComponent
}
}
/** Include additional bindings when building a [TaskbarSandboxComponent]. */
data class SandboxParams(
val systemUiProxyProvider: (Context) -> SystemUiProxy = { SystemUiProxy(it) },