Improving tests to fix testBindNormalWidget_withoutConfig, and beyond
1. Make waitXXX methods fail if the condition diesn’t turn true.
2. Waiting for loading to complete in tearDown instead of reloading the
model
3. Avoiding waiting for load-complete where loading didn’t start
4. Disabling last test in AddConfigWidgetTest
5. Waiting for loading to complete inside setupAndVerifyContents(), not
outside
6. Unifying how we wait for loader to complete
7. Adding more logging
Bug: 117332845
Test: running all Nexus tests
Change-Id: I3070e1ac2b9161179cc3e0800b0cd8162807389a
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d571cf4..cbfde25 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -451,6 +451,11 @@
* @return true if the page could be bound synchronously.
*/
public boolean startLoader(int synchronousBindPage) {
+ if (com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
+ && com.android.launcher3.Utilities.IS_DEBUG_DEVICE) {
+ android.util.Log.d("b/117332845",
+ android.util.Log.getStackTraceString(new Throwable()));
+ }
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
synchronized (mLock) {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 773ec9d..6db784a 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -47,6 +47,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.MainThreadExecutor;
@@ -133,7 +134,8 @@
try {
// Create launcher activity if necessary and bring it to the front.
mDevice.pressHome();
- waitForLauncherCondition(launcher -> launcher != null);
+ waitForLauncherCondition("Launcher activity wasn't created",
+ launcher -> launcher != null);
executeOnLauncher(launcher ->
launcher.getRotationHelper().forceAllowRotationForTesting(true));
@@ -170,7 +172,7 @@
@After
public void tearDown() throws Exception {
// Limits UI tests affecting tests running after them.
- resetLoaderState();
+ waitForModelLoaded();
}
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
@@ -320,8 +322,7 @@
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
- waitForLauncherCondition(launcher ->
- LauncherAppState.getInstance(mTargetContext).getModel().isModelLoaded());
+ waitForModelLoaded();
if (com.android.launcher3.Utilities.IS_RUNNING_IN_TEST_HARNESS
&& com.android.launcher3.Utilities.IS_DEBUG_DEVICE) {
android.util.Log.d("b/117332845",
@@ -329,6 +330,13 @@
}
}
+ protected void waitForModelLoaded() {
+ waitForLauncherCondition("Launcher model didn't load", launcher -> {
+ final LauncherModel model = LauncherAppState.getInstance(mTargetContext).getModel();
+ return model.getCallback() == null || model.isModelLoaded();
+ });
+ }
+
/**
* Runs the callback on the UI thread and returns the result.
*/
@@ -354,22 +362,23 @@
// Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
// the results of that gesture because the wait can hide flakeness.
- protected boolean waitForState(LauncherState state) {
- return waitForLauncherCondition(launcher -> launcher.getStateManager().getState() == state);
+ protected void waitForState(String message, LauncherState state) {
+ waitForLauncherCondition(message,
+ launcher -> launcher.getStateManager().getState() == state);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
- protected boolean waitForLauncherCondition(Function<Launcher, Boolean> condition) {
- return waitForLauncherCondition(condition, DEFAULT_ACTIVITY_TIMEOUT);
+ protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
+ waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
- protected boolean waitForLauncherCondition(
- Function<Launcher, Boolean> condition, long timeout) {
- if (!TestHelpers.isInLauncherProcess()) return true;
- return Wait.atMost(() -> getFromLauncher(condition), timeout);
+ protected void waitForLauncherCondition(
+ String message, Function<Launcher, Boolean> condition, long timeout) {
+ if (!TestHelpers.isInLauncherProcess()) return;
+ Wait.atMost(message, () -> getFromLauncher(condition), timeout);
}
/**
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
index 600d390..a6830fc 100644
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -46,7 +46,7 @@
// Open all apps and wait for load complete.
final UiObject2 appsContainer = openAllApps();
- assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
// Drag icon to homescreen.
UiObject2 icon = scrollAndFind(appsContainer, By.text(settingsApp.getLabel().toString()));
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
index 6a007ae..f773000 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
@@ -47,8 +47,7 @@
// Open all apps and wait for load complete
final UiObject2 appsContainer = openAllApps();
- assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2),
- DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
// Find settings app and verify shortcuts appear when long pressed
UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
index c23f6ef..bc1b519 100644
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
@@ -49,8 +49,7 @@
// Open all apps and wait for load complete.
final UiObject2 appsContainer = openAllApps();
- assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2),
- DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
// Find the app and long press it to show shortcuts.
UiObject2 icon = scrollAndFind(appsContainer, By.text(testApp.getLabel().toString()));
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 83fcc60..14141f0 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -106,7 +106,7 @@
// Open widget tray and wait for load complete.
final UiObject2 widgetContainer = openWidgetsTray();
- assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
@@ -128,12 +128,12 @@
setResult(acceptConfig);
if (acceptConfig) {
- assertTrue(Wait.atMost(new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT));
+ Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
// Verify that the widget id is deleted.
- assertTrue(Wait.atMost(() -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT));
+ Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
+ DEFAULT_ACTIVITY_TIMEOUT);
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index d9fef81..8a05e2c 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -70,7 +70,7 @@
// Open widget tray and wait for load complete.
final UiObject2 widgetContainer = openWidgetsTray();
- assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
// Drag widget to homescreen
UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 738ad84..b7342c4 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -141,7 +141,6 @@
// Since there is no widget to verify, just wait until the workspace is ready.
setupAndVerifyContents(item, Workspace.class, null);
- waitUntilLoaderIdle();
// Item deleted from db
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
null, null, null, null, null);
@@ -187,7 +186,6 @@
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
- waitUntilLoaderIdle();
// Item deleted from db
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
null, null, null, null, null);
@@ -216,7 +214,6 @@
// The view does not exist
assertFalse(mDevice.findObject(
new UiSelector().className(PendingAppWidgetHostView.class)).exists());
- waitUntilLoaderIdle();
// Item deleted from db
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
null, null, null, null, null);
@@ -234,7 +231,6 @@
setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
// Verify item still exists in db
- waitUntilLoaderIdle();
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
null, null, null, null, null);
assertEquals(1, mCursor.getCount());
@@ -261,7 +257,6 @@
setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
// Verify item still exists in db
- waitUntilLoaderIdle();
mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
null, null, null, null, null);
assertEquals(1, mCursor.getCount());
@@ -306,6 +301,8 @@
// Launch the home activity
mActivityMonitor.startLauncher();
+ waitForModelLoaded();
+
// Verify UI
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
.className(widgetClass);
@@ -390,15 +387,4 @@
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
return item;
}
-
- /**
- * Blocks the current thread until all the jobs in the main worker thread are complete.
- */
- private void waitUntilLoaderIdle() throws Exception {
- new LooperExecutor(LauncherModel.getWorkerLooper())
- .submit(new Runnable() {
- @Override
- public void run() { }
- }).get(DEFAULT_WORKER_TIMEOUT_SECS, TimeUnit.SECONDS);
- }
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 67a7fde..839cfb2 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -152,7 +152,7 @@
// Open all apps and wait for load complete
final UiObject2 appsContainer = openAllApps();
- assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+ Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT);
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
@@ -191,7 +191,7 @@
// Go back to home
mActivityMonitor.returnToHome();
- assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT));
+ Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
}
/**
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index f9e53ba..0e41c02 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -2,6 +2,8 @@
import android.os.SystemClock;
+import org.junit.Assert;
+
/**
* A utility class for waiting for a condition to be true.
*/
@@ -9,16 +11,16 @@
private static final long DEFAULT_SLEEP_MS = 200;
- public static boolean atMost(Condition condition, long timeout) {
- return atMost(condition, timeout, DEFAULT_SLEEP_MS);
+ public static void atMost(String message, Condition condition, long timeout) {
+ atMost(message, condition, timeout, DEFAULT_SLEEP_MS);
}
- public static boolean atMost(Condition condition, long timeout, long sleepMillis) {
+ public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
long endTime = SystemClock.uptimeMillis() + timeout;
while (SystemClock.uptimeMillis() < endTime) {
try {
if (condition.isTrue()) {
- return true;
+ return;
}
} catch (Throwable t) {
// Ignore
@@ -29,11 +31,11 @@
// Check once more before returning false.
try {
if (condition.isTrue()) {
- return true;
+ return;
}
} catch (Throwable t) {
// Ignore
}
- return false;
+ Assert.fail(message);
}
}