diff options
author | Michael Rosenfeld <mrosenfeld@google.com> | 2021-12-06 16:35:20 -0800 |
---|---|---|
committer | Michael Rosenfeld <mrosenfeld@google.com> | 2021-12-07 17:08:56 -0800 |
commit | 27ca87215e3214b15f00a7190ff85a4f6599e5c3 (patch) | |
tree | a444ff1289398b44c8b4cc0924fc5b6caabc36ae | |
parent | d2c2aeef3bb56c1ef604b4b88aebcc17d0733476 (diff) | |
download | platform_testing-27ca87215e3214b15f00a7190ff85a4f6599e5c3.tar.gz |
Add an option to control screen recording quality.
Bug: 150140232
Test: added
Change-Id: Ie4fd062a0c52cc4ad5a53c5ef9252d2eeb39d628
2 files changed, 154 insertions, 5 deletions
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/ScreenRecordCollector.java b/libraries/device-collectors/src/main/java/android/device/collectors/ScreenRecordCollector.java index 895ddd14a..76fc1f945 100644 --- a/libraries/device-collectors/src/main/java/android/device/collectors/ScreenRecordCollector.java +++ b/libraries/device-collectors/src/main/java/android/device/collectors/ScreenRecordCollector.java @@ -18,6 +18,7 @@ package android.device.collectors; import static org.junit.Assert.assertNotNull; import android.device.collectors.annotations.OptionClass; +import android.os.Bundle; import android.os.SystemClock; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -41,6 +42,12 @@ import org.junit.runner.Description; */ @OptionClass(alias = "screen-record-collector") public class ScreenRecordCollector extends BaseMetricListener { + // Quality is relative to screen resolution. + // * "medium" is 1/2 the resolution. + // * "low" is 1/8 the resolution. + // * Otherwise, use the resolution. + @VisibleForTesting static final String QUALITY_ARG = "video-quality"; + // Maximum parts per test (each part is <= 3min). @VisibleForTesting static final int MAX_RECORDING_PARTS = 5; private static final long VIDEO_TAIL_BUFFER = 500; @@ -51,13 +58,59 @@ public class ScreenRecordCollector extends BaseMetricListener { private RecordingThread mCurrentThread; + private String mVideoDimensions; + // Tracks the test iterations to ensure that each failure gets unique filenames. // Key: test description; value: number of iterations. private Map<String, Integer> mTestIterations = new HashMap<String, Integer>(); + public ScreenRecordCollector() { + super(); + } + + /** Constructors for overriding instrumentation arguments only. */ + @VisibleForTesting + ScreenRecordCollector(Bundle args) { + super(args); + } + @Override public void onTestRunStart(DataRecord runData, Description description) { mDestDir = createAndEmptyDirectory(OUTPUT_DIR); + + try { + long scaleDown = 1; + switch (getArgsBundle().getString(QUALITY_ARG, "default")) { + case "high": + scaleDown = 1; + break; + + case "medium": + scaleDown = 2; + break; + + case "low": + scaleDown = 8; + break; + + default: + return; + } + + // Display metrics isn't the absolute size, so use "wm size". + String[] dims = + getDevice() + .executeShellCommand("wm size") + .substring("Physical size: ".length()) + .trim() + .split("x"); + int width = Integer.parseInt(dims[0]); + int height = Integer.parseInt(dims[1]); + mVideoDimensions = String.format("%dx%d", width / scaleDown, height / scaleDown); + Log.v(getTag(), String.format("Using video dimensions: %s", mVideoDimensions)); + } catch (Exception e) { + Log.e(getTag(), "Failed to query the device dimensions. Using default.", e); + } } @Override @@ -167,9 +220,15 @@ public class ScreenRecordCollector extends BaseMetricListener { // Make sure not to block on this background command in the main thread so // that the test continues to run, but block in this thread so it does not // trigger a new screen recording session before the prior one completes. + String dimensionsOpt = + mVideoDimensions == null + ? "" + : String.format("--size=%s", mVideoDimensions); getDevice() .executeShellCommand( - String.format("screenrecord %s", output.getAbsolutePath())); + String.format( + "screenrecord %s %s", + dimensionsOpt, output.getAbsolutePath())); } } catch (IOException e) { throw new RuntimeException("Caught exception while screen recording."); diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/ScreenRecordCollectorTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/ScreenRecordCollectorTest.java index 664a2947d..ce1bfa55c 100644 --- a/libraries/device-collectors/src/test/java/android/device/collectors/ScreenRecordCollectorTest.java +++ b/libraries/device-collectors/src/test/java/android/device/collectors/ScreenRecordCollectorTest.java @@ -21,8 +21,10 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.endsWith; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.matches; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -85,8 +87,13 @@ public class ScreenRecordCollectorTest { } } - private ScreenRecordCollector initListener() throws IOException { - ScreenRecordCollector listener = spy(new ScreenRecordCollector()); + private ScreenRecordCollector initListener(Bundle b) throws IOException { + ScreenRecordCollector listener; + if (b != null) { + listener = spy(new ScreenRecordCollector(b)); + } else { + listener = spy(new ScreenRecordCollector()); + } listener.setInstrumentation(mInstrumentation); doReturn(mLogDir).when(listener).createAndEmptyDirectory(anyString()); doReturn(0L).when(listener).getTailBuffer(); @@ -102,7 +109,7 @@ public class ScreenRecordCollectorTest { */ @Test public void testScreenRecord() throws Exception { - mListener = initListener(); + mListener = initListener(null); // Verify output directories are created on test run start. mListener.testRunStarted(mRunDesc); @@ -158,7 +165,7 @@ public class ScreenRecordCollectorTest { /** Test that screen recording is properly done for multiple tests and labels iterations. */ @Test public void testScreenRecord_multipleTests() throws Exception { - mListener = initListener(); + mListener = initListener(null); // Run through a sequence of `NUM_TEST_CASE` failing tests. mListener.testRunStarted(mRunDesc); @@ -190,4 +197,87 @@ public class ScreenRecordCollectorTest { } } } + + /** Test that quality options (high) are respected by screen recordings. */ + @Test + public void testScreenRecord_qualityHigh() throws Exception { + Bundle args = new Bundle(); + args.putString(ScreenRecordCollector.QUALITY_ARG, "high"); + mListener = initListener(args); + doReturn("Physical size: 1080x720 ").when(mDevice).executeShellCommand("wm size"); + + mListener.testRunStarted(mRunDesc); + mListener.testStarted(mTestDesc); + + // Delay verification by 100 ms to ensure the thread was started. + SystemClock.sleep(100); + verify(mDevice).executeShellCommand(matches("screenrecord --size=1080x720 .*video.mp4")); + } + + /** Test that quality options (medium) are respected by screen recordings. */ + @Test + public void testScreenRecord_qualityMedium() throws Exception { + Bundle args = new Bundle(); + args.putString(ScreenRecordCollector.QUALITY_ARG, "medium"); + mListener = initListener(args); + doReturn("Physical size: 1080x720 ").when(mDevice).executeShellCommand("wm size"); + + mListener.testRunStarted(mRunDesc); + mListener.testStarted(mTestDesc); + + // Delay verification by 100 ms to ensure the thread was started. + SystemClock.sleep(100); + verify(mDevice).executeShellCommand(matches("screenrecord --size=540x360 .*video.mp4")); + } + + /** Test that quality options (low) are respected by screen recordings. */ + @Test + public void testScreenRecord_qualityLow() throws Exception { + Bundle args = new Bundle(); + args.putString(ScreenRecordCollector.QUALITY_ARG, "low"); + mListener = initListener(args); + doReturn("Physical size: 1080x720 ").when(mDevice).executeShellCommand("wm size"); + + mListener.testRunStarted(mRunDesc); + mListener.testStarted(mTestDesc); + + // Delay verification by 100 ms to ensure the thread was started. + SystemClock.sleep(100); + verify(mDevice).executeShellCommand(matches("screenrecord --size=135x90 .*video.mp4")); + } + + /** Test that quality options (invalid) defaults to 1x. */ + @Test + public void testScreenRecord_qualityUnknown() throws Exception { + Bundle args = new Bundle(); + args.putString(ScreenRecordCollector.QUALITY_ARG, "other"); + mListener = initListener(args); + + mListener.testRunStarted(mRunDesc); + mListener.testStarted(mTestDesc); + + // Delay verification by 100 ms to ensure the thread was started. + SystemClock.sleep(100); + verify(mDevice, never()).executeShellCommand(matches("screenrecord.*size.*video.mp4")); + verify(mDevice, atLeastOnce()) + .executeShellCommand(not(matches("screenrecord .*video.mp4"))); + } + + /** Test that unexpected wm size contents defaults to unspecified size/quality option. */ + @Test + public void testScreenRecord_dimensionsInvalid() throws Exception { + Bundle args = new Bundle(); + args.putString(ScreenRecordCollector.QUALITY_ARG, "high"); + mListener = initListener(args); + doReturn("Physical size: axb ").when(mDevice).executeShellCommand("wm size"); + + mListener.testRunStarted(mRunDesc); + mListener.testStarted(mTestDesc); + + // Delay verification by 100 ms to ensure the thread was started. + SystemClock.sleep(100); + verify(mDevice, never()).executeShellCommand(matches("screenrecord.*size.*video.mp4")); + verify(mDevice, atLeastOnce()) + .executeShellCommand(not(matches("screenrecord .*video.mp4"))); + } } |