2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.junit5;
18 import com.intellij.junit4.ExpectedPatterns;
19 import com.intellij.junit4.JUnit4TestListener;
20 import com.intellij.rt.execution.junit.ComparisonFailureData;
21 import com.intellij.rt.execution.junit.MapSerializerUtil;
22 import org.junit.platform.engine.TestExecutionResult;
23 import org.junit.platform.engine.reporting.ReportEntry;
24 import org.junit.platform.engine.support.descriptor.JavaClassSource;
25 import org.junit.platform.engine.support.descriptor.JavaMethodSource;
26 import org.junit.platform.launcher.TestExecutionListener;
27 import org.junit.platform.launcher.TestIdentifier;
28 import org.junit.platform.launcher.TestPlan;
29 import org.opentest4j.AssertionFailedError;
30 import org.opentest4j.MultipleFailuresError;
31 import org.opentest4j.ValueWrapper;
33 import java.io.PrintStream;
34 import java.io.PrintWriter;
35 import java.io.StringWriter;
36 import java.util.HashMap;
40 public class JUnit5TestExecutionListener implements TestExecutionListener {
41 private final PrintStream myPrintStream;
42 private TestPlan myTestPlan;
43 private long myCurrentTestStart;
44 private int myFinishCount;
45 private String myRootName;
46 private Set<TestIdentifier> myRoots;
47 private boolean mySuccessful;
49 public JUnit5TestExecutionListener() {
53 public JUnit5TestExecutionListener(PrintStream printStream) {
54 myPrintStream = printStream;
55 myPrintStream.println("##teamcity[enteredTheMatrix]");
58 public boolean wasSuccessful() {
62 public void initialize() {
68 public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) {
69 StringBuilder builder = new StringBuilder();
70 builder.append("timestamp = ").append(entry.getTimestamp());
71 entry.getKeyValuePairs().forEach((key, value) -> builder.append(", ").append(key).append(" = ").append(value));
72 myPrintStream.println(builder.toString());
76 public void testPlanExecutionStarted(TestPlan testPlan) {
77 if (myRootName != null) {
78 int lastPointIdx = myRootName.lastIndexOf('.');
79 String name = myRootName;
80 String comment = null;
81 if (lastPointIdx >= 0) {
82 name = myRootName.substring(lastPointIdx + 1);
83 comment = myRootName.substring(0, lastPointIdx);
86 myPrintStream.println("##teamcity[rootName name = \'" + escapeName(name) +
87 (comment != null ? ("\' comment = \'" + escapeName(comment)) : "") + "\'" +
88 " location = \'java:suite://" + escapeName(myRootName) +
94 public void testPlanExecutionFinished(TestPlan testPlan) {
99 public void executionSkipped(TestIdentifier testIdentifier, String reason) {
100 executionStarted (testIdentifier);
101 executionFinished(testIdentifier, TestExecutionResult.Status.ABORTED, null, reason);
105 public void executionStarted(TestIdentifier testIdentifier) {
106 if (testIdentifier.isTest()) {
107 testStarted(testIdentifier);
108 myCurrentTestStart = System.currentTimeMillis();
110 else if (!myRoots.contains(testIdentifier)){
112 myPrintStream.println("##teamcity[testSuiteStarted" + idAndName(testIdentifier) + "\']");
116 private static String idAndName(TestIdentifier testIdentifier) {
117 return idAndName(testIdentifier, testIdentifier.getDisplayName());
120 private static String idAndName(TestIdentifier testIdentifier, String displayName) {
121 return " id=\'" + testIdentifier.getUniqueId().toString() + "\' name=\'" + escapeName(displayName);
125 public void dynamicTestRegistered(TestIdentifier testIdentifier) {
130 public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
131 final TestExecutionResult.Status status = testExecutionResult.getStatus();
132 final Throwable throwableOptional = testExecutionResult.getThrowable().orElse(null);
133 executionFinished(testIdentifier, status, throwableOptional, null);
134 mySuccessful &= TestExecutionResult.Status.SUCCESSFUL == testExecutionResult.getStatus();
137 private void executionFinished(TestIdentifier testIdentifier,
138 TestExecutionResult.Status status,
139 Throwable throwableOptional,
141 final String displayName = testIdentifier.getDisplayName();
142 if (testIdentifier.isTest()) {
143 final long duration = getDuration();
144 if (status == TestExecutionResult.Status.FAILED) {
145 testFailure(testIdentifier, MapSerializerUtil.TEST_FAILED, throwableOptional, duration, reason, true);
147 else if (status == TestExecutionResult.Status.ABORTED) {
148 testFailure(testIdentifier, MapSerializerUtil.TEST_IGNORED, throwableOptional, duration, reason, true);
150 testFinished(testIdentifier, duration);
153 else if (!myRoots.contains(testIdentifier)){
154 String messageName = null;
155 if (status == TestExecutionResult.Status.FAILED) {
156 messageName = MapSerializerUtil.TEST_FAILED;
158 else if (status == TestExecutionResult.Status.ABORTED) {
159 messageName = MapSerializerUtil.TEST_IGNORED;
161 if (messageName != null) {
162 if (status == TestExecutionResult.Status.FAILED) {
163 myPrintStream.println("\n##teamcity[testStarted name=\'" + JUnit4TestListener.CLASS_CONFIGURATION + "\']");
164 testFailure(JUnit4TestListener.CLASS_CONFIGURATION, JUnit4TestListener.CLASS_CONFIGURATION, messageName, throwableOptional, 0, reason, true);
165 myPrintStream.println("\n##teamcity[testFinished name=\'" + JUnit4TestListener.CLASS_CONFIGURATION + "\']");
168 final Set<TestIdentifier> descendants = myTestPlan.getDescendants(testIdentifier);
169 if (!descendants.isEmpty() && myFinishCount == 0) {
170 for (TestIdentifier childIdentifier : descendants) {
171 testStarted(childIdentifier);
172 testFailure(childIdentifier, MapSerializerUtil.TEST_IGNORED, status == TestExecutionResult.Status.ABORTED ? throwableOptional : null, 0, reason, status == TestExecutionResult.Status.ABORTED);
173 testFinished(childIdentifier, 0);
177 myPrintStream.println("##teamcity[testSuiteFinished " + idAndName(testIdentifier, displayName) + "\']");
181 protected long getDuration() {
182 return System.currentTimeMillis() - myCurrentTestStart;
185 private void testStarted(TestIdentifier testIdentifier) {
186 myPrintStream.println("\n##teamcity[testStarted" + idAndName(testIdentifier) + getLocationHint(testIdentifier) + "\']");
189 private void testFinished(TestIdentifier testIdentifier, long duration) {
190 myPrintStream.println("\n##teamcity[testFinished" + idAndName(testIdentifier) + (duration > 0 ? "\' duration=\'" + Long.toString(duration) : "") + "\']");
193 private void testFailure(TestIdentifier testIdentifier,
198 boolean includeThrowable) {
199 testFailure(testIdentifier.getDisplayName(), testIdentifier.getUniqueId().toString(), messageName, ex, duration, reason, includeThrowable);
202 private void testFailure(String methodName,
208 boolean includeThrowable) {
209 final Map<String, String> attrs = new HashMap<>();
210 attrs.put("name", methodName);
213 attrs.put("duration", Long.toString(duration));
215 if (reason != null) {
216 attrs.put("message", reason);
220 ComparisonFailureData failureData = null;
221 if (ex instanceof MultipleFailuresError && ((MultipleFailuresError)ex).hasFailures()) {
222 for (AssertionError assertionError : ((MultipleFailuresError)ex).getFailures()) {
223 testFailure(methodName, id, messageName, assertionError, duration, reason, false);
226 else if (ex instanceof AssertionFailedError && ((AssertionFailedError)ex).isActualDefined() && ((AssertionFailedError)ex).isExpectedDefined()) {
227 final ValueWrapper actual = ((AssertionFailedError)ex).getActual();
228 final ValueWrapper expected = ((AssertionFailedError)ex).getExpected();
229 failureData = new ComparisonFailureData(expected.getStringRepresentation(), actual.getStringRepresentation());
232 //try to detect failure with junit 4 if present in the classpath
234 failureData = ExpectedPatterns.createExceptionNotification(ex);
236 catch (Throwable ignore) {}
239 if (includeThrowable || failureData == null) {
240 ComparisonFailureData.registerSMAttributes(failureData, getTrace(ex), ex.getMessage(), attrs, ex);
243 ComparisonFailureData.registerSMAttributes(failureData, "", "", attrs, ex, "");
248 myPrintStream.println("\n" + MapSerializerUtil.asString(messageName, attrs));
252 protected String getTrace(Throwable ex) {
253 final StringWriter stringWriter = new StringWriter();
254 final PrintWriter writer = new PrintWriter(stringWriter);
255 ex.printStackTrace(writer);
256 return stringWriter.toString();
260 public void sendTree(TestPlan testPlan, String rootName) {
261 myTestPlan = testPlan;
262 myRootName = rootName;
263 myRoots = testPlan.getRoots();
264 for (TestIdentifier root : myRoots) {
265 assert root.isContainer();
266 for (TestIdentifier testIdentifier : testPlan.getChildren(root)) {
267 sendTreeUnderRoot(testPlan, testIdentifier);
270 myPrintStream.println("##teamcity[treeEnded]");
273 private void sendTreeUnderRoot(TestPlan testPlan, TestIdentifier root) {
274 final String idAndName = idAndName(root);
275 if (root.isContainer()) {
276 myPrintStream.println("##teamcity[suiteTreeStarted" + idAndName + getLocationHint(root) + "\']");
277 for (TestIdentifier childIdentifier : testPlan.getChildren(root)) {
278 sendTreeUnderRoot(testPlan, childIdentifier);
280 myPrintStream.println("##teamcity[suiteTreeEnded" + idAndName + "\']");
282 else if (root.isTest()) {
283 myPrintStream.println("##teamcity[suiteTreeNode " + idAndName + getLocationHint(root) + "\']");
287 private String getLocationHint(TestIdentifier root) {
288 final String className = getClassName(root);
289 final String methodName = getMethodName(root);
290 return "\' locationHint=\'java:" + (root.isTest() ? "test" : "suite") + "://" + escapeName(className + (methodName != null ? "." + methodName : ""));
294 private static String escapeName(String str) {
295 return MapSerializerUtil.escapeStr(str, MapSerializerUtil.STD_ESCAPER);
298 static String getClassName(TestIdentifier description) {
299 return description.getSource().map(source -> {
300 if (source instanceof JavaMethodSource) {
301 return ((JavaMethodSource)source).getJavaClass().getName();
303 if (source instanceof JavaClassSource) {
304 return ((JavaClassSource)source).getJavaClass().getName();
310 static String getMethodName(TestIdentifier testIdentifier) {
311 return testIdentifier.getSource().map((source) -> {
312 if (source instanceof JavaMethodSource) {
313 return ((JavaMethodSource)source).getJavaMethodName();