blob: ea5d913ef671eb233d95ac260b9a5ae32a0b07d5 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.testtype;
import com.android.ddmlib.testrunner.TestIdentifier;
import com.android.tradefed.config.ConfigurationException;
import com.android.tradefed.config.OptionCopier;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.ResultForwarder;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* A Test that runs a set of instrumentation tests by running one adb command for per test.
*/
class InstrumentationSerialTest implements IRemoteTest {
/** number of attempts to make if test fails to run */
static final int FAILED_RUN_TEST_ATTEMPTS = 2;
/** the set of tests to run */
private final Collection<TestIdentifier> mTests;
private InstrumentationTest mInstrumentationTest = null;
/**
* Creates a {@link InstrumentationSerialTest}.
*
* @param instrumentationTest {@link InstrumentationTest} used to configure this class
* @param testsToRun a {@link Collection} of tests to run. Note this {@link Collection} will be
* used as is (ie a reference to the testsToRun object will be kept).
*/
InstrumentationSerialTest(InstrumentationTest instrumentationTest,
Collection<TestIdentifier> testsToRun) throws ConfigurationException {
// reuse the InstrumentationTest class to perform actual test run
mInstrumentationTest = createInstrumentationTest(instrumentationTest);
// keep local copy of tests to be run
mTests = testsToRun;
}
/**
* Create and initialize new instance of {@link InstrumentationTest}. Exposed for unit testing.
*
* @param instrumentationTest {@link InstrumentationTest} used to configure this class
* @return the newly created {@link InstrumentationTest}
*/
InstrumentationTest createInstrumentationTest(InstrumentationTest instrumentationTest)
throws ConfigurationException {
InstrumentationTest runner = new InstrumentationTest();
OptionCopier.copyOptions(instrumentationTest, runner);
runner.setDevice(instrumentationTest.getDevice());
runner.setForceAbi(instrumentationTest.getForceAbi());
// ensure testFile is not used.
runner.setReRunUsingTestFile(false);
// no need to rerun when executing tests one by one
runner.setRerunMode(false);
return runner;
}
/**
* {@inheritDoc}
*/
@Override
public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
if (mInstrumentationTest.getDevice() == null) {
throw new IllegalArgumentException("Device has not been set");
}
// reuse the InstrumentationTest class to perform actual test run
runTestsIndividually(listener);
}
private void runTestsIndividually(final ITestInvocationListener listener)
throws DeviceNotAvailableException {
try {
for (TestIdentifier testToRun : mTests) {
InstrumentationTest runner = createInstrumentationTest(mInstrumentationTest);
runner.setClassName(testToRun.getClassName());
runner.setMethodName(testToRun.getTestName());
runTest(runner, listener, testToRun);
}
} catch (ConfigurationException e) {
CLog.e("Failed to create new InstrumentationTest: %s", e.getMessage());
}
}
private void runTest(InstrumentationTest runner, ITestInvocationListener listener,
TestIdentifier testToRun) throws DeviceNotAvailableException {
// use a listener filter, to track if the test failed to run
TestTrackingListener trackingListener = new TestTrackingListener(listener, testToRun);
for (int i=1; i <= FAILED_RUN_TEST_ATTEMPTS; i++) {
runner.run(trackingListener);
if (trackingListener.didTestRun()) {
return;
} else {
CLog.w("Expected test %s did not run on attempt %d of %d", testToRun, i,
FAILED_RUN_TEST_ATTEMPTS);
}
}
trackingListener.markTestAsFailed();
}
private static class TestTrackingListener extends ResultForwarder {
private String mRunErrorMsg = null;
private final TestIdentifier mExpectedTest;
private boolean mDidTestRun = false;
private String mRunName;
public TestTrackingListener(ITestInvocationListener listener,
TestIdentifier testToRun) {
super(listener);
mExpectedTest = testToRun;
}
@Override
public void testRunStarted(String runName, int testCount) {
super.testRunStarted(runName, testCount);
mRunName = runName;
}
@Override
public void testRunFailed(String errorMessage) {
super.testRunFailed(errorMessage);
mRunErrorMsg = errorMessage;
}
@Override
public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
super.testEnded(test, testMetrics);
if (mExpectedTest.equals(test)) {
mDidTestRun = true;
} else {
// weird, should never happen
CLog.w(String.format("Expected test %s, but got test %s", mExpectedTest, test));
}
}
public boolean didTestRun() {
return mDidTestRun;
}
@SuppressWarnings("unchecked")
public void markTestAsFailed() {
super.testRunStarted(mRunName, 1);
super.testStarted(mExpectedTest);
super.testFailed(mExpectedTest, String.format(
"Test failed to run. Test run failed due to : %s", mRunErrorMsg));
if (mRunErrorMsg != null) {
super.testRunFailed(mRunErrorMsg);
}
super.testEnded(mExpectedTest, Collections.EMPTY_MAP);
super.testRunEnded(0, Collections.EMPTY_MAP);
}
}
}