From 50097ef272fd1e0c6f564aac34b8185da4f93ecd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 3 Nov 2022 10:34:56 -0700 Subject: [PATCH] Waiting for app install to finish before procedding with the test Bug: 256659409 Test: Presubmit Change-Id: Ia0f4cdd072c4c439d09070b0395fcfd6909c2a8f --- .../testing/DebugTestInformationHandler.java | 4 + .../testing/TestInformationHandler.java | 32 ++++--- .../testing/shared/TestProtocol.java | 1 + tests/AndroidManifest-common.xml | 1 + .../launcher3/ui/TaplTestsLauncher3.java | 20 +++-- .../com/android/launcher3/util/TestUtil.java | 83 ++++++++++++++++--- .../tapl/LauncherInstrumentation.java | 4 + 7 files changed, 115 insertions(+), 30 deletions(-) diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index a91ff44c8a..bdac88a043 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -17,6 +17,7 @@ package com.android.launcher3.testing; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.app.Activity; import android.app.Application; @@ -248,6 +249,9 @@ public class DebugTestInformationHandler extends TestInformationHandler { return response; } + case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED: + return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new); + default: return super.call(method, arg, extras); } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index fdd30e185b..acb7eb38a2 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -47,7 +47,9 @@ import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.widget.picker.WidgetsFullSheet; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.function.Supplier; @@ -214,8 +216,7 @@ public class TestInformationHandler implements ResourceBasedOverride { } case TestProtocol.REQUEST_HAS_TIS: { - response.putBoolean( - TestProtocol.REQUEST_HAS_TIS, false); + response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false); return response; } @@ -266,17 +267,24 @@ public class TestInformationHandler implements ResourceBasedOverride { */ private static Bundle getUIProperty( BundleSetter bundleSetter, Function provider, Supplier targetSupplier) { + return getFromExecutorSync(MAIN_EXECUTOR, () -> { + S target = targetSupplier.get(); + if (target == null) { + return null; + } + T value = provider.apply(target); + Bundle response = new Bundle(); + bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value); + return response; + }); + } + + /** + * Executes the callback on the executor and waits for the result + */ + protected static T getFromExecutorSync(ExecutorService executor, Callable callback) { try { - return MAIN_EXECUTOR.submit(() -> { - S target = targetSupplier.get(); - if (target == null) { - return null; - } - T value = provider.apply(target); - Bundle response = new Bundle(); - bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value); - return response; - }).get(); + return executor.submit(callback).get(); } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java index 792d4757cb..3fbce888f1 100644 --- a/src/com/android/launcher3/testing/shared/TestProtocol.java +++ b/src/com/android/launcher3/testing/shared/TestProtocol.java @@ -128,6 +128,7 @@ public final class TestProtocol { public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing"; public static final String REQUEST_ENABLE_ROTATION = "enable_rotation"; public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion"; + public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared"; public static boolean sDebugTracing = false; public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing"; diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 4af8468765..bedf2772ba 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -24,6 +24,7 @@ + diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 01e6ed760f..978e84c393 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -29,7 +29,6 @@ import static org.junit.Assume.assumeTrue; import android.content.Intent; import android.graphics.Point; -import android.os.SystemClock; import android.platform.test.annotations.IwTest; import androidx.test.filters.LargeTest; @@ -479,7 +478,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testUninstallFromWorkspace() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { verifyAppUninstalledFromAllApps( createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); @@ -492,10 +491,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape @ScreenRecord // (b/256659409) public void testUninstallFromAllApps() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { - // b/256659409 - SystemClock.sleep(5000); Workspace workspace = mLauncher.getWorkspace(); final HomeAllApps allApps = workspace.switchToAllApps(); allApps.freeze(); @@ -539,7 +536,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { Point[] gridPositions = getCornersAndCenterPositions(); createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]); createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]); - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]); Map initialPositions = @@ -590,6 +587,17 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME)); } + private void installDummyAppAndWaitForUIUpdate() throws IOException { + TestUtil.installDummyApp(); + // Wait for model thread completion as it may be processing + // the install event from the SystemService + mLauncher.waitForModelQueueCleared(); + // Wait for Launcher UI thread completion, as it may be processing updating the UI in + // response to the model update. Not that `waitForLauncherInitialized` is just a proxy + // method, we can use any method which touches Launcher UI thread, + mLauncher.waitForLauncherInitialized(); + } + /** * @return List of workspace grid coordinates. Those are not pixels. See {@link * Workspace#getIconGridDimensions()} diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java index 67f3902095..d7c6c4fda9 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -17,8 +17,13 @@ package com.android.launcher3.util; import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static androidx.test.InstrumentationRegistry.getTargetContext; +import android.content.pm.LauncherApps; import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; import androidx.test.uiautomator.UiDevice; @@ -27,6 +32,7 @@ import org.junit.Assert; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.CountDownLatch; public class TestUtil { public static final String DUMMY_PACKAGE = "com.example.android.aardwolf"; @@ -40,24 +46,77 @@ public class TestUtil { final String apkFilename = getInstrumentation().getTargetContext(). getFilesDir().getPath() + "/dummy_app.apk"; - final FileOutputStream out = new FileOutputStream(apkFilename); - byte[] buff = new byte[1024]; - int read; + try (PackageInstallCheck pic = new PackageInstallCheck()) { + final FileOutputStream out = new FileOutputStream(apkFilename); + byte[] buff = new byte[1024]; + int read; - while ((read = in.read(buff)) > 0) { - out.write(buff, 0, read); + while ((read = in.read(buff)) > 0) { + out.write(buff, 0, read); + } + in.close(); + out.close(); + + final String result = UiDevice.getInstance(getInstrumentation()) + .executeShellCommand("pm install " + apkFilename); + Assert.assertTrue( + "Failed to install wellbeing test apk; make sure the device is rooted", + "Success".equals(result.replaceAll("\\s+", ""))); + pic.mAddWait.await(); + } catch (InterruptedException e) { + throw new IOException(e); } - in.close(); - out.close(); - - final String result = UiDevice.getInstance(getInstrumentation()) - .executeShellCommand("pm install " + apkFilename); - Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted", - "Success".equals(result.replaceAll("\\s+", ""))); } public static void uninstallDummyApp() throws IOException { UiDevice.getInstance(getInstrumentation()).executeShellCommand( "pm uninstall " + DUMMY_PACKAGE); } + + private static class PackageInstallCheck extends LauncherApps.Callback + implements AutoCloseable { + + final CountDownLatch mAddWait = new CountDownLatch(1); + final LauncherApps mLauncherApps; + + PackageInstallCheck() { + mLauncherApps = getTargetContext().getSystemService(LauncherApps.class); + mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper())); + } + + private void verifyPackage(String packageName) { + if (DUMMY_PACKAGE.equals(packageName)) { + mAddWait.countDown(); + } + } + + @Override + public void onPackageAdded(String packageName, UserHandle user) { + verifyPackage(packageName); + } + + @Override + public void onPackageChanged(String packageName, UserHandle user) { + verifyPackage(packageName); + } + + @Override + public void onPackageRemoved(String packageName, UserHandle user) { } + + @Override + public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) { + for (String packageName : packageNames) { + verifyPackage(packageName); + } + } + + @Override + public void onPackagesUnavailable(String[] packageNames, UserHandle user, + boolean replacing) { } + + @Override + public void close() { + mLauncherApps.unregisterCallback(this); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 449b7b7631..1c5c5fa812 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -848,6 +848,10 @@ public final class LauncherInstrumentation { } } + public void waitForModelQueueCleared() { + getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED); + } + public void waitForLauncherInitialized() { for (int i = 0; i < 100; ++i) { if (getTestInfo(