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