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.metrics.proto.MetricMeasurement.Metric;
19 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.LogAnnotation;
20 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.MetricAnnotation;
21 import com.android.tradefed.testtype.MetricTestCase.LogHolder;
22 import com.android.tradefed.util.StreamUtil;
23 
24 import org.junit.AssumptionViolatedException;
25 import org.junit.runner.Description;
26 import org.junit.runner.notification.Failure;
27 import org.junit.runner.notification.RunListener;
28 import org.junit.runners.model.MultipleFailureException;
29 
30 import java.lang.annotation.Annotation;
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
34 
35 /**
36  * Result forwarder from JUnit4 Runner.
37  */
38 public class JUnit4ResultForwarder extends RunListener {
39 
40     private ITestInvocationListener mListener;
41     private List<Throwable> mTestCaseFailures;
42 
JUnit4ResultForwarder(ITestInvocationListener listener)43     public JUnit4ResultForwarder(ITestInvocationListener listener) {
44         mListener = listener;
45         mTestCaseFailures = new ArrayList<>();
46     }
47 
48     @Override
testFailure(Failure failure)49     public void testFailure(Failure failure) throws Exception {
50         Description description = failure.getDescription();
51         if (description.getMethodName() == null) {
52             // In case of exception in @BeforeClass, the method name will be null
53             mListener.testRunFailed(String.format("Failed with trace: %s", failure.getTrace()));
54             return;
55         }
56         mTestCaseFailures.add(failure.getException());
57     }
58 
59     @Override
testAssumptionFailure(Failure failure)60     public void testAssumptionFailure(Failure failure) {
61         mTestCaseFailures.add(failure.getException());
62     }
63 
64     @Override
testStarted(Description description)65     public void testStarted(Description description) {
66         mTestCaseFailures.clear();
67         TestDescription testid =
68                 new TestDescription(
69                         description.getClassName(),
70                         description.getMethodName(),
71                         description.getAnnotations());
72         mListener.testStarted(testid);
73     }
74 
75     @Override
testFinished(Description description)76     public void testFinished(Description description) {
77         TestDescription testid =
78                 new TestDescription(
79                         description.getClassName(),
80                         description.getMethodName(),
81                         description.getAnnotations());
82         handleFailures(testid);
83         // Explore the Description to see if we find any Annotation metrics carrier
84         HashMap<String, Metric> metrics = new HashMap<>();
85         for (Description child : description.getChildren()) {
86             for (Annotation a : child.getAnnotations()) {
87                 if (a instanceof MetricAnnotation) {
88                     metrics.putAll(((MetricAnnotation) a).mMetrics);
89                 }
90                 if (a instanceof LogAnnotation) {
91                     // Log all the logs found.
92                     for (LogHolder log : ((LogAnnotation) a).mLogs) {
93                         mListener.testLog(log.mDataName, log.mDataType, log.mDataStream);
94                         StreamUtil.cancel(log.mDataStream);
95                     }
96                     ((LogAnnotation) a).mLogs.clear();
97                 }
98             }
99         }
100         //description.
101         mListener.testEnded(testid, metrics);
102     }
103 
104     @Override
testIgnored(Description description)105     public void testIgnored(Description description) throws Exception {
106         TestDescription testid =
107                 new TestDescription(
108                         description.getClassName(),
109                         description.getMethodName(),
110                         description.getAnnotations());
111         // We complete the event life cycle since JUnit4 fireIgnored is not within fireTestStarted
112         // and fireTestEnded.
113         mListener.testStarted(testid);
114         mListener.testIgnored(testid);
115         mListener.testEnded(testid, new HashMap<String, Metric>());
116     }
117 
118     /**
119      * Handle all the failure received from the JUnit4 tests, if a single
120      * AssumptionViolatedException is received then treat the test as assumption failure. Otherwise
121      * treat everything else as failure.
122      */
handleFailures(TestDescription testid)123     private void handleFailures(TestDescription testid) {
124         if (mTestCaseFailures.isEmpty()) {
125             return;
126         }
127         if (mTestCaseFailures.size() == 1) {
128             Throwable t = mTestCaseFailures.get(0);
129             if (t instanceof AssumptionViolatedException) {
130                 mListener.testAssumptionFailure(testid, StreamUtil.getStackTrace(t));
131             } else {
132                 mListener.testFailed(testid, StreamUtil.getStackTrace(t));
133             }
134         } else {
135             MultipleFailureException multiException =
136                     new MultipleFailureException(mTestCaseFailures);
137             mListener.testFailed(testid, StreamUtil.getStackTrace(multiException));
138         }
139     }
140 }
141