Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cbc07b42 authored by Brian Isganitis's avatar Brian Isganitis
Browse files

Introduce TestRule for setting Taskbar mode.

This rule is separate from TaskbarUnitTestRule, because mode isn't
relevant to all Taskbar controllers.

To keep MainThreadInitializedObject behavior consistent across
Robolectric and Instrumented environments, context needs to be a SandboxContext.

Test: TaskbarModeRuleTest
Bug: 230027385
Flag: TEST_ONLY
Change-Id: Iae2e3627b9002e13ee6da135113e7b8bcc4e7d47
parent d0261a8e
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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

import com.android.launcher3.taskbar.TaskbarModeRule.Mode
import com.android.launcher3.taskbar.TaskbarModeRule.TaskbarMode
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.MainThreadInitializedObject
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
import com.android.launcher3.util.NavigationMode
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.spy

/**
 * Allows tests to specify which Taskbar [Mode] to run under.
 *
 * [context] should match the test's target context, so that [MainThreadInitializedObject] instances
 * are properly sandboxed.
 *
 * Annotate tests with [TaskbarMode] to set a mode. If the annotation is omitted for any tests, this
 * rule is a no-op.
 *
 * Make sure this rule precedes any rules that depend on [DisplayController], or else the instance
 * might be inconsistent across the test lifecycle.
 */
class TaskbarModeRule(private val context: SandboxContext) : TestRule {
    /** The selected Taskbar mode. */
    enum class Mode {
        TRANSIENT,
        PINNED,
        THREE_BUTTONS,
    }

    /** Overrides Taskbar [mode] for a test. */
    @Retention(AnnotationRetention.RUNTIME)
    @Target(AnnotationTarget.FUNCTION)
    annotation class TaskbarMode(val mode: Mode)

    override fun apply(base: Statement, description: Description): Statement {
        val taskbarMode = description.getAnnotation(TaskbarMode::class.java) ?: return base

        return object : Statement() {
            override fun evaluate() {
                val mode = taskbarMode.mode

                context.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()
            }
        }
    }
}
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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

import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.taskbar.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.TaskbarModeRule.Mode.THREE_BUTTONS
import com.android.launcher3.taskbar.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.TaskbarModeRule.TaskbarMode
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.LauncherMultivalentJUnit
import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
import com.android.launcher3.util.NavigationMode
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(LauncherMultivalentJUnit::class)
class TaskbarModeRuleTest {

    private val context = SandboxContext(getInstrumentation().targetContext)

    @get:Rule val taskbarModeRule = TaskbarModeRule(context)

    @Test
    @TaskbarMode(TRANSIENT)
    fun testTaskbarMode_transient_overridesDisplayController() {
        assertThat(DisplayController.isTransientTaskbar(context)).isTrue()
        assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
        assertThat(DisplayController.getNavigationMode(context)).isEqualTo(NavigationMode.NO_BUTTON)
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testTaskbarMode_transient_overridesDeviceProfile() {
        val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
        assertThat(dp.isTransientTaskbar).isTrue()
        assertThat(dp.isGestureMode).isTrue()
    }

    @Test
    @TaskbarMode(PINNED)
    fun testTaskbarMode_pinned_overridesDisplayController() {
        assertThat(DisplayController.isTransientTaskbar(context)).isFalse()
        assertThat(DisplayController.isPinnedTaskbar(context)).isTrue()
        assertThat(DisplayController.getNavigationMode(context)).isEqualTo(NavigationMode.NO_BUTTON)
    }

    @Test
    @TaskbarMode(PINNED)
    fun testTaskbarMode_pinned_overridesDeviceProfile() {
        val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
        assertThat(dp.isTransientTaskbar).isFalse()
        assertThat(dp.isGestureMode).isTrue()
    }

    @Test
    @TaskbarMode(THREE_BUTTONS)
    fun testTaskbarMode_threeButtons_overridesDisplayController() {
        assertThat(DisplayController.isTransientTaskbar(context)).isFalse()
        assertThat(DisplayController.isPinnedTaskbar(context)).isFalse()
        assertThat(DisplayController.getNavigationMode(context))
            .isEqualTo(NavigationMode.THREE_BUTTONS)
    }

    @Test
    @TaskbarMode(THREE_BUTTONS)
    fun testTaskbarMode_threeButtons_overridesDeviceProfile() {
        val dp = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context)
        assertThat(dp.isTransientTaskbar).isFalse()
        assertThat(dp.isGestureMode).isFalse()
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.launcher3.taskbar

import android.app.Instrumentation
import android.app.PendingIntent
import android.content.Context
import android.content.IIntentSender
import android.content.Intent
import androidx.test.platform.app.InstrumentationRegistry
@@ -37,6 +38,8 @@ import org.junit.runners.model.Statement
/**
 * Manages the Taskbar lifecycle for unit tests.
 *
 * Tests need to provide their target [context] through the constructor.
 *
 * See [InjectController] for grabbing controller(s) under test with minimal boilerplate.
 *
 * The rule interacts with [TaskbarManager] on the main thread. A good rule of thumb for tests is
@@ -58,7 +61,7 @@ import org.junit.runners.model.Statement
 * }
 * ```
 */
class TaskbarUnitTestRule : MethodRule {
class TaskbarUnitTestRule(private val context: Context) : MethodRule {
    private val instrumentation = InstrumentationRegistry.getInstrumentation()
    private val serviceTestRule = ServiceTestRule()

@@ -76,7 +79,6 @@ class TaskbarUnitTestRule : MethodRule {
            override fun evaluate() {
                this@TaskbarUnitTestRule.target = target

                val context = instrumentation.targetContext
                instrumentation.runOnMainSync {
                    assumeTrue(
                        LauncherAppState.getIDP(context).getDeviceProfile(context).isTaskbarPresent
+1 −1
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ import org.junit.runner.RunWith
@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
class TaskbarAllAppsControllerTest {

    @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule()
    @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(getInstrumentation().targetContext)
    @get:Rule val animatorTestRule = AnimatorTestRule(this)

    @InjectController lateinit var allAppsController: TaskbarAllAppsController
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ import org.junit.runner.RunWith
@EmulatedDevices(["pixelFoldable2023"])
class TaskbarOverlayControllerTest {

    @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule()
    @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(getInstrumentation().targetContext)
    @InjectController lateinit var overlayController: TaskbarOverlayController

    private val taskbarContext: TaskbarActivityContext