blob: 9dd1d1640e1eeb36789b1e94ceb99cdadefa1d3f [file] [log] [blame]
/*
* Copyright (C) 2008 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.ddmlib.testrunner;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Map.Entry;
/**
* Runs a Android test command remotely and reports results.
*/
public class RemoteAndroidTestRunner {
private final String mPackageName;
private final String mRunnerName;
private IDevice mRemoteDevice;
/** map of name-value instrumentation argument pairs */
private Map<String, String> mArgMap;
private InstrumentationResultParser mParser;
private static final String LOG_TAG = "RemoteAndroidTest";
private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner";
private static final char CLASS_SEPARATOR = ',';
private static final char METHOD_SEPARATOR = '#';
private static final char RUNNER_SEPARATOR = '/';
// defined instrumentation argument names
private static final String CLASS_ARG_NAME = "class";
private static final String LOG_ARG_NAME = "log";
private static final String DEBUG_ARG_NAME = "debug";
private static final String COVERAGE_ARG_NAME = "coverage";
private static final String PACKAGE_ARG_NAME = "package";
/**
* Creates a remote Android test runner.
*
* @param packageName the Android application package that contains the tests to run
* @param runnerName the instrumentation test runner to execute. If null, will use default
* runner
* @param remoteDevice the Android device to execute tests on
*/
public RemoteAndroidTestRunner(String packageName,
String runnerName,
IDevice remoteDevice) {
mPackageName = packageName;
mRunnerName = runnerName;
mRemoteDevice = remoteDevice;
mArgMap = new Hashtable<String, String>();
}
/**
* Alternate constructor. Uses default instrumentation runner.
*
* @param packageName the Android application package that contains the tests to run
* @param remoteDevice the Android device to execute tests on
*/
public RemoteAndroidTestRunner(String packageName,
IDevice remoteDevice) {
this(packageName, null, remoteDevice);
}
/**
* Returns the application package name.
*/
public String getPackageName() {
return mPackageName;
}
/**
* Returns the runnerName.
*/
public String getRunnerName() {
if (mRunnerName == null) {
return DEFAULT_RUNNER_NAME;
}
return mRunnerName;
}
/**
* Returns the complete instrumentation component path.
*/
private String getRunnerPath() {
return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
}
/**
* Sets to run only tests in this class
* Must be called before 'run'.
*
* @param className fully qualified class name (eg x.y.z)
*/
public void setClassName(String className) {
addInstrumentationArg(CLASS_ARG_NAME, className);
}
/**
* Sets to run only tests in the provided classes
* Must be called before 'run'.
* <p>
* If providing more than one class, requires a InstrumentationTestRunner that supports
* the multiple class argument syntax.
*
* @param classNames array of fully qualified class names (eg x.y.z)
*/
public void setClassNames(String[] classNames) {
StringBuilder classArgBuilder = new StringBuilder();
for (int i = 0; i < classNames.length; i++) {
if (i != 0) {
classArgBuilder.append(CLASS_SEPARATOR);
}
classArgBuilder.append(classNames[i]);
}
setClassName(classArgBuilder.toString());
}
/**
* Sets to run only specified test method
* Must be called before 'run'.
*
* @param className fully qualified class name (eg x.y.z)
* @param testName method name
*/
public void setMethodName(String className, String testName) {
setClassName(className + METHOD_SEPARATOR + testName);
}
/**
* Sets to run all tests in specified package
* Must be called before 'run'.
*
* @param packageName fully qualified package name (eg x.y.z)
*/
public void setTestPackageName(String packageName) {
addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
}
/**
* Adds a argument to include in instrumentation command.
* <p/>
* Must be called before 'run'. If an argument with given name has already been provided, it's
* value will be overridden.
*
* @param name the name of the instrumentation bundle argument
* @param value the value of the argument
*/
public void addInstrumentationArg(String name, String value) {
if (name == null || value == null) {
throw new IllegalArgumentException("name or value arguments cannot be null");
}
mArgMap.put(name, value);
}
/**
* Adds a boolean argument to include in instrumentation command.
* <p/>
* @see RemoteAndroidTestRunner#addInstrumentationArg
*
* @param name the name of the instrumentation bundle argument
* @param value the value of the argument
*/
public void addBooleanArg(String name, boolean value) {
addInstrumentationArg(name, Boolean.toString(value));
}
/**
* Sets this test run to log only mode - skips test execution.
*/
public void setLogOnly(boolean logOnly) {
addBooleanArg(LOG_ARG_NAME, logOnly);
}
/**
* Sets this debug mode of this test run. If true, the Android test runner will wait for a
* debugger to attach before proceeding with test execution.
*/
public void setDebug(boolean debug) {
addBooleanArg(DEBUG_ARG_NAME, debug);
}
/**
* Sets this code coverage mode of this test run.
*/
public void setCoverage(boolean coverage) {
addBooleanArg(COVERAGE_ARG_NAME, coverage);
}
/**
* Execute this test run.
*
* @param listener listens for test results
*/
public void run(ITestRunListener listener) {
final String runCaseCommandStr = String.format("am instrument -w -r %s %s",
getArgsCommand(), getRunnerPath());
Log.d(LOG_TAG, runCaseCommandStr);
mParser = new InstrumentationResultParser(listener);
try {
mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser);
} catch (IOException e) {
Log.e(LOG_TAG, e);
listener.testRunFailed(e.toString());
}
}
/**
* Requests cancellation of this test run.
*/
public void cancel() {
if (mParser != null) {
mParser.cancel();
}
}
/**
* Returns the full instrumentation command line syntax for the provided instrumentation
* arguments.
* Returns an empty string if no arguments were specified.
*/
private String getArgsCommand() {
StringBuilder commandBuilder = new StringBuilder();
for (Entry<String, String> argPair : mArgMap.entrySet()) {
final String argCmd = String.format(" -e %s %s", argPair.getKey(),
argPair.getValue());
commandBuilder.append(argCmd);
}
return commandBuilder.toString();
}
}