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