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