blob: aca10434aaf11c9ebdad990aedb43ec048a8f836 [file] [log] [blame]
/*
* Copyright (C) 2021 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.result;
import com.android.ddmlib.testrunner.TestResult.TestStatus;
import com.android.tradefed.config.IConfiguration;
import com.android.tradefed.config.IConfigurationReceiver;
import com.android.tradefed.invoker.IInvocationContext;
import com.android.tradefed.log.ITestLogger;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.testtype.suite.ModuleDefinition;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map.Entry;
import java.util.Set;
/** Report in a file possible filters to exclude passed test. */
public class ReportPassedTests extends CollectingTestListener implements IConfigurationReceiver {
private static final String PASSED_TEST_LOG = "passed_tests";
private boolean mInvocationFailed = false;
private ITestLogger mLogger;
private boolean mModuleInProgress;
private IInvocationContext mContextForEmptyModule;
private Integer mShardIndex;
private Set<String> mExtraTestCases = new LinkedHashSet<>();
public void setLogger(ITestLogger logger) {
mLogger = logger;
}
@Override
public void setConfiguration(IConfiguration configuration) {
if (configuration.getCommandOptions().getShardIndex() != null) {
mShardIndex = configuration.getCommandOptions().getShardIndex();
}
}
@Override
public void testModuleStarted(IInvocationContext moduleContext) {
super.testModuleStarted(moduleContext);
mModuleInProgress = true;
mContextForEmptyModule = moduleContext;
}
@Override
public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) {
mContextForEmptyModule = null;
super.testRunEnded(elapsedTime, runMetrics);
if (!mModuleInProgress) {
// Remove right away any run failure they will be excluded
if (getCurrentRunResults().isRunFailure() || mInvocationFailed) {
gatherPassedTests(
getCurrentRunResults(),
getBaseName(getCurrentRunResults()), mInvocationFailed);
clearResultsForName(getCurrentRunResults().getName());
// Clear the failure for aggregation
getCurrentRunResults().resetRunFailure();
}
}
}
@Override
public void testModuleEnded() {
if (mContextForEmptyModule != null) {
// If the module was empty
String moduleId = mContextForEmptyModule.getAttributes()
.getUniqueMap().get(ModuleDefinition.MODULE_ID);
if (moduleId != null) {
super.testRunStarted(moduleId, 0);
super.testRunEnded(0L, new HashMap<String, Metric>());
}
mContextForEmptyModule = null;
}
super.testModuleEnded();
// Remove right away any run failure they will be excluded
if (getCurrentRunResults().isRunFailure() || mInvocationFailed) {
gatherPassedTests(
getCurrentRunResults(), getBaseName(getCurrentRunResults()), mInvocationFailed);
clearResultsForName(getCurrentRunResults().getName());
// Clear the failure for aggregation
getCurrentRunResults().resetRunFailure();
}
mModuleInProgress = false;
}
@Override
public void invocationFailed(FailureDescription failure) {
super.invocationFailed(failure);
mInvocationFailed = true;
}
@Override
public void invocationEnded(long elapsedTime) {
super.invocationEnded(elapsedTime);
createPassedLog();
}
private void createPassedLog() {
if (mLogger == null) {
return;
}
StringBuilder sb = new StringBuilder();
for (TestRunResult result : getMergedTestRunResults()) {
sb.append(createFilters(result, getBaseName(result), false));
}
if (!mExtraTestCases.isEmpty()) {
sb.append(Joiner.on("\n").join(mExtraTestCases));
mExtraTestCases.clear();
}
if (sb.length() == 0) {
CLog.d("No new filter for passed_test");
return;
}
testLog(sb.toString());
}
@VisibleForTesting
void testLog(String toBeLogged) {
try (ByteArrayInputStreamSource source =
new ByteArrayInputStreamSource(toBeLogged.getBytes())) {
mLogger.testLog(PASSED_TEST_LOG, LogDataType.PASSED_TESTS, source);
}
}
private String getBaseName(TestRunResult runResult) {
IInvocationContext context = getModuleContextForRunResult(runResult.getName());
// If it's a test module
if (context != null) {
return context.getAttributes().getUniqueMap().get(ModuleDefinition.MODULE_ID);
} else {
return runResult.getName();
}
}
private String createFilters(
TestRunResult runResult, String baseName, boolean invocationFailure) {
if (mShardIndex != null) {
baseName = "shard_" + mShardIndex + " " + baseName;
}
StringBuilder sb = new StringBuilder();
if (!runResult.hasFailedTests() && !runResult.isRunFailure() && !invocationFailure) {
sb.append(baseName);
sb.append("\n");
return sb.toString();
}
for (Entry<TestDescription, TestResult> res : runResult.getTestResults().entrySet()) {
if (TestStatus.FAILURE.equals(res.getValue().getStatus())) {
continue;
}
sb.append(baseName + " " + res.getKey().toString());
sb.append("\n");
}
return sb.toString();
}
private void gatherPassedTests(
TestRunResult runResult, String baseName, boolean invocationFailure) {
StringBuilder sb = new StringBuilder();
sb.append(createFilters(runResult, baseName, invocationFailure));
if (sb.length() == 0L) {
return;
}
mExtraTestCases.add(sb.toString());
}
}