Merge "Waiting for app install to finish before procedding with the test" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-11-04 02:04:10 +00:00
committed by Android (Google) Code Review
7 changed files with 115 additions and 30 deletions

View File

@@ -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);
}

View File

@@ -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 <S, T> Bundle getUIProperty(
BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> 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> T getFromExecutorSync(ExecutorService executor, Callable<T> 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);
}

View File

@@ -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";

View File

@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.READ_LOGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application android:debuggable="true" android:extractNativeLibs="true">
<uses-library android:name="android.test.runner"/>

View File

@@ -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<String, Point> 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()}

View File

@@ -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);
}
}
}

View File

@@ -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(