1 /* 2 * Copyright (C) 2021 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 package com.android.tradefed.result; 17 18 import com.android.tradefed.config.IConfiguration; 19 import com.android.tradefed.config.IConfigurationReceiver; 20 import com.android.tradefed.invoker.IInvocationContext; 21 import com.android.tradefed.log.ITestLogger; 22 import com.android.tradefed.log.LogUtil.CLog; 23 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric; 24 import com.android.tradefed.result.retry.ISupportGranularResults; 25 import com.android.tradefed.testtype.suite.ModuleDefinition; 26 import com.android.tradefed.util.FileUtil; 27 28 import com.google.common.annotations.VisibleForTesting; 29 30 import java.io.File; 31 import java.io.IOException; 32 import java.util.HashMap; 33 import java.util.Map.Entry; 34 35 /** Report in a file possible filters to exclude passed test. */ 36 public class ReportPassedTests extends CollectingTestListener 37 implements IConfigurationReceiver, ISupportGranularResults { 38 39 private static final String PASSED_TEST_LOG = "passed_tests"; 40 private boolean mInvocationFailed = false; 41 private ITestLogger mLogger; 42 private boolean mModuleInProgress; 43 private IInvocationContext mContextForEmptyModule; 44 private Integer mShardIndex; 45 private File mPassedTests; 46 setLogger(ITestLogger logger)47 public void setLogger(ITestLogger logger) { 48 mLogger = logger; 49 } 50 51 @Override supportGranularResults()52 public boolean supportGranularResults() { 53 return false; 54 } 55 56 @Override invocationStarted(IInvocationContext context)57 public void invocationStarted(IInvocationContext context) { 58 super.invocationStarted(context); 59 try { 60 mPassedTests = FileUtil.createTempFile(PASSED_TEST_LOG, ".txt"); 61 } catch (IOException e) { 62 CLog.e(e); 63 } 64 } 65 66 @Override setConfiguration(IConfiguration configuration)67 public void setConfiguration(IConfiguration configuration) { 68 if (configuration.getCommandOptions().getShardIndex() != null) { 69 mShardIndex = configuration.getCommandOptions().getShardIndex(); 70 } 71 } 72 73 @Override testModuleStarted(IInvocationContext moduleContext)74 public void testModuleStarted(IInvocationContext moduleContext) { 75 super.testModuleStarted(moduleContext); 76 mModuleInProgress = true; 77 mContextForEmptyModule = moduleContext; 78 } 79 80 @Override testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics)81 public void testRunEnded(long elapsedTime, HashMap<String, Metric> runMetrics) { 82 mContextForEmptyModule = null; 83 super.testRunEnded(elapsedTime, runMetrics); 84 if (!mModuleInProgress) { 85 gatherPassedTests( 86 getCurrentRunResults(), getBaseName(getCurrentRunResults()), mInvocationFailed); 87 clearResultsForName(getCurrentRunResults().getName()); 88 // Clear the failure for aggregation 89 getCurrentRunResults().resetRunFailure(); 90 } 91 } 92 93 @Override testModuleEnded()94 public void testModuleEnded() { 95 if (mContextForEmptyModule != null) { 96 // If the module was empty 97 String moduleId = mContextForEmptyModule.getAttributes() 98 .getUniqueMap().get(ModuleDefinition.MODULE_ID); 99 if (moduleId != null) { 100 super.testRunStarted(moduleId, 0); 101 super.testRunEnded(0L, new HashMap<String, Metric>()); 102 } 103 mContextForEmptyModule = null; 104 } 105 super.testModuleEnded(); 106 gatherPassedTests( 107 getCurrentRunResults(), getBaseName(getCurrentRunResults()), mInvocationFailed); 108 clearResultsForName(getCurrentRunResults().getName()); 109 // Clear the failure for aggregation 110 getCurrentRunResults().resetRunFailure(); 111 mModuleInProgress = false; 112 } 113 114 @Override invocationFailed(FailureDescription failure)115 public void invocationFailed(FailureDescription failure) { 116 super.invocationFailed(failure); 117 mInvocationFailed = true; 118 } 119 120 @Override invocationEnded(long elapsedTime)121 public void invocationEnded(long elapsedTime) { 122 try { 123 super.invocationEnded(elapsedTime); 124 createPassedLog(); 125 } finally { 126 FileUtil.deleteFile(mPassedTests); 127 } 128 } 129 createPassedLog()130 private void createPassedLog() { 131 if (mLogger == null || mPassedTests == null) { 132 return; 133 } 134 StringBuilder sb = new StringBuilder(); 135 for (TestRunResult result : getMergedTestRunResults()) { 136 sb.append(createFilters(result, getBaseName(result), false)); 137 } 138 if (sb.length() > 0) { 139 try { 140 FileUtil.writeToFile(sb.toString(), mPassedTests, true); 141 } catch (IOException e) { 142 CLog.e(e); 143 } 144 } 145 if (mPassedTests.length() == 0) { 146 CLog.d("No new filter for passed_test"); 147 return; 148 } 149 testLog(mPassedTests); 150 } 151 152 @VisibleForTesting testLog(File toBeLogged)153 void testLog(File toBeLogged) { 154 try (FileInputStreamSource source = new FileInputStreamSource(toBeLogged)) { 155 mLogger.testLog(PASSED_TEST_LOG, LogDataType.PASSED_TESTS, source); 156 } 157 } 158 getBaseName(TestRunResult runResult)159 private String getBaseName(TestRunResult runResult) { 160 IInvocationContext context = getModuleContextForRunResult(runResult.getName()); 161 // If it's a test module 162 if (context != null) { 163 return context.getAttributes().getUniqueMap().get(ModuleDefinition.MODULE_ID); 164 } else { 165 return runResult.getName(); 166 } 167 } 168 createFilters( TestRunResult runResult, String baseName, boolean invocationFailure)169 private String createFilters( 170 TestRunResult runResult, String baseName, boolean invocationFailure) { 171 if (mShardIndex != null) { 172 baseName = "shard_" + mShardIndex + " " + baseName; 173 } 174 StringBuilder sb = new StringBuilder(); 175 if (!runResult.hasFailedTests() && !runResult.isRunFailure() && !invocationFailure) { 176 sb.append(baseName); 177 sb.append("\n"); 178 return sb.toString(); 179 } 180 for (Entry<TestDescription, TestResult> res : runResult.getTestResults().entrySet()) { 181 if (TestStatus.FAILURE.equals(res.getValue().getResultStatus())) { 182 continue; 183 } 184 // Consider SKIPPED as failure so it can be retried 185 if (TestStatus.SKIPPED.equals(res.getValue().getResultStatus())) { 186 continue; 187 } 188 sb.append(baseName + " " + res.getKey().toString()); 189 sb.append("\n"); 190 } 191 return sb.toString(); 192 } 193 gatherPassedTests( TestRunResult runResult, String baseName, boolean invocationFailure)194 private void gatherPassedTests( 195 TestRunResult runResult, String baseName, boolean invocationFailure) { 196 StringBuilder sb = new StringBuilder(); 197 sb.append(createFilters(runResult, baseName, invocationFailure)); 198 if (sb.length() == 0L) { 199 return; 200 } 201 try { 202 FileUtil.writeToFile(sb.toString(), mPassedTests, true); 203 } catch (IOException e) { 204 CLog.e(e); 205 } 206 } 207 } 208