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

Commit 8e5de657 authored by Brian Isganitis's avatar Brian Isganitis
Browse files

Don't use UiThreadTest for Taskbar Unit tests.

This annotation also runs the rule on the main thread, which can lead to
deadlocks with other threads (e.g. loading the model synchronously).

Change-Id: Ib276e9dc322f6f65bd32658e774d6076efb94f2e
Flag: TEST_ONLY
Test: Taskbar Unit Tests
Bug: 230027385
parent 8f1a1c4b
Loading
Loading
Loading
Loading
+63 −53
Original line number Diff line number Diff line
@@ -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() {
        getInstrumentation().runOnMainSync {
            allAppsController.toggle()
            allAppsController.toggle()
        }
        assertThat(allAppsController.isOpen).isFalse()
    }

@@ -77,54 +77,62 @@ class TaskbarAllAppsControllerTest {
    }

    @Test
    @UiThreadTest
    fun testSetApps_beforeOpened_cachesInfo() {
        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() {
        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() {
        val predictedApps =
            TestUtil.getOnUiThread {
                allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
                allAppsController.toggle()

        val predictedApps =
                overlayController
                    .requestWindow()
                    .appsView
                    .floatingHeaderView
                    .findFixedRowByType(PredictionRowView::class.java)
                    .predictedApps
            }

        assertThat(predictedApps).isEqualTo(TEST_PREDICTED_APPS)
    }

    @Test
    @UiThreadTest
    fun testSetPredictedApps_afterOpened_cachesInfo() {
        val predictedApps =
            TestUtil.getOnUiThread {
                allAppsController.toggle()
                allAppsController.setPredictedApps(TEST_PREDICTED_APPS)

        val predictedApps =
                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 =
            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() {
        getInstrumentation().runOnMainSync {
            allAppsController.setPredictedApps(TEST_PREDICTED_APPS)
            allAppsController.toggle()

            taskbarUnitTestRule.activityContext.popupDataProvider.onNotificationPosted(
                PackageUserKey.fromItemInfo(TEST_PREDICTED_APPS[0]),
                NotificationKeyData("key"),
            )
        }

        val predictionRowView =
        val btv =
            TestUtil.getOnUiThread {
                overlayController
                    .requestWindow()
                    .appsView
                    .floatingHeaderView
                    .findFixedRowByType(PredictionRowView::class.java)
        val btv = predictionRowView.getChildAt(0) as BubbleTextView
                    .getChildAt(0) as BubbleTextView
            }
        assertThat(btv.hasDot()).isTrue()
    }

+69 −71
Original line number Diff line number Diff line
@@ -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()
        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() {
        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())
        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 (overlay1, overlay2) =
            getOnUiThread {
                val context = overlayController.requestWindow()
        val overlay1 = TestOverlayView.show(context)
        val overlay2 = TestOverlayView.show(context)
                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 (overlay1, overlay2) =
            getOnUiThread {
                val context = overlayController.requestWindow()
        val overlay1 = TestOverlayView.show(context)
        val overlay2 = TestOverlayView.show(context)
                Pair(TestOverlayView.show(context), TestOverlayView.show(context))
            }

        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 }
        }

        getInstrumentation().runOnMainSync {
            overlayController.updateLauncherDeviceProfile(
                overlayController.launcherDeviceProfile
                .toBuilder(overlayContext)
                    .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 }
        }

        getInstrumentation().runOnMainSync {
            overlayController.updateLauncherDeviceProfile(
                overlayController.launcherDeviceProfile
                .toBuilder(overlayContext)
                    .toBuilder(context)
                    .setGestureMode(false)
                    .build()
            )
        }

        assertThat(overlay.isOpen).isTrue()
    }
+8 −8
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarNavButtonCallbacks
import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
import com.android.launcher3.util.LauncherMultivalentJUnit.Companion.isRunningInRobolectric
import com.android.launcher3.util.TestUtil
import com.android.quickstep.AllAppsActionManager
import com.android.quickstep.TouchInteractionService
import com.android.quickstep.TouchInteractionService.TISBinder
@@ -48,12 +49,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() {
@@ -105,8 +105,8 @@ class TaskbarUnitTestRule(
                        null
                    }

                instrumentation.runOnMainSync {
                taskbarManager =
                    TestUtil.getOnUiThread {
                        TaskbarManager(
                            context,
                            AllAppsActionManager(context, UI_HELPER_EXECUTOR) {