Convert the flickerlib transition runner mechanism to kotlin
Test: atest FlickerLibTest
Change-Id: I9472cff3d3fd8eb9375e0c154960b1f5425391eb
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/LayersTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/LayersTraceSubject.kt
index 618a04d..7fbe80c 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/LayersTraceSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/LayersTraceSubject.kt
@@ -19,7 +19,6 @@
import android.graphics.Rect
import android.graphics.Region
import android.util.Log
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionResult.kt b/libraries/flicker/src/com/android/server/wm/flicker/TransitionResult.kt
new file mode 100644
index 0000000..4566f12
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/TransitionResult.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 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.server.wm.flicker
+
+import androidx.annotation.VisibleForTesting
+import java.nio.file.Files
+import java.nio.file.Path
+
+/** Stores paths to all test artifacts. */
+class TransitionResult @VisibleForTesting internal constructor(
+ val layersTracePath: Path?,
+ val layersTraceChecksum: String,
+ val windowManagerTracePath: Path?,
+ val windowManagerTraceChecksum: String,
+ val screenCaptureVideo: Path?,
+ val screenCaptureVideoChecksum: String
+) {
+ private var flaggedForSaving = true
+
+ val layersTrace: ByteArray
+ get() {
+ return try {
+ Files.readAllBytes(layersTracePath)
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+
+ val windowManagerTrace: ByteArray
+ get() {
+ return try {
+ Files.readAllBytes(windowManagerTracePath)
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+
+ fun flagForSaving() {
+ flaggedForSaving = true
+ }
+
+ fun canDelete(): Boolean {
+ return !flaggedForSaving
+ }
+
+ fun layersTraceExists(): Boolean {
+ return layersTracePath != null && Files.exists(layersTracePath)
+ }
+
+ fun windowManagerTraceExists(): Boolean {
+ return windowManagerTracePath != null && Files.exists(windowManagerTracePath)
+ }
+
+ fun screenCaptureVideoExists(): Boolean {
+ return screenCaptureVideo != null && Files.exists(screenCaptureVideo)
+ }
+
+ fun screenCaptureVideoPath(): Path? {
+ return screenCaptureVideo
+ }
+
+ fun delete() {
+ if (layersTraceExists()) {
+ layersTracePath?.toFile()?.delete()
+ }
+ if (windowManagerTraceExists()) {
+ windowManagerTracePath?.toFile()?.delete()
+ }
+ if (screenCaptureVideoExists()) {
+ screenCaptureVideo?.toFile()?.delete()
+ }
+ }
+}
\ No newline at end of file
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.java b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.java
deleted file mode 100644
index da2771b..0000000
--- a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm.flicker;
-
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.test.InstrumentationRegistry;
-
-import com.android.server.wm.flicker.monitor.ITransitionMonitor;
-import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
-import com.android.server.wm.flicker.monitor.ScreenRecorder;
-import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
-import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Builds and runs UI transitions capturing test artifacts.
- *
- * <p>User can compose a transition from simpler steps, specifying setup and teardown steps. During
- * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
- * stats can be captured.
- *
- * <pre>
- * Transition builder options:
- * {@link TransitionBuilder#run(Runnable)} run transition under test. Monitors will be started
- * before the transition and stopped after the transition is completed.
- * {@link TransitionBuilder#repeat(int)} repeat transitions under test multiple times recording
- * result for each run.
- * {@link TransitionBuilder#withTag(String)} specify a string identifier used to prefix logs and
- * artifacts generated.
- * {@link TransitionBuilder#runBeforeAll(Runnable)} run setup transitions once before all other
- * transition are run to set up an initial state on device.
- * {@link TransitionBuilder#runBefore(Runnable)} run setup transitions before each test transition
- * run.
- * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions after each test
- * transition.
- * {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions once after all
- * other transition are run.
- * {@link TransitionBuilder#includeJankyRuns()} disables {@link WindowAnimationFrameStatsMonitor}
- * to monitor janky frames. If janky frames are detected, then the test run is skipped. This
- * monitor is enabled by default.
- * {@link TransitionBuilder#skipLayersTrace()} disables {@link LayersTraceMonitor} used to
- * capture Layers trace during a transition. This monitor is enabled by default.
- * {@link TransitionBuilder#skipWindowManagerTrace()} disables {@link WindowManagerTraceMonitor}
- * used to capture WindowManager trace during a transition. This monitor is enabled by
- * default.
- * {@link TransitionBuilder#recordAllRuns()} records the screen contents and saves it to a file.
- * All the runs including setup and teardown transitions are included in the recording. This
- * monitor is used for debugging purposes.
- * {@link TransitionBuilder#recordEachRun()} records the screen contents during test transitions
- * and saves it to a file for each run. This monitor is used for debugging purposes.
- *
- * Example transition to capture WindowManager and Layers trace when opening a test app:
- * {@code
- * TransitionRunner.newBuilder()
- * .withTag("OpenTestAppFast")
- * .runBeforeAll(UiAutomationLib::wakeUp)
- * .runBeforeAll(UiAutomationLib::UnlockDevice)
- * .runBeforeAll(UiAutomationLib::openTestApp)
- * .runBefore(UiAutomationLib::closeTestApp)
- * .run(UiAutomationLib::openTestApp)
- * .runAfterAll(UiAutomationLib::closeTestApp)
- * .repeat(5)
- * .build()
- * .run();
- * }
- * </pre>
- */
-public class TransitionRunner {
- private static final String TAG = "FLICKER";
- private final ScreenRecorder mScreenRecorder;
- private final WindowManagerTraceMonitor mWmTraceMonitor;
- private final LayersTraceMonitor mLayersTraceMonitor;
- private final WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
-
- private final List<ITransitionMonitor> mAllRunsMonitors;
- private final List<ITransitionMonitor> mPerRunMonitors;
- private final List<Runnable> mBeforeAlls;
- private final List<Runnable> mBefores;
- private final List<Runnable> mTransitions;
- private final List<Runnable> mAfters;
- private final List<Runnable> mAfterAlls;
-
- private final int mIterations;
- private final String mTestTag;
-
- @Nullable private List<TransitionResult> mResults = null;
-
- private TransitionRunner(TransitionBuilder builder) {
- mScreenRecorder = builder.mScreenRecorder;
- mWmTraceMonitor = builder.mWmTraceMonitor;
- mLayersTraceMonitor = builder.mLayersTraceMonitor;
- mFrameStatsMonitor = builder.mFrameStatsMonitor;
-
- mAllRunsMonitors = builder.mAllRunsMonitors;
- mPerRunMonitors = builder.mPerRunMonitors;
- mBeforeAlls = builder.mBeforeAlls;
- mBefores = builder.mBefores;
- mTransitions = builder.mTransitions;
- mAfters = builder.mAfters;
- mAfterAlls = builder.mAfterAlls;
-
- mIterations = builder.mIterations;
- mTestTag = builder.mTestTag;
- }
-
- public static TransitionBuilder newBuilder(String outputDir) {
- return new TransitionBuilder(Paths.get(outputDir));
- }
-
- public static TransitionBuilder newBuilder() {
- return new TransitionBuilder();
- }
-
- /**
- * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor is
- * enabled, transitions with jank are skipped.
- *
- * @return itself
- */
- public TransitionRunner run() {
- mResults = new ArrayList<>();
- mAllRunsMonitors.forEach(ITransitionMonitor::start);
- mBeforeAlls.forEach(Runnable::run);
- for (int iteration = 0; iteration < mIterations; iteration++) {
- mBefores.forEach(Runnable::run);
- mPerRunMonitors.forEach(ITransitionMonitor::start);
- mTransitions.forEach(Runnable::run);
- mPerRunMonitors.forEach(ITransitionMonitor::stop);
- mAfters.forEach(Runnable::run);
- if (runJankFree() && mFrameStatsMonitor.jankyFramesDetected()) {
- String msg =
- String.format(
- Locale.getDefault(),
- "Skipping iteration %d/%d for test %s due to jank. %s",
- iteration,
- mIterations - 1,
- mTestTag,
- mFrameStatsMonitor.toString());
- Log.e(TAG, msg);
- continue;
- }
- mResults.add(saveResult(iteration));
- }
- mAfterAlls.forEach(Runnable::run);
- mAllRunsMonitors.forEach(
- monitor -> {
- monitor.stop();
- monitor.save(mTestTag);
- });
- return this;
- }
-
- /**
- * Returns a list of transition results.
- *
- * @return list of transition results.
- */
- public List<TransitionResult> getResults() {
- if (mResults == null) {
- throw new IllegalStateException("Results do not exist!");
- }
- return mResults;
- }
-
- /**
- * Deletes all transition results that are not marked for saving.
- *
- * @return list of transition results.
- */
- public void deleteResults() {
- if (mResults == null) {
- return;
- }
- mResults.stream().filter(TransitionResult::canDelete).forEach(TransitionResult::delete);
- mResults = null;
- }
-
- /**
- * Saves monitor results to file.
- *
- * @return object containing paths to test artifacts
- */
- private TransitionResult saveResult(int iteration) {
- Path windowTrace = null;
- String windowTraceChecksum = "";
- Path layerTrace = null;
- String layerTraceChecksum = "";
- Path screenCaptureVideo = null;
- String screenCaptureVideoChecksum = "";
-
- if (mPerRunMonitors.contains(mWmTraceMonitor)) {
- windowTrace = mWmTraceMonitor.save(mTestTag, iteration);
- windowTraceChecksum = mWmTraceMonitor.getChecksum();
- }
- if (mPerRunMonitors.contains(mLayersTraceMonitor)) {
- layerTrace = mLayersTraceMonitor.save(mTestTag, iteration);
- layerTraceChecksum = mLayersTraceMonitor.getChecksum();
- }
- if (mPerRunMonitors.contains(mScreenRecorder)) {
- screenCaptureVideo = mScreenRecorder.save(mTestTag, iteration);
- screenCaptureVideoChecksum = mScreenRecorder.getChecksum();
- }
- return new TransitionResult(
- layerTrace,
- layerTraceChecksum,
- windowTrace,
- windowTraceChecksum,
- screenCaptureVideo,
- screenCaptureVideoChecksum);
- }
-
- private boolean runJankFree() {
- return mPerRunMonitors.contains(mFrameStatsMonitor);
- }
-
- public String getTestTag() {
- return mTestTag;
- }
-
- /** Stores paths to all test artifacts. */
- @VisibleForTesting
- public static class TransitionResult {
- @Nullable private final Path layersTrace;
- private final String layersTraceChecksum;
- @Nullable private final Path windowManagerTrace;
- private final String windowManagerTraceChecksum;
- @Nullable private final Path screenCaptureVideo;
- private final String screenCaptureVideoChecksum;
- private boolean flaggedForSaving = true;
-
- public TransitionResult(
- @Nullable Path layersTrace,
- String layersTraceChecksum,
- @Nullable Path windowManagerTrace,
- String windowManagerTraceChecksum,
- @Nullable Path screenCaptureVideo,
- String screenCaptureVideoChecksum) {
- this.layersTrace = layersTrace;
- this.layersTraceChecksum = layersTraceChecksum;
- this.windowManagerTrace = windowManagerTrace;
- this.windowManagerTraceChecksum = windowManagerTraceChecksum;
- this.screenCaptureVideo = screenCaptureVideo;
- this.screenCaptureVideoChecksum = screenCaptureVideoChecksum;
- }
-
- public void flagForSaving() {
- flaggedForSaving = true;
- }
-
- public boolean canDelete() {
- return !flaggedForSaving;
- }
-
- public boolean layersTraceExists() {
- return layersTrace != null && layersTrace.toFile().exists();
- }
-
- public byte[] getLayersTrace() {
- try {
- return Files.readAllBytes(this.layersTrace);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public Path getLayersTracePath() {
- return layersTrace;
- }
-
- public String getLayersTraceChecksum() {
- return layersTraceChecksum;
- }
-
- public boolean windowManagerTraceExists() {
- return windowManagerTrace != null && windowManagerTrace.toFile().exists();
- }
-
- public byte[] getWindowManagerTrace() {
- try {
- return Files.readAllBytes(this.windowManagerTrace);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public Path getWindowManagerTracePath() {
- return windowManagerTrace;
- }
-
- public String getWindowManagerTraceChecksum() {
- return windowManagerTraceChecksum;
- }
-
- public boolean screenCaptureVideoExists() {
- return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
- }
-
- public Path screenCaptureVideoPath() {
- return screenCaptureVideo;
- }
-
- public String getScreenCaptureVideoChecksum() {
- return screenCaptureVideoChecksum;
- }
-
- public void delete() {
- if (layersTraceExists()) layersTrace.toFile().delete();
- if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
- if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
- }
- }
-
- /** Builds a {@link TransitionRunner} instance. */
- public static class TransitionBuilder {
- private ScreenRecorder mScreenRecorder;
- private WindowManagerTraceMonitor mWmTraceMonitor;
- private LayersTraceMonitor mLayersTraceMonitor;
- private WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
-
- private List<ITransitionMonitor> mAllRunsMonitors = new LinkedList<>();
- private List<ITransitionMonitor> mPerRunMonitors = new LinkedList<>();
- private List<Runnable> mBeforeAlls = new LinkedList<>();
- private List<Runnable> mBefores = new LinkedList<>();
- private List<Runnable> mTransitions = new LinkedList<>();
- private List<Runnable> mAfters = new LinkedList<>();
- private List<Runnable> mAfterAlls = new LinkedList<>();
-
- private boolean mRunJankFree = true;
- private boolean mCaptureWindowManagerTrace = true;
- private boolean mCaptureLayersTrace = true;
- private boolean mRecordEachRun = false;
- private int mIterations = 1;
- private String mTestTag = "";
-
- private boolean mRecordAllRuns = false;
-
- private TransitionBuilder(@NonNull Path outputDir) {
- mScreenRecorder = new ScreenRecorder();
- mWmTraceMonitor = new WindowManagerTraceMonitor(outputDir);
- mLayersTraceMonitor = new LayersTraceMonitor(outputDir);
- mFrameStatsMonitor =
- new WindowAnimationFrameStatsMonitor(
- InstrumentationRegistry.getInstrumentation());
- }
-
- public TransitionBuilder() {
- mScreenRecorder = new ScreenRecorder();
- mWmTraceMonitor = new WindowManagerTraceMonitor();
- mLayersTraceMonitor = new LayersTraceMonitor();
- mFrameStatsMonitor =
- new WindowAnimationFrameStatsMonitor(
- InstrumentationRegistry.getInstrumentation());
- }
-
- public TransitionRunner build() {
- if (mCaptureWindowManagerTrace) {
- mPerRunMonitors.add(mWmTraceMonitor);
- }
-
- if (mCaptureLayersTrace) {
- mPerRunMonitors.add(mLayersTraceMonitor);
- }
-
- if (mRunJankFree) {
- mPerRunMonitors.add(mFrameStatsMonitor);
- }
-
- if (mRecordAllRuns) {
- mAllRunsMonitors.add(mScreenRecorder);
- }
-
- if (mRecordEachRun) {
- mPerRunMonitors.add(mScreenRecorder);
- }
-
- return new TransitionRunner(this);
- }
-
- public TransitionBuilder runBeforeAll(Runnable runnable) {
- mBeforeAlls.add(runnable);
- return this;
- }
-
- public TransitionBuilder runBefore(Runnable runnable) {
- mBefores.add(runnable);
- return this;
- }
-
- public TransitionBuilder run(Runnable runnable) {
- mTransitions.add(runnable);
- return this;
- }
-
- public TransitionBuilder runAfter(Runnable runnable) {
- mAfters.add(runnable);
- return this;
- }
-
- public TransitionBuilder runAfterAll(Runnable runnable) {
- mAfterAlls.add(runnable);
- return this;
- }
-
- public TransitionBuilder repeat(int iterations) {
- mIterations = iterations;
- return this;
- }
-
- public TransitionBuilder skipWindowManagerTrace() {
- mCaptureWindowManagerTrace = false;
- return this;
- }
-
- public TransitionBuilder skipLayersTrace() {
- mCaptureLayersTrace = false;
- return this;
- }
-
- public TransitionBuilder includeJankyRuns() {
- mRunJankFree = false;
- return this;
- }
-
- public TransitionBuilder recordEachRun() {
- if (mRecordAllRuns) {
- throw new IllegalArgumentException("Invalid option with recordAllRuns");
- }
- mRecordEachRun = true;
- return this;
- }
-
- public TransitionBuilder recordAllRuns() {
- if (mRecordEachRun) {
- throw new IllegalArgumentException("Invalid option with recordEachRun");
- }
- mRecordAllRuns = true;
- return this;
- }
-
- public TransitionBuilder withTag(String testTag) {
- if (testTag.contains(" ")) {
- throw new IllegalArgumentException(
- "The test tag can not contain spaces since it "
- + "is a part of the file name");
- }
- mTestTag = testTag;
- return this;
- }
- }
-}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt
new file mode 100644
index 0000000..70367d2
--- /dev/null
+++ b/libraries/flicker/src/com/android/server/wm/flicker/TransitionRunner.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2020 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.server.wm.flicker
+
+import android.app.Instrumentation
+import android.util.Log
+import com.android.server.wm.flicker.monitor.ITransitionMonitor
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor
+import com.android.server.wm.flicker.monitor.ScreenRecorder
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor
+import java.nio.file.Path
+
+/**
+ * Builds and runs UI transitions capturing test artifacts.
+ *
+ *
+ * User can compose a transition from simpler steps, specifying setup and teardown steps. During
+ * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
+ * stats can be captured.
+ *
+ * <pre>
+ * Transition builder options:
+ * [TransitionBuilder.run] run transition under test. Monitors will be started
+ * before the transition and stopped after the transition is completed.
+ * [TransitionBuilder.repeat] repeat transitions under test multiple times recording
+ * result for each run.
+ * [TransitionBuilder.withTag] specify a string identifier used to prefix logs and
+ * artifacts generated.
+ * [TransitionBuilder.runBeforeAll] run setup transitions once before all other
+ * transition are run to set up an initial state on device.
+ * [TransitionBuilder.runBefore] run setup transitions before each test transition
+ * run.
+ * [TransitionBuilder.runAfter] run teardown transitions after each test
+ * transition.
+ * [TransitionBuilder.runAfter] run teardown transitions once after all
+ * other transition are run.
+ * [TransitionBuilder.includeJankyRuns] disables [WindowAnimationFrameStatsMonitor]
+ * to monitor janky frames. If janky frames are detected, then the test run is skipped. This
+ * monitor is enabled by default.
+ * [TransitionBuilder.skipLayersTrace] disables [LayersTraceMonitor] used to
+ * capture Layers trace during a transition. This monitor is enabled by default.
+ * [TransitionBuilder.skipWindowManagerTrace] disables [WindowManagerTraceMonitor]
+ * used to capture WindowManager trace during a transition. This monitor is enabled by
+ * default.
+ * [TransitionBuilder.recordAllRuns] records the screen contents and saves it to a file.
+ * All the runs including setup and teardown transitions are included in the recording. This
+ * monitor is used for debugging purposes.
+ * [TransitionBuilder.recordEachRun] records the screen contents during test transitions
+ * and saves it to a file for each run. This monitor is used for debugging purposes.
+ *
+ * Example transition to capture WindowManager and Layers trace when opening a test app:
+ * `TransitionRunner.newBuilder()
+ * .withTag("OpenTestAppFast")
+ * .runBeforeAll(UiAutomationLib::wakeUp)
+ * .runBeforeAll(UiAutomationLib::UnlockDevice)
+ * .runBeforeAll(UiAutomationLib::openTestApp)
+ * .runBefore(UiAutomationLib::closeTestApp)
+ * .run(UiAutomationLib::openTestApp)
+ * .runAfterAll(UiAutomationLib::closeTestApp)
+ * .repeat(5)
+ * .build()
+ * .run();
+` *
+</pre> *
+ */
+class TransitionRunner private constructor(
+ private val screenRecorder: ScreenRecorder,
+ private val wmTraceMonitor: WindowManagerTraceMonitor,
+ private val layersTraceMonitor: LayersTraceMonitor,
+ private val frameStatsMonitor: WindowAnimationFrameStatsMonitor,
+ private val allRunsMonitors: List<ITransitionMonitor>,
+ private val perRunMonitors: List<ITransitionMonitor>,
+ private val beforeAlls: List<() -> Any>,
+ private val befores: List<() -> Any>,
+ private val transitions: List<() -> Any>,
+ private val afters: List<() -> Any>,
+ private val afterAlls: List<() -> Any>,
+ private val iterations: Int,
+ val testTag: String
+) {
+ private val _results = mutableListOf<TransitionResult>()
+
+ /**
+ * Returns a list of transition results.
+ *
+ * @return list of transition results.
+ */
+ val results: List<TransitionResult>
+ get() {
+ check(_results.isNotEmpty()) { "Results do not exist!" }
+ return _results
+ }
+
+ /**
+ * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor is
+ * enabled, transitions with jank are skipped.
+ *
+ * @return itself
+ */
+ fun run(): TransitionRunner {
+ allRunsMonitors.forEach { it.start() }
+ beforeAlls.forEach { it.invoke() }
+ for (iteration in 0 until iterations) {
+ befores.forEach { it.invoke() }
+ perRunMonitors.forEach { it.start() }
+ transitions.forEach { it.invoke() }
+ perRunMonitors.forEach { it.stop() }
+ afters.forEach { it.invoke() }
+ if (runJankFree() && frameStatsMonitor.jankyFramesDetected()) {
+ Log.e(FLICKER_TAG, "Skipping iteration ${iteration}/${iterations - 1} " +
+ "for test $testTag due to jank. $frameStatsMonitor")
+ continue
+ }
+ _results.add(saveResult(iteration))
+ }
+ afterAlls.forEach { it.invoke() }
+ allRunsMonitors.forEach {
+ it.stop()
+ it.save(testTag)
+ }
+ return this
+ }
+
+ /**
+ * Deletes all transition results that are not marked for saving.
+ *
+ * @return list of transition results.
+ */
+ fun deleteResults() {
+ if (_results.isEmpty()) {
+ return
+ }
+ _results.filter { it.canDelete() }.forEach { it.delete() }
+ _results.clear()
+ }
+
+ /**
+ * Saves monitor results to file.
+ *
+ * @return object containing paths to test artifacts
+ */
+ private fun saveResult(iteration: Int): TransitionResult {
+ var windowTrace: Path? = null
+ var windowTraceChecksum = ""
+ var layerTrace: Path? = null
+ var layerTraceChecksum = ""
+ var screenCaptureVideo: Path? = null
+ var screenCaptureVideoChecksum = ""
+
+ if (perRunMonitors.contains(wmTraceMonitor)) {
+ windowTrace = wmTraceMonitor.save(testTag, iteration)
+ windowTraceChecksum = wmTraceMonitor.checksum
+ }
+ if (perRunMonitors.contains(layersTraceMonitor)) {
+ layerTrace = layersTraceMonitor.save(testTag, iteration)
+ layerTraceChecksum = layersTraceMonitor.checksum
+ }
+ if (perRunMonitors.contains(screenRecorder)) {
+ screenCaptureVideo = screenRecorder.save(testTag, iteration)
+ screenCaptureVideoChecksum = screenRecorder.checksum
+ }
+ return TransitionResult(
+ layerTrace,
+ layerTraceChecksum,
+ windowTrace,
+ windowTraceChecksum,
+ screenCaptureVideo,
+ screenCaptureVideoChecksum)
+ }
+
+ private fun runJankFree(): Boolean {
+ return perRunMonitors.contains(frameStatsMonitor)
+ }
+
+ /** Builds a [TransitionRunner] instance. */
+ data class TransitionBuilder @JvmOverloads constructor(
+ private val instrumentation: Instrumentation,
+ private val outputDir: Path? = null
+ ) {
+ private var runJankFree = true
+ private var captureWindowManagerTrace = true
+ private var captureLayersTrace = true
+ private var recordEachRun = false
+ private var iterations = 1
+ private var testTag = ""
+ private var recordAllRuns = false
+
+ private val allRunsMonitors= mutableListOf<ITransitionMonitor>()
+ private val perRunMonitors= mutableListOf<ITransitionMonitor>()
+ private val beforeAlls= mutableListOf<() -> Any>()
+ private val befores= mutableListOf<() -> Any>()
+ private val transitions= mutableListOf<() -> Any>()
+ private val afters= mutableListOf<() -> Any>()
+ private val afterAlls= mutableListOf<() -> Any>()
+
+ fun build(): TransitionRunner {
+ val wmTraceMonitor = WindowManagerTraceMonitor(outputDir)
+ val layersTraceMonitor = LayersTraceMonitor(outputDir)
+ val frameStatsMonitor = WindowAnimationFrameStatsMonitor(instrumentation)
+ val screenRecorder = ScreenRecorder()
+ if (captureWindowManagerTrace) {
+ perRunMonitors.add(wmTraceMonitor)
+ }
+ if (captureLayersTrace) {
+ perRunMonitors.add(layersTraceMonitor)
+ }
+ if (runJankFree) {
+ perRunMonitors.add(frameStatsMonitor)
+ }
+ if (recordAllRuns) {
+ allRunsMonitors.add(screenRecorder)
+ }
+ if (recordEachRun) {
+ perRunMonitors.add(screenRecorder)
+ }
+ return TransitionRunner(
+ screenRecorder,
+ wmTraceMonitor,
+ layersTraceMonitor,
+ frameStatsMonitor,
+ allRunsMonitors,
+ perRunMonitors,
+ beforeAlls,
+ befores,
+ transitions,
+ afters,
+ afterAlls,
+ iterations,
+ testTag
+ )
+ }
+
+ /**
+ * Execute [runnable] before activating the winscope tracing.
+ *
+ * Repeats this execution for each iteration.
+ */
+ fun runBeforeAll(runnable: Runnable) = apply{ runBeforeAll { runnable.run() } }
+
+ /**
+ * Execute [command] before activating the winscope tracing.
+ *
+ * Repeats this execution for each iteration.
+ */
+ fun runBeforeAll(command: () -> Any) = apply { beforeAlls.add(command) }
+
+ /**
+ * Execute [runnable] before activating the winscope tracing.
+ *
+ * Repeats this execution once, irrespectively of the number of iterations
+ */
+ fun runBefore(runnable: Runnable) = apply { runBefore { runnable.run() } }
+
+ /**
+ * Execute [command] before activating the winscope tracing.
+ *
+ * Repeats this execution once, irrespectively of the number of iterations
+ */
+ fun runBefore(command: () -> Any) = apply { befores.add(command) }
+
+ /**
+ * Execute [runnable] while tracing is active.
+ */
+ fun run(runnable: Runnable) = apply { run { runnable.run() } }
+
+ /**
+ * Execute [command] while tracing is active.
+ */
+ fun run(command: () -> Any) = apply { transitions.add(command) }
+
+ /**
+ * Execute [runnable] after deactivating the winscope tracing.
+ *
+ * Repeats this execution once, irrespectively of the number of iterations
+ */
+ fun runAfter(runnable: Runnable) = apply {runAfter { runnable.run() } }
+
+ /**
+ * Execute [command] after deactivating the winscope tracing.
+ *
+ * Repeats this execution once, irrespectively of the number of iterations
+ */
+ fun runAfter(command: () -> Any) = apply { afters.add(command) }
+
+ /**
+ * Execute [runnable] after deactivating the winscope tracing.
+ *
+ * Repeats this execution for each iteration.
+ */
+ fun runAfterAll(runnable: Runnable) = apply { runAfterAll { runnable } }
+
+ /**
+ * Execute [command] after deactivating the winscope tracing.
+ *
+ * Repeats this execution for each iteration.
+ */
+ fun runAfterAll(command: () -> Any) = apply { afterAlls.add(command) }
+
+ /**
+ * Run the test [iterations] times
+ */
+ fun repeat(iterations: Int) = apply { this.iterations = iterations }
+
+ fun skipWindowManagerTrace() = apply { captureWindowManagerTrace = false }
+
+ fun skipLayersTrace() = apply { captureLayersTrace = false }
+
+ fun includeJankyRuns() = apply { runJankFree = false }
+
+ /**
+ * Record a video of each run
+ */
+ fun recordEachRun() = apply {
+ require(!recordAllRuns) { "Invalid option with recordAllRuns" }
+ recordEachRun = true
+ }
+
+ /**
+ * Record a single video for all runs
+ */
+ fun recordAllRuns() = apply {
+ require(!recordEachRun) { "Invalid option with recordEachRun" }
+ recordAllRuns = true
+ }
+
+ fun withTag(testTag: String) = apply {
+ require(!testTag.contains(" ")) {
+ "The test tag can not contain spaces since it is a part of the file name"
+ }
+ this.testTag = testTag
+ }
+ }
+}
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/WmTraceSubject.kt b/libraries/flicker/src/com/android/server/wm/flicker/WmTraceSubject.kt
index d4d5396..b31300f 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/WmTraceSubject.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/WmTraceSubject.kt
@@ -16,7 +16,6 @@
package com.android.server.wm.flicker
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
import com.google.common.truth.Subject.Factory
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
index fdb29a8..4494d04 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.kt
@@ -22,8 +22,8 @@
/** Captures Layers trace from SurfaceFlinger. */
open class LayersTraceMonitor @JvmOverloads constructor(
- outputDir: Path = OUTPUT_DIR
-) : TraceMonitor(outputDir, "layers_trace.pb") {
+ outputDir: Path? = OUTPUT_DIR
+) : TraceMonitor(outputDir ?: OUTPUT_DIR, "layers_trace.pb") {
private val windowManager= WindowManagerGlobal.getWindowManagerService()
override fun start() {
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
index a2fb367..4d03cb3 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.kt
@@ -22,8 +22,8 @@
/** Captures WindowManager trace from WindowManager. */
open class WindowManagerTraceMonitor @JvmOverloads constructor(
- outputDir: Path = OUTPUT_DIR
-) : TraceMonitor(outputDir, "wm_trace.pb") {
+ outputDir: Path? = OUTPUT_DIR
+) : TraceMonitor(outputDir ?: OUTPUT_DIR, "wm_trace.pb") {
private val windowManager= WindowManagerGlobal.getWindowManagerService()
override fun start() {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
index 10562d9..928b278 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
@@ -26,10 +26,9 @@
import android.os.Environment;
+import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
-import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
import com.android.server.wm.flicker.monitor.ScreenRecorder;
import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
@@ -57,7 +56,8 @@
@Mock private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
@Mock private LayersTraceMonitor mLayersTraceMonitorMock;
@Mock private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
- @InjectMocks private TransitionBuilder mTransitionBuilder = TransitionRunner.newBuilder();
+ @InjectMocks private TransitionRunner.TransitionBuilder mTransitionBuilder =
+ new TransitionRunner.TransitionBuilder(InstrumentationRegistry.getInstrumentation());
@Before
public void init() {
@@ -66,7 +66,7 @@
@Test
public void transitionsRunInOrder() {
- TransitionRunner.newBuilder()
+ mTransitionBuilder
.runBeforeAll(mTransitionsMock::turnOnDevice)
.runBefore(mTransitionsMock::openApp)
.run(mTransitionsMock::performMagic)
@@ -87,7 +87,7 @@
@Test
public void canCombineTransitions() {
- TransitionRunner.newBuilder()
+ mTransitionBuilder
.runBeforeAll(mTransitionsMock::turnOnDevice)
.runBeforeAll(mTransitionsMock::turnOnDevice)
.runBefore(mTransitionsMock::openApp)
@@ -114,7 +114,7 @@
@Test
public void emptyTransitionPasses() {
List<TransitionResult> results =
- TransitionRunner.newBuilder()
+ mTransitionBuilder
.skipLayersTrace()
.skipWindowManagerTrace()
.build()
@@ -129,7 +129,7 @@
@Test
public void canRepeatTransitions() {
final int wantedNumberOfInvocations = 10;
- TransitionRunner.newBuilder()
+ mTransitionBuilder
.runBeforeAll(mTransitionsMock::turnOnDevice)
.runBefore(mTransitionsMock::openApp)
.run(mTransitionsMock::performMagic)