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

Commit 5af1bfbf authored by Jagrut Desai's avatar Jagrut Desai
Browse files

Unit Testing for TaskbarEduTooltipController

This CL includes
	- TaskbarEduTooltipController Unit Test for all edu logic and hide tooltip
	- Test util class for taskbar controller test to run the test on main thread.
	- Refactored other controller test to use the new util function to run test on main thread.
	- Ability to disable running test in test harness mode.

Test: Presubmit
Bug: 346394824
Flag: TEST_ONLY
Change-Id: Ic623df8e7a58ac48d260e592ffab61d1f0658aef
parent 2d36c463
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
import androidx.annotation.IntDef
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import androidx.core.text.HtmlCompat
import androidx.core.view.updateLayoutParams
import com.airbnb.lottie.LottieAnimationView
@@ -87,7 +88,7 @@ open class TaskbarEduTooltipController(context: Context) :
                !activityContext.isTinyTaskbar
        }

    private val isOpen: Boolean
    val isTooltipOpen: Boolean
        get() = tooltip?.isOpen ?: false

    val isBeforeTooltipFeaturesStep: Boolean
@@ -96,7 +97,8 @@ open class TaskbarEduTooltipController(context: Context) :
    private lateinit var controllers: TaskbarControllers

    // Keep track of whether the user has seen the Search Edu
    private var userHasSeenSearchEdu: Boolean
    @VisibleForTesting
    var userHasSeenSearchEdu: Boolean
        get() {
            return TASKBAR_SEARCH_EDU_SEEN.get(activityContext)
        }
@@ -409,7 +411,7 @@ open class TaskbarEduTooltipController(context: Context) :
    override fun dumpLogs(prefix: String?, pw: PrintWriter?) {
        pw?.println(prefix + "TaskbarEduTooltipController:")
        pw?.println("$prefix\tisTooltipEnabled=$isTooltipEnabled")
        pw?.println("$prefix\tisOpen=$isOpen")
        pw?.println("$prefix\tisOpen=$isTooltipOpen")
        pw?.println("$prefix\ttooltipStep=$tooltipStep")
    }

+25 −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

object TaskbarControllerTestUtil {
    inline fun runOnMainSync(crossinline runTest: () -> Unit) {
        getInstrumentation().runOnMainSync { runTest() }
    }
}
+205 −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
import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.THREE_BUTTONS
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
import com.android.launcher3.taskbar.rules.TaskbarPinningPreferenceRule
import com.android.launcher3.taskbar.rules.TaskbarPreferenceRule
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.LauncherMultivalentJUnit
import com.android.launcher3.util.LauncherMultivalentJUnit.EmulatedDevices
import com.android.launcher3.util.OnboardingPrefs
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelFoldable2023", "pixelTablet2023"])
class TaskbarEduTooltipControllerTest {

    private val context =
        TaskbarWindowSandboxContext.create(
            InstrumentationRegistry.getInstrumentation().targetContext
        )

    @get:Rule
    val tooltipStepPreferenceRule =
        TaskbarPreferenceRule(
            context,
            OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.prefItem,
        )

    @get:Rule
    val searchEduPreferenceRule =
        TaskbarPreferenceRule(
            context,
            OnboardingPrefs.TASKBAR_SEARCH_EDU_SEEN,
        )

    @get:Rule val taskbarPinningPreferenceRule = TaskbarPinningPreferenceRule(context)

    @get:Rule val taskbarModeRule = TaskbarModeRule(context)

