1 /*
2  * Copyright (C) 2014 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.tradefed.targetprep;
18 
19 import com.android.ddmlib.testrunner.TestResult.TestStatus;
20 import com.android.tradefed.build.IBuildInfo;
21 import com.android.tradefed.config.Option;
22 import com.android.tradefed.config.Option.Importance;
23 import com.android.tradefed.config.OptionClass;
24 import com.android.tradefed.device.DeviceNotAvailableException;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.log.LogUtil.CLog;
27 import com.android.tradefed.result.CollectingTestListener;
28 import com.android.tradefed.result.TestDescription;
29 import com.android.tradefed.result.TestResult;
30 import com.android.tradefed.result.TestRunResult;
31 import com.android.tradefed.testtype.InstrumentationTest;
32 import com.android.tradefed.util.RunUtil;
33 
34 import java.util.HashMap;
35 import java.util.Map;
36 import java.util.Map.Entry;
37 
38 /** A {@link ITargetPreparer} that runs instrumentation */
39 @OptionClass(alias = "instrumentation-preparer")
40 public class InstrumentationPreparer extends BaseTargetPreparer {
41 
42     @Option(name = "package", shortName = 'p',
43             description="The manifest package name of the Android test application to run.",
44             importance = Importance.IF_UNSET)
45     private String mPackageName = null;
46 
47     @Option(name = "runner",
48             description="The instrumentation test runner class name to use.")
49     private String mRunnerName = "android.test.InstrumentationTestRunner";
50 
51     @Option(name = "class", shortName = 'c',
52             description="The test class name to run.")
53     private String mClassName = null;
54 
55     @Option(name = "method", shortName = 'm',
56             description="The test method name to run.")
57     private String mMethodName = null;
58 
59     @Option(
60         name = "shell-timeout",
61         description =
62                 "The defined timeout (in milliseconds) is used as a maximum waiting time "
63                         + "when expecting the command output from the device. At any time, if the "
64                         + "shell command does not output anything for a period longer than defined "
65                         + "timeout the TF run terminates. For no timeout, set to 0.",
66         isTimeVal = true
67     )
68     private long mShellTimeout = 10 * 60 * 1000L; // default to 10 minutes
69 
70     @Option(
71         name = "test-timeout",
72         description =
73                 "Sets timeout (in milliseconds) that will be applied to each test. In the "
74                         + "event of a test timeout it will log the results and proceed with executing "
75                         + "the next test. For no timeout, set to 0.",
76         isTimeVal = true
77     )
78     private long mTestTimeout = 10 * 60 * 1000L; // default to 10 minutes
79 
80     @Option(name = "instrumentation-arg",
81             description = "Instrumentation arguments to provide.")
82     private Map<String, String> mInstrArgMap = new HashMap<String, String>();
83 
84     @Option(name = "attempts",
85             description =
86             "The max number of attempts to make to run the instrumentation successfully.")
87     private int mAttempts = 1;
88 
89     @Option(
90         name = "delay-before-retry",
91         description = "Time to delay before retrying another instrumentation attempt.",
92         isTimeVal = true
93     )
94     private long mRetryDelayMs = 0L;
95 
96     @Override
setUp(ITestDevice device, IBuildInfo buildInfo)97     public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, BuildError,
98             DeviceNotAvailableException {
99         if (isDisabled()) {
100             return;
101         }
102 
103         BuildError e = new BuildError("unknown error", device.getDeviceDescriptor());
104         for (int i = 0; i < mAttempts; i++) {
105             try {
106                 runInstrumentation(device);
107                 return;
108             } catch (BuildError e1) {
109                 e = e1;
110                 CLog.d("sleeping %d msecs on device %s before retrying instrumentation test run",
111                         mRetryDelayMs, device.getSerialNumber());
112                 RunUtil.getDefault().sleep(mRetryDelayMs);
113             }
114         }
115         // all attempts failed!
116         throw e;
117     }
118 
runInstrumentation(ITestDevice device)119     private void runInstrumentation(ITestDevice device) throws DeviceNotAvailableException,
120             BuildError {
121         final InstrumentationTest test = createInstrumentationTest();
122         test.setDevice(device);
123         test.setPackageName(mPackageName);
124         test.setRunnerName(mRunnerName);
125         test.setClassName(mClassName);
126         test.setMethodName(mMethodName);
127         test.setShellTimeout(mShellTimeout);
128         test.setTestTimeout(mTestTimeout);
129         for (Map.Entry<String, String> entry : mInstrArgMap.entrySet()) {
130             test.addInstrumentationArg(entry.getKey(), entry.getValue());
131         }
132 
133         final CollectingTestListener listener = new CollectingTestListener();
134         test.run(listener);
135         if (listener.hasFailedTests()) {
136             String msg = String.format("Failed to run instrumentation %s on %s. failed tests = %s",
137                     mPackageName, device.getSerialNumber(), getFailedTestNames(listener));
138             CLog.w(msg);
139             throw new BuildError(msg, device.getDeviceDescriptor());
140         }
141     }
142 
getFailedTestNames(CollectingTestListener listener)143     private String getFailedTestNames(CollectingTestListener listener) {
144         final StringBuilder builder = new StringBuilder();
145         for (TestRunResult result : listener.getRunResults()) {
146             if (!result.hasFailedTests()) {
147                 continue;
148             }
149             for (Entry<TestDescription, TestResult> entry : result.getTestResults().entrySet()) {
150                 if (entry.getValue().getStatus().equals(TestStatus.PASSED)) {
151                     continue;
152                 }
153 
154                 if (0 < builder.length()) {
155                     builder.append(",");
156                 }
157                 builder.append(entry.getKey());
158             }
159         }
160         return builder.toString();
161     }
162 
createInstrumentationTest()163     InstrumentationTest createInstrumentationTest() {
164         return new InstrumentationTest();
165     }
166 
setPackageName(String packageName)167     void setPackageName(String packageName) {
168         mPackageName = packageName;
169     }
170 
setRunnerName(String runnerName)171     void setRunnerName(String runnerName) {
172         mRunnerName = runnerName;
173     }
174 
setClassName(String className)175     void setClassName(String className) {
176         mClassName = className;
177     }
178 
setMethodName(String methodName)179     void setMethodName(String methodName) {
180         mMethodName = methodName;
181     }
182 
183     /**
184      * @deprecated Use {@link #setShellTimeout(long)} or {@link #setTestTimeout(int)}
185      */
186     @Deprecated
setTimeout(int timeout)187     void setTimeout(int timeout) {
188         setShellTimeout(timeout);
189     }
190 
setShellTimeout(long timeout)191     void setShellTimeout(long timeout) {
192         mShellTimeout = timeout;
193     }
194 
setTestTimeout(int timeout)195     void setTestTimeout(int timeout) {
196         mTestTimeout = timeout;
197     }
198 
setAttempts(int attempts)199     void setAttempts(int attempts) {
200         mAttempts = attempts;
201     }
202 
setRetryDelay(int delayMs)203     void setRetryDelay(int delayMs) {
204         mRetryDelayMs = delayMs;
205     }
206 }
207