Loading quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +2 −3 Original line number Diff line number Diff line Loading @@ -116,12 +116,11 @@ public class FallbackRecentsTest { Utilities.enableRunningInTestHarnessForTests(); } final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); mOrderSensitiveRules = RuleChain .outerRule(new SamplerRule()) .around(new NavigationModeSwitchRule(mLauncher)) .around(viewCaptureRule) .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); .around(new ViewCaptureRule()) .around(new FailureWatcher(mDevice, mLauncher)); mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), Loading tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +2 −3 Original line number Diff line number Diff line Loading @@ -216,11 +216,10 @@ public abstract class AbstractLauncherUiTest { } protected TestRule getRulesInsideActivityMonitor() { final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) .around(viewCaptureRule) .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); .around(new ViewCaptureRule()) .around(new FailureWatcher(mDevice, mLauncher)); return TestHelpers.isInLauncherProcess() ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner) Loading tests/src/com/android/launcher3/util/rule/FailureWatcher.java +2 −22 Original line number Diff line number Diff line Loading @@ -6,12 +6,8 @@ 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; Loading @@ -32,14 +28,10 @@ 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, @NonNull ViewCapture viewCapture) { public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) { mDevice = device; mLauncher = launcher; mViewCapture = viewCapture; } @Override Loading Loading @@ -71,7 +63,7 @@ public class FailureWatcher extends TestWatcher { @Override protected void failed(Throwable e, Description description) { onError(mLauncher, description, e, mViewCapture); onError(mLauncher, description, e); } static File diagFile(Description description, String prefix, String ext) { Loading @@ -82,12 +74,6 @@ 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"); Loading @@ -102,12 +88,6 @@ 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) { } Loading tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt +73 −34 Original line number Diff line number Diff line Loading @@ -19,29 +19,51 @@ 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 org.junit.rules.TestRule 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.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 : TestRule { val viewCapture = SimpleViewCapture("test-view-capture") class ViewCaptureRule : TestWatcher() { private val viewCapture = SimpleViewCapture("test-view-capture") private val windowListenerCloseables = mutableListOf<SafeCloseable>() override fun apply(base: Statement, description: Description): Statement { val testWatcherStatement = super.apply(base, description) return object : Statement() { override fun evaluate() { val windowListenerCloseables = mutableListOf<SafeCloseable>() val lifecycleCallbacks = createLifecycleCallbacks(description) with(ApplicationProvider.getApplicationContext<Application>()) { registerActivityLifecycleCallbacks(lifecycleCallbacks) try { testWatcherStatement.evaluate() } finally { unregisterActivityLifecycleCallbacks(lifecycleCallbacks) } } } } } val lifecycleCallbacks = private fun createLifecycleCallbacks(description: Description) = object : ActivityLifecycleCallbacksAdapter { override fun onActivityCreated(activity: Activity, bundle: Bundle?) { super.onActivityCreated(activity, bundle) Loading @@ -59,22 +81,39 @@ class ViewCaptureRule : TestRule { } } val application = ApplicationProvider.getApplicationContext<Application>() application.registerActivityLifecycleCallbacks(lifecycleCallbacks) override fun succeeded(description: Description) = cleanup() try { base.evaluate() } finally { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) /** If the test fails, this function will output the ViewCapture information. */ override fun failed(e: Throwable, description: Description) { super.failed(e, description) // 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) } } val testName = "${description.testClass.simpleName}.${description.methodName}" val application: Application = ApplicationProvider.getApplicationContext() val zip = File(application.filesDir, "ViewCapture-$testName.zip") ZipOutputStream(FileOutputStream(zip)).use { it.putNextEntry(ZipEntry("FS/data/misc/wmtrace/failed_test.vc")) viewCapture.dumpTo(it, ApplicationProvider.getApplicationContext()) it.closeEntry() } 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) } } } Loading
quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +2 −3 Original line number Diff line number Diff line Loading @@ -116,12 +116,11 @@ public class FallbackRecentsTest { Utilities.enableRunningInTestHarnessForTests(); } final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); mOrderSensitiveRules = RuleChain .outerRule(new SamplerRule()) .around(new NavigationModeSwitchRule(mLauncher)) .around(viewCaptureRule) .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); .around(new ViewCaptureRule()) .around(new FailureWatcher(mDevice, mLauncher)); mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), Loading
tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +2 −3 Original line number Diff line number Diff line Loading @@ -216,11 +216,10 @@ public abstract class AbstractLauncherUiTest { } protected TestRule getRulesInsideActivityMonitor() { final ViewCaptureRule viewCaptureRule = new ViewCaptureRule(); final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) .around(viewCaptureRule) .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture())); .around(new ViewCaptureRule()) .around(new FailureWatcher(mDevice, mLauncher)); return TestHelpers.isInLauncherProcess() ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner) Loading
tests/src/com/android/launcher3/util/rule/FailureWatcher.java +2 −22 Original line number Diff line number Diff line Loading @@ -6,12 +6,8 @@ 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; Loading @@ -32,14 +28,10 @@ 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, @NonNull ViewCapture viewCapture) { public FailureWatcher(UiDevice device, LauncherInstrumentation launcher) { mDevice = device; mLauncher = launcher; mViewCapture = viewCapture; } @Override Loading Loading @@ -71,7 +63,7 @@ public class FailureWatcher extends TestWatcher { @Override protected void failed(Throwable e, Description description) { onError(mLauncher, description, e, mViewCapture); onError(mLauncher, description, e); } static File diagFile(Description description, String prefix, String ext) { Loading @@ -82,12 +74,6 @@ 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"); Loading @@ -102,12 +88,6 @@ 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) { } Loading
tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt +73 −34 Original line number Diff line number Diff line Loading @@ -19,29 +19,51 @@ 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 org.junit.rules.TestRule 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.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 : TestRule { val viewCapture = SimpleViewCapture("test-view-capture") class ViewCaptureRule : TestWatcher() { private val viewCapture = SimpleViewCapture("test-view-capture") private val windowListenerCloseables = mutableListOf<SafeCloseable>() override fun apply(base: Statement, description: Description): Statement { val testWatcherStatement = super.apply(base, description) return object : Statement() { override fun evaluate() { val windowListenerCloseables = mutableListOf<SafeCloseable>() val lifecycleCallbacks = createLifecycleCallbacks(description) with(ApplicationProvider.getApplicationContext<Application>()) { registerActivityLifecycleCallbacks(lifecycleCallbacks) try { testWatcherStatement.evaluate() } finally { unregisterActivityLifecycleCallbacks(lifecycleCallbacks) } } } } } val lifecycleCallbacks = private fun createLifecycleCallbacks(description: Description) = object : ActivityLifecycleCallbacksAdapter { override fun onActivityCreated(activity: Activity, bundle: Bundle?) { super.onActivityCreated(activity, bundle) Loading @@ -59,22 +81,39 @@ class ViewCaptureRule : TestRule { } } val application = ApplicationProvider.getApplicationContext<Application>() application.registerActivityLifecycleCallbacks(lifecycleCallbacks) override fun succeeded(description: Description) = cleanup() try { base.evaluate() } finally { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) /** If the test fails, this function will output the ViewCapture information. */ override fun failed(e: Throwable, description: Description) { super.failed(e, description) // 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) } } val testName = "${description.testClass.simpleName}.${description.methodName}" val application: Application = ApplicationProvider.getApplicationContext() val zip = File(application.filesDir, "ViewCapture-$testName.zip") ZipOutputStream(FileOutputStream(zip)).use { it.putNextEntry(ZipEntry("FS/data/misc/wmtrace/failed_test.vc")) viewCapture.dumpTo(it, ApplicationProvider.getApplicationContext()) it.closeEntry() } 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) } } }