    @get:Rule val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)

    @InjectController lateinit var taskbarEduTooltipController: TaskbarEduTooltipController

    private val taskbarContext: TaskbarActivityContext
        get() = taskbarUnitTestRule.activityContext

    private val wasInTestHarness = Utilities.isRunningInTestHarness()

    @Before
    fun setUp() {
        Utilities.disableRunningInTestHarnessForTests()
    }

    @After
    fun tearDown() {
        if (wasInTestHarness) {
            Utilities.enableRunningInTestHarnessForTests()
        }
    }

    @Test
    @TaskbarMode(THREE_BUTTONS)
    fun testMaybeShowSwipeEdu_whenTaskbarIsInThreeButtonMode_doesNotShowSwipeEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
        runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowSwipeEdu_whenSwipeEduAlreadyShown_doesNotShowSwipeEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
        runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowSwipeEdu_whenUserHasNotSeen_doesShowSwipeEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
        runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowFeaturesEdu_whenFeatureEduAlreadyShown_doesNotShowFeatureEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
        runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowFeaturesEdu_whenUserHasNotSeen_doesShowFeatureEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_FEATURES
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_FEATURES)
        runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
    }

    @Test
    @TaskbarMode(THREE_BUTTONS)
    fun testMaybeShowPinningEdu_whenTaskbarIsInThreeButtonMode_doesNotShowPinningEdu() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
        runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowPinningEdu_whenUserHasNotSeen_doesShowPinningEdu() {
        // Test standalone pinning edu, where user has seen taskbar edu before, but not pinning edu.
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_PINNING
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_PINNING)
        runOnMainSync { taskbarEduTooltipController.maybeShowFeaturesEdu() }
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_NONE)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testIsBeforeTooltipFeaturesStep_whenUserHasNotSeenFeatureEdu_shouldReturnTrue() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
        assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isTrue()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testIsBeforeTooltipFeaturesStep_whenUserHasSeenFeatureEdu_shouldReturnFalse() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_NONE
        assertThat(taskbarEduTooltipController.isBeforeTooltipFeaturesStep).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testHide_whenTooltipIsOpen_shouldCloseTooltip() {
        tooltipStepPreferenceRule.value = TOOLTIP_STEP_SWIPE
        assertThat(taskbarEduTooltipController.tooltipStep).isEqualTo(TOOLTIP_STEP_SWIPE)
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
        runOnMainSync { taskbarEduTooltipController.maybeShowSwipeEdu() }
        assertThat(taskbarEduTooltipController.isTooltipOpen).isTrue()
        runOnMainSync { taskbarEduTooltipController.hide() }
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(TRANSIENT)
    fun testMaybeShowSearchEdu_whenTaskbarIsTransient_shouldNotShowSearchEdu() {
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
        runOnMainSync { taskbarEduTooltipController.init(taskbarContext.controllers) }
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }

    @Test
    @TaskbarMode(PINNED)
    fun testMaybeShowSearchEdu_whenTaskbarIsPinnedAndUserHasSeenSearchEdu_shouldNotShowSearchEdu() {
        searchEduPreferenceRule.value = true
        assertThat(taskbarEduTooltipController.userHasSeenSearchEdu).isTrue()
        runOnMainSync { taskbarEduTooltipController.hide() }
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
        runOnMainSync { taskbarEduTooltipController.init(taskbarContext.controllers) }
        assertThat(taskbarEduTooltipController.isTooltipOpen).isFalse()
    }
}
+9 −8
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.launcher3.appprediction.PredictionRowView
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.notification.NotificationKeyData
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
@@ -56,13 +57,13 @@ class TaskbarAllAppsControllerTest {

    @Test
    fun testToggle_once_showsAllApps() {
        getInstrumentation().runOnMainSync { allAppsController.toggle() }
        runOnMainSync { allAppsController.toggle() }
        assertThat(allAppsController.isOpen).isTrue()
    }

    @Test
    fun testToggle_twice_closesAllApps() {
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            allAppsController.toggle()
            allAppsController.toggle()
        }
@@ -71,7 +72,7 @@ class TaskbarAllAppsControllerTest {

    @Test
    fun testToggle_taskbarRecreated_allAppsReopened() {
        getInstrumentation().runOnMainSync { allAppsController.toggle() }
        runOnMainSync { allAppsController.toggle() }
        taskbarUnitTestRule.recreateTaskbar()
        assertThat(allAppsController.isOpen).isTrue()
    }
@@ -138,7 +139,7 @@ class TaskbarAllAppsControllerTest {

    @Test
    fun testUpdateNotificationDots_appInfo_hasDot() {
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            allAppsController.setApps(TEST_APPS, 0, emptyMap())
            allAppsController.toggle()
            taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
@@ -162,7 +163,7 @@ class TaskbarAllAppsControllerTest {

    @Test
    fun testUpdateNotificationDots_predictedApp_hasDot() {
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
            allAppsController.toggle()
            taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
@@ -185,12 +186,12 @@ class TaskbarAllAppsControllerTest {

    @Test
    fun testToggleSearch_searchEditTextFocused() {
        getInstrumentation().runOnMainSync { allAppsController.toggleSearch() }
        getInstrumentation().runOnMainSync {
        runOnMainSync { allAppsController.toggleSearch() }
        runOnMainSync {
            // All Apps is now attached to window. Open animation is posted but not started.
        }

        getInstrumentation().runOnMainSync {
        runOnMainSync {
            // Animation has started. Advance to end of animation.
            animatorTestRule.advanceTimeBy(overlayController.openDuration.toLong())
        }
+18 −25
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY
import com.android.launcher3.AbstractFloatingView.hasOpenView
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarControllerTestUtil.runOnMainSync
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -64,7 +65,7 @@ class TaskbarOverlayControllerTest {
    @Test
    fun testRequestWindow_afterHidingExistingWindow_createsNewWindow() {
        val context1 = getOnUiThread { overlayController.requestWindow() }
        getInstrumentation().runOnMainSync { overlayController.hideWindow() }
        runOnMainSync { overlayController.hideWindow() }

        val context2 = getOnUiThread { overlayController.requestWindow() }
        assertThat(context1).isNotSameInstanceAs(context2)
@@ -73,7 +74,7 @@ class TaskbarOverlayControllerTest {
    @Test
    fun testRequestWindow_afterHidingOverlay_createsNewWindow() {
        val context1 = getOnUiThread { overlayController.requestWindow() }
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            TestOverlayView.show(context1)
            overlayController.hideWindow()
        }
@@ -84,16 +85,14 @@ class TaskbarOverlayControllerTest {

    @Test
    fun testRequestWindow_addsProxyView() {
        getInstrumentation().runOnMainSync {
            TestOverlayView.show(overlayController.requestWindow())
        }
        runOnMainSync { TestOverlayView.show(overlayController.requestWindow()) }
        assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
    }

    @Test
    fun testRequestWindow_closeProxyView_closesOverlay() {
        val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            AbstractFloatingView.closeOpenContainer(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)
        }
        assertThat(overlay.isOpen).isFalse()
@@ -103,13 +102,13 @@ class TaskbarOverlayControllerTest {
    fun testRequestWindow_attachesDragLayer() {
        val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }
        // Allow drag layer to attach before checking.
        getInstrumentation().runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
        runOnMainSync { assertThat(dragLayer.isAttachedToWindow).isTrue() }
    }

    @Test
    fun testHideWindow_closesOverlay() {
        val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
        getInstrumentation().runOnMainSync { overlayController.hideWindow() }
        runOnMainSync { overlayController.hideWindow() }
        assertThat(overlay.isOpen).isFalse()
    }

@@ -118,7 +117,7 @@ class TaskbarOverlayControllerTest {
        val dragLayer = getOnUiThread { overlayController.requestWindow().dragLayer }

        // Wait for drag layer to be attached to window before hiding.
        getInstrumentation().runOnMainSync {
        runOnMainSync {
            overlayController.hideWindow()
            assertThat(dragLayer.isAttachedToWindow).isFalse()
        }
@@ -132,7 +131,7 @@ class TaskbarOverlayControllerTest {
                Pair(TestOverlayView.show(context), TestOverlayView.show(context))
            }

        getInstrumentation().runOnMainSync { overlay1.close(false) }
        runOnMainSync { overlay1.close(false) }
        assertThat(overlay2.isOpen).isTrue()
        assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isTrue()
    }
@@ -145,7 +144,7 @@ class TaskbarOverlayControllerTest {
                Pair(TestOverlayView.show(context), TestOverlayView.show(context))
            }

        getInstrumentation().runOnMainSync {
        runOnMainSync {
            overlay1.close(false)
            overlay2.close(false)
        }
@@ -154,9 +153,7 @@ class TaskbarOverlayControllerTest {

    @Test
    fun testRecreateTaskbar_closesWindow() {
        getInstrumentation().runOnMainSync {
            TestOverlayView.show(overlayController.requestWindow())
        }
        runOnMainSync { TestOverlayView.show(overlayController.requestWindow()) }
        taskbarUnitTestRule.recreateTaskbar()
        assertThat(hasOpenView(taskbarContext, TYPE_TASKBAR_OVERLAY_PROXY)).isFalse()
    }
@@ -166,29 +163,25 @@ class TaskbarOverlayControllerTest {
        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() }
        runOnMainSync { assertThat(overlay.isOpen).isFalse() }
    }

    @Test
    fun testTaskStackChanged_allAppsClosed_overlayStaysOpen() {
        val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
        getInstrumentation().runOnMainSync {
            taskbarContext.controllers.sharedState?.allAppsVisible = false
        }
        runOnMainSync { taskbarContext.controllers.sharedState?.allAppsVisible = false }

        TaskStackChangeListeners.getInstance().listenerImpl.onTaskStackChanged()
        getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isTrue() }
        runOnMainSync { assertThat(overlay.isOpen).isTrue() }
    }

    @Test
    fun testTaskStackChanged_allAppsOpen_closesOverlay() {
        val overlay = getOnUiThread { TestOverlayView.show(overlayController.requestWindow()) }
        getInstrumentation().runOnMainSync {
            taskbarContext.controllers.sharedState?.allAppsVisible = true
        }
        runOnMainSync { taskbarContext.controllers.sharedState?.allAppsVisible = true }

        TaskStackChangeListeners.getInstance().listenerImpl.onTaskStackChanged()
        getInstrumentation().runOnMainSync { assertThat(overlay.isOpen).isFalse() }
        runOnMainSync { assertThat(overlay.isOpen).isFalse() }
    }

    @Test
@@ -198,7 +191,7 @@ class TaskbarOverlayControllerTest {
            TestOverlayView.show(context).apply { type = TYPE_OPTIONS_POPUP }
        }

        getInstrumentation().runOnMainSync {
        runOnMainSync {
            overlayController.updateLauncherDeviceProfile(
                overlayController.launcherDeviceProfile
                    .toBuilder(context)
@@ -217,7 +210,7 @@ class TaskbarOverlayControllerTest {
            TestOverlayView.show(context).apply { type = TYPE_TASKBAR_ALL_APPS }
        }

        getInstrumentation().runOnMainSync {
        runOnMainSync {
            overlayController.updateLauncherDeviceProfile(
                overlayController.launcherDeviceProfile
                    .toBuilder(context)
Loading