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