1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.surefire.junit;
20
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.Method;
23 import java.util.HashSet;
24 import java.util.Set;
25
26 import org.apache.maven.surefire.api.report.LegacyPojoStackTraceWriter;
27 import org.apache.maven.surefire.api.report.ReportEntry;
28 import org.apache.maven.surefire.api.report.RunListener;
29 import org.apache.maven.surefire.api.report.SimpleReportEntry;
30 import org.apache.maven.surefire.api.report.StackTraceWriter;
31
32 import static java.util.Objects.requireNonNull;
33 import static org.apache.maven.surefire.api.report.RunMode.NORMAL_RUN;
34 import static org.apache.maven.surefire.api.report.SimpleReportEntry.withException;
35 import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractClassName;
36 import static org.apache.maven.surefire.api.util.internal.TestClassMethodNameUtils.extractMethodName;
37
38
39
40
41
42 public class TestListenerInvocationHandler implements InvocationHandler {
43
44 private static final String START_TEST = "startTest";
45
46 private static final String ADD_FAILURE = "addFailure";
47
48 private static final String ADD_ERROR = "addError";
49
50 private static final String END_TEST = "endTest";
51
52 private final Set<FailedTest> failedTestsSet = new HashSet<>();
53
54 private final JUnit3Reporter reporter;
55
56 private static final Class<?>[] EMPTY_CLASS_ARRAY = {};
57
58 private static final Object[] EMPTY_STRING_ARRAY = {};
59
60 private static class FailedTest {
61 private final Object testThatFailed;
62 private final Thread threadOnWhichTestFailed;
63
64 FailedTest(Object testThatFailed, Thread threadOnWhichTestFailed) {
65 this.testThatFailed = requireNonNull(testThatFailed, "testThatFailed is null");
66
67 this.threadOnWhichTestFailed = requireNonNull(threadOnWhichTestFailed, "threadOnWhichTestFailed is null");
68 }
69
70 @Override
71 public boolean equals(Object obj) {
72 boolean retVal = true;
73
74 if (obj == null || getClass() != obj.getClass()) {
75 retVal = false;
76 } else {
77 FailedTest ft = (FailedTest) obj;
78
79 if (ft.testThatFailed != testThatFailed) {
80 retVal = false;
81 } else if (!ft.threadOnWhichTestFailed.equals(threadOnWhichTestFailed)) {
82 retVal = false;
83 }
84 }
85
86 return retVal;
87 }
88
89 @Override
90 public int hashCode() {
91 return threadOnWhichTestFailed.hashCode();
92 }
93 }
94
95 public TestListenerInvocationHandler(JUnit3Reporter reporter) {
96 this.reporter = requireNonNull(reporter, "reporter is null");
97 }
98
99 @Override
100 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
101 String methodName = method.getName();
102
103 switch (methodName) {
104 case START_TEST:
105 handleStartTest(args);
106 break;
107 case ADD_ERROR:
108 handleAddError(args);
109 break;
110 case ADD_FAILURE:
111 handleAddFailure(args);
112 break;
113 case END_TEST:
114 handleEndTest(args);
115 break;
116 default:
117 break;
118 }
119
120 return null;
121 }
122
123
124 private void handleStartTest(Object[] args) {
125 ReportEntry report = createStartEndReportEntry(args);
126
127 reporter.testStarting(report);
128 }
129
130
131 private void handleAddError(Object[] args) throws ReflectiveOperationException {
132 ReportEntry report = toReportEntryWithException(args);
133
134 reporter.testError(report);
135
136 failedTestsSet.add(new FailedTest(args[0], Thread.currentThread()));
137 }
138
139 private static LegacyPojoStackTraceWriter toStackTraceWriter(Object[] args) throws ReflectiveOperationException {
140 String testName;
141
142 try {
143 Method m = args[0].getClass().getMethod("getName", EMPTY_CLASS_ARRAY);
144 testName = (String) m.invoke(args[0], EMPTY_STRING_ARRAY);
145 } catch (NoSuchMethodException e) {
146 testName = "UNKNOWN";
147 }
148
149 return new LegacyPojoStackTraceWriter(args[0].getClass().getName(), testName, (Throwable) args[1]);
150 }
151
152 private void handleAddFailure(Object[] args) throws ReflectiveOperationException {
153 ReportEntry report = toReportEntryWithException(args);
154
155 reporter.testFailed(report);
156
157 failedTestsSet.add(new FailedTest(args[0], Thread.currentThread()));
158 }
159
160 private void handleEndTest(Object[] args) {
161 boolean testHadFailed = failedTestsSet.remove(new FailedTest(args[0], Thread.currentThread()));
162
163 if (!testHadFailed) {
164 ReportEntry report = createStartEndReportEntry(args);
165
166 reporter.testSucceeded(report);
167 }
168 }
169
170 private ReportEntry toReportEntryWithException(Object[] args) throws ReflectiveOperationException {
171 String description = args[0].toString();
172 String className = extractClassName(description);
173 String methodName = extractMethodName(description);
174 StackTraceWriter stackTraceWriter = toStackTraceWriter(args);
175 long testRunId = reporter.getClassMethodIndexer().indexClassMethod(className, methodName);
176 return withException(NORMAL_RUN, testRunId, className, null, methodName, null, stackTraceWriter);
177 }
178
179 private SimpleReportEntry createStartEndReportEntry(Object[] args) {
180 String description = args[0].toString();
181 String className = extractClassName(description);
182 String methodName = extractMethodName(description);
183 long testRunId = reporter.getClassMethodIndexer().indexClassMethod(className, methodName);
184 return new SimpleReportEntry(NORMAL_RUN, testRunId, className, null, methodName, null);
185 }
186 }