Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa809166 authored by Stefan Andonian's avatar Stefan Andonian
Browse files

Added ViewCapture to all tests which implement AbstractLauncherUiTest.

This enables developers to watch failed tests in classes like
TaplTestsQuickstep in the go/web-hv tool.

Bug: 242867462
Test: Failed a test and verified that the TimeLapse bugreport entry
showed up properly in the go/web-hv tool.

Change-Id: Ic89af2a0e7102db52c52ddc668607a81c4809ed6
parent 4d559a6c
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.launcher3.util.rule.ViewCaptureRule;
import com.android.quickstep.views.RecentsView;

import org.junit.After;
@@ -115,10 +116,12 @@ public class FallbackRecentsTest {
            Utilities.enableRunningInTestHarnessForTests();
        }

        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
        mOrderSensitiveRules = RuleChain
                .outerRule(new SamplerRule())
                .around(new NavigationModeSwitchRule(mLauncher))
                .around(new FailureWatcher(mDevice, mLauncher));
                .around(viewCaptureRule)
                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));

        mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
                getHomeIntentInPackage(context),
+2 −1
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ filegroup {
      "src/com/android/launcher3/util/WidgetUtils.java",
      "src/com/android/launcher3/util/rule/FailureWatcher.java",
      "src/com/android/launcher3/util/rule/LauncherActivityRule.java",
      "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
      "src/com/android/launcher3/util/rule/SamplerRule.java",
      "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
      "src/com/android/launcher3/util/rule/ShellCommandRule.java",
+6 −4
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import com.android.launcher3.util.rule.SamplerRule;
import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.launcher3.util.rule.ViewCaptureRule;

import org.junit.After;
import org.junit.Assert;
@@ -215,14 +216,15 @@ public abstract class AbstractLauncherUiTest {
    }

    protected TestRule getRulesInsideActivityMonitor() {
        final ViewCaptureRule viewCaptureRule = new ViewCaptureRule();
        final RuleChain inner = RuleChain
                .outerRule(new PortraitLandscapeRunner(this))
                .around(new FailureWatcher(mDevice, mLauncher));
                .around(viewCaptureRule)
                .around(new FailureWatcher(mDevice, mLauncher, viewCaptureRule.getViewCapture()));

        return TestHelpers.isInLauncherProcess()
                ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher())
                .around(inner) :
                inner;
                ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner)
                : inner;
    }

    @Rule
+24 −5
Original line number Diff line number Diff line
@@ -7,8 +7,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;

@@ -29,10 +33,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
@@ -82,7 +90,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) {
@@ -93,8 +101,12 @@ public class FailureWatcher extends TestWatcher {

    public static void onError(LauncherInstrumentation launcher, Description description,
            Throwable e) {
        final UiDevice device = launcher.getDevice();
        if (device == null) return;
        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");

@@ -109,13 +121,20 @@ public class FailureWatcher extends TestWatcher {
            out.putNextEntry(new ZipEntry("visible_windows.zip"));
            dumpCommand("cmd window dump-visible-window-views", out);
            out.closeEntry();
        } catch (IOException ex) {

            if (viewCapture != null) {
                out.putNextEntry(new ZipEntry("FS/data/misc/wmtrace/failed_test.vc"));
                viewCapture.dumpTo(out, ApplicationProvider.getApplicationContext());
                out.closeEntry();
            }
        } catch (Exception ignored) {
        }

        Log.e(TAG, "Failed test " + description.getMethodName()
                + ",\nscreenshot will be saved to " + sceenshot
                + ",\nUI dump at: " + hierarchy
                + " (use go/web-hv to open the dump file)", e);
        final UiDevice device = launcher.getDevice();
        device.takeScreenshot(sceenshot);

        // Dump accessibility hierarchy
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.android.launcher3.util.rule

import android.app.Activity
import android.app.Application
import android.media.permission.SafeCloseable
import android.os.Bundle
import androidx.test.core.app.ApplicationProvider
import com.android.app.viewcapture.SimpleViewCapture
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement

/**
 * 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")

    override fun apply(base: Statement, description: Description): Statement {
        return object : Statement() {
            override fun evaluate() {
                val windowListenerCloseables = mutableListOf<SafeCloseable>()

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

                val application = ApplicationProvider.getApplicationContext<Application>()
                application.registerActivityLifecycleCallbacks(lifecycleCallbacks)

                try {
                    base.evaluate()
                } finally {
                    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)

                    // 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.
                    windowListenerCloseables.onEach(SafeCloseable::close)
                }
            }
        }
    }
}
+1 −1

File changed.

Contains only whitespace changes.

+1 −1

File changed.

Contains only whitespace changes.

Loading