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