1 /*
2  * Copyright (C) 2010 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.testtype;
18 
19 import com.android.tradefed.config.ConfigurationException;
20 import com.android.tradefed.config.OptionCopier;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.log.LogUtil.CLog;
23 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
24 import com.android.tradefed.result.CollectingTestListener;
25 import com.android.tradefed.result.ITestInvocationListener;
26 import com.android.tradefed.result.ResultForwarder;
27 import com.android.tradefed.result.TestDescription;
28 import com.android.tradefed.result.TestRunResult;
29 
30 import java.util.Collection;
31 import java.util.HashMap;
32 
33 /**
34  * A Test that runs a set of instrumentation tests by running one adb command for per test.
35  */
36 class InstrumentationSerialTest implements IRemoteTest {
37 
38     /** number of attempts to make if test fails to run */
39     static final int FAILED_RUN_TEST_ATTEMPTS = 2;
40 
41     /** the set of tests to run */
42     private final Collection<TestDescription> mTests;
43 
44     private final InstrumentationTest mInstrumentationTest;
45 
46     /**
47      * Creates a {@link InstrumentationSerialTest}.
48      *
49      * @param instrumentationTest {@link InstrumentationTest} used to configure this class
50      * @param testsToRun a {@link Collection} of tests to run. Note this {@link Collection} will be
51      *     used as is (ie a reference to the testsToRun object will be kept).
52      */
InstrumentationSerialTest( InstrumentationTest instrumentationTest, Collection<TestDescription> testsToRun)53     InstrumentationSerialTest(
54             InstrumentationTest instrumentationTest, Collection<TestDescription> testsToRun)
55             throws ConfigurationException {
56         // reuse the InstrumentationTest class to perform actual test run
57         mInstrumentationTest = createInstrumentationTest(instrumentationTest);
58         // keep local copy of tests to be run
59         mTests = testsToRun;
60     }
61 
62     /**
63      * Create and initialize new instance of {@link InstrumentationTest}. Exposed for unit testing.
64      *
65      * @param instrumentationTest  {@link InstrumentationTest} used to configure this class
66      * @return  the newly created {@link InstrumentationTest}
67      */
createInstrumentationTest(InstrumentationTest instrumentationTest)68     InstrumentationTest createInstrumentationTest(InstrumentationTest instrumentationTest)
69             throws ConfigurationException {
70         InstrumentationTest runner = new InstrumentationTest();
71         OptionCopier.copyOptions(instrumentationTest, runner);
72         runner.setDevice(instrumentationTest.getDevice());
73         runner.setForceAbi(instrumentationTest.getForceAbi());
74         // ensure testFile is not used.
75         runner.setReRunUsingTestFile(false);
76         // no need to rerun when executing tests one by one
77         runner.setRerunMode(false);
78         return runner;
79     }
80 
81     /**
82      * {@inheritDoc}
83      */
84     @Override
run(final ITestInvocationListener listener)85     public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
86         if (mInstrumentationTest.getDevice() == null) {
87             throw new IllegalArgumentException("Device has not been set");
88         }
89         // reuse the InstrumentationTest class to perform actual test run
90         try {
91             for (TestDescription testToRun : mTests) {
92                 InstrumentationTest runner = createInstrumentationTest(mInstrumentationTest);
93                 runner.setClassName(testToRun.getClassName());
94                 runner.setMethodName(testToRun.getTestName());
95                 runTest(runner, listener, testToRun);
96             }
97         } catch (ConfigurationException e) {
98             CLog.e("Failed to create new InstrumentationTest: %s", e.getMessage());
99         }
100     }
101 
runTest( InstrumentationTest runner, ITestInvocationListener listener, TestDescription testToRun)102     private void runTest(
103             InstrumentationTest runner, ITestInvocationListener listener, TestDescription testToRun)
104             throws DeviceNotAvailableException {
105         // use a listener filter, to track if the test failed to run
106         CollectingTestListener trackingListener = new CollectingTestListener();
107         for (int i=1; i <= FAILED_RUN_TEST_ATTEMPTS; i++) {
108             runner.run(new ResultForwarder(trackingListener, listener));
109             if (trackingListener.getCurrentRunResults().getTestResults().containsKey(testToRun)) {
110                 return;
111             }
112             CLog.w(
113                     "Expected test %s did not run on attempt %d of %d",
114                     testToRun, i, FAILED_RUN_TEST_ATTEMPTS);
115         }
116         markTestAsFailed(testToRun, trackingListener.getCurrentRunResults(), listener);
117     }
118 
markTestAsFailed( TestDescription test, TestRunResult testRun, ITestInvocationListener listener)119     private void markTestAsFailed(
120             TestDescription test, TestRunResult testRun, ITestInvocationListener listener) {
121         listener.testRunStarted(testRun.getName(), 1);
122         listener.testStarted(test);
123 
124         String message =
125                 testRun.isRunFailure()
126                         ? testRun.getRunFailureMessage()
127                         : "The test was not initialized by the test runner.";
128         listener.testFailed(
129                 test, String.format("Test failed to run. Test run failed due to : %s", message));
130 
131         if (testRun.isRunFailure()) {
132             listener.testRunFailed(message);
133         }
134         listener.testEnded(test, new HashMap<String, Metric>());
135         listener.testRunEnded(0, new HashMap<String, Metric>());
136     }
137 }
138