Home | History | Annotate | Download | only in targetprep
      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