• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ddmlib.testrunner;
18 
19 
20 import com.android.ddmlib.IDevice;
21 import com.android.ddmlib.Log;
22 
23 import java.io.IOException;
24 import java.util.Hashtable;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 
28 /**
29  * Runs a Android test command remotely and reports results.
30  */
31 public class RemoteAndroidTestRunner  {
32 
33     private final String mPackageName;
34     private final  String mRunnerName;
35     private IDevice mRemoteDevice;
36     /** map of name-value instrumentation argument pairs */
37     private Map<String, String> mArgMap;
38     private InstrumentationResultParser mParser;
39 
40     private static final String LOG_TAG = "RemoteAndroidTest";
41     private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner";
42 
43     private static final char CLASS_SEPARATOR = ',';
44     private static final char METHOD_SEPARATOR = '#';
45     private static final char RUNNER_SEPARATOR = '/';
46 
47     // defined instrumentation argument names
48     private static final String CLASS_ARG_NAME = "class";
49     private static final String LOG_ARG_NAME = "log";
50     private static final String DEBUG_ARG_NAME = "debug";
51     private static final String COVERAGE_ARG_NAME = "coverage";
52     private static final String PACKAGE_ARG_NAME = "package";
53 
54     /**
55      * Creates a remote Android test runner.
56      *
57      * @param packageName the Android application package that contains the tests to run
58      * @param runnerName the instrumentation test runner to execute. If null, will use default
59      *   runner
60      * @param remoteDevice the Android device to execute tests on
61      */
RemoteAndroidTestRunner(String packageName, String runnerName, IDevice remoteDevice)62     public RemoteAndroidTestRunner(String packageName,
63                                    String runnerName,
64                                    IDevice remoteDevice) {
65 
66         mPackageName = packageName;
67         mRunnerName = runnerName;
68         mRemoteDevice = remoteDevice;
69         mArgMap = new Hashtable<String, String>();
70     }
71 
72     /**
73      * Alternate constructor. Uses default instrumentation runner.
74      *
75      * @param packageName the Android application package that contains the tests to run
76      * @param remoteDevice the Android device to execute tests on
77      */
RemoteAndroidTestRunner(String packageName, IDevice remoteDevice)78     public RemoteAndroidTestRunner(String packageName,
79                                    IDevice remoteDevice) {
80         this(packageName, null, remoteDevice);
81     }
82 
83     /**
84      * Returns the application package name.
85      */
getPackageName()86     public String getPackageName() {
87         return mPackageName;
88     }
89 
90     /**
91      * Returns the runnerName.
92      */
getRunnerName()93     public String getRunnerName() {
94         if (mRunnerName == null) {
95             return DEFAULT_RUNNER_NAME;
96         }
97         return mRunnerName;
98     }
99 
100     /**
101      * Returns the complete instrumentation component path.
102      */
getRunnerPath()103     private String getRunnerPath() {
104         return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
105     }
106 
107     /**
108      * Sets to run only tests in this class
109      * Must be called before 'run'.
110      *
111      * @param className fully qualified class name (eg x.y.z)
112      */
setClassName(String className)113     public void setClassName(String className) {
114         addInstrumentationArg(CLASS_ARG_NAME, className);
115     }
116 
117     /**
118      * Sets to run only tests in the provided classes
119      * Must be called before 'run'.
120      * <p>
121      * If providing more than one class, requires a InstrumentationTestRunner that supports
122      * the multiple class argument syntax.
123      *
124      * @param classNames array of fully qualified class names (eg x.y.z)
125      */
setClassNames(String[] classNames)126     public void setClassNames(String[] classNames) {
127         StringBuilder classArgBuilder = new StringBuilder();
128 
129         for (int i = 0; i < classNames.length; i++) {
130             if (i != 0) {
131                 classArgBuilder.append(CLASS_SEPARATOR);
132             }
133             classArgBuilder.append(classNames[i]);
134         }
135         setClassName(classArgBuilder.toString());
136     }
137 
138     /**
139      * Sets to run only specified test method
140      * Must be called before 'run'.
141      *
142      * @param className fully qualified class name (eg x.y.z)
143      * @param testName method name
144      */
setMethodName(String className, String testName)145     public void setMethodName(String className, String testName) {
146         setClassName(className + METHOD_SEPARATOR + testName);
147     }
148 
149     /**
150      * Sets to run all tests in specified package
151      * Must be called before 'run'.
152      *
153      * @param packageName fully qualified package name (eg x.y.z)
154      */
setTestPackageName(String packageName)155     public void setTestPackageName(String packageName) {
156         addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
157     }
158 
159     /**
160      * Adds a argument to include in instrumentation command.
161      * <p/>
162      * Must be called before 'run'. If an argument with given name has already been provided, it's
163      * value will be overridden.
164      *
165      * @param name the name of the instrumentation bundle argument
166      * @param value the value of the argument
167      */
addInstrumentationArg(String name, String value)168     public void addInstrumentationArg(String name, String value) {
169         if (name == null || value == null) {
170             throw new IllegalArgumentException("name or value arguments cannot be null");
171         }
172         mArgMap.put(name, value);
173     }
174 
175     /**
176      * Adds a boolean argument to include in instrumentation command.
177      * <p/>
178      * @see RemoteAndroidTestRunner#addInstrumentationArg
179      *
180      * @param name the name of the instrumentation bundle argument
181      * @param value the value of the argument
182      */
addBooleanArg(String name, boolean value)183     public void addBooleanArg(String name, boolean value) {
184         addInstrumentationArg(name, Boolean.toString(value));
185     }
186 
187     /**
188      * Sets this test run to log only mode - skips test execution.
189      */
setLogOnly(boolean logOnly)190     public void setLogOnly(boolean logOnly) {
191         addBooleanArg(LOG_ARG_NAME, logOnly);
192     }
193 
194     /**
195      * Sets this debug mode of this test run. If true, the Android test runner will wait for a
196      * debugger to attach before proceeding with test execution.
197      */
setDebug(boolean debug)198     public void setDebug(boolean debug) {
199         addBooleanArg(DEBUG_ARG_NAME, debug);
200     }
201 
202     /**
203      * Sets this code coverage mode of this test run.
204      */
setCoverage(boolean coverage)205     public void setCoverage(boolean coverage) {
206         addBooleanArg(COVERAGE_ARG_NAME, coverage);
207     }
208 
209     /**
210      * Execute this test run.
211      *
212      * @param listener listens for test results
213      */
run(ITestRunListener listener)214     public void run(ITestRunListener listener) {
215         final String runCaseCommandStr = String.format("am instrument -w -r %s %s",
216             getArgsCommand(), getRunnerPath());
217         Log.d(LOG_TAG, runCaseCommandStr);
218         mParser = new InstrumentationResultParser(listener);
219 
220         try {
221             mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser);
222         } catch (IOException e) {
223             Log.e(LOG_TAG, e);
224             listener.testRunFailed(e.toString());
225         }
226     }
227 
228     /**
229      * Requests cancellation of this test run.
230      */
cancel()231     public void cancel() {
232         if (mParser != null) {
233             mParser.cancel();
234         }
235     }
236 
237     /**
238      * Returns the full instrumentation command line syntax for the provided instrumentation
239      * arguments.
240      * Returns an empty string if no arguments were specified.
241      */
getArgsCommand()242     private String getArgsCommand() {
243         StringBuilder commandBuilder = new StringBuilder();
244         for (Entry<String, String> argPair : mArgMap.entrySet()) {
245             final String argCmd = String.format(" -e %s %s", argPair.getKey(),
246                     argPair.getValue());
247             commandBuilder.append(argCmd);
248         }
249         return commandBuilder.toString();
250     }
251 }
252