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