Merge "Enable screen recording in legacy app launch test." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0e82ef5b1d
@@ -8,6 +8,8 @@ android_test {
|
|||||||
"android.test.base",
|
"android.test.base",
|
||||||
"android.test.runner",
|
"android.test.runner",
|
||||||
],
|
],
|
||||||
static_libs: ["androidx.test.rules"],
|
static_libs: [
|
||||||
|
"androidx.test.rules",
|
||||||
|
"ub-uiautomator"],
|
||||||
test_suites: ["device-tests"],
|
test_suites: ["device-tests"],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.tests.applaunch;
|
package com.android.tests.applaunch;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
@@ -29,7 +31,9 @@ import android.content.pm.ResolveInfo;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.support.test.uiautomator.UiDevice;
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
import android.test.InstrumentationTestRunner;
|
import android.test.InstrumentationTestRunner;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -46,6 +50,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@@ -67,6 +72,7 @@ import java.util.Set;
|
|||||||
* in the following format:
|
* in the following format:
|
||||||
* -e apps <app name>^<result key>|<app name>^<result key>
|
* -e apps <app name>^<result key>|<app name>^<result key>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AppLaunch extends InstrumentationTestCase {
|
public class AppLaunch extends InstrumentationTestCase {
|
||||||
|
|
||||||
private static final int JOIN_TIMEOUT = 10000;
|
private static final int JOIN_TIMEOUT = 10000;
|
||||||
@@ -94,6 +100,9 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
|
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
|
||||||
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
|
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
|
||||||
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
|
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
|
||||||
|
private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
|
||||||
|
private static final int MAX_RECORDING_PARTS = 5;
|
||||||
|
private static final long VIDEO_TAIL_BUFFER = 500;
|
||||||
|
|
||||||
private static final String SIMPLEPERF_APP_CMD =
|
private static final String SIMPLEPERF_APP_CMD =
|
||||||
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
|
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
|
||||||
@@ -144,14 +153,17 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
|
|
||||||
private Map<String, Intent> mNameToIntent;
|
private Map<String, Intent> mNameToIntent;
|
||||||
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
|
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
|
||||||
|
private RecordingThread mCurrentThread;
|
||||||
private Map<String, String> mNameToResultKey;
|
private Map<String, String> mNameToResultKey;
|
||||||
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
|
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
|
||||||
private IActivityManager mAm;
|
private IActivityManager mAm;
|
||||||
|
private File launchSubDir = null;
|
||||||
private String mSimplePerfCmd = null;
|
private String mSimplePerfCmd = null;
|
||||||
private String mLaunchOrder = null;
|
private String mLaunchOrder = null;
|
||||||
private boolean mDropCache = false;
|
private boolean mDropCache = false;
|
||||||
private int mLaunchIterations = 10;
|
private int mLaunchIterations = 10;
|
||||||
private boolean mForceStopApp = true;
|
private boolean mForceStopApp = true;
|
||||||
|
private boolean mEnableRecording = false;
|
||||||
private int mTraceLaunchCount = 0;
|
private int mTraceLaunchCount = 0;
|
||||||
private String mTraceDirectoryStr = null;
|
private String mTraceDirectoryStr = null;
|
||||||
private Bundle mResult = new Bundle();
|
private Bundle mResult = new Bundle();
|
||||||
@@ -166,6 +178,7 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
private boolean mCycleCleanUp = false;
|
private boolean mCycleCleanUp = false;
|
||||||
private boolean mTraceAll = false;
|
private boolean mTraceAll = false;
|
||||||
private boolean mIterationCycle = false;
|
private boolean mIterationCycle = false;
|
||||||
|
private UiDevice mDevice;
|
||||||
|
|
||||||
enum IorapStatus {
|
enum IorapStatus {
|
||||||
UNDEFINED,
|
UNDEFINED,
|
||||||
@@ -222,7 +235,7 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
|
launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
|
||||||
|
|
||||||
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
|
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
|
||||||
throw new IOException("Unable to create the lauch file sub directory "
|
throw new IOException("Unable to create the lauch file sub directory "
|
||||||
@@ -923,9 +936,16 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
mLaunchIterations = Integer.parseInt(launchIterations);
|
mLaunchIterations = Integer.parseInt(launchIterations);
|
||||||
}
|
}
|
||||||
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
|
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
|
||||||
|
|
||||||
if (forceStopApp != null) {
|
if (forceStopApp != null) {
|
||||||
mForceStopApp = Boolean.parseBoolean(forceStopApp);
|
mForceStopApp = Boolean.parseBoolean(forceStopApp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
|
||||||
|
|
||||||
|
if (enableRecording != null) {
|
||||||
|
mEnableRecording = Boolean.parseBoolean(enableRecording);
|
||||||
|
}
|
||||||
String appList = args.getString(KEY_APPS);
|
String appList = args.getString(KEY_APPS);
|
||||||
if (appList == null)
|
if (appList == null)
|
||||||
return;
|
return;
|
||||||
@@ -1038,6 +1058,9 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
private AppLaunchResult startApp(String appName, String launchReason)
|
private AppLaunchResult startApp(String appName, String launchReason)
|
||||||
throws NameNotFoundException, RemoteException {
|
throws NameNotFoundException, RemoteException {
|
||||||
Log.i(TAG, "Starting " + appName);
|
Log.i(TAG, "Starting " + appName);
|
||||||
|
if(mEnableRecording) {
|
||||||
|
startRecording(appName, launchReason);
|
||||||
|
}
|
||||||
|
|
||||||
Intent startIntent = mNameToIntent.get(appName);
|
Intent startIntent = mNameToIntent.get(appName);
|
||||||
if (startIntent == null) {
|
if (startIntent == null) {
|
||||||
@@ -1053,6 +1076,10 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mEnableRecording) {
|
||||||
|
stopRecording();
|
||||||
|
}
|
||||||
return runnable.getResult();
|
return runnable.getResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,4 +1387,126 @@ public class AppLaunch extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the screen recording while launching the app.
|
||||||
|
*
|
||||||
|
* @param appName
|
||||||
|
* @param launchReason
|
||||||
|
*/
|
||||||
|
private void startRecording(String appName, String launchReason) {
|
||||||
|
Log.v(TAG, "Started Recording");
|
||||||
|
mCurrentThread = new RecordingThread("test-screen-record",
|
||||||
|
String.format("%s_%s", appName, launchReason));
|
||||||
|
mCurrentThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop already started screen recording.
|
||||||
|
*/
|
||||||
|
private void stopRecording() {
|
||||||
|
// Skip if not directory.
|
||||||
|
if (launchSubDir == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some extra time to the video end.
|
||||||
|
SystemClock.sleep(VIDEO_TAIL_BUFFER);
|
||||||
|
// Ctrl + C all screen record processes.
|
||||||
|
mCurrentThread.cancel();
|
||||||
|
// Wait for the thread to completely die.
|
||||||
|
try {
|
||||||
|
mCurrentThread.join();
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
Log.e(TAG, "Interrupted when joining the recording thread.", ex);
|
||||||
|
}
|
||||||
|
Log.v(TAG, "Stopped Recording");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the recording's name for part {@code part} of launch description. */
|
||||||
|
private File getOutputFile(String description, int part) {
|
||||||
|
// Omit the iteration number for the first iteration.
|
||||||
|
final String fileName =
|
||||||
|
String.format(
|
||||||
|
"%s-video%s.mp4", description, part == 1 ? "" : part);
|
||||||
|
return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates the start and stop screen recording logic.
|
||||||
|
* Copied from ScreenRecordCollector.
|
||||||
|
*/
|
||||||
|
private class RecordingThread extends Thread {
|
||||||
|
private final String mDescription;
|
||||||
|
private final List<File> mRecordings;
|
||||||
|
|
||||||
|
private boolean mContinue;
|
||||||
|
|
||||||
|
public RecordingThread(String name, String description) {
|
||||||
|
super(name);
|
||||||
|
|
||||||
|
mContinue = true;
|
||||||
|
mRecordings = new ArrayList<>();
|
||||||
|
|
||||||
|
assertNotNull("No test description provided for recording.", description);
|
||||||
|
mDescription = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
|
||||||
|
for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
|
||||||
|
File output = getOutputFile(mDescription, i);
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
String.format("Recording screen to %s", output.getAbsolutePath()));
|
||||||
|
mRecordings.add(output);
|
||||||
|
// 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.
|
||||||
|
getDevice().executeShellCommand(
|
||||||
|
String.format("screenrecord %s", output.getAbsolutePath()));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Caught exception while screen recording.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
mContinue = false;
|
||||||
|
|
||||||
|
// Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
|
||||||
|
try {
|
||||||
|
String[] pids = getDevice().executeShellCommand(
|
||||||
|
"pidof screenrecord").split(" ");
|
||||||
|
for (String pid : pids) {
|
||||||
|
// Avoid empty process ids, because of weird splitting behavior.
|
||||||
|
if (pid.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDevice().executeShellCommand(
|
||||||
|
String.format("kill -2 %s", pid));
|
||||||
|
Log.d(
|
||||||
|
TAG,
|
||||||
|
String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("Failed to kill screen recording process.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<File> getRecordings() {
|
||||||
|
return mRecordings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UiDevice getDevice() {
|
||||||
|
if (mDevice == null) {
|
||||||
|
mDevice = UiDevice.getInstance(getInstrumentation());
|
||||||
|
}
|
||||||
|
return mDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user