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.ddmlib.testrunner.TestIdentifier;
     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.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.TestEndedEventInfo;
     29 import com.android.tradefed.util.SubprocessEventHelper.TestLogEventInfo;
     30 import com.android.tradefed.util.SubprocessEventHelper.TestRunEndedEventInfo;
     31 import com.android.tradefed.util.SubprocessEventHelper.TestRunFailedEventInfo;
     32 import com.android.tradefed.util.SubprocessEventHelper.TestRunStartedEventInfo;
     33 import com.android.tradefed.util.SubprocessEventHelper.TestStartedEventInfo;
     34 import com.android.tradefed.util.SubprocessTestResultsParser;
     35 
     36 import java.io.File;
     37 import java.io.FileWriter;
     38 import java.io.IOException;
     39 import java.io.PrintWriter;
     40 import java.net.Socket;
     41 import java.util.Map;
     42 
     43 /**
     44  * Implements {@link ITestInvocationListener} to be specified as a result_reporter and forward
     45  * from the subprocess the results of tests, test runs, test invocations.
     46  */
     47 public class SubprocessResultsReporter implements ITestInvocationListener, AutoCloseable {
     48 
     49     @Option(name = "subprocess-report-file", description = "the file where to log the events.")
     50     private File mReportFile = null;
     51 
     52     @Option(name = "subprocess-report-port", description = "the port where to connect to send the"
     53             + "events.")
     54     private Integer mReportPort = null;
     55 
     56     @Option(name = "output-test-log", description = "Option to report test logs to parent process.")
     57     private boolean mOutputTestlog = false;
     58 
     59     private Socket mReportSocket = null;
     60     private PrintWriter mPrintWriter = null;
     61 
     62     private boolean mPrintWarning = true;
     63 
     64     /**
     65      * {@inheritDoc}
     66      */
     67     @Override
     68     public void testAssumptionFailure(TestIdentifier testId, String trace) {
     69         FailedTestEventInfo info =
     70                 new FailedTestEventInfo(testId.getClassName(), testId.getTestName(), trace);
     71         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_ASSUMPTION_FAILURE, info);
     72     }
     73 
     74     /**
     75      * {@inheritDoc}
     76      */
     77     @Override
     78     public void testEnded(TestIdentifier testId, Map<String, String> metrics) {
     79         testEnded(testId, System.currentTimeMillis(), metrics);
     80     }
     81 
     82     /** {@inheritDoc} */
     83     @Override
     84     public void testEnded(TestIdentifier testId, long endTime, Map<String, String> metrics) {
     85         TestEndedEventInfo info =
     86                 new TestEndedEventInfo(
     87                         testId.getClassName(), testId.getTestName(), endTime, metrics);
     88         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_ENDED, info);
     89     }
     90 
     91     /** {@inheritDoc} */
     92     @Override
     93     public void testFailed(TestIdentifier testId, String reason) {
     94         FailedTestEventInfo info =
     95                 new FailedTestEventInfo(testId.getClassName(), testId.getTestName(), reason);
     96         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_FAILED, info);
     97     }
     98 
     99     /**
    100      * {@inheritDoc}
    101      */
    102     @Override
    103     public void testIgnored(TestIdentifier testId) {
    104         BaseTestEventInfo info = new BaseTestEventInfo(testId.getClassName(), testId.getTestName());
    105         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_IGNORED, info);
    106     }
    107 
    108     /**
    109      * {@inheritDoc}
    110      */
    111     @Override
    112     public void testRunEnded(long time, Map<String, String> runMetrics) {
    113         TestRunEndedEventInfo info = new TestRunEndedEventInfo(time, runMetrics);
    114         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_ENDED, info);
    115     }
    116 
    117     /**
    118      * {@inheritDoc}
    119      */
    120     @Override
    121     public void testRunFailed(String reason) {
    122         TestRunFailedEventInfo info = new TestRunFailedEventInfo(reason);
    123         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_FAILED, info);
    124     }
    125 
    126     @Override
    127     public void testRunStarted(String runName, int testCount) {
    128         TestRunStartedEventInfo info = new TestRunStartedEventInfo(runName, testCount);
    129         printEvent(SubprocessTestResultsParser.StatusKeys.TEST_RUN_STARTED, info);
    130     }
    131 
    132     /**
    133      * {@inheritDoc}
    134      */
    135     @Override
    136     public void testRunStopped(long arg0) {
    137         // ignore
    138     }
    139 
    140     /**
    141      * {@inheritDoc}
    142      */
    143     @Override
    144     public void testStarted(TestIdentifier testId) {
    145         testStarted(testId, System.currentTimeMillis());
    146     }
    147 
    148     /** {@inheritDoc} */
    149     @Override
    150     public void testStarted(TestIdentifier 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     /**
    190      * {@inheritDoc}
    191      */
    192     @Override
    193     public void invocationEnded(long elapsedTime) {
    194         // ignore
    195     }
    196 
    197     /**
    198      * {@inheritDoc}
    199      */
    200     @Override
    201     public void invocationFailed(Throwable cause) {
    202         InvocationFailedEventInfo info = new InvocationFailedEventInfo(cause);
    203         printEvent(SubprocessTestResultsParser.StatusKeys.INVOCATION_FAILED, info);
    204     }
    205 
    206     /**
    207      * {@inheritDoc}
    208      */
    209     @Override
    210     public TestSummary getSummary() {
    211         return null;
    212     }
    213 
    214     /**
    215      * Helper to print the event key and then the json object.
    216      */
    217     public void printEvent(String key, Object event) {
    218         if (mReportFile != null) {
    219             if (mReportFile.canWrite()) {
    220                 try {
    221                     try (FileWriter fw = new FileWriter(mReportFile, true)) {
    222                         String eventLog = String.format("%s %s\n", key, event.toString());
    223                         fw.append(eventLog);
    224                         fw.flush();
    225                     }
    226                 } catch (IOException e) {
    227                     throw new RuntimeException(e);
    228                 }
    229             } else {
    230                 throw new RuntimeException(
    231                         String.format("report file: %s is not writable",
    232                                 mReportFile.getAbsolutePath()));
    233             }
    234         }
    235         if(mReportPort != null) {
    236             try {
    237                 if (mReportSocket == null) {
    238                     mReportSocket = new Socket("localhost", mReportPort.intValue());
    239                     mPrintWriter = new PrintWriter(mReportSocket.getOutputStream(), true);
    240                 }
    241                 if (!mReportSocket.isConnected()) {
    242                     throw new RuntimeException("Reporter Socket is not connected");
    243                 }
    244                 String eventLog = String.format("%s %s\n", key, event.toString());
    245                 mPrintWriter.print(eventLog);
    246                 mPrintWriter.flush();
    247             } catch (IOException e) {
    248                 throw new RuntimeException(e);
    249             }
    250         }
    251         if (mReportFile == null && mReportPort == null) {
    252             if (mPrintWarning) {
    253                 // Only print the warning the first time.
    254                 mPrintWarning = false;
    255                 CLog.w("No report file or socket has been configured, skipping this reporter.");
    256             }
    257         }
    258     }
    259 
    260     /** {@inheritDoc} */
    261     @Override
    262     public void close() {
    263         StreamUtil.close(mReportSocket);
    264         StreamUtil.close(mPrintWriter);
    265     }
    266 }
    267