From 14c6c8cee4d2e2cccf7f8f55dfd66be87f3256a8 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 9 Oct 2018 18:46:45 -0700 Subject: [PATCH] Improving tests to fix testBindNormalWidget_withoutConfig, and beyond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/com/android/launcher3/LauncherModel.java | 5 +++ .../launcher3/ui/AbstractLauncherUiTest.java | 33 ++++++++++++------- .../launcher3/ui/AllAppsIconToHomeTest.java | 2 +- .../launcher3/ui/ShortcutsLaunchTest.java | 3 +- .../launcher3/ui/ShortcutsToHomeTest.java | 3 +- .../ui/widget/AddConfigWidgetTest.java | 8 ++--- .../launcher3/ui/widget/AddWidgetTest.java | 2 +- .../launcher3/ui/widget/BindWidgetTest.java | 18 ++-------- .../ui/widget/RequestPinItemTest.java | 4 +-- .../src/com/android/launcher3/util/Wait.java | 14 ++++---- 10 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d571cf47de..cbfde25596 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -451,6 +451,11 @@ public class LauncherModel extends BroadcastReceiver * @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 773ec9d189..6db784aa48 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -47,6 +47,7 @@ import androidx.test.uiautomator.Until; 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 @@ public abstract class AbstractLauncherUiTest { 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 @@ public abstract class AbstractLauncherUiTest { @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 @@ public abstract class AbstractLauncherUiTest { } 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 @@ public abstract class AbstractLauncherUiTest { } } + 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 @@ public abstract class AbstractLauncherUiTest { // 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 condition) { - return waitForLauncherCondition(condition, DEFAULT_ACTIVITY_TIMEOUT); + protected void waitForLauncherCondition(String message, Function 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 condition, long timeout) { - if (!TestHelpers.isInLauncherProcess()) return true; - return Wait.atMost(() -> getFromLauncher(condition), timeout); + protected void waitForLauncherCondition( + String message, Function 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 600d39065e..a6830fc14e 100644 --- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java @@ -46,7 +46,7 @@ public class AllAppsIconToHomeTest extends AbstractLauncherUiTest { // 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 6a007ae4de..f77300011f 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java @@ -47,8 +47,7 @@ public class ShortcutsLaunchTest extends AbstractLauncherUiTest { // 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 c23f6ef812..bc1b519323 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java @@ -49,8 +49,7 @@ public class ShortcutsToHomeTest extends AbstractLauncherUiTest { // 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 83fcc6059b..14141f0dee 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 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { // 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 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { 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 d9fef8171f..8a05e2cf32 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 @@ public class AddWidgetTest extends AbstractLauncherUiTest { // 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 738ad84437..b7342c454f 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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 @@ public class BindWidgetTest extends AbstractLauncherUiTest { // Launch the home activity mActivityMonitor.startLauncher(); + waitForModelLoaded(); + // Verify UI UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) .className(widgetClass); @@ -390,15 +387,4 @@ public class BindWidgetTest extends AbstractLauncherUiTest { 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 67a7fde19f..839cfb2f3f 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 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // 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 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // 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 f9e53ba8b1..0e41c0206b 100644 --- a/tests/src/com/android/launcher3/util/Wait.java +++ b/tests/src/com/android/launcher3/util/Wait.java @@ -2,6 +2,8 @@ package com.android.launcher3.util; import android.os.SystemClock; +import org.junit.Assert; + /** * A utility class for waiting for a condition to be true. */ @@ -9,16 +11,16 @@ public class Wait { 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 @@ public class Wait { // Check once more before returning false. try { if (condition.isTrue()) { - return true; + return; } } catch (Throwable t) { // Ignore } - return false; + Assert.fail(message); } }