1 package org.apache.maven.surefire.junitplatform;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static org.apache.maven.surefire.report.SimpleReportEntry.ignored;
23 import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;
24 import static org.junit.platform.engine.TestExecutionResult.Status.FAILED;
25
26 import java.util.Optional;
27 import java.util.Set;
28 import java.util.concurrent.ConcurrentHashMap;
29
30 import org.apache.maven.surefire.report.PojoStackTraceWriter;
31 import org.apache.maven.surefire.report.RunListener;
32 import org.apache.maven.surefire.report.SimpleReportEntry;
33 import org.apache.maven.surefire.report.StackTraceWriter;
34 import org.junit.platform.engine.TestExecutionResult;
35 import org.junit.platform.engine.TestSource;
36 import org.junit.platform.engine.support.descriptor.ClassSource;
37 import org.junit.platform.engine.support.descriptor.MethodSource;
38 import org.junit.platform.launcher.TestExecutionListener;
39 import org.junit.platform.launcher.TestIdentifier;
40 import org.junit.platform.launcher.TestPlan;
41 import org.junit.platform.launcher.listeners.LegacyReportingUtils;
42
43
44
45
46 final class RunListenerAdapter
47 implements TestExecutionListener
48 {
49
50 private final RunListener runListener;
51
52 private TestPlan testPlan;
53
54 private Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet();
55
56 RunListenerAdapter( RunListener runListener )
57 {
58 this.runListener = runListener;
59 }
60
61 @Override
62 public void testPlanExecutionStarted( TestPlan testPlan )
63 {
64 updateTestPlan( testPlan );
65 }
66
67 @Override
68 public void testPlanExecutionFinished( TestPlan testPlan )
69 {
70 updateTestPlan( null );
71 }
72
73 @Override
74 public void executionStarted( TestIdentifier testIdentifier )
75 {
76 if ( testIdentifier.isContainer()
77 && testIdentifier.getSource().filter( ClassSource.class::isInstance ).isPresent() )
78 {
79 startTestSetIfPossible( testIdentifier );
80 }
81 if ( testIdentifier.isTest() )
82 {
83 ensureTestSetStarted( testIdentifier );
84 runListener.testStarting( createReportEntry( testIdentifier ) );
85 }
86 }
87
88 @Override
89 public void executionSkipped( TestIdentifier testIdentifier, String reason )
90 {
91 ensureTestSetStarted( testIdentifier );
92 String source = getLegacyReportingClassName( testIdentifier );
93 runListener.testSkipped( ignored( source, getLegacyReportingName( testIdentifier ), reason ) );
94 completeTestSetIfNecessary( testIdentifier );
95 }
96
97 @Override
98 public void executionFinished(
99 TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
100 {
101 if ( testExecutionResult.getStatus() == ABORTED )
102 {
103 runListener.testAssumptionFailure( createReportEntry( testIdentifier, testExecutionResult ) );
104 }
105 else if ( testExecutionResult.getStatus() == FAILED )
106 {
107 reportFailedTest( testIdentifier, testExecutionResult );
108 }
109 else if ( testIdentifier.isTest() )
110 {
111 runListener.testSucceeded( createReportEntry( testIdentifier ) );
112 }
113 completeTestSetIfNecessary( testIdentifier );
114 }
115
116 private void updateTestPlan( TestPlan testPlan )
117 {
118 this.testPlan = testPlan;
119 testSetNodes.clear();
120 }
121
122 private void ensureTestSetStarted( TestIdentifier testIdentifier )
123 {
124 if ( isTestSetStarted( testIdentifier ) )
125 {
126 return;
127 }
128 if ( testIdentifier.isTest() )
129 {
130 startTestSet( testPlan.getParent( testIdentifier ).orElse( testIdentifier ) );
131 }
132 else
133 {
134 startTestSet( testIdentifier );
135 }
136 }
137
138 private boolean isTestSetStarted( TestIdentifier testIdentifier )
139 {
140 return testSetNodes.contains( testIdentifier )
141 || testPlan.getParent( testIdentifier ).map( this::isTestSetStarted ).orElse( false );
142 }
143
144 private void startTestSetIfPossible( TestIdentifier testIdentifier )
145 {
146 if ( !isTestSetStarted( testIdentifier ) )
147 {
148 startTestSet( testIdentifier );
149 }
150 }
151
152 private void completeTestSetIfNecessary( TestIdentifier testIdentifier )
153 {
154 if ( testSetNodes.contains( testIdentifier ) )
155 {
156 completeTestSet( testIdentifier );
157 }
158 }
159
160 private void startTestSet( TestIdentifier testIdentifier )
161 {
162 runListener.testSetStarting( createTestSetReportEntry( testIdentifier ) );
163 testSetNodes.add( testIdentifier );
164 }
165
166 private void completeTestSet( TestIdentifier testIdentifier )
167 {
168 runListener.testSetCompleted( createTestSetReportEntry( testIdentifier ) );
169 testSetNodes.remove( testIdentifier );
170 }
171
172 private void reportFailedTest(
173 TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
174 {
175 SimpleReportEntry reportEntry = createReportEntry( testIdentifier, testExecutionResult );
176 if ( testExecutionResult.getThrowable().filter( AssertionError.class::isInstance ).isPresent() )
177 {
178 runListener.testFailed( reportEntry );
179 }
180 else
181 {
182 runListener.testError( reportEntry );
183 }
184 }
185
186 private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier )
187 {
188 return new SimpleReportEntry(
189 JUnitPlatformProvider.class.getName(), testIdentifier.getLegacyReportingName() );
190 }
191
192 private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
193 {
194 return createReportEntry( testIdentifier, (StackTraceWriter) null );
195 }
196
197 private SimpleReportEntry createReportEntry(
198 TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
199 {
200 return createReportEntry(
201 testIdentifier, getStackTraceWriter( testIdentifier, testExecutionResult ) );
202 }
203
204 private SimpleReportEntry createReportEntry(
205 TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter )
206 {
207 String source = getLegacyReportingClassName( testIdentifier );
208 String name = getLegacyReportingName( testIdentifier );
209
210 return SimpleReportEntry.withException( source, name, stackTraceWriter );
211 }
212
213 private String getLegacyReportingName( TestIdentifier testIdentifier )
214 {
215
216
217
218 return testIdentifier
219 .getLegacyReportingName()
220 .replace( "()", "" )
221 .replace( '(', '{' )
222 .replace( ')', '}' );
223 }
224
225 private String getLegacyReportingClassName( TestIdentifier testIdentifier )
226 {
227 return LegacyReportingUtils.getClassName( testPlan, testIdentifier );
228 }
229
230 private StackTraceWriter getStackTraceWriter(
231 TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
232 {
233 Optional<Throwable> throwable = testExecutionResult.getThrowable();
234 if ( testExecutionResult.getStatus() == FAILED )
235 {
236
237 return getStackTraceWriter( testIdentifier, throwable.orElse( null ) );
238 }
239 return throwable.map( t -> getStackTraceWriter( testIdentifier, t ) ).orElse( null );
240 }
241
242 private StackTraceWriter getStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
243 {
244 String className = getClassName( testIdentifier );
245 String methodName = getMethodName( testIdentifier ).orElse( "" );
246 return new PojoStackTraceWriter( className, methodName, throwable );
247 }
248
249 private String getClassName( TestIdentifier testIdentifier )
250 {
251 TestSource testSource = testIdentifier.getSource().orElse( null );
252 if ( testSource instanceof ClassSource )
253 {
254 return ( (ClassSource) testSource ).getJavaClass().getName();
255 }
256 if ( testSource instanceof MethodSource )
257 {
258 return ( (MethodSource) testSource ).getClassName();
259 }
260 return testPlan.getParent( testIdentifier ).map( this::getClassName ).orElse( "" );
261 }
262
263 private Optional<String> getMethodName( TestIdentifier testIdentifier )
264 {
265 TestSource testSource = testIdentifier.getSource().orElse( null );
266 if ( testSource instanceof MethodSource )
267 {
268 return Optional.of( ( (MethodSource) testSource ).getMethodName() );
269 }
270 return Optional.empty();
271 }
272 }