summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java5
-rw-r--r--tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java5
-rw-r--r--tests/src/com/android/launcher3/util/rule/FailureWatcher.java24
-rw-r--r--tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt107
4 files changed, 62 insertions, 79 deletions
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index dbe4402812..97e34c5f10 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -116,11 +116,12 @@ public class FallbackRecentsTest {
Utilities.enableRunningInTestHarnessForTests();
}
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
mOrderSensitiveRules = RuleChain
.outerRule(new SamplerRule())
.around(new NavigationModeSwitchRule(mLauncher))
- .around(new ViewCaptureRule())
- .around(new FailureWatcher(mDevice, mLauncher));
+ .around(viewCaptureRule)
+ .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
getHomeIntentInPackage(context),
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 5bd28d85a3..d7c4ae3857 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -216,10 +216,11 @@ public abstract class AbstractLauncherUiTest {
}
protected TestRule getRulesInsideActivityMonitor() {
+ final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
final RuleChain inner = RuleChain
.outerRule(new PortraitLandscapeRunner(this))
- .around(new ViewCaptureRule())
- .around(new FailureWatcher(mDevice, mLauncher));
+ .around(viewCaptureRule)
+ .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));
return TestHelpers.isInLauncherProcess()
? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 6b11fd6af4..7ca6a06ed2 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -6,8 +6,12 @@ import android.os.FileUtils;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
import androidx.test.uiautomator.UiDevice;
+import com.android.app.viewcapture.ViewCapture;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -28,10 +32,14 @@ public class FailureWatcher extends TestWatcher {
private static boolean sSavedBugreport = false;
final private UiDevice mDevice;
private final LauncherInstrumentation mLauncher;
+ @NonNull
+ private final ViewCapture mViewCapture;
- public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) {
+ public FailureWatcher(UiDevice device, LauncherInstrumentation launcher,
+ @NonNull ViewCapture viewCapture) {
mDevice = device;
mLauncher = launcher;
+ mViewCapture = viewCapture;
}
@Override
@@ -63,7 +71,7 @@ public class FailureWatcher extends TestWatcher {
@Override
protected void failed(Throwable e, Description description) {
- onError(mLauncher, description, e);
+ onError(mLauncher, description, e, mViewCapture);
}
static File diagFile(Description description, String prefix, String ext) {
@@ -74,6 +82,12 @@ public class FailureWatcher extends TestWatcher {
public static void onError(LauncherInstrumentation launcher, Description description,
Throwable e) {
+ onError(launcher, description, e, null);
+ }
+
+ private static void onError(LauncherInstrumentation launcher, Description description,
+ Throwable e, @Nullable ViewCapture viewCapture) {
+
final File sceenshot = diagFile(description, "TestScreenshot", "png");
final File hierarchy = diagFile(description, "Hierarchy", "zip");
@@ -88,6 +102,12 @@ public class FailureWatcher extends TestWatcher {
out.putNextEntry(new ZipEntry("visible_windows.zip"));
dumpCommand("cmd window dump-visible-window-views", out);
out.closeEntry();
+
+ if (viewCapture != null) {
+ out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
+ viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext());
+ out.closeEntry();
+ }
} catch (Exception ignored) {
}
diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
index f3fff35c90..0c6553998d 100644
--- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
+++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt
@@ -19,101 +19,62 @@ import android.app.Activity
import android.app.Application
import android.media.permission.SafeCloseable
import android.os.Bundle
-import android.util.Log
-import androidx.annotation.AnyThread
import androidx.test.core.app.ApplicationProvider
import com.android.app.viewcapture.SimpleViewCapture
import com.android.app.viewcapture.ViewCapture.MAIN_EXECUTOR
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
-import java.io.File
-import java.io.FileOutputStream
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
-import org.junit.rules.TestWatcher
+import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
-private const val TAG = "ViewCaptureRule"
-
/**
* This JUnit TestRule registers a listener for activity lifecycle events to attach a ViewCapture
* instance that other test rules use to dump the timelapse hierarchy upon an error during a test.
*
* This rule will not work in OOP tests that don't have access to the activity under test.
*/
-class ViewCaptureRule : TestWatcher() {
- private val viewCapture = SimpleViewCapture("test-view-capture")
- private val windowListenerCloseables = mutableListOf<SafeCloseable>()
+class ViewCaptureRule : TestRule {
+ val viewCapture = SimpleViewCapture("test-view-capture")
override fun apply(base: Statement, description: Description): Statement {
- val testWatcherStatement = super.apply(base, description)
-
return object : Statement() {
override fun evaluate() {
- val lifecycleCallbacks = createLifecycleCallbacks(description)
- with(ApplicationProvider.getApplicationContext<Application>()) {
- registerActivityLifecycleCallbacks(lifecycleCallbacks)
- try {
- testWatcherStatement.evaluate()
- } finally {
- unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
- }
- }
- }
- }
- }
+ val windowListenerCloseables = mutableListOf<SafeCloseable>()
- private fun createLifecycleCallbacks(description: Description) =
- object : ActivityLifecycleCallbacksAdapter {
- override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
- super.onActivityCreated(activity, bundle)
- windowListenerCloseables.add(
- viewCapture.startCapture(
- activity.window.decorView,
- "${description.testClass?.simpleName}.${description.methodName}"
- )
- )
- }
+ val lifecycleCallbacks =
+ object : ActivityLifecycleCallbacksAdapter {
+ override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
+ super.onActivityCreated(activity, bundle)
+ windowListenerCloseables.add(
+ viewCapture.startCapture(
+ activity.window.decorView,
+ "${description.testClass?.simpleName}.${description.methodName}"
+ )
+ )
+ }
- override fun onActivityDestroyed(activity: Activity) {
- super.onActivityDestroyed(activity)
- viewCapture.stopCapture(activity.window.decorView)
- }
- }
-
- override fun succeeded(description: Description) = cleanup()
+ override fun onActivityDestroyed(activity: Activity) {
+ super.onActivityDestroyed(activity)
+ viewCapture.stopCapture(activity.window.decorView)
+ }
+ }
- /** If the test fails, this function will output the ViewCapture information. */
- override fun failed(e: Throwable, description: Description) {
- super.failed(e, description)
+ val application = ApplicationProvider.getApplicationContext<Application>()
+ application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
- val testName = "${description.testClass.simpleName}.${description.methodName}"
- val application: Application = ApplicationProvider.getApplicationContext()
- val zip = File(application.filesDir, "ViewCapture-$testName.zip")
+ try {
+ base.evaluate()
+ } finally {
+ application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
- ZipOutputStream(FileOutputStream(zip)).use {
- it.putNextEntry(ZipEntry("FS/data/misc/wmtrace/failed_test.vc"))
- viewCapture.dumpTo(it, ApplicationProvider.getApplicationContext())
- it.closeEntry()
+ // Clean up ViewCapture references here rather than in onActivityDestroyed so
+ // test code can access view hierarchy capture. onActivityDestroyed would delete
+ // view capture data before FailureWatcher could output it as a test artifact.
+ // This is on the main thread to avoid a race condition where the onDrawListener
+ // is removed while onDraw is running, resulting in an IllegalStateException.
+ MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) }
+ }
+ }
}
- cleanup()
-
- Log.d(
- TAG,
- "Failed $testName due to ${e::class.java.simpleName}.\n" +
- "\tUse go/web-hv to open dump file: \n\t\t${zip.absolutePath}"
- )
- }
-
- /**
- * Clean up ViewCapture references can't happen in onActivityDestroyed otherwise view
- * hierarchies would be erased before they could be outputted.
- *
- * This is on the main thread to avoid a race condition where the onDrawListener is removed
- * while onDraw is running, resulting in an IllegalStateException.
- */
- @AnyThread
- private fun cleanup() {
- MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) }
}
}