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