blob: 8196fa566bed6a7182f7e22fe50210c2d9e88ff6 [file] [log] [blame]
/*
* Copyright (C) 2015 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.media.tests;
import com.android.ddmlib.CollectingOutputReceiver;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.IConfigurationReceiver;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.ByteArrayInputStreamSource;
import com.android.tradefed.result.CollectingTestListener;
import com.android.tradefed.result.FileInputStreamSource;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.testtype.IDeviceTest;
import com.android.tradefed.testtype.IRemoteTest;
import com.android.tradefed.testtype.InstrumentationTest;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;
import com.android.tradefed.util.StreamUtil;
import com.android.tradefed.util.proto.TfMetricProtoUtil;
import org.junit.Assert;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
/**
* Camera test base class
*
* Camera2StressTest, CameraStartupTest, Camera2LatencyTest and CameraPerformanceTest use this base
* class for Camera ivvavik and later.
*/
public class CameraTestBase implements IDeviceTest, IRemoteTest, IConfigurationReceiver {
private static final long SHELL_TIMEOUT_MS = 60 * 1000; // 1 min
private static final int SHELL_MAX_ATTEMPTS = 3;
protected static final String PROCESS_CAMERA_DAEMON = "mm-qcamera-daemon";
protected static final String PROCESS_MEDIASERVER = "mediaserver";
protected static final String PROCESS_CAMERA_APP = "com.google.android.GoogleCamera";
protected static final String DUMP_ION_HEAPS_COMMAND = "cat /d/ion/heaps/system";
protected static final String ARGUMENT_TEST_ITERATIONS = "iterations";
@Option(name = "test-package", description = "Test package to run.")
private String mTestPackage = "com.google.android.camera";
@Option(name = "test-class", description = "Test class to run.")
private String mTestClass = null;
@Option(name = "test-methods", description = "Test method to run. May be repeated.")
private Collection<String> mTestMethods = new ArrayList<>();
@Option(name = "test-runner", description = "Test runner for test instrumentation.")
private String mTestRunner = "android.test.InstrumentationTestRunner";
@Option(name = "test-timeout", description = "Max time allowed in ms for a test run.")
private int mTestTimeoutMs = 60 * 60 * 1000; // 1 hour
@Option(name = "shell-timeout",
description="The defined timeout (in milliseconds) is used as a maximum waiting time "
+ "when expecting the command output from the device. At any time, if the "
+ "shell command does not output anything for a period longer than defined "
+ "timeout the TF run terminates. For no timeout, set to 0.")
private long mShellTimeoutMs = 60 * 60 * 1000; // default to 1 hour
@Option(name = "ru-key", description = "Result key to use when posting to the dashboard.")
private String mRuKey = null;
@Option(name = "logcat-on-failure", description =
"take a logcat snapshot on every test failure.")
private boolean mLogcatOnFailure = false;
@Option(
name = "instrumentation-arg",
description = "Additional instrumentation arguments to provide."
)
private Map<String, String> mInstrArgMap = new HashMap<>();
@Option(name = "dump-meminfo", description =
"take a dumpsys meminfo at a given interval time.")
private boolean mDumpMeminfo = false;
@Option(name="dump-meminfo-interval-ms",
description="Interval of calling dumpsys meminfo in milliseconds.")
private int mMeminfoIntervalMs = 5 * 60 * 1000; // 5 minutes
@Option(name = "dump-ion-heap", description =
"dump ION allocations at the end of test.")
private boolean mDumpIonHeap = false;
@Option(name = "dump-thread-count", description =
"Count the number of threads in Camera process at a given interval time.")
private boolean mDumpThreadCount = false;
@Option(name="dump-thread-count-interval-ms",
description="Interval of calling ps to count the number of threads in milliseconds.")
private int mThreadCountIntervalMs = 5 * 60 * 1000; // 5 minutes
@Option(name="iterations", description="The number of iterations to run. Default to 1. "
+ "This takes effect only when Camera2InstrumentationTestRunner is used to execute "
+ "framework stress tests.")
private int mIterations = 1;
private ITestDevice mDevice = null;
// A base listener to collect the results from each test run. Test results will be forwarded
// to other listeners.
private AbstractCollectingListener mCollectingListener = null;
private long mStartTimeMs = 0;
private MeminfoTimer mMeminfoTimer = null;
private ThreadTrackerTimer mThreadTrackerTimer = null;
protected IConfiguration mConfiguration;
/**
* {@inheritDoc}
*/
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
// ignore
}
/**
* Run Camera instrumentation test with a default listener.
*
* @param listener the ITestInvocationListener of test results
* @throws DeviceNotAvailableException
*/
protected void runInstrumentationTest(ITestInvocationListener listener)
throws DeviceNotAvailableException {
if (mCollectingListener == null) {
mCollectingListener = new DefaultCollectingListener(listener);
}
runInstrumentationTest(listener, mCollectingListener);
}
/**
* Run Camera instrumentation test with a listener to gather the metrics from the individual
* test runs.
*
* @param listener the ITestInvocationListener of test results
* @param collectingListener the {@link CollectingTestListener} to collect the metrics from
* test runs
* @throws DeviceNotAvailableException
*/
protected void runInstrumentationTest(ITestInvocationListener listener,
AbstractCollectingListener collectingListener)
throws DeviceNotAvailableException {
Assert.assertNotNull(collectingListener);
mCollectingListener = collectingListener;
InstrumentationTest instr = new InstrumentationTest();
instr.setDevice(getDevice());
instr.setPackageName(getTestPackage());
instr.setRunnerName(getTestRunner());
instr.setClassName(getTestClass());
instr.setTestTimeout(getTestTimeoutMs());
instr.setShellTimeout(getShellTimeoutMs());
instr.setLogcatOnFailure(mLogcatOnFailure);
instr.setRunName(getRuKey());
instr.setRerunMode(false);
// Set test iteration.
if (getIterationCount() > 1) {
CLog.v("Setting test iterations: %d", getIterationCount());
Map<String, String> instrArgMap = getInstrumentationArgMap();
instrArgMap.put(ARGUMENT_TEST_ITERATIONS, String.valueOf(getIterationCount()));
}
for (Map.Entry<String, String> entry : getInstrumentationArgMap().entrySet()) {
instr.addInstrumentationArg(entry.getKey(), entry.getValue());
}
// Check if dumpheap needs to be taken for native processes before test runs.
if (shouldDumpMeminfo()) {
mMeminfoTimer = new MeminfoTimer(0, mMeminfoIntervalMs);
}
if (shouldDumpThreadCount()) {
long delayMs = mThreadCountIntervalMs / 2; // Not to run all dump at the same interval.
mThreadTrackerTimer = new ThreadTrackerTimer(delayMs, mThreadCountIntervalMs);
}
// Run tests.
mStartTimeMs = System.currentTimeMillis();
if (mTestMethods.size() > 0) {
CLog.d(String.format("The number of test methods is: %d", mTestMethods.size()));
for (String testName : mTestMethods) {
instr.setMethodName(testName);
instr.run(mCollectingListener);
}
} else {
instr.run(mCollectingListener);
}
dumpIonHeaps(mCollectingListener, getTestClass());
}
/**
* A base listener to collect all test results and metrics from Camera instrumentation test run.
* Abstract methods can be overridden to handle test metrics or inform of test run ended.
*/
protected abstract class AbstractCollectingListener extends CollectingTestListener {
private ITestInvocationListener mListener = null;
private Map<String, String> mMetrics = new HashMap<>();
private Map<String, String> mFatalErrors = new HashMap<>();
private static final String INCOMPLETE_TEST_ERR_MSG_PREFIX =
"Test failed to run to completion. Reason: 'Instrumentation run failed";
public AbstractCollectingListener(ITestInvocationListener listener) {
mListener = listener;
}
/**
* Override only when subclasses need to get the test metrics from an individual
* instrumentation test. To aggregate the metrics from each test, update the
* getAggregatedMetrics and post them at the end of test run.
*
* @param test identifies the test
* @param testMetrics a {@link Map} of the metrics emitted
*/
abstract public void handleMetricsOnTestEnded(TestDescription test,
Map<String, String> testMetrics);
/**
* Override only when it needs to inform subclasses of instrumentation test run ended,
* so that subclasses have a chance to peek the aggregated results at the end of test run
* and to decide what metrics to be posted.
* Either {@link ITestInvocationListener#testRunEnded} or
* {@link ITestInvocationListener#testRunFailed} should be called in this function to
* report the test run status.
*
* @param listener - the ITestInvocationListener of test results
* @param elapsedTime - device reported elapsed time, in milliseconds
* @param runMetrics - key-value pairs reported at the end of an instrumentation test run.
* Use getAggregatedMetrics to retrieve the metrics aggregated
* from an individual test, instead.
*/
abstract public void handleTestRunEnded(ITestInvocationListener listener,
long elapsedTime, Map<String, String> runMetrics);
/**
* Report the end of an individual camera test and delegate handling the collected metrics
* to subclasses. Do not override testEnded to manipulate the test metrics after each test.
* Instead, use handleMetricsOnTestEnded.
*
* @param test identifies the test
* @param testMetrics a {@link Map} of the metrics emitted
*/
@Override
public void testEnded(
TestDescription test, long endTime, HashMap<String, Metric> testMetrics) {
super.testEnded(test, endTime, testMetrics);
handleMetricsOnTestEnded(test, TfMetricProtoUtil.compatibleConvert(testMetrics));
stopDumping(test);
mListener.testEnded(test, endTime, testMetrics);
}
@Override
public void testStarted(TestDescription test, long startTime) {
super.testStarted(test, startTime);
startDumping(test);
mListener.testStarted(test, startTime);
}
@Override
public void testFailed(TestDescription test, String trace) {
super.testFailed(test, trace);
// If the test failed to run to complete, this is an exceptional case.
// Let this test run fail so that it can rerun.
if (trace.startsWith(INCOMPLETE_TEST_ERR_MSG_PREFIX)) {
mFatalErrors.put(test.getTestName(), trace);
CLog.d("Test (%s) failed due to fatal error : %s", test.getTestName(), trace);
}
mListener.testFailed(test, trace);
}
@Override
public void testRunFailed(String errorMessage) {
super.testRunFailed(errorMessage);
mFatalErrors.put(getRuKey(), errorMessage);
}
@Override
public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
super.testRunEnded(elapsedTime, runMetrics);
handleTestRunEnded(mListener, elapsedTime,
TfMetricProtoUtil.compatibleConvert(runMetrics));
// never be called since handleTestRunEnded will handle it if needed.
//mListener.testRunEnded(elapsedTime, runMetrics);
}
@Override
public void testRunStarted(String runName, int testCount) {
super.testRunStarted(runName, testCount);
mListener.testRunStarted(runName, testCount);
}
@Override
public void testRunStopped(long elapsedTime) {
super.testRunStopped(elapsedTime);
mListener.testRunStopped(elapsedTime);
}
@Override
public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
super.testLog(dataName, dataType, dataStream);
mListener.testLog(dataName, dataType, dataStream);
}
protected void startDumping(TestDescription test) {
if (shouldDumpMeminfo()) {
mMeminfoTimer.start(test);
}
if (shouldDumpThreadCount()) {
mThreadTrackerTimer.start(test);
}
}
protected void stopDumping(TestDescription test) {
InputStreamSource outputSource = null;
File outputFile = null;
if (shouldDumpMeminfo()) {
mMeminfoTimer.stop();
// Grab a snapshot of meminfo file and post it to dashboard.
try {
outputFile = mMeminfoTimer.getOutputFile();
outputSource = new FileInputStreamSource(outputFile, true /* delete */);
String logName = String.format("meminfo_%s", test.getTestName());
mListener.testLog(logName, LogDataType.TEXT, outputSource);
} finally {
StreamUtil.cancel(outputSource);
}
}
if (shouldDumpThreadCount()) {
mThreadTrackerTimer.stop();
try {
outputFile = mThreadTrackerTimer.getOutputFile();
outputSource = new FileInputStreamSource(outputFile, true /* delete */);
String logName = String.format("ps_%s", test.getTestName());
mListener.testLog(logName, LogDataType.TEXT, outputSource);
} finally {
StreamUtil.cancel(outputSource);
}
}
}
public Map<String, String> getAggregatedMetrics() {
return mMetrics;
}
public ITestInvocationListener getListeners() {
return mListener;
}
/**
* Determine that the test run failed with fatal errors.
*
* @return True if test run has a failure due to fatal error.
*/
public boolean hasTestRunFatalError() {
return (getNumTotalTests() > 0 && mFatalErrors.size() > 0);
}
public Map<String, String> getFatalErrors() {
return mFatalErrors;
}
public String getErrorMessage() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> error : mFatalErrors.entrySet()) {
sb.append(error.getKey());
sb.append(" : ");
sb.append(error.getValue());
sb.append("\n");
}
return sb.toString();
}
}
protected class DefaultCollectingListener extends AbstractCollectingListener {
public DefaultCollectingListener(ITestInvocationListener listener) {
super(listener);
}
@Override
public void handleMetricsOnTestEnded(TestDescription test, Map<String, String> testMetrics) {
if (testMetrics == null) {
return; // No-op if there is nothing to post.
}
getAggregatedMetrics().putAll(testMetrics);
}
@Override
public void handleTestRunEnded(ITestInvocationListener listener, long elapsedTime,
Map<String, String> runMetrics) {
// Post aggregated metrics at the end of test run.
listener.testRunEnded(getTestDurationMs(), TfMetricProtoUtil.upgradeConvert(getAggregatedMetrics()));
}
}
// TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and processes/threads)
private class MeminfoTimer {
private static final String LOG_HEADER =
"uptime,pssCameraDaemon,pssCameraApp,ramTotal,ramFree,ramUsed";
private static final String DUMPSYS_MEMINFO_COMMAND =
"dumpsys meminfo -c | grep -w -e " + "^ram -e ^time";
private String[] mDumpsysMemInfoProc = {
PROCESS_CAMERA_DAEMON, PROCESS_CAMERA_APP, PROCESS_MEDIASERVER
};
private static final int STATE_STOPPED = 0;
private static final int STATE_SCHEDULED = 1;
private static final int STATE_RUNNING = 2;
private int mState = STATE_STOPPED;
private Timer mTimer = new Timer(true); // run as a daemon thread
private long mDelayMs = 0;
private long mPeriodMs = 60 * 1000; // 60 sec
private File mOutputFile = null;
private String mCommand;
public MeminfoTimer(long delayMs, long periodMs) {
mDelayMs = delayMs;
mPeriodMs = periodMs;
mCommand = DUMPSYS_MEMINFO_COMMAND;
for (String process : mDumpsysMemInfoProc) {
mCommand += " -e " + process;
}
}
synchronized void start(TestDescription test) {
if (isRunning()) {
stop();
}
// Create an output file.
if (createOutputFile(test) == null) {
CLog.w("Stop collecting meminfo since meminfo log file not found.");
mState = STATE_STOPPED;
return; // No-op
}
mTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
mState = STATE_RUNNING;
dumpMeminfo(mCommand, mOutputFile);
}
}, mDelayMs, mPeriodMs);
mState = STATE_SCHEDULED;
}
synchronized void stop() {
mState = STATE_STOPPED;
mTimer.cancel();
}
synchronized boolean isRunning() {
return (mState == STATE_RUNNING);
}
public File getOutputFile() {
return mOutputFile;
}
private File createOutputFile(TestDescription test) {
try {
mOutputFile = FileUtil.createTempFile(
String.format("meminfo_%s", test.getTestName()), "csv");
BufferedWriter writer = new BufferedWriter(new FileWriter(mOutputFile, false));
writer.write(LOG_HEADER);
writer.newLine();
writer.flush();
writer.close();
} catch (IOException e) {
CLog.w("Failed to create meminfo log file %s:", mOutputFile.getAbsolutePath());
CLog.e(e);
return null;
}
return mOutputFile;
}
}
void dumpMeminfo(String command, File outputFile) {
try {
CollectingOutputReceiver receiver = new CollectingOutputReceiver();
// Dump meminfo in a compact form.
getDevice().executeShellCommand(command, receiver,
SHELL_TIMEOUT_MS, TimeUnit.MILLISECONDS, SHELL_MAX_ATTEMPTS);
printMeminfo(outputFile, receiver.getOutput());
} catch (DeviceNotAvailableException e) {
CLog.w("Failed to dump meminfo:");
CLog.e(e);
}
}
void printMeminfo(File outputFile, String meminfo) {
// Parse meminfo and print each iteration in a line in a .csv format. The meminfo output
// are separated into three different formats:
//
// Format: time,<uptime>,<realtime>
// eg. "time,59459911,63354673"
//
// Format: proc,<oom_label>,<process_name>,<pid>,<pss>,<hasActivities>
// eg. "proc,native,mm-qcamera-daemon,522,12881,e"
// "proc,fore,com.google.android.GoogleCamera,26560,70880,a"
//
// Format: ram,<total>,<free>,<used>
// eg. "ram,1857364,810189,541516"
BufferedWriter writer = null;
BufferedReader reader = null;
try {
final String DELIMITER = ",";
writer = new BufferedWriter(new FileWriter(outputFile, true));
reader = new BufferedReader(new StringReader(meminfo));
String line;
String uptime = null;
String pssCameraNative = null;
String pssCameraApp = null;
String ramTotal = null;
String ramFree = null;
String ramUsed = null;
while ((line = reader.readLine()) != null) {
if (line.startsWith("time")) {
uptime = line.split(DELIMITER)[1];
} else if (line.startsWith("ram")) {
String[] ram = line.split(DELIMITER);
ramTotal = ram[1];
ramFree = ram[2];
ramUsed = ram[3];
} else if (line.contains(PROCESS_CAMERA_DAEMON)) {
pssCameraNative = line.split(DELIMITER)[4];
} else if (line.contains(PROCESS_CAMERA_APP)) {
pssCameraApp = line.split(DELIMITER)[4];
}
}
String printMsg = String.format(
"%s,%s,%s,%s,%s,%s", uptime, pssCameraNative, pssCameraApp,
ramTotal, ramFree, ramUsed);
writer.write(printMsg);
writer.newLine();
writer.flush();
} catch (IOException e) {
CLog.w("Failed to print meminfo to %s:", outputFile.getAbsolutePath());
CLog.e(e);
} finally {
StreamUtil.close(writer);
}
}
// TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and processes/threads)
private class ThreadTrackerTimer {
// list all threads in a given process, remove the first header line, squeeze whitespaces,
// select thread name (in 14th column), then sort and group by its name.
// Examples:
// 3 SoundPoolThread
// 3 SoundPool
// 2 Camera Job Disp
// 1 pool-7-thread-1
// 1 pool-6-thread-1
// FIXME: Resolve the error "sh: syntax error: '|' unexpected" using the command below
// $ /system/bin/ps -t -p %s | tr -s ' ' | cut -d' ' -f13- | sort | uniq -c | sort -nr"
private static final String PS_COMMAND_FORMAT = "/system/bin/ps -t -p %s";
private static final String PGREP_COMMAND_FORMAT = "pgrep %s";
private static final int STATE_STOPPED = 0;
private static final int STATE_SCHEDULED = 1;
private static final int STATE_RUNNING = 2;
private int mState = STATE_STOPPED;
private Timer mTimer = new Timer(true); // run as a daemon thread
private long mDelayMs = 0;
private long mPeriodMs = 60 * 1000; // 60 sec
private File mOutputFile = null;
public ThreadTrackerTimer(long delayMs, long periodMs) {
mDelayMs = delayMs;
mPeriodMs = periodMs;
}
synchronized void start(TestDescription test) {
if (isRunning()) {
stop();
}
// Create an output file.
if (createOutputFile(test) == null) {
CLog.w("Stop collecting thread counts since log file not found.");
mState = STATE_STOPPED;
return; // No-op
}
mTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
mState = STATE_RUNNING;
dumpThreadCount(PS_COMMAND_FORMAT, getPid(PROCESS_CAMERA_APP), mOutputFile);
}
}, mDelayMs, mPeriodMs);
mState = STATE_SCHEDULED;
}
synchronized void stop() {
mState = STATE_STOPPED;
mTimer.cancel();
}
synchronized boolean isRunning() {
return (mState == STATE_RUNNING);
}
public File getOutputFile() {
return mOutputFile;
}
File createOutputFile(TestDescription test) {
try {
mOutputFile = FileUtil.createTempFile(
String.format("ps_%s", test.getTestName()), "txt");
new BufferedWriter(new FileWriter(mOutputFile, false)).close();
} catch (IOException e) {
CLog.w("Failed to create processes and threads file %s:",
mOutputFile.getAbsolutePath());
CLog.e(e);
return null;
}
return mOutputFile;
}
String getPid(String processName) {
String result = null;
try {
result = getDevice().executeShellCommand(String.format(PGREP_COMMAND_FORMAT,
processName));
} catch (DeviceNotAvailableException e) {
CLog.w("Failed to get pid %s:", processName);
CLog.e(e);
}
return result;
}
String getUptime() {
String uptime = null;
try {
// uptime will typically have a format like "5278.73 1866.80". Use the first one
// (which is wall-time)
uptime = getDevice().executeShellCommand("cat /proc/uptime").split(" ")[0];
Float.parseFloat(uptime);
} catch (NumberFormatException e) {
CLog.w("Failed to get valid uptime %s: %s", uptime, e);
} catch (DeviceNotAvailableException e) {
CLog.w("Failed to get valid uptime: %s", e);
}
return uptime;
}
void dumpThreadCount(String commandFormat, String pid, File outputFile) {
try {
if ("".equals(pid)) {
return;
}
String result = getDevice().executeShellCommand(String.format(commandFormat, pid));
String header = String.format("UPTIME: %s", getUptime());
BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile, true));
writer.write(header);
writer.newLine();
writer.write(result);
writer.newLine();
writer.flush();
writer.close();
} catch (DeviceNotAvailableException | IOException e) {
CLog.w("Failed to dump thread count:");
CLog.e(e);
}
}
}
// TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and
// processes/threads)
protected void dumpIonHeaps(ITestInvocationListener listener, String testClass) {
if (!shouldDumpIonHeap()) {
return; // No-op if option is not set.
}
try {
String result = getDevice().executeShellCommand(DUMP_ION_HEAPS_COMMAND);
if (!"".equals(result)) {
String fileName = String.format("ionheaps_%s_onEnd", testClass);
listener.testLog(fileName, LogDataType.TEXT,
new ByteArrayInputStreamSource(result.getBytes()));
}
} catch (DeviceNotAvailableException e) {
CLog.w("Failed to dump ION heaps:");
CLog.e(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setDevice(ITestDevice device) {
mDevice = device;
}
/**
* {@inheritDoc}
*/
@Override
public ITestDevice getDevice() {
return mDevice;
}
/**
* {@inheritDoc}
*/
@Override
public void setConfiguration(IConfiguration configuration) {
mConfiguration = configuration;
}
/**
* Get the {@link IRunUtil} instance to use.
* <p/>
* Exposed so unit tests can mock.
*/
IRunUtil getRunUtil() {
return RunUtil.getDefault();
}
/**
* Get the duration of Camera test instrumentation in milliseconds.
*
* @return the duration of Camera instrumentation test until it is called.
*/
public long getTestDurationMs() {
return System.currentTimeMillis() - mStartTimeMs;
}
public String getTestPackage() {
return mTestPackage;
}
public void setTestPackage(String testPackage) {
mTestPackage = testPackage;
}
public String getTestClass() {
return mTestClass;
}
public void setTestClass(String testClass) {
mTestClass = testClass;
}
public String getTestRunner() {
return mTestRunner;
}
public void setTestRunner(String testRunner) {
mTestRunner = testRunner;
}
public int getTestTimeoutMs() {
return mTestTimeoutMs;
}
public void setTestTimeoutMs(int testTimeoutMs) {
mTestTimeoutMs = testTimeoutMs;
}
public long getShellTimeoutMs() {
return mShellTimeoutMs;
}
public void setShellTimeoutMs(long shellTimeoutMs) {
mShellTimeoutMs = shellTimeoutMs;
}
public String getRuKey() {
return mRuKey;
}
public void setRuKey(String ruKey) {
mRuKey = ruKey;
}
public boolean shouldDumpMeminfo() {
return mDumpMeminfo;
}
public boolean shouldDumpIonHeap() {
return mDumpIonHeap;
}
public boolean shouldDumpThreadCount() {
return mDumpThreadCount;
}
public AbstractCollectingListener getCollectingListener() {
return mCollectingListener;
}
public void setLogcatOnFailure(boolean logcatOnFailure) {
mLogcatOnFailure = logcatOnFailure;
}
public int getIterationCount() {
return mIterations;
}
public Map<String, String> getInstrumentationArgMap() { return mInstrArgMap; }
}