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