Merge changes I6e7c349d,If6c74b3b,Ib276e9dc,Ida0f7cc0 into main

* changes:
  Override DisplayController on main thread.
  Add annotations for manipulating secure settings.
  Don't use UiThreadTest for Taskbar Unit tests.
  Suspend Launcher taskbar while removed for tests.
This commit is contained in:
Brian Isganitis
2024-07-04 02:26:46 +00:00
committed by Android (Google) Code Review
7 changed files with 279 additions and 154 deletions
@@ -43,6 +43,8 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.wm.shell.Flags.enableTinyTaskbar;
import static java.lang.invoke.MethodHandles.Lookup.PROTECTED;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
@@ -1515,7 +1517,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
return mIsNavBarKidsMode && isThreeButtonNav();
}
protected boolean isNavBarForceVisible() {
@VisibleForTesting(otherwise = PROTECTED)
public boolean isNavBarForceVisible() {
return mIsNavBarForceVisible;
}
@@ -115,6 +115,7 @@ public class TaskbarManager {
private WindowManager mWindowManager;
private FrameLayout mTaskbarRootLayout;
private boolean mAddedWindow;
private boolean mIsSuspended;
private final TaskbarNavButtonController mNavButtonController;
private final ComponentCallbacks mComponentCallbacks;
@@ -443,6 +444,8 @@ public class TaskbarManager {
*/
@VisibleForTesting
public synchronized void recreateTaskbar() {
if (mIsSuspended) return;
Trace.beginSection("recreateTaskbar");
try {
DeviceProfile dp = mUserUnlocked ?
@@ -648,8 +651,22 @@ public class TaskbarManager {
}
}
/**
* Removes Taskbar from the window manager and prevents recreation if {@code true}.
* <p>
* Suspending is for testing purposes only; avoid calling this method in production.
*/
@VisibleForTesting
public void addTaskbarRootViewToWindow() {
public void setSuspended(boolean isSuspended) {
mIsSuspended = isSuspended;
if (mIsSuspended) {
removeTaskbarRootViewFromWindow();
} else {
addTaskbarRootViewToWindow();
}
}
private void addTaskbarRootViewToWindow() {
if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) {
mWindowManager.addView(mTaskbarRootLayout,
mTaskbarActivityContext.getWindowLayoutParams());
@@ -657,8 +674,7 @@ public class TaskbarManager {
}
}
@VisibleForTesting
public void removeTaskbarRootViewFromWindow() {
private void removeTaskbarRootViewFromWindow() {
if (enableTaskbarNoRecreate() && mAddedWindow) {
mWindowManager.removeViewImmediate(mTaskbarRootLayout);
mAddedWindow = false;
@@ -20,7 +20,6 @@ import android.animation.AnimatorTestRule
import android.content.ComponentName
import android.content.Intent
import android.os.Process
import androidx.test.annotation.UiThreadTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.BubbleTextView
import com.android.launcher3.appprediction.PredictionRowView
@@ -34,6 +33,7 @@ import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -55,17 +55,17 @@ class TaskbarAllAppsControllerTest {
@InjectController lateinit var overlayController: TaskbarOverlayController
@Test
@UiThreadTest
fun testToggle_once_showsAllApps() {
allAppsController.toggle()
getInstrumentation().runOnMainSync { allAppsController.toggle() }
assertThat(allAppsController.isOpen).isTrue()
}
@Test
@UiThreadTest
fun testToggle_twice_closesAllApps() {
allAppsController.toggle()
allAppsController.toggle()
getInstrumentation().runOnMainSync {
allAppsController.toggle()
allAppsController.toggle()
}
assertThat(allAppsController.isOpen).isFalse()
}
@@ -77,54 +77,62 @@ class TaskbarAllAppsControllerTest {
}
@Test
@UiThreadTest
fun testSetApps_beforeOpened_cachesInfo() {
allAppsController.setApps(TEST_APPS, 0, emptyMap())
allAppsController.toggle()
val overlayContext =
TestUtil.getOnUiThread {
allAppsController.setApps(TEST_APPS, 0, emptyMap())
allAppsController.toggle()
overlayController.requestWindow()
}
val overlayContext = overlayController.requestWindow()
assertThat(overlayContext.appsView.appsStore.apps).isEqualTo(TEST_APPS)
}
@Test
@UiThreadTest
fun testSetApps_afterOpened_updatesStore() {
allAppsController.toggle()
allAppsController.setApps(TEST_APPS, 0, emptyMap())
val overlayContext =
TestUtil.getOnUiThread {
allAppsController.toggle()
allAppsController.setApps(TEST_APPS, 0, emptyMap())
overlayController.requestWindow()
}
val overlayContext = overlayController.requestWindow()
assertThat(overlayContext.appsView.appsStore.apps).isEqualTo(TEST_APPS)
}
@Test
@UiThreadTest
fun testSetPredictedApps_beforeOpened_cachesInfo() {
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
allAppsController.toggle()
val predictedApps =
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
.predictedApps
TestUtil.getOnUiThread {
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
allAppsController.toggle()
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
.predictedApps
}
assertThat(predictedApps).isEqualTo(TEST_PREDICTED_APPS)
}
@Test
@UiThreadTest
fun testSetPredictedApps_afterOpened_cachesInfo() {
allAppsController.toggle()
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
val predictedApps =
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
.predictedApps
TestUtil.getOnUiThread {
allAppsController.toggle()
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
.predictedApps
}
assertThat(predictedApps).isEqualTo(TEST_PREDICTED_APPS)
}
@@ -140,36 +148,38 @@ class TaskbarAllAppsControllerTest {
}
// Ensure the recycler view fully inflates before trying to grab an icon.
getInstrumentation().runOnMainSync {
val btv =
val btv =
TestUtil.getOnUiThread {
overlayController
.requestWindow()
.appsView
.activeRecyclerView
.findViewHolderForAdapterPosition(0)
?.itemView as? BubbleTextView
assertThat(btv?.hasDot()).isTrue()
}
}
assertThat(btv?.hasDot()).isTrue()
}
@Test
@UiThreadTest
fun testUpdateNotificationDots_predictedApp_hasDot() {
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
allAppsController.toggle()
getInstrumentation().runOnMainSync {
allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
allAppsController.toggle()
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
PackageUserKey.fromItemInfo(TEST_PREDICTED_APPS[0]),
NotificationKeyData("key"),
)
}
taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
PackageUserKey.fromItemInfo(TEST_PREDICTED_APPS[0]),
NotificationKeyData("key"),
)
val predictionRowView =
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
val btv = predictionRowView.getChildAt(0) as BubbleTextView
val btv =
TestUtil.getOnUiThread {
overlayController
.requestWindow()
.appsView
.floatingHeaderView
.findFixedRowByType(PredictionRowView::class.java)
.getChildAt(0) as BubbleTextView
}
assertThat(btv.hasDot()).isTrue()
}
@@ -18,7 +18,6 @@ package com.android.launcher3.taskbar.overlay
import android.app.ActivityManager.RunningTaskInfo
import android.view.MotionEvent
import androidx.test.annotation.UiThreadTest
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.AbstractFloatingView
import com.android.launcher3.AbstractFloatingView.TYPE_OPTIONS_POPUP
@@ -31,7 +30,7 @@ import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.launcher3.views.BaseDragLayer
import com.android.launcher3.util.TestUtil.getOnUiThread
import com.android.systemui.shared.system.TaskStackChangeListeners
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -54,74 +53,69 @@ class TaskbarOverlayControllerTest {
get() = taskbarUnitTestRule.activityContext
@Test
@UiThreadTest
fun testRequestWindow_twice_reusesWindow() {
val context1 = overlayController.requestWindow()
val context2 = overlayController.requestWindow()
val (context1, context2) =
getOnUiThread {
Pair(overlayController.requestWindow(), overlayController.requestWindow())
}
assertThat(context1).isSameInstanceAs(context2)
}
@Test
@UiThreadTest
fun testRequestWindow_afterHidingExistingWindow_createsNewWindow() {
val context1 = overlayController.requestWindow()
overlayController.hideWindow()
val context1 = getOnUiThread { overlayController.requestWindow() }
getInstrumentation().runOnMainSync { overlayController.hideWindow() }
val context2 = overlayController.requestWindow()
val context2 = getOnUiThread { overlayController.requestWindow() }
assertThat(context1).isNotSameInstanceAs(context2)
}
@Test
@UiThreadTest
fun testRequestWindow_afterHidingOverlay_createsNewWindow() {
val context1 = overlayController.requestWindow()
TestOverlayView.show(context1)
overlayController.hideWindow()
val context1 = getOnUiThread { overlayController.requestWindow() }
getInstrumentation().runOnMainSync {
TestOverlayView.show(context1)
overlayController.hideWindow()
}
val context2 = overlayController.requestWindow()
val context2 = getOnUiThread { overlayController.requestWindow() }
assertThat(context1).isNotSameInstanceAs(context2)
}
@Test
@UiThreadTest
fun testRequestWindow_addsProxyView() {
TestOverlayView.show(overlayController.requestWindow())
getInstrumentation().runOnMainSync {
TestOverlayView.show(overlayController.requestWindow())
}
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
}
@Test
@UiThreadTest
fun testRequestWindow_closeProxyView_closesOverlay() {
val overlay = TestOverlayView.show(overlayController.requestWindow())
AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
getInstrumentation().runOnMainSync {
AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
}
assertThat(overlay.isOpen).isFalse()
}
@Test
fun testRequestWindow_attachesDragLayer() {
lateinit var dragLayer: BaseDragLayer<*>
getInstrumentation().runOnMainSync {
dragLayer = overlayController.requestWindow().dragLayer
}
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
// Allow drag layer to attach before checking.
getInstrumentation().runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
}
@Test
@UiThreadTest
fun testHideWindow_closesOverlay() {
val overlay = TestOverlayView.show(overlayController.requestWindow())
overlayController.hideWindow()
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
getInstrumentation().runOnMainSync { overlayController.hideWindow() }
assertThat(overlay.isOpen).isFalse()
}
@Test
fun testHideWindow_detachesDragLayer() {
lateinit var dragLayer: BaseDragLayer<*>
getInstrumentation().runOnMainSync {
dragLayer = overlayController.requestWindow().dragLayer
}
val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
// Wait for drag layer to be attached to window before hiding.
getInstrumentation().runOnMainSync {
@@ -131,26 +125,30 @@ class TaskbarOverlayControllerTest {
}
@Test
@UiThreadTest
fun testTwoOverlays_closeOne_windowStaysOpen() {
val context = overlayController.requestWindow()
val overlay1 = TestOverlayView.show(context)
val overlay2 = TestOverlayView.show(context)
val (overlay1, overlay2) =
getOnUiThread {
val context = overlayController.requestWindow()
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
}
overlay1.close(false)
getInstrumentation().runOnMainSync { overlay1.close(false) }
assertThat(overlay2.isOpen).isTrue()
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
}
@Test
@UiThreadTest
fun testTwoOverlays_closeAll_closesWindow() {
val context = overlayController.requestWindow()
val overlay1 = TestOverlayView.show(context)
val overlay2 = TestOverlayView.show(context)
val (overlay1, overlay2) =
getOnUiThread {
val context = overlayController.requestWindow()
Pair(TestOverlayView.show(context), TestOverlayView.show(context))
}
overlay1.close(false)
overlay2.close(false)
getInstrumentation().runOnMainSync {
overlay1.close(false)
overlay2.close(false)
}
assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isFalse()
}
@@ -165,11 +163,7 @@ class TaskbarOverlayControllerTest {
@Test
fun testTaskMovedToFront_closesOverlay() {
lateinit var overlay: TestOverlayView
getInstrumentation().runOnMainSync {
overlay = TestOverlayView.show(overlayController.requestWindow())
}
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
TaskStackChangeListeners.getInstance().listenerImpl.onTaskMovedToFront(RunningTaskInfo())
// Make sure TaskStackChangeListeners' Handler posts the callback before checking state.
getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isFalse() }
@@ -177,9 +171,8 @@ class TaskbarOverlayControllerTest {
@Test
fun testTaskStackChanged_allAppsClosed_overlayStaysOpen() {
lateinit var overlay: TestOverlayView
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
getInstrumentation().runOnMainSync {
overlay = TestOverlayView.show(overlayController.requestWindow())
taskbarContext.controllers.sharedState?.allAppsVisible = false
}
@@ -189,9 +182,8 @@ class TaskbarOverlayControllerTest {
@Test
fun testTaskStackChanged_allAppsOpen_closesOverlay() {
lateinit var overlay: TestOverlayView
val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
getInstrumentation().runOnMainSync {
overlay = TestOverlayView.show(overlayController.requestWindow())
taskbarContext.controllers.sharedState?.allAppsVisible = true
}
@@ -200,33 +192,39 @@ class TaskbarOverlayControllerTest {
}
@Test
@UiThreadTest
fun testUpdateLauncherDeviceProfile_overlayNotRebindSafe_closesOverlay() {
val overlayContext = overlayController.requestWindow()
val overlay = TestOverlayView.show(overlayContext).apply { type = TYPE_OPTIONS_POPUP }
val context = getOnUiThread { overlayController.requestWindow() }
val overlay = getOnUiThread {
TestOverlayView.show(context).apply { type = TYPE_OPTIONS_POPUP }
}
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(overlayContext)
.setGestureMode(false)
.build()
)
getInstrumentation().runOnMainSync {
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(context)
.setGestureMode(false)
.build()
)
}
assertThat(overlay.isOpen).isFalse()
}
@Test
@UiThreadTest
fun testUpdateLauncherDeviceProfile_overlayRebindSafe_overlayStaysOpen() {
val overlayContext = overlayController.requestWindow()
val overlay = TestOverlayView.show(overlayContext).apply { type = TYPE_TASKBAR_ALL_APPS }
val context = getOnUiThread { overlayController.requestWindow() }
val overlay = getOnUiThread {
TestOverlayView.show(context).apply { type = TYPE_TASKBAR_ALL_APPS }
}
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(overlayContext)
.setGestureMode(false)
.build()
)
getInstrumentation().runOnMainSync {
overlayController.updateLauncherDeviceProfile(
overlayController.launcherDeviceProfile
.toBuilder(context)
.setGestureMode(false)
.build()
)
}
assertThat(overlay.isOpen).isTrue()
}
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar.rules
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
import com.android.launcher3.util.DisplayController
@@ -59,23 +60,25 @@ class TaskbarModeRule(private val context: TaskbarWindowSandboxContext) : TestRu
override fun evaluate() {
val mode = taskbarMode.mode
context.applicationContext.putObject(
DisplayController.INSTANCE,
object : DisplayController(context) {
override fun getInfo(): Info {
return spy(super.getInfo()) {
on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
on { navigationMode } doReturn
when (mode) {
Mode.TRANSIENT,
Mode.PINNED -> NavigationMode.NO_BUTTON
Mode.THREE_BUTTONS -> NavigationMode.THREE_BUTTONS
}
getInstrumentation().runOnMainSync {
context.applicationContext.putObject(
DisplayController.INSTANCE,
object : DisplayController(context) {
override fun getInfo(): Info {
return spy(super.getInfo()) {
on { isTransientTaskbar } doReturn (mode == Mode.TRANSIENT)
on { isPinnedTaskbar } doReturn (mode == Mode.PINNED)
on { navigationMode } doReturn
when (mode) {
Mode.TRANSIENT,
Mode.PINNED -> NavigationMode.NO_BUTTON
Mode.THREE_BUTTONS -> NavigationMode.THREE_BUTTONS
}
}
}
}
},
)
},
)
}
base.evaluate()
}
@@ -20,18 +20,25 @@ import android.app.Instrumentation
import android.app.PendingIntent
import android.content.IIntentSender
import android.content.Intent
import android.provider.Settings
import android.provider.Settings.Secure.NAV_BAR_KIDS_MODE
import android.provider.Settings.Secure.USER_SETUP_COMPLETE
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ServiceTestRule
import com.android.launcher3.LauncherAppState
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
import com.android.launcher3.util.ModelTestExtensions.loadModelSync
import com.android.launcher3.util.TestUtil
import com.android.quickstep.AllAppsActionManager
import com.android.quickstep.TouchInteractionService
import com.android.quickstep.TouchInteractionService.TISBinder
import org.junit.Assume.assumeTrue
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@@ -48,12 +55,11 @@ import org.junit.runners.model.Statement
* that code that is executed on the main thread in production should also happen on that thread
* when tested.
*
* `@UiThreadTest` is a simple way to run an entire test body on the main thread. But if a test
* executes code that appends message(s) to the main thread's `MessageQueue`, the annotation will
* prevent those messages from being processed until after the test body finishes.
* `@UiThreadTest` is incompatible with this rule. The annotation causes this rule to run on the
* main thread, but it needs to be run on the test thread for it to work properly. Instead, only run
* code that requires the main thread using something like [Instrumentation.runOnMainSync] or
* [TestUtil.getOnUiThread].
*
* To test pending messages, instead use something like [Instrumentation.runOnMainSync] to perform
* only sections of the test body on the main thread synchronously:
* ```
* @Test
* fun example() {
@@ -71,6 +77,10 @@ class TaskbarUnitTestRule(
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private val serviceTestRule = ServiceTestRule()
private val userSetupCompleteRule = TaskbarSecureSettingRule(USER_SETUP_COMPLETE)
private val kidsModeRule = TaskbarSecureSettingRule(NAV_BAR_KIDS_MODE)
private val settingRules = RuleChain.outerRule(userSetupCompleteRule).around(kidsModeRule)
private lateinit var taskbarManager: TaskbarManager
val activityContext: TaskbarActivityContext
@@ -80,15 +90,34 @@ class TaskbarUnitTestRule(
}
override fun apply(base: Statement, description: Description): Statement {
return settingRules.apply(createStatement(base, description), description)
}
private fun createStatement(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
// Only run test when Taskbar is enabled.
instrumentation.runOnMainSync {
assumeTrue(
LauncherAppState.getIDP(context).getDeviceProfile(context).isTaskbarPresent
)
}
// Process secure setting annotations.
instrumentation.runOnMainSync {
userSetupCompleteRule.putInt(
if (description.getAnnotation(UserSetupMode::class.java) != null) {
0
} else {
1
}
)
kidsModeRule.putInt(
if (description.getAnnotation(NavBarKidsMode::class.java) != null) 1 else 0
)
}
// Check for existing Taskbar instance from Launcher process.
val launcherTaskbarManager: TaskbarManager? =
if (!isRunningInRobolectric) {
@@ -105,8 +134,8 @@ class TaskbarUnitTestRule(
null
}
instrumentation.runOnMainSync {
taskbarManager =
taskbarManager =
TestUtil.getOnUiThread {
TaskbarManager(
context,
AllAppsActionManager(context, UI_HELPER_EXECUTOR) {
@@ -114,12 +143,14 @@ class TaskbarUnitTestRule(
},
object : TaskbarNavButtonCallbacks {},
)
}
}
try {
LauncherAppState.getInstance(context).model.loadModelSync()
// Replace Launcher Taskbar window with test instance.
instrumentation.runOnMainSync {
launcherTaskbarManager?.removeTaskbarRootViewFromWindow()
launcherTaskbarManager?.setSuspended(true)
taskbarManager.onUserUnlocked() // Required to complete initialization.
}
@@ -129,7 +160,7 @@ class TaskbarUnitTestRule(
// Revert Taskbar window.
instrumentation.runOnMainSync {
taskbarManager.destroy()
launcherTaskbarManager?.addTaskbarRootViewToWindow()
launcherTaskbarManager?.setSuspended(false)
}
}
}
@@ -167,4 +198,35 @@ class TaskbarUnitTestRule(
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class InjectController
/** Overrides [USER_SETUP_COMPLETE] to be `false` for tests. */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class UserSetupMode
/** Overrides [NAV_BAR_KIDS_MODE] to be `true` for tests. */
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class NavBarKidsMode
/** Rule for Taskbar integer-based secure settings. */
private inner class TaskbarSecureSettingRule(private val settingName: String) : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
val originalValue =
Settings.Secure.getInt(context.contentResolver, settingName, /* def= */ 0)
try {
base.evaluate()
} finally {
instrumentation.runOnMainSync { putInt(originalValue) }
}
}
}
}
/** Puts [value] into secure settings under [settingName]. */
fun putInt(value: Int) = Settings.Secure.putInt(context.contentResolver, settingName, value)
}
}
@@ -22,6 +22,8 @@ import com.android.launcher3.taskbar.TaskbarKeyguardController
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarStashController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.NavBarKidsMode
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.UserSetupMode
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.google.common.truth.Truth.assertThat
@@ -125,9 +127,40 @@ class TaskbarUnitTestRuleTest {
}
}
/** Executes [runTest] after the [testRule] setup phase completes. */
@Test
fun testUserSetupMode_default_isComplete() {
onSetup { assertThat(activityContext.isUserSetupComplete).isTrue() }
}
@Test
fun testUserSetupMode_withAnnotation_isIncomplete() {
@UserSetupMode class Mode
onSetup(description = Description.createSuiteDescription(Mode::class.java)) {
assertThat(activityContext.isUserSetupComplete).isFalse()
}
}
@Test
fun testNavBarKidsMode_default_navBarNotForcedVisible() {
onSetup { assertThat(activityContext.isNavBarForceVisible).isFalse() }
}
@Test
fun testNavBarKidsMode_withAnnotation_navBarForcedVisible() {
@NavBarKidsMode class Mode
onSetup(description = Description.createSuiteDescription(Mode::class.java)) {
assertThat(activityContext.isNavBarForceVisible).isTrue()
}
}
/**
* Executes [runTest] after the [testRule] setup phase completes.
*
* A [description] can also be provided to mimic annotating a test or test class.
*/
private fun onSetup(
testRule: TaskbarUnitTestRule = TaskbarUnitTestRule(this, context),
description: Description = DESCRIPTION,
runTest: TaskbarUnitTestRule.() -> Unit,
) {
testRule
@@ -135,7 +168,7 @@ class TaskbarUnitTestRuleTest {
object : Statement() {
override fun evaluate() = runTest(testRule)
},
DESCRIPTION,
description,
)
.evaluate()
}