blob: baafb76bfb6c83cd329a060456e167fe480b3a70 [file] [log] [blame]
/*
* Copyright (C) 2020 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.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.FailureDescription;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.result.LogcatCrashResultForwarder;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.result.proto.TestRecordProto.FailureStatus;
import com.android.tradefed.util.ProcessInfo;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Internal listener to Trade Federation for {@link InstrumentationTest}. It allows to collect extra
* information needed for easier debugging.
*/
final class InstrumentationListener extends LogcatCrashResultForwarder {
// Message from ddmlib InstrumentationResultParser for interrupted instrumentation.
private static final String DDMLIB_INSTRU_FAILURE_MSG = "Test run failed to complete";
private Set<TestDescription> mTests = new HashSet<>();
private Set<TestDescription> mDuplicateTests = new HashSet<>();
private final Collection<TestDescription> mExpectedTests;
private boolean mDisableDuplicateCheck = false;
private boolean mReportUnexecutedTests = false;
private ProcessInfo mSystemServerProcess = null;
/**
* @param device
* @param listeners
*/
public InstrumentationListener(
ITestDevice device,
Collection<TestDescription> expectedTests,
ITestInvocationListener... listeners) {
super(device, listeners);
mExpectedTests = expectedTests;
}
/** Whether or not to disable the duplicate test method check. */
public void setDisableDuplicateCheck(boolean disable) {
mDisableDuplicateCheck = disable;
}
public void setOriginalSystemServer(ProcessInfo info) {
mSystemServerProcess = info;
}
public void setReportUnexecutedTests(boolean enable) {
mReportUnexecutedTests = enable;
}
@Override
public void testRunStarted(String runName, int testCount) {
// In case of crash, run will attempt to report with 0
if (testCount == 0 && !mExpectedTests.isEmpty()) {
CLog.e("Run reported 0 tests while we collected %s", mExpectedTests.size());
super.testRunStarted(runName, mExpectedTests.size());
} else {
super.testRunStarted(runName, testCount);
}
}
@Override
public void testStarted(TestDescription test, long startTime) {
super.testStarted(test, startTime);
if (!mTests.add(test)) {
mDuplicateTests.add(test);
}
}
@Override
public void testRunFailed(FailureDescription error) {
if (error.getErrorMessage().startsWith(DDMLIB_INSTRU_FAILURE_MSG)) {
Set<TestDescription> expected = new LinkedHashSet<>(mExpectedTests);
expected.removeAll(mTests);
String helpMessage = String.format("The following tests didn't run: %s", expected);
error.setDebugHelpMessage(helpMessage);
error.setFailureStatus(FailureStatus.TEST_FAILURE);
if (mSystemServerProcess != null) {
boolean restarted = false;
try {
restarted = getDevice().deviceSoftRestarted(mSystemServerProcess);
} catch (DeviceNotAvailableException e) {
// Ignore
}
if (restarted) {
error.setFailureStatus(FailureStatus.SYSTEM_UNDER_TEST_CRASHED);
}
}
}
super.testRunFailed(error);
}
@Override
public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
if (!mDuplicateTests.isEmpty() && !mDisableDuplicateCheck) {
FailureDescription error =
FailureDescription.create(
String.format(
"The following tests ran more than once: %s. Check "
+ "your run configuration, you might be "
+ "including the same test class several "
+ "times.",
mDuplicateTests));
error.setFailureStatus(FailureStatus.TEST_FAILURE);
super.testRunFailed(error);
} else if (mReportUnexecutedTests && mExpectedTests.size() > mTests.size()) {
Set<TestDescription> missingTests = new LinkedHashSet<>(mExpectedTests);
missingTests.removeAll(mTests);
for (TestDescription miss : missingTests) {
super.testStarted(miss);
FailureDescription failure =
FailureDescription.create(
"test did not run due to instrumentation issue.",
FailureStatus.NOT_EXECUTED);
super.testFailed(miss, failure);
super.testEnded(miss, new HashMap<String, Metric>());
}
}
super.testRunEnded(elapsedTime, runMetrics);
}
}