• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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.resultdb;
17 
18 import com.android.tradefed.build.IBuildInfo;
19 import com.android.tradefed.config.ConfigurationDescriptor;
20 import com.android.tradefed.invoker.IInvocationContext;
21 import com.android.tradefed.invoker.InvocationContext;
22 import com.android.tradefed.metrics.proto.MetricMeasurement;
23 import com.android.tradefed.result.FailureDescription;
24 import com.android.tradefed.result.ILogSaverListener;
25 import com.android.tradefed.result.ITestSummaryListener;
26 import com.android.tradefed.result.LogFile;
27 import com.android.tradefed.result.TestDescription;
28 import com.android.tradefed.result.TestSummary;
29 import com.android.tradefed.result.skipped.SkipReason;
30 import com.android.tradefed.util.MultiMap;
31 
32 import java.time.LocalDateTime;
33 import java.time.ZoneId;
34 import java.time.ZonedDateTime;
35 import java.time.format.DateTimeFormatter;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 
43 /** Invocation simulator for testing. */
44 public class InvocationSimulator {
45 
46     public enum TestStatus {
47         PASS("pass"),
48         FAIL("fail"),
49         IGNORED("ignored"),
50         ASSUMPTION_FAILURE("assumptionFailure"),
51         TEST_ERROR("testError"),
52         TEST_SKIPPED("testSkipped");
53 
54         private final String value;
55 
TestStatus(String value)56         private TestStatus(String value) {
57             this.value = value;
58         }
59 
60         @Override
toString()61         public String toString() {
62             return value;
63         }
64     }
65 
66     private String mModuleName = "example-module";
67     private MultiMap<String, String> mModuleMetadata = new MultiMap<>();
68     private Map<String, String> mModuleAttributes = new HashMap<>();
69     private List<TestDescription> mTests;
70     private List<TestStatus> mTestOutcomes;
71     private List<HashMap<String, MetricMeasurement.Metric>> mTestMetricsList;
72     private Map<TestDescription, FailureDescription> mFailureDescriptions;
73     private String mSummary = "";
74     private String mTestRunError = "";
75     private FailureDescription mTestRunDescription;
76     private HashMap<String, MetricMeasurement.Metric> mTestRunMetrics;
77     private String mInvocationId;
78     private String mWorkUnitId;
79     private String mLegacyResultId;
80     private Map<String, String> mInvocationAttributes;
81     private List<IBuildInfo> mBuildInfos;
82     private List<LogFile> mTestLogs;
83     private List<LogFile> mTestRunLogs;
84     private List<LogFile> mInvocationLogs;
85     private boolean mIgnoreExceptions = false;
86     private boolean mSkipTestRuns = false;
87 
create()88     public static InvocationSimulator create() {
89         return new InvocationSimulator();
90     }
91 
InvocationSimulator()92     private InvocationSimulator() {
93         mTests = new ArrayList<>();
94         mTestOutcomes = new ArrayList<>();
95         mTestMetricsList = new ArrayList<>();
96         mFailureDescriptions = new HashMap<>();
97         mTestRunMetrics = new HashMap<>();
98         mBuildInfos = new ArrayList<>();
99         mTestLogs = new ArrayList<>();
100         mTestRunLogs = new ArrayList<>();
101         mInvocationLogs = new ArrayList<>();
102         mInvocationAttributes = new HashMap<>();
103     }
104 
setInvocationId(String invocationId)105     public InvocationSimulator setInvocationId(String invocationId) {
106         mInvocationId = invocationId;
107         return this;
108     }
109 
setWorkUnitId(String workUnitId)110     public InvocationSimulator setWorkUnitId(String workUnitId) {
111         mWorkUnitId = workUnitId;
112         return this;
113     }
114 
setLegacyResultId(String id)115     public InvocationSimulator setLegacyResultId(String id) {
116         mLegacyResultId = id;
117         return this;
118     }
119 
withBuildInfo(IBuildInfo info)120     public InvocationSimulator withBuildInfo(IBuildInfo info) {
121         mBuildInfos.add(info);
122         return this;
123     }
124 
withInvocationAttribute(String name, String value)125     public InvocationSimulator withInvocationAttribute(String name, String value) {
126         mInvocationAttributes.put(name, value);
127         return this;
128     }
129 
getModule()130     private IInvocationContext getModule() {
131         ConfigurationDescriptor module = new ConfigurationDescriptor();
132         module.setModuleName(mModuleName);
133         if (!mModuleMetadata.isEmpty()) {
134             module.setMetaData(mModuleMetadata);
135         }
136         InvocationContext moduleContext = createInvocation();
137         moduleContext.setConfigurationDescriptor(module);
138         for (Map.Entry<String, String> attribute : mModuleAttributes.entrySet()) {
139             moduleContext.addInvocationAttribute(attribute.getKey(), attribute.getValue());
140         }
141         return moduleContext;
142     }
143 
getStartTime()144     private long getStartTime() {
145         String in = "2018-09-07 15:23:45";
146         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
147         ZonedDateTime date = formatter.parse(in, LocalDateTime::from).atZone(ZoneId.of("UTC"));
148         return date.toInstant().toEpochMilli();
149     }
150 
withTest(String clazz, String method)151     public InvocationSimulator withTest(String clazz, String method) {
152         return withTest(clazz, method, TestStatus.PASS);
153     }
154 
withTest(String clazz, String method, TestStatus outcome)155     public InvocationSimulator withTest(String clazz, String method, TestStatus outcome) {
156         return withTest(clazz, method, outcome, new HashMap<String, MetricMeasurement.Metric>());
157     }
158 
withTestFailure( String clazz, String method, FailureDescription failure)159     public InvocationSimulator withTestFailure(
160             String clazz, String method, FailureDescription failure) {
161         mFailureDescriptions.put(new TestDescription(clazz, method), failure);
162         return this;
163     }
164 
withTest( String clazz, String method, TestStatus outcome, HashMap<String, MetricMeasurement.Metric> metrics)165     public InvocationSimulator withTest(
166             String clazz,
167             String method,
168             TestStatus outcome,
169             HashMap<String, MetricMeasurement.Metric> metrics) {
170         mTests.add(new TestDescription(clazz, method));
171         mTestOutcomes.add(outcome);
172         mTestMetricsList.add(metrics);
173         return this;
174     }
175 
withModule(String module)176     public InvocationSimulator withModule(String module) {
177         mModuleName = module;
178         return this;
179     }
180 
withModuleMetadata(String name, String value)181     public InvocationSimulator withModuleMetadata(String name, String value) {
182         mModuleMetadata.put(name, value);
183         return this;
184     }
185 
withModuleAttribute(String name, String value)186     public InvocationSimulator withModuleAttribute(String name, String value) {
187         mModuleAttributes.put(name, value);
188         return this;
189     }
190 
withoutModules()191     public InvocationSimulator withoutModules() {
192         mModuleName = "";
193         return this;
194     }
195 
withSummary(String summary)196     public InvocationSimulator withSummary(String summary) {
197         mSummary = summary;
198         return this;
199     }
200 
withTestRunMetrics( HashMap<String, MetricMeasurement.Metric> runMetrics)201     public InvocationSimulator withTestRunMetrics(
202             HashMap<String, MetricMeasurement.Metric> runMetrics) {
203         mTestRunMetrics = runMetrics;
204         return this;
205     }
206 
withTestRunFailure(String message)207     public InvocationSimulator withTestRunFailure(String message) {
208         mTestRunError = message;
209         return this;
210     }
211 
withTestRunFailure(FailureDescription failure)212     public InvocationSimulator withTestRunFailure(FailureDescription failure) {
213         mTestRunDescription = failure;
214         return this;
215     }
216 
withTestLog(LogFile log)217     public InvocationSimulator withTestLog(LogFile log) {
218         mTestLogs.add(log);
219         return this;
220     }
221 
withTestRunLog(LogFile log)222     public InvocationSimulator withTestRunLog(LogFile log) {
223         mTestRunLogs.add(log);
224         return this;
225     }
226 
withInvocationLog(LogFile log)227     public InvocationSimulator withInvocationLog(LogFile log) {
228         mInvocationLogs.add(log);
229         return this;
230     }
231 
ignoreExceptions()232     public InvocationSimulator ignoreExceptions() {
233         mIgnoreExceptions = true;
234         return this;
235     }
236 
skipTestRuns()237     public InvocationSimulator skipTestRuns() {
238         mSkipTestRuns = true;
239         return this;
240     }
241 
simulateInvocation( T reporter)242     public <T extends ILogSaverListener & ITestSummaryListener> void simulateInvocation(
243             T reporter) {
244         if (!mSummary.isEmpty()) {
245             reporter.putEarlySummary(Arrays.asList(new TestSummary(mSummary)));
246         }
247         try {
248             reporter.invocationStarted(createInvocation());
249         } catch (Exception e) {
250             if (!mIgnoreExceptions) {
251                 throw e;
252             }
253         }
254         try {
255             if (!mModuleName.isEmpty()) {
256                 reporter.testModuleStarted(getModule());
257             }
258         } catch (Exception e) {
259             if (!mIgnoreExceptions) {
260                 throw e;
261             }
262         }
263         try {
264             if (!mSkipTestRuns) {
265                 reporter.testRunStarted("TestRun1", 1, 1);
266             }
267         } catch (Exception e) {
268             if (!mIgnoreExceptions) {
269                 throw e;
270             }
271         }
272 
273         simulateTests(reporter);
274         if (!mTestRunError.isEmpty()) {
275             reporter.testRunFailed(mTestRunError);
276         }
277         if (mTestRunDescription != null) {
278             reporter.testRunFailed(mTestRunDescription);
279         }
280         simulateLogs("test-run-", mTestRunLogs, reporter);
281         if (!mSkipTestRuns) {
282             reporter.testRunEnded(1000L, mTestRunMetrics);
283         }
284         if (!mModuleName.isEmpty()) {
285             reporter.testModuleEnded();
286         }
287         simulateLogs("invocation-log-", mInvocationLogs, reporter);
288         reporter.invocationEnded(100);
289     }
290 
createInvocation()291     private InvocationContext createInvocation() {
292         InvocationContext context = new InvocationContext();
293         context.setTestTag("test tag");
294         context.addInvocationAttribute("invocation_id", mInvocationId);
295         context.addInvocationAttribute("work_unit_id", mWorkUnitId);
296         context.addInvocationAttribute("test_result_id", mLegacyResultId);
297         for (int i = 0; i < mBuildInfos.size(); i++) {
298             context.addDeviceBuildInfo(String.format("device_%d", i), mBuildInfos.get(i));
299         }
300         for (Map.Entry<String, String> attr : mInvocationAttributes.entrySet()) {
301             context.addInvocationAttribute(attr.getKey(), attr.getValue());
302         }
303         return context;
304     }
305 
simulateTests(T reporter)306     private <T extends ILogSaverListener & ITestSummaryListener> void simulateTests(T reporter) {
307         long startTime = getStartTime();
308         for (int i = 0; i < mTests.size(); i++) {
309             reporter.testStarted(mTests.get(i), startTime);
310             startTime += 100;
311             switch (mTestOutcomes.get(i)) {
312                 case FAIL:
313                     reporter.testFailed(mTests.get(i), "Fail Trace");
314                     break;
315                 case ASSUMPTION_FAILURE:
316                     reporter.testAssumptionFailure(mTests.get(i), "Assumption Fail Trace");
317                     break;
318                 case IGNORED:
319                     reporter.testIgnored(mTests.get(i));
320                     break;
321                 case TEST_SKIPPED:
322                     reporter.testSkipped(
323                             mTests.get(i),
324                             new SkipReason("skip reason message", "skip trigger", "bugId"));
325                     break;
326                 default:
327                     break;
328             }
329             simulateLogs("test-log-", mTestLogs, reporter);
330             reporter.testEnded(mTests.get(i), startTime, mTestMetricsList.get(i));
331             startTime += 100;
332         }
333         for (Map.Entry<TestDescription, FailureDescription> entry :
334                 mFailureDescriptions.entrySet()) {
335             reporter.testStarted(entry.getKey(), startTime);
336             startTime += 100;
337             reporter.testFailed(entry.getKey(), entry.getValue());
338             reporter.testEnded(entry.getKey(), startTime, Collections.emptyMap());
339             startTime += 100;
340         }
341     }
342 
simulateLogs(String prefix, List<LogFile> logs, ILogSaverListener reporter)343     private void simulateLogs(String prefix, List<LogFile> logs, ILogSaverListener reporter) {
344         for (int i = 0; i < logs.size(); i++) {
345             String dataName = String.format("%s%d", prefix, i);
346             reporter.logAssociation(dataName, logs.get(i));
347         }
348     }
349 }
350