• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.build.IBuildInfo;
19 import com.android.tradefed.config.Option;
20 import com.android.tradefed.invoker.IInvocationContext;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
23 import com.android.tradefed.util.FileUtil;
24 import com.android.tradefed.util.StreamUtil;
25 import com.android.tradefed.util.SubprocessEventHelper.BaseTestEventInfo;
26 import com.android.tradefed.util.SubprocessEventHelper.FailedTestEventInfo;
27 import com.android.tradefed.util.SubprocessEventHelper.InvocationEndedEventInfo;
28 import com.android.tradefed.util.SubprocessEventHelper.InvocationFailedEventInfo;
29 import com.android.tradefed.util.SubprocessEventHelper.InvocationStartedEventInfo;
30 import com.android.tradefed.util.SubprocessEventHelper.LogAssociationEventInfo;
31 import com.android.tradefed.util.SubprocessEventHelper.TestEndedEventInfo;
32 import com.android.tradefed.util.SubprocessEventHelper.TestLogEventInfo;
33 import com.android.tradefed.util.SubprocessEventHelper.TestModuleStartedEventInfo;
34 import com.android.tradefed.util.SubprocessEventHelper.TestRunEndedEventInfo;
35 import com.android.tradefed.util.SubprocessEventHelper.TestRunFailedEventInfo;
36 import com.android.tradefed.util.SubprocessEventHelper.TestRunStartedEventInfo;
37 import com.android.tradefed.util.SubprocessEventHelper.TestStartedEventInfo;
38 import com.android.tradefed.util.SubprocessTestResultsParser;
39 import com.android.tradefed.util.proto.TfMetricProtoUtil;
40 
41 import org.json.JSONObject;
42 
43 import java.io.File;
44 import java.io.FileWriter;
45 import java.io.IOException;
46 import java.io.PrintWriter;
47 import java.net.Socket;
48 import java.util.HashMap;
49 import java.util.List;
50 
51 /**
52  * Implements {@link ITestInvocationListener} to be specified as a result_reporter and forward from
53  * the subprocess the results of tests, test runs, test invocations.
54  */
55 public class SubprocessResultsReporter
56         implements ITestInvocationListener, ILogSaverListener, AutoCloseable {
57 
58     @Option(name = "subprocess-report-file", description = "the file where to log the events.")
59     private File mReportFile = null;
60 
61     @Option(name = "subprocess-report-port", description = "the port where to connect to send the"
62             + "events.")
63     private Integer mReportPort = null;
64 
65     @Option(name = "output-test-log", description = "Option to report test logs to parent process.")
66     private boolean mOutputTestlog = false;
67 
68     private IBuildInfo mPrimaryBuildInfo = null;
69     private Socket mReportSocket = null;
70     private PrintWriter mPrintWriter = null;
71 
72     private boolean mPrintWarning = true;
73 
74     /** {@inheritDoc} */
75     @Override
testAssumptionFailure(TestDescription testId, String trace)76     public void testAssumptionFailure(TestDescription testId, String trace) {
77         FailedTestEventInfo info =
78                 new FailedTestEventInfo(testId.getClassName(), testId.getTestName(), trace);
79         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_ASSUMPTION_FAILURE, info);
80     }
81 
82     /** {@inheritDoc} */
83     @Override
testEnded(TestDescription testId, HashMap<String, Metric> metrics)84     public void testEnded(TestDescription testId, HashMap<String, Metric> metrics) {
85         testEnded(testId, System.currentTimeMillis(), metrics);
86     }
87 
88     /** {@inheritDoc} */
89     @Override
testEnded(TestDescription testId, long endTime, HashMap<String, Metric> metrics)90     public void testEnded(TestDescription testId, long endTime, HashMap<String, Metric> metrics) {
91         // TODO: transfer the proto metrics instead of string metrics
92         TestEndedEventInfo info =
93                 new TestEndedEventInfo(
94                         testId.getClassName(),
95                         testId.getTestName(),
96                         endTime,
97                         TfMetricProtoUtil.compatibleConvert(metrics));
98         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_ENDED, info);
99     }
100 
101     /** {@inheritDoc} */
102     @Override
testFailed(TestDescription testId, String reason)103     public void testFailed(TestDescription testId, String reason) {
104         FailedTestEventInfo info =
105                 new FailedTestEventInfo(testId.getClassName(), testId.getTestName(), reason);
106         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_FAILED, info);
107     }
108 
109     /** {@inheritDoc} */
110     @Override
testIgnored(TestDescription testId)111     public void testIgnored(TestDescription testId) {
112         BaseTestEventInfo info = new BaseTestEventInfo(testId.getClassName(), testId.getTestName());
113         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_IGNORED, info);
114     }
115 
116     /** {@inheritDoc} */
117     @Override
testRunEnded(long time, HashMap<String, Metric> runMetrics)118     public void testRunEnded(long time, HashMap<String, Metric> runMetrics) {
119         // TODO: Transfer the full proto instead of just Strings.
120         TestRunEndedEventInfo info =
121                 new TestRunEndedEventInfo(time, TfMetricProtoUtil.compatibleConvert(runMetrics));
122         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_ENDED, info);
123     }
124 
125     /** {@inheritDoc} */
126     @Override
testRunFailed(String reason)127     public void testRunFailed(String reason) {
128         TestRunFailedEventInfo info = new TestRunFailedEventInfo(reason);
129         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_FAILED, info);
130     }
131 
132     /** {@inheritDoc} */
133     @Override
testRunStarted(String runName, int testCount)134     public void testRunStarted(String runName, int testCount) {
135         testRunStarted(runName, testCount, 0);
136     }
137 
138     /** {@inheritDoc} */
139     @Override
testRunStarted(String runName, int testCount, int attemptNumber)140     public void testRunStarted(String runName, int testCount, int attemptNumber) {
141         TestRunStartedEventInfo info =
142                 new TestRunStartedEventInfo(runName, testCount, attemptNumber);
143         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_STARTED, info);
144     }
145 
146     /**
147      * {@inheritDoc}
148      */
149     @Override
testRunStopped(long arg0)150     public void testRunStopped(long arg0) {
151         // ignore
152     }
153 
154     /** {@inheritDoc} */
155     @Override
testStarted(TestDescription testId)156     public void testStarted(TestDescription testId) {
157         testStarted(testId, System.currentTimeMillis());
158     }
159 
160     /** {@inheritDoc} */
161     @Override
testStarted(TestDescription testId, long startTime)162     public void testStarted(TestDescription testId, long startTime) {
163         TestStartedEventInfo info =
164                 new TestStartedEventInfo(testId.getClassName(), testId.getTestName(), startTime);
165         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_STARTED, info);
166     }
167 
168     /**
169      * {@inheritDoc}
170      */
171     @Override
invocationStarted(IInvocationContext context)172     public void invocationStarted(IInvocationContext context) {
173         InvocationStartedEventInfo info =
174                 new InvocationStartedEventInfo(context.getTestTag(), System.currentTimeMillis());
175         printEvent(SubprocessTestResultsParser.StatusKeys.INVOCATION_STARTED, info);
176 
177         // Save off primary build info so that we can parse it later during invocation ended.
178         List<IBuildInfo> infos = context.getBuildInfos();
179         mPrimaryBuildInfo = infos.isEmpty() ? null : infos.get(0);
180     }
181 
182     /** {@inheritDoc} */
183     @Override
testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)184     public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) {
185         if (!mOutputTestlog || (mReportPort == null && mReportFile == null)) {
186             return;
187         }
188         if (dataStream != null && dataStream.size() != 0) {
189             File tmpFile = null;
190             try {
191                 // put 'subprocess' in front to identify the files.
192                 tmpFile =
193                         FileUtil.createTempFile(
194                                 "subprocess-" + dataName, "." + dataType.getFileExt());
195                 FileUtil.writeToFile(dataStream.createInputStream(), tmpFile);
196                 TestLogEventInfo info = new TestLogEventInfo(dataName, dataType, tmpFile);
197                 printEvent(SubprocessTestResultsParser.StatusKeys.TEST_LOG, info);
198             } catch (IOException e) {
199                 CLog.e(e);
200                 FileUtil.deleteFile(tmpFile);
201             }
202         }
203     }
204 
205     /** {@inheritDoc} */
206     @Override
logAssociation(String dataName, LogFile logFile)207     public void logAssociation(String dataName, LogFile logFile) {
208         LogAssociationEventInfo info =
209                 new LogAssociationEventInfo("subprocess-a-" + dataName, logFile);
210         printEvent(SubprocessTestResultsParser.StatusKeys.LOG_ASSOCIATION, info);
211     }
212 
213     /**
214      * {@inheritDoc}
215      */
216     @Override
invocationEnded(long elapsedTime)217     public void invocationEnded(long elapsedTime) {
218         if (mPrimaryBuildInfo == null) {
219             return;
220         }
221         InvocationEndedEventInfo eventEnd =
222                 new InvocationEndedEventInfo(mPrimaryBuildInfo.getBuildAttributes());
223         printEvent(SubprocessTestResultsParser.StatusKeys.INVOCATION_ENDED, eventEnd);
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
invocationFailed(Throwable cause)230     public void invocationFailed(Throwable cause) {
231         InvocationFailedEventInfo info = new InvocationFailedEventInfo(cause);
232         printEvent(SubprocessTestResultsParser.StatusKeys.INVOCATION_FAILED, info);
233     }
234 
235     /** {@inheritDoc} */
236     @Override
testModuleStarted(IInvocationContext moduleContext)237     public void testModuleStarted(IInvocationContext moduleContext) {
238         TestModuleStartedEventInfo info = new TestModuleStartedEventInfo(moduleContext);
239         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_MODULE_STARTED, info);
240     }
241 
242     /** {@inheritDoc} */
243     @Override
testModuleEnded()244     public void testModuleEnded() {
245         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_MODULE_ENDED, new JSONObject());
246     }
247 
248     /**
249      * {@inheritDoc}
250      */
251     @Override
getSummary()252     public TestSummary getSummary() {
253         return null;
254     }
255 
256     /**
257      * Helper to print the event key and then the json object.
258      */
printEvent(String key, Object event)259     public void printEvent(String key, Object event) {
260         if (mReportFile != null) {
261             if (mReportFile.canWrite()) {
262                 try {
263                     try (FileWriter fw = new FileWriter(mReportFile, true)) {
264                         String eventLog = String.format("%s %s\n", key, event.toString());
265                         fw.append(eventLog);
266                         fw.flush();
267                     }
268                 } catch (IOException e) {
269                     throw new RuntimeException(e);
270                 }
271             } else {
272                 throw new RuntimeException(
273                         String.format("report file: %s is not writable",
274                                 mReportFile.getAbsolutePath()));
275             }
276         }
277         if(mReportPort != null) {
278             try {
279                 if (mReportSocket == null) {
280                     mReportSocket = new Socket("localhost", mReportPort.intValue());
281                     mPrintWriter = new PrintWriter(mReportSocket.getOutputStream(), true);
282                 }
283                 if (!mReportSocket.isConnected()) {
284                     throw new RuntimeException("Reporter Socket is not connected");
285                 }
286                 String eventLog = String.format("%s %s\n", key, event.toString());
287                 mPrintWriter.print(eventLog);
288                 mPrintWriter.flush();
289             } catch (IOException e) {
290                 throw new RuntimeException(e);
291             }
292         }
293         if (mReportFile == null && mReportPort == null) {
294             if (mPrintWarning) {
295                 // Only print the warning the first time.
296                 mPrintWarning = false;
297                 CLog.w("No report file or socket has been configured, skipping this reporter.");
298             }
299         }
300     }
301 
302     /** {@inheritDoc} */
303     @Override
close()304     public void close() {
305         StreamUtil.close(mReportSocket);
306         StreamUtil.close(mPrintWriter);
307     }
308 
309     /** Sets whether or not we should output the test logged or not. */
setOutputTestLog(boolean outputTestLog)310     public void setOutputTestLog(boolean outputTestLog) {
311         mOutputTestlog = outputTestLog;
312     }
313 }
314