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
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
index 9ecd935..2f0b446 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsControllerTest.kt
@@ -20,7 +20,6 @@
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.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 @@
@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 @@
}
@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 @@
}
// 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()
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
index fae5562..f946d4d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayControllerTest.kt
@@ -18,7 +18,6 @@
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.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 @@
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 @@
}
@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 @@
@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 @@
@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 @@
@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 @@
}
@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()
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index 39ac720..663a807 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -28,6 +28,7 @@
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 @@
* 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 @@
null
}
- instrumentation.runOnMainSync {
- taskbarManager =
+ taskbarManager =
+ TestUtil.getOnUiThread {
TaskbarManager(
context,
AllAppsActionManager(context, UI_HELPER_EXECUTOR) {
@@ -114,7 +114,7 @@
},
object : TaskbarNavButtonCallbacks {},
)
- }
+ }
try {
// Replace Launcher Taskbar window with test instance.