Fix internal tests from flickerlib
The flickerlib currently crashes on its internal tests. It fails due to:
- Invalid use of Mockito's @InjectMocks
- Orphaned layers with parent Id 0 or -1
- Null pointer exception on screen recording.
Test: atest FlickerLibTest
Change-Id: I934219ea749c7d0d24ab5f738de74cb242696178
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java b/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
index 74faca7..38b9004 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
+++ b/libraries/flicker/src/com/android/server/wm/flicker/LayersTrace.java
@@ -126,6 +126,33 @@
this.mRootLayers = rootLayers;
}
+ /**
+ * Determines the id of the root element.
+ *
+ * <p>On some files, such as the ones used in the FlickerLib testdata, the root nodes are
+ * those that have parent=0, on newer traces, the root nodes are those that have parent=-1
+ *
+ * <p>This function keeps compatibility with both new and older traces by searching for a
+ * known root layer (Display Root) and considering its parent Id as overall root.
+ */
+ private static Layer getRootLayer(SparseArray<Layer> layerMap) {
+ Layer knownRoot = null;
+ int numKeys = layerMap.size();
+ for (int i = 0; i < numKeys; ++i) {
+ Layer currentLayer = layerMap.valueAt(i);
+ if (currentLayer.getName().contains("Display Root")) {
+ knownRoot = currentLayer;
+ break;
+ }
+ }
+
+ if (knownRoot == null) {
+ throw new IllegalStateException("Display root layer not found.");
+ }
+
+ return layerMap.get(knownRoot.getParentId());
+ }
+
/** Constructs the layer hierarchy from a flattened list of layers. */
public static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos,
Consumer<Layer> orphanLayerCallback) {
@@ -156,13 +183,14 @@
newLayer.addParent(layerMap.get(parentId));
}
- // Remove root node (id = 0)
- orphans.remove(layerMap.get(-1));
+ // Remove root node
+ Layer rootLayer = getRootLayer(layerMap);
+ orphans.remove(rootLayer);
// Fail if we find orphan layers.
orphans.forEach(
orphan -> {
if (orphanLayerCallback != null) {
- // Workaround for b/141326137, ignore the existance of an orphan layer
+ // Workaround for b/141326137, ignore the existence of an orphan layer
orphanLayerCallback.accept(orphan);
return;
}
@@ -171,7 +199,7 @@
.stream()
.map(node -> Integer.toString(node.getId()))
.collect(Collectors.joining(", "));
- int orphanId = orphan.mChildren.get(0).mProto.parent;
+ int orphanId = orphan.mChildren.get(0).getParentId();
throw new RuntimeException(
"Failed to parse layers trace. Found orphan layers with parent "
+ "layer id:"
@@ -180,7 +208,7 @@
+ childNodes);
});
- return new Entry(timestamp, layerMap.get(-1).mChildren);
+ return new Entry(timestamp, rootLayer.mChildren);
}
/** Extracts {@link Rect} from {@link RectProto}. */
@@ -363,6 +391,18 @@
return mProto.id;
}
+ public int getParentId() {
+ return mProto.parent;
+ }
+
+ public String getName() {
+ if (mProto != null) {
+ return mProto.name;
+ }
+
+ return "";
+ }
+
public boolean isActiveBufferEmpty() {
return this.mProto.activeBuffer == null
|| this.mProto.activeBuffer.height == 0
diff --git a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
index 8b138aa..feedee7 100644
--- a/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
+++ b/libraries/flicker/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
@@ -22,8 +22,6 @@
import android.util.Log;
-import androidx.annotation.VisibleForTesting;
-
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -31,38 +29,36 @@
/** Captures screen contents and saves it as a mp4 video file. */
public class ScreenRecorder implements ITransitionMonitor {
- @VisibleForTesting
- public static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
-
private static final String TAG = "FLICKER";
private int mWidth;
private int mHeight;
+ private Path mOutputPath;
private Thread mRecorderThread;
public ScreenRecorder() {
- this(720, 1280);
+ this(720, 1280, OUTPUT_DIR.resolve("transition.mp4"));
}
- public ScreenRecorder(int width, int height) {
+ public ScreenRecorder(int width, int height, Path outputPath) {
mWidth = width;
mHeight = height;
+ mOutputPath = outputPath;
}
- @VisibleForTesting
- public static Path getPath(String testTag) {
- return OUTPUT_DIR.resolve(testTag + ".mp4");
+ public Path getPath() {
+ return mOutputPath;
}
@Override
public void start() {
- OUTPUT_DIR.toFile().mkdirs();
+ mOutputPath.getParent().toFile().mkdirs();
String command =
String.format(
Locale.getDefault(),
"screenrecord --size %dx%d %s",
mWidth,
mHeight,
- DEFAULT_OUTPUT_PATH);
+ mOutputPath);
mRecorderThread =
new Thread(
() -> {
@@ -87,13 +83,14 @@
@Override
public Path save(String testTag) {
- if (!Files.exists(DEFAULT_OUTPUT_PATH)) {
- Log.w(TAG, "No video file found on " + DEFAULT_OUTPUT_PATH);
+ if (!Files.exists(mOutputPath)) {
+ Log.w(TAG, "No video file found on " + mOutputPath);
return null;
}
try {
- Path targetPath = Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag), REPLACE_EXISTING);
+ Path targetPath =
+ Files.move(mOutputPath, OUTPUT_DIR.resolve(testTag + ".mp4"), REPLACE_EXISTING);
Log.i(TAG, "Video saved to " + targetPath.toString());
return targetPath;
} catch (IOException e) {
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 d75c58a..92c1d8c 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
@@ -58,7 +58,7 @@
@Mock private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
@Mock private LayersTraceMonitor mLayersTraceMonitorMock;
@Mock private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
- @InjectMocks private TransitionBuilder mTransitionBuilder;
+ @InjectMocks private TransitionBuilder mTransitionBuilder = TransitionRunner.newBuilder();
@Before
public void init() {
diff --git a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
index 967f565..01aa3c9 100644
--- a/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
+++ b/libraries/flicker/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
@@ -18,9 +18,6 @@
import static android.os.SystemClock.sleep;
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
-import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
-
import static com.google.common.truth.Truth.assertThat;
import androidx.test.runner.AndroidJUnit4;
@@ -33,6 +30,7 @@
import org.junit.runners.MethodSorters;
import java.io.File;
+import java.nio.file.Path;
/**
* Contains {@link ScreenRecorder} tests. To run this test: {@code atest
@@ -43,6 +41,7 @@
public class ScreenRecorderTest {
private static final String TEST_VIDEO_FILENAME = "test.mp4";
private ScreenRecorder mScreenRecorder;
+ private Path mSavedVideoPath = null;
@Before
public void setup() {
@@ -51,8 +50,10 @@
@After
public void teardown() {
- DEFAULT_OUTPUT_PATH.toFile().delete();
- getPath(TEST_VIDEO_FILENAME).toFile().delete();
+ mScreenRecorder.getPath().toFile().delete();
+ if (mSavedVideoPath != null) {
+ mSavedVideoPath.toFile().delete();
+ }
}
@Test
@@ -60,7 +61,7 @@
mScreenRecorder.start();
sleep(100);
mScreenRecorder.stop();
- File file = DEFAULT_OUTPUT_PATH.toFile();
+ File file = mScreenRecorder.getPath().toFile();
assertThat(file.exists()).isTrue();
}
@@ -69,8 +70,7 @@
mScreenRecorder.start();
sleep(100);
mScreenRecorder.stop();
- mScreenRecorder.save(TEST_VIDEO_FILENAME);
- File file = getPath(TEST_VIDEO_FILENAME).toFile();
+ File file = mScreenRecorder.save(TEST_VIDEO_FILENAME).toFile();
assertThat(file.exists()).isTrue();
}
}