diff --git a/java/java-runtime/src/com/intellij/rt/execution/junit/MapSerializerUtil.java b/java/java-runtime/src/com/intellij/rt/execution/junit/MapSerializerUtil.java index f195abb7c446..2b6f0124e6ed 100644 --- a/java/java-runtime/src/com/intellij/rt/execution/junit/MapSerializerUtil.java +++ b/java/java-runtime/src/com/intellij/rt/execution/junit/MapSerializerUtil.java @@ -1,14 +1,25 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.rt.execution.junit; -import java.util.Iterator; import java.util.Map; public final class MapSerializerUtil { + public static final String TEST_STARTED = "testStarted"; + public static final String TEST_FINISHED = "testFinished"; + public static final String TEST_FAILED = "testFailed"; public static final String TEST_IGNORED = "testIgnored"; + public static final String TEST_STD_OUT = "testStdOut"; + public static final String TEST_SUITE_STARTED = "testSuiteStarted"; + public static final String TEST_SUITE_FINISHED = "testSuiteFinished"; + + public static final String SUITE_TREE_STARTED = "suiteTreeStarted"; + public static final String SUITE_TREE_ENDED = "suiteTreeEnded"; + public static final String SUITE_TREE_NODE = "suiteTreeNode"; + + public static final String ROOT_NAME = "rootName"; /** * String escaping info provider. @@ -99,14 +110,16 @@ public final class MapSerializerUtil { return result; } - public static String asString(final String messageName, final Map attributes) { - String text = "##teamcity[" + messageName; - for (Iterator iterator = attributes.keySet().iterator(); iterator.hasNext(); ) { - final Object attrName = iterator.next(); - text += " " + attrName + "='" + escapeStr((String)attributes.get(attrName), STD_ESCAPER) + "'"; + public static String asString(final String messageName, final Map attributes) { + StringBuilder text = new StringBuilder("##teamcity[" + messageName); + for (final Object attrName : attributes.keySet()) { + text.append(" ") + .append(attrName) + .append("='") + .append(escapeStr(attributes.get(attrName), STD_ESCAPER)) + .append("'"); } - text += "]"; - return text; + text.append("]"); + return text.toString(); } - -} +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/JUnit5IdeaTestRunner.java b/plugins/junit5_rt/src/com/intellij/junit5/JUnit5IdeaTestRunner.java index 12d905eb71ca..03e63163c93c 100644 --- a/plugins/junit5_rt/src/com/intellij/junit5/JUnit5IdeaTestRunner.java +++ b/plugins/junit5_rt/src/com/intellij/junit5/JUnit5IdeaTestRunner.java @@ -85,6 +85,7 @@ public class JUnit5IdeaTestRunner implements IdeaTestRunner { myForkedTestPlan = LauncherFactory.create().discover(discoveryRequest); final Set roots = myForkedTestPlan.getRoots(); if (roots.isEmpty()) return null; + @SuppressWarnings("SSBasedInspection") List nonEmptyRoots = roots.stream() .filter(identifier -> !myForkedTestPlan.getChildren(identifier).isEmpty()) .collect(Collectors.toList()); @@ -115,6 +116,7 @@ public class JUnit5IdeaTestRunner implements IdeaTestRunner { final TestIdentifier testIdentifier = child; final String className = JUnit5TestExecutionListener.getClassName(testIdentifier); final String methodSignature = JUnit5TestExecutionListener.getMethodSignature(testIdentifier); + //noinspection ConstantValue if (methodSignature != null) { return className + "," + methodSignature; } @@ -155,4 +157,4 @@ public class JUnit5IdeaTestRunner implements IdeaTestRunner { } } } -} +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/JUnit5TestExecutionListener.java b/plugins/junit5_rt/src/com/intellij/junit5/JUnit5TestExecutionListener.java index 441d5a2a0467..301ca2e30d02 100644 --- a/plugins/junit5_rt/src/com/intellij/junit5/JUnit5TestExecutionListener.java +++ b/plugins/junit5_rt/src/com/intellij/junit5/JUnit5TestExecutionListener.java @@ -1,405 +1,118 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.junit5; -import com.intellij.junit4.ExpectedPatterns; -import com.intellij.rt.execution.junit.ComparisonFailureData; -import com.intellij.rt.execution.junit.MapSerializerUtil; +import com.intellij.junit5.report.ExecutionState; +import com.intellij.junit5.report.TeamCityTestReporter; import org.junit.platform.engine.TestExecutionResult; -import org.junit.platform.engine.TestSource; -import org.junit.platform.engine.UniqueId; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.engine.support.descriptor.ClassSource; -import org.junit.platform.engine.support.descriptor.CompositeTestSource; -import org.junit.platform.engine.support.descriptor.FileSource; import org.junit.platform.engine.support.descriptor.MethodSource; import org.junit.platform.launcher.TestExecutionListener; import org.junit.platform.launcher.TestIdentifier; import org.junit.platform.launcher.TestPlan; -import org.opentest4j.MultipleFailuresError; -import java.io.File; import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.*; - -import static com.intellij.rt.execution.TestListenerProtocol.CLASS_CONFIGURATION; +import java.util.HashSet; +import java.util.Set; @SuppressWarnings("UseOfSystemOutOrSystemErr") public class JUnit5TestExecutionListener implements TestExecutionListener { - private static final String NO_LOCATION_HINT = ""; - private static final String NO_LOCATION_HINT_VALUE = ""; - - private final PrintStream myPrintStream; - private TestPlan myTestPlan; - private long myCurrentTestStart; - private int myFinishCount = 0; - private String myRootName; - private String myPresentableName; - private boolean mySuccessful = true; - private String myIdSuffix = ""; - private boolean mySendTree; + private final ExecutionState myState; public JUnit5TestExecutionListener() { this(System.out); } public JUnit5TestExecutionListener(PrintStream printStream) { - myPrintStream = printStream; - myPrintStream.println("##teamcity[enteredTheMatrix]"); + myState = new ExecutionState(printStream); + myState.print("##teamcity[enteredTheMatrix]"); } public boolean wasSuccessful() { - return mySuccessful; + return myState.wasSuccessful(); } public void initializeIdSuffix(boolean forked) { - if (forked && myIdSuffix.isEmpty()) { - myIdSuffix = String.valueOf(System.currentTimeMillis()); - } - } - - public void initializeIdSuffix(int i) { - myIdSuffix = i + "th"; + myState.initializeIdSuffix(forked); } - @Override - public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { - StringBuilder builder = new StringBuilder(); - builder.append("timestamp = ").append(entry.getTimestamp()); - entry.getKeyValuePairs().forEach((key, value) -> builder.append(", ").append(key).append(" = ").append(value)); - builder.append("\n"); - myPrintStream.println("##teamcity[testStdOut" + idAndName(testIdentifier) + " out = '" + escapeName(builder.toString()) + "']"); + public void initializeIdSuffix(int i) { + myState.initializeIdSuffix(i); + } + + public void setRootName(String rootName) { + myState.setRootName(rootName); + } + + public void setPresentableName(String presentableName) { + myState.setPresentableName(presentableName); + } + + public void setSendTree() { + myState.setSendTree(true); } @Override public void testPlanExecutionStarted(TestPlan testPlan) { - myTestPlan = testPlan; - if (mySendTree) { - for (TestIdentifier root : myTestPlan.getRoots()) { + myState.setPlan(testPlan); + + if (myState.isSendTree()) { + for (TestIdentifier root : testPlan.getRoots()) { assert root.isContainer(); - for (TestIdentifier testIdentifier : myTestPlan.getChildren(root)) { + + for (TestIdentifier testIdentifier : testPlan.getChildren(root)) { String legacyReportingName = testIdentifier.getLegacyReportingName(); - if (legacyReportingName != null && legacyReportingName.equals(myRootName)) setPresentableName(testIdentifier.getDisplayName()); + if (legacyReportingName != null && legacyReportingName.equals(myState.getRootName())) { + myState.setPresentableName(testIdentifier.getDisplayName()); + } sendTreeUnderRoot(testIdentifier, new HashSet<>()); } } - myPrintStream.println("##teamcity[treeEnded]"); - } - - if (myRootName != null) { - int lastPointIdx = myRootName.lastIndexOf('.'); - String name = myRootName; - String comment = null; - if (lastPointIdx >= 0) { - name = myRootName.substring(lastPointIdx + 1); - comment = myRootName.substring(0, lastPointIdx); - } - - String messageName = (myPresentableName == null || myPresentableName.isEmpty()) ? name : myPresentableName; - - myPrintStream.println("##teamcity[rootName name = '" + escapeName(messageName) + - (comment != null ? ("' comment = '" + escapeName(comment)) : "") + "'" + - " location = 'java:suite://" + escapeName(myRootName) + - "']"); + myState.print("##teamcity[treeEnded]"); } + myState.printRootNameIfNeeded(); } - private void sendTreeUnderRoot(TestIdentifier root, HashSet visited) { - String idAndName = idAndName(root); - if (root.isContainer()) { - boolean skipContainer = shouldSkipContainer(root); - if (!skipContainer) myPrintStream.println("##teamcity[suiteTreeStarted" + idAndName + " " + getLocationHint(root) + "]"); - for (TestIdentifier childIdentifier : myTestPlan.getChildren(root)) { - if (visited.add(childIdentifier)) { - sendTreeUnderRoot(childIdentifier, visited); - } - else { - System.err.println("Identifier '" + getId(childIdentifier) + "' is reused"); - } + public void sendTreeUnderRoot(TestIdentifier root, Set visited) { + TeamCityTestReporter reporter = TeamCityTestReporter.get(root, myState); + + reporter.treeStarted().forEach(myState::print); + for (TestIdentifier child : myState.plan().getChildren(root)) { + if (visited.add(child)) { + sendTreeUnderRoot(child, visited); + } + else { + System.err.println("Identifier '" + child.getUniqueId() + "' is reused"); } - if (!skipContainer) myPrintStream.println("##teamcity[suiteTreeEnded" + idAndName + "]"); - } - else if (root.isTest()) { - myPrintStream.println("##teamcity[suiteTreeNode " + idAndName + " " + getLocationHint(root) + "]"); } + reporter.treeFinished().forEach(myState::print); + } + + @Override + public void reportingEntryPublished(TestIdentifier testIdentifier, ReportEntry entry) { + TeamCityTestReporter.get(testIdentifier, myState).output(entry) + .forEach(myState::print); } @Override public void executionSkipped(TestIdentifier testIdentifier, String reason) { - executionStarted (testIdentifier); - executionFinished(testIdentifier, TestExecutionResult.Status.ABORTED, null, reason); + executionStarted(testIdentifier); + TeamCityTestReporter.get(testIdentifier, myState) + .skip(reason) + .forEach(myState::print); } @Override public void executionStarted(TestIdentifier testIdentifier) { - if (testIdentifier.isTest()) { - testStarted(testIdentifier); - myCurrentTestStart = System.nanoTime(); - } - else if (!shouldSkipContainer(testIdentifier)) { - myFinishCount = 0; - myPrintStream.println("##teamcity[testSuiteStarted" + idAndName(testIdentifier) + getLocationHint(testIdentifier) + "]"); - } + TeamCityTestReporter.get(testIdentifier, myState).start() + .forEach(myState::print); } @Override public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { - TestExecutionResult.Status status = testExecutionResult.getStatus(); - Throwable throwableOptional = testExecutionResult.getThrowable().orElse(null); - executionFinished(testIdentifier, status, throwableOptional, null); - mySuccessful &= TestExecutionResult.Status.SUCCESSFUL == testExecutionResult.getStatus(); - } - - private void executionFinished(TestIdentifier testIdentifier, - TestExecutionResult.Status status, - Throwable throwableOptional, - String reason) { - if (testIdentifier.isTest()) { - long duration = getDuration(); - if (status == TestExecutionResult.Status.FAILED) { - testFailure(testIdentifier, MapSerializerUtil.TEST_FAILED, throwableOptional, duration, reason, true); - } - else if (status == TestExecutionResult.Status.ABORTED) { - testFailure(testIdentifier, MapSerializerUtil.TEST_IGNORED, throwableOptional, duration, reason, true); - } - testFinished(testIdentifier, duration); - myFinishCount++; - } - else if (!shouldSkipContainer(testIdentifier) || status == TestExecutionResult.Status.FAILED){ - String messageName = null; - if (status == TestExecutionResult.Status.FAILED) { - messageName = MapSerializerUtil.TEST_FAILED; - } - else if (status == TestExecutionResult.Status.ABORTED) { - messageName = MapSerializerUtil.TEST_IGNORED; - } - if (messageName != null) { - Set descendants = myTestPlan != null ? myTestPlan.getDescendants(testIdentifier) : Collections.emptySet(); - if (status == TestExecutionResult.Status.FAILED) { - String parentId = getParentId(testIdentifier); - String nameAndId = " name='" + CLASS_CONFIGURATION + - "' nodeId='" + escapeName(getId(testIdentifier)) + - "' parentNodeId='" + escapeName(parentId) + "' "; - myPrintStream.println("##teamcity[testStarted " + nameAndId + " ]"); - testFailure(CLASS_CONFIGURATION, getId(testIdentifier), parentId, messageName, throwableOptional, 0, reason, true); - myPrintStream.println("##teamcity[testFinished" + nameAndId + "]"); - } - else if (descendants.isEmpty()) { - testFailure(testIdentifier, MapSerializerUtil.TEST_IGNORED, throwableOptional, 0, reason, true); - } - - if (!descendants.isEmpty() && myFinishCount == 0) { - for (TestIdentifier childIdentifier : descendants) { - testStarted(childIdentifier); - Throwable throwable = status == TestExecutionResult.Status.ABORTED ? throwableOptional : null; - testFailure(childIdentifier, MapSerializerUtil.TEST_IGNORED, throwable, 0, reason, status == TestExecutionResult.Status.ABORTED); - testFinished(childIdentifier, 0); - } - myFinishCount = 0; - } - } - myPrintStream.println("##teamcity[testSuiteFinished" + idAndName(testIdentifier) + "]"); - } - } - - private boolean shouldSkipContainer(TestIdentifier testIdentifier) { - UniqueId id = UniqueId.parse(testIdentifier.getUniqueId()); - List segments = id.getSegments(); - if (segments.isEmpty()) return false; - UniqueId.Segment lastSegment = segments.get(segments.size() - 1); - return lastSegment.getType().equals("engine") || - myRootName != null && myRootName.equals(lastSegment.getValue()); - } - - protected long getDuration() { - return (System.nanoTime() - myCurrentTestStart) / 1_000_000; - } - - private void testStarted(TestIdentifier testIdentifier) { - myPrintStream.println("##teamcity[testStarted" + idAndName(testIdentifier) + " " + getLocationHint(testIdentifier) + "]"); - } - - private void testFinished(TestIdentifier testIdentifier, long duration) { - myPrintStream.println("##teamcity[testFinished" + idAndName(testIdentifier) + (duration > 0 ? " duration='" + duration + "'" : "") + "]"); - } - - private void testFailure(TestIdentifier testIdentifier, - String messageName, - Throwable ex, - long duration, - String reason, - boolean includeThrowable) { - testFailure(testIdentifier.getDisplayName(), getId(testIdentifier), getParentId(testIdentifier), messageName, ex, duration, reason, includeThrowable); - } - - private void testFailure(String methodName, - String id, - String parentId, - String messageName, - Throwable ex, - long duration, - String reason, - boolean includeThrowable) { - Map attrs = new LinkedHashMap<>(); - attrs.put("name", methodName); - attrs.put("id", id); - attrs.put("nodeId", id); - attrs.put("parentNodeId", parentId); - if (duration > 0) { - attrs.put("duration", Long.toString(duration)); - } - if (reason != null) { - attrs.put("message", reason); - } - try { - if (ex != null) { - ComparisonFailureData failureData = null; - if (ex instanceof MultipleFailuresError && ((MultipleFailuresError)ex).hasFailures()) { - for (Throwable assertionError : ((MultipleFailuresError)ex).getFailures()) { - testFailure(methodName, id, parentId, messageName, assertionError, duration, reason, true); - } - } - else { - try { - failureData = ExpectedPatterns.createExceptionNotification(ex); - } - catch (Throwable ignore) {} - } - - if (includeThrowable || failureData == null) { - ComparisonFailureData.registerSMAttributes(failureData, getTrace(ex), ex.getMessage(), attrs, ex, "Comparison Failure: ", "expected: <"); - } - else { - ComparisonFailureData.registerSMAttributes(failureData, "", ex.getMessage(), attrs, ex, "Comparison Failure: ", "expected: <"); - } - } - } - finally { - myPrintStream.println(MapSerializerUtil.asString(messageName, attrs)); - } - } - - protected String getTrace(Throwable ex) { - StringWriter stringWriter = new StringWriter(); - PrintWriter writer = new PrintWriter(stringWriter); - ex.printStackTrace(writer); - return stringWriter.toString(); - } - - public void setRootName(String rootName) { - myRootName = rootName; - } - - public void setPresentableName(String presentableName) { - myPresentableName = presentableName; - } - - public void setSendTree() { - mySendTree = true; - } - - private String getId(TestIdentifier identifier) { - return identifier.getUniqueId() + myIdSuffix; - } - - private String idAndName(TestIdentifier testIdentifier) { - return " id='" + escapeName(getId(testIdentifier)) + - "' name='" + escapeName(testIdentifier.getDisplayName()) + - "' nodeId='" + escapeName(getId(testIdentifier)) + - "' parentNodeId='" + escapeName(getParentId(testIdentifier)) + "'"; - } - - private String getParentId(TestIdentifier testIdentifier) { - Optional parent = myTestPlan.getParent(testIdentifier); - return parent - .map(identifier -> shouldSkipContainer(identifier) ? getParentId(identifier) : identifier.getUniqueId() + myIdSuffix) - .orElse("0"); - } - - - private String getLocationHint(TestIdentifier root) { - return getLocationHint(root, myTestPlan.getParent(root).orElse(null)); - } - - public static String getLocationHint(TestIdentifier root, TestIdentifier rootParent) { - return root.getSource() - .map(testSource -> getLocationHintValue(testSource, rootParent != null ? rootParent.getSource().orElse(null) : null)) - .filter(maybeLocationHintValue -> !NO_LOCATION_HINT_VALUE.equals(maybeLocationHintValue)) - .map(locationHintValue -> "locationHint='" + locationHintValue + "'" + getMetaInfo(root)) - .orElse(NO_LOCATION_HINT); - } - - private static String getMetaInfo(TestIdentifier root) { - return root.getSource() - .map(testSource -> { - if (testSource instanceof MethodSource) { - //noinspection SpellCheckingInspection - return " metainfo='" + ((MethodSource)testSource).getMethodParameterTypes() + "'"; - } - if (testSource instanceof ClassSource) { - //noinspection SpellCheckingInspection - return ((ClassSource)testSource).getPosition() - .map(position -> - " metainfo='" - // Convert JUnit's 1-based values to 0-based - + (position.getLine() - 1) + ":" - + (position.getColumn().orElse(1) - 1) + "'") - .orElse(NO_LOCATION_HINT); - } - return NO_LOCATION_HINT; - }) - .orElse(NO_LOCATION_HINT); - } - - public static String getLocationHintValue(TestSource testSource, TestSource parentSource) { - - if (testSource instanceof CompositeTestSource) { - CompositeTestSource compositeTestSource = ((CompositeTestSource)testSource); - for (TestSource sourceFromComposite : compositeTestSource.getSources()) { - String locationHintValue = getLocationHintValue(sourceFromComposite, parentSource); - if (!NO_LOCATION_HINT_VALUE.equals(locationHintValue)) { - return locationHintValue; - } - } - return NO_LOCATION_HINT_VALUE; - } - - if (testSource instanceof FileSource) { - FileSource fileSource = (FileSource)testSource; - File file = fileSource.getFile(); - String line = fileSource.getPosition() - .map(position -> ":" + position.getLine()) - .orElse(""); - return "file://" + file.getAbsolutePath() + line; - } - - if (testSource instanceof MethodSource) { - MethodSource methodSource = (MethodSource)testSource; - return javaLocation(methodSource.getClassName(), methodSource.getMethodName(), true); - } - - if (testSource instanceof ClassSource) { - String className = ((ClassSource)testSource).getClassName(); - return javaLocation(className, null, false); - } - - if (parentSource != null) { - return getLocationHintValue(parentSource,null); - } - - return NO_LOCATION_HINT_VALUE; - } - - private static String javaLocation(String className, String maybeMethodName, boolean isTest) { - String type = isTest ? "test" : "suite"; - String methodName = maybeMethodName == null ? "" : "/" + maybeMethodName; - String location = escapeName(className + methodName); - return "java:" + type + "://" + location; - } - - private static String escapeName(String str) { - return MapSerializerUtil.escapeStr(str, MapSerializerUtil.STD_ESCAPER); + TeamCityTestReporter.get(testIdentifier, myState).finish(testExecutionResult) + .forEach(myState::print); + myState.updateSuccessful(testExecutionResult.getStatus()); } public static String getClassName(TestIdentifier description) { diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/AbstractTestReporter.java b/plugins/junit5_rt/src/com/intellij/junit5/report/AbstractTestReporter.java new file mode 100644 index 000000000000..4eaeb7a06a19 --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/AbstractTestReporter.java @@ -0,0 +1,113 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import com.intellij.rt.execution.junit.ComparisonFailureData; +import com.intellij.rt.execution.junit.MapSerializerUtil; +import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.launcher.TestIdentifier; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.*; + +public abstract class AbstractTestReporter implements TeamCityTestReporter { + protected final TestIdentifier identifier; + protected final ExecutionState state; + + protected AbstractTestReporter(TestIdentifier identifier, ExecutionState state) { + this.identifier = identifier; + this.state = state; + } + + @Override + public List output(ReportEntry entry) { + Map attrs = attributes(ReportedField.ID, ReportedField.NAME, ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID); + + StringBuilder builder = new StringBuilder(); + builder.append("timestamp = ").append(entry.getTimestamp()); + entry.getKeyValuePairs().forEach((key, value) -> builder.append(", ").append(key).append(" = ").append(value)); + builder.append("\n"); + + attrs.put("out", builder.toString()); + return Collections.singletonList(MapSerializerUtil.asString(MapSerializerUtil.TEST_STD_OUT, attrs)); + } + + protected String id() { + return identifier.getUniqueId() + state.suffix(); + } + + protected Optional getParent() { + //noinspection SimplifyOptionalCallChains + return state.plan().getParent(identifier) + .map(i -> new SuiteReporter(i, state)) + .map(r -> r.isSkipped() ? r.getParent() : Optional.of(r)) + .orElse(Optional.empty()); + } + + protected Map attributes(ReportedField... fields) { + Map attrs = new LinkedHashMap<>(fields.length * 2); + LocationInfo location = locationInfo(); + + for (ReportedField field : fields) { + String value = null; + switch (field) { + case ID: + case NODE_ID: + value = id(); + break; + case NAME: + value = name(); + break; + case PARENT_NODE_ID: + value = getParent().map(AbstractTestReporter::id).orElse("0"); + break; + case HINT: + value = location.locationHint(); + break; + case METAINFO: + value = location.metainfo(); + break; + } + if (value != null) { + attrs.put(field.getId(), value); + } + } + return attrs; + } + + protected String name() { + return identifier.getDisplayName(); + } + + private LocationInfo locationInfo() { + if (state.plan() == null) return LocationInfo.EMPTY; + TestIdentifier parent = state.plan().getParent(identifier).orElse(null); + return LocationInfo.compute(identifier, parent); + } + + protected static String getTrace(Throwable ex) { + if (ComparisonFailureData.isAssertionError(ex.getClass())) return ""; + StringWriter writer = new StringWriter(); + ex.printStackTrace(new PrintWriter(writer)); + return writer.toString(); + } + + protected enum ReportedField { + ID("id"), + NAME("name"), + NODE_ID("nodeId"), + PARENT_NODE_ID("parentNodeId"), + HINT("locationHint"), + METAINFO("metainfo"); + + private final String myId; + + ReportedField(String id) { + myId = id; + } + + public String getId() { + return myId; + } + } +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/CompositeTestReporter.java b/plugins/junit5_rt/src/com/intellij/junit5/report/CompositeTestReporter.java new file mode 100644 index 000000000000..c13b1ba59143 --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/CompositeTestReporter.java @@ -0,0 +1,64 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import com.intellij.rt.execution.junit.MapSerializerUtil; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.launcher.TestIdentifier; + +import java.util.ArrayList; +import java.util.List; + +// e.g., parametrized test +public class CompositeTestReporter extends AbstractTestReporter { + private final TestReporter asTest; + private final SuiteReporter asSuite; + + public CompositeTestReporter(TestIdentifier identifier, ExecutionState state) { + super(identifier, state); + asTest = new TestReporter(identifier, state); + asSuite = new SuiteReporter(identifier, state); + } + + @Override + public List start() { + List out = new ArrayList<>(); + out.addAll(asSuite.start()); + out.addAll(asTest.start()); + return out; + } + + @Override + public List output(ReportEntry entry) { + return asTest.output(entry); + } + + @Override + public List finish(TestExecutionResult result) { + List out = new ArrayList<>(); + out.addAll(asTest.finish(result)); + out.addAll(asSuite.finish(result)); + return out; + } + + @Override + public List treeStarted() { + List out = new ArrayList<>(asSuite.treeStarted()); + String testNode = MapSerializerUtil.asString(MapSerializerUtil.SUITE_TREE_NODE, + attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO)); + out.add(testNode); + return out; + } + + @Override + public List treeFinished() { + return asSuite.treeFinished(); + } + + @Override + public List skip(String reason) { + return asTest.skip(reason); + } +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/ExecutionState.java b/plugins/junit5_rt/src/com/intellij/junit5/report/ExecutionState.java new file mode 100644 index 000000000000..cc2b0a1ef333 --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/ExecutionState.java @@ -0,0 +1,127 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import com.intellij.rt.execution.junit.MapSerializerUtil; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.launcher.TestPlan; + +import java.io.PrintStream; +import java.util.LinkedHashMap; +import java.util.Map; + +final public class ExecutionState { + private final PrintStream myOut; + + private TestPlan myPlan; + + private long myCurrentTestStartNanos; + private int myFinishCount; + private String myRootName; + private String myPresentableName; + private boolean mySuccessful = true; + private String myIdSuffix = ""; + private boolean mySendTree; + + public ExecutionState(PrintStream out) { + myOut = out; + } + + public void setPlan(TestPlan plan) { + myPlan = plan; + } + + public TestPlan plan() { + return myPlan; + } + + public void print(String line) { + myOut.println(line); + } + + public boolean wasSuccessful() { + return mySuccessful; + } + + public void updateSuccessful(TestExecutionResult.Status status) { + mySuccessful &= (status == TestExecutionResult.Status.SUCCESSFUL); + } + + public void initializeIdSuffix(boolean forked) { + if (forked && myIdSuffix.isEmpty()) { + myIdSuffix = String.valueOf(System.currentTimeMillis()); + } + } + + public void initializeIdSuffix(int i) { + myIdSuffix = i + "th"; + } + + public String suffix() { + return myIdSuffix; + } + + public void setRootName(String rootName) { + myRootName = rootName; + } + + public String getRootName() { + return myRootName; + } + + public void setPresentableName(String presentableName) { + myPresentableName = presentableName; + } + + public String getPresentableName() { + return myPresentableName; + } + + public void setSendTree(boolean sendTree) { + mySendTree = sendTree; + } + + public boolean isSendTree() { + return mySendTree; + } + + public void onLeafTestStarted() { + myCurrentTestStartNanos = System.nanoTime(); + } + + public long onLeafTestFinishedAndGetDurationMs() { + return (System.nanoTime() - myCurrentTestStartNanos) / 1_000_000L; + } + + public void resetFinishCount() { + myFinishCount = 0; + } + + public void incrementFinishCount() { + myFinishCount++; + } + + public int finishCount() { + return myFinishCount; + } + + public void printRootNameIfNeeded() { + if (myRootName == null) return; + + int lastPointIdx = myRootName.lastIndexOf('.'); + String name = myRootName; + String comment = null; + if (lastPointIdx >= 0) { + name = myRootName.substring(lastPointIdx + 1); + comment = myRootName.substring(0, lastPointIdx); + } + + String messageName = (myPresentableName == null || myPresentableName.isEmpty()) ? name : myPresentableName; + + Map attrs = new LinkedHashMap<>(); + attrs.put("name", messageName); + if (comment != null) attrs.put("comment", comment); + attrs.put("location", "java:suite://" + myRootName); + + print(MapSerializerUtil.asString(MapSerializerUtil.ROOT_NAME, attrs)); + } +} diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/LocationInfo.java b/plugins/junit5_rt/src/com/intellij/junit5/report/LocationInfo.java new file mode 100644 index 000000000000..2965f9dfe2ba --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/LocationInfo.java @@ -0,0 +1,104 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import com.intellij.rt.execution.junit.MapSerializerUtil; +import org.junit.platform.engine.TestSource; +import org.junit.platform.engine.support.descriptor.*; +import org.junit.platform.launcher.TestIdentifier; + +import java.io.File; +import java.util.Optional; + +final public class LocationInfo { + static final LocationInfo EMPTY = new LocationInfo(null, null, null); + + private final String locationHint; + private final String metainfo; + + public LocationInfo(TestSource opt, TestSource parent, TestIdentifier node) { + this.locationHint = getLocationHintValue(opt, parent); + if (this.locationHint.isEmpty()) { + this.metainfo = null; + } else { + this.metainfo = getMetaInfoValue(node); + } + } + + public String locationHint() { + return locationHint; + } + + public String metainfo() { + return metainfo; + } + + public static LocationInfo compute(TestIdentifier node, TestIdentifier parent) { + TestSource parentSource = parent != null ? parent.getSource().orElse(null) : null; + Optional sourceOpt = node.getSource(); + //noinspection OptionalIsPresent + if (!sourceOpt.isPresent()) return EMPTY; + + return new LocationInfo(sourceOpt.get(), parentSource, node); + } + + private static String getMetaInfoValue(TestIdentifier root) { + if (root == null || !root.getSource().isPresent()) return null; + + TestSource source = root.getSource().get(); + if (source instanceof MethodSource) { + return ((MethodSource)source).getMethodParameterTypes(); + } + else if (source instanceof ClassSource) { + Optional position = ((ClassSource)source).getPosition(); + if (!position.isPresent()) return null; + + FilePosition pos = position.get(); + return (pos.getLine() - 1) + ":" + (pos.getColumn().orElse(1) - 1); + } + else { + return null; + } + } + + private static String getLocationHintValue(TestSource testSource, TestSource parentSource) { + if (testSource instanceof CompositeTestSource) { + CompositeTestSource composite = (CompositeTestSource)testSource; + for (TestSource s : composite.getSources()) { + String v = getLocationHintValue(s, parentSource); + if (!v.isEmpty()) return v; + } + return ""; + } + + if (testSource instanceof FileSource) { + FileSource fileSource = (FileSource)testSource; + File file = fileSource.getFile(); + String line = fileSource.getPosition() + .map(position -> ":" + position.getLine()) + .orElse(""); + return "file://" + file.getAbsolutePath() + line; + } + + if (testSource instanceof MethodSource) { + MethodSource methodSource = (MethodSource)testSource; + return javaLocation(methodSource.getClassName(), methodSource.getMethodName(), true); + } + + if (testSource instanceof ClassSource) { + String className = ((ClassSource)testSource).getClassName(); + return javaLocation(className, null, false); + } + + if (parentSource != null) { + return getLocationHintValue(parentSource, null); + } + + return ""; + } + + private static String javaLocation(String className, String maybeMethodName, boolean isTest) { + String type = isTest ? "test" : "suite"; + String methodName = maybeMethodName == null ? "" : "/" + maybeMethodName; + return "java:" + type + "://" + MapSerializerUtil.escapeStr(className + methodName, MapSerializerUtil.STD_ESCAPER); + } +} diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/SuiteReporter.java b/plugins/junit5_rt/src/com/intellij/junit5/report/SuiteReporter.java new file mode 100644 index 000000000000..45a6be3d3650 --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/SuiteReporter.java @@ -0,0 +1,139 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.UniqueId; +import org.junit.platform.launcher.TestIdentifier; + +import java.util.*; + +import static com.intellij.rt.execution.TestListenerProtocol.CLASS_CONFIGURATION; +import static com.intellij.rt.execution.junit.MapSerializerUtil.*; + +public class SuiteReporter extends AbstractTestReporter { + public SuiteReporter(TestIdentifier identifier, ExecutionState state) { + super(identifier, state); + } + + // only for suites + public boolean isSkipped() { + UniqueId id = UniqueId.parse(identifier.getUniqueId()); + List segments = id.getSegments(); + if (segments.isEmpty()) return false; + + UniqueId.Segment lastSegment = segments.get(segments.size() - 1); + if ("engine".equals(lastSegment.getType())) return true; + + String root = state.getRootName(); + return root != null && root.equals(lastSegment.getValue()); + } + + @Override + public List start() { + if (isSkipped()) return Collections.emptyList(); + + state.resetFinishCount(); + String start = asString(TEST_SUITE_STARTED, attributes(ReportedField.ID, ReportedField.NAME, ReportedField.NODE_ID, + ReportedField.PARENT_NODE_ID, ReportedField.HINT, ReportedField.METAINFO)); + return Collections.singletonList(start); + } + + @Override + public List finish(TestExecutionResult result) { + if (isSkipped() && result.getStatus() != TestExecutionResult.Status.FAILED) { + return Collections.emptyList(); + } + + List out = new ArrayList<>(); + TestExecutionResult.Status status = result.getStatus(); + Throwable throwable = result.getThrowable().orElse(null); + Set descendants = state.plan() != null ? state.plan().getDescendants(identifier) : Collections.emptySet(); + + if (status == TestExecutionResult.Status.FAILED) { + // Report class-level failure as CLASS_CONFIGURATION test + TestReporter reporter = new TestReporter(identifier, state, CLASS_CONFIGURATION); + out.add(asString(TEST_STARTED, reporter.attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO))); + out.addAll(reporter.reportFailure(throwable, 0, true, TEST_FAILED, null)); + out.add(asString(TEST_FINISHED, reporter.attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID))); + } + else if (status == TestExecutionResult.Status.ABORTED) { + if (descendants.isEmpty()) { + // Report as ignored + Map attrs = attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID); + if (throwable != null) { + attrs.put("message", throwable.getMessage()); + } + out.add(asString(TEST_IGNORED, attrs)); + } + } + + if (!descendants.isEmpty() && state.finishCount() == 0) { + String reason = (throwable != null) ? throwable.getMessage() : null; + for (TestIdentifier child : descendants) { + AbstractTestReporter reporter = TeamCityTestReporter.get(child, state); + if (reporter instanceof TestReporter) { + Throwable childEx = (status == TestExecutionResult.Status.ABORTED) ? throwable : null; + out.addAll(((TestReporter)reporter).ignore(childEx, reason)); + } + } + state.resetFinishCount(); + } + + if (!isSkipped()) { + out.add(asString(TEST_SUITE_FINISHED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID))); + } + + return out; + } + + @Override + public List treeStarted() { + if (isSkipped()) return Collections.emptyList(); + + String started = asString(SUITE_TREE_STARTED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO + )); + return Collections.singletonList(started); + } + + @Override + public List treeFinished() { + if (isSkipped()) return Collections.emptyList(); + String finished = asString(SUITE_TREE_ENDED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID)); + return Collections.singletonList(finished); + } + + @Override + public List skip(String reason) { + List out = new ArrayList<>(); + Set descendants = state.plan() != null ? state.plan().getDescendants(identifier) : Collections.emptySet(); + + if (descendants.isEmpty()) { + Map attrs = attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID); + if (reason != null && !reason.isEmpty()) attrs.put("message", reason); + out.add(asString(TEST_IGNORED, attrs)); + } else { + for (TestIdentifier child : descendants) { + AbstractTestReporter reporter = TeamCityTestReporter.get(child, state); + out.addAll(reporter.start()); + out.addAll(reporter.skip(reason)); + } + state.resetFinishCount(); + } + + if (!isSkipped()) { + out.add(asString(TEST_SUITE_FINISHED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID))); + } + + return out; + } +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/TeamCityTestReporter.java b/plugins/junit5_rt/src/com/intellij/junit5/report/TeamCityTestReporter.java new file mode 100644 index 000000000000..b2aa183b522b --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/TeamCityTestReporter.java @@ -0,0 +1,34 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.engine.reporting.ReportEntry; +import org.junit.platform.launcher.TestIdentifier; + +import java.util.List; + +public interface TeamCityTestReporter { + List start(); + + List output(ReportEntry entry); + + List finish(TestExecutionResult result); + + List skip(String reason); + + List treeStarted(); + + List treeFinished(); + + static AbstractTestReporter get(TestIdentifier identifier, ExecutionState state) { + if (identifier.isTest() && identifier.isContainer()) { + return new CompositeTestReporter(identifier, state); + } + else if (identifier.isTest()) { + return new TestReporter(identifier, state); + } + else { + return new SuiteReporter(identifier, state); + } + } +} \ No newline at end of file diff --git a/plugins/junit5_rt/src/com/intellij/junit5/report/TestReporter.java b/plugins/junit5_rt/src/com/intellij/junit5/report/TestReporter.java new file mode 100644 index 000000000000..d2cfe8529f1a --- /dev/null +++ b/plugins/junit5_rt/src/com/intellij/junit5/report/TestReporter.java @@ -0,0 +1,153 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.junit5.report; + +import com.intellij.junit4.ExpectedPatterns; +import com.intellij.rt.execution.junit.ComparisonFailureData; +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.launcher.TestIdentifier; +import org.opentest4j.MultipleFailuresError; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.intellij.rt.execution.junit.MapSerializerUtil.*; + +public class TestReporter extends AbstractTestReporter { + private final String name; + + public TestReporter(TestIdentifier identifier, ExecutionState state, String name) { + super(identifier, state); + this.name = name; + } + + public TestReporter(TestIdentifier identifier, ExecutionState state) { + this(identifier, state, identifier.getDisplayName()); + } + + @Override + protected String name() { + return name; + } + + @Override + public List start() { + state.onLeafTestStarted(); + String start = asString(TEST_STARTED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO)); + return Collections.singletonList(start); + } + + @Override + public List finish(TestExecutionResult result) { + Throwable ex = result.getThrowable().orElse(null); + long duration = state.onLeafTestFinishedAndGetDurationMs(); + + List out = new ArrayList<>(); + + switch (result.getStatus()) { + case ABORTED: + out.addAll(reportFailure(ex, duration, true, TEST_IGNORED)); + break; + case FAILED: + out.addAll(reportFailure(ex, duration, true, TEST_FAILED)); + break; + case SUCCESSFUL: // do nothing + break; + } + + Map attributes = attributes(ReportedField.ID, ReportedField.NAME, ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID); + if (duration > 0) { + attributes.put("duration", Long.toString(duration)); + } + out.add(asString(TEST_FINISHED, attributes)); + + state.incrementFinishCount(); + return out; + } + + @Override + public List treeStarted() { + String started = asString(SUITE_TREE_NODE, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO + )); + return Collections.singletonList(started); + } + + @Override + public List treeFinished() { + return Collections.emptyList(); + } + + @Override + public List skip(String reason) { + List out = new ArrayList<>(reportFailure(null, 0, true, TEST_IGNORED, reason)); + out.add(asString(TEST_FINISHED, attributes(ReportedField.ID, ReportedField.NAME, ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID))); + + state.incrementFinishCount(); + return out; + } + + @SuppressWarnings("SameParameterValue") + private List reportFailure(Throwable ex, + long duration, + boolean includeThrowable, + String messageType) { + return reportFailure(ex, duration, includeThrowable, messageType, null); + } + + protected List ignore(Throwable ex, String reason) { + List out = new ArrayList<>(); + out.add(asString(TEST_STARTED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID, + ReportedField.HINT, ReportedField.METAINFO))); + out.addAll(reportFailure(ex, 0, true, TEST_IGNORED, reason)); + out.add(asString(TEST_FINISHED, attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID))); + return out; + } + + List reportFailure(Throwable ex, + long duration, + boolean includeThrowable, + String messageType, // TEST_FAILED or TEST_IGNORED + String reason) { + Map attributes = attributes(ReportedField.ID, ReportedField.NAME, + ReportedField.NODE_ID, ReportedField.PARENT_NODE_ID); + + if (duration > 0) { + attributes.put("duration", Long.toString(duration)); + } + if (reason != null) { + attributes.put("message", reason); + } + + List out = new ArrayList<>(); + + if (ex != null) { + ComparisonFailureData failureData = null; + if (ex instanceof MultipleFailuresError && ((MultipleFailuresError)ex).hasFailures()) { + for (Throwable t : ((MultipleFailuresError)ex).getFailures()) { + out.addAll(reportFailure(t, duration, true, messageType, reason)); + } + } + else { + try { + failureData = ExpectedPatterns.createExceptionNotification(ex); + } + catch (Throwable ignore) { + } + } + + ComparisonFailureData.registerSMAttributes(failureData, includeThrowable ? getTrace(ex) : "", + ex.getMessage(), attributes, ex, + "Comparison Failure: ", "expected: <"); + } + + out.add(asString(messageType, attributes)); + return out; + } +} \ No newline at end of file diff --git a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5EventsTest.java b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5EventsTest.java index d049d0766772..14981c4dc5d4 100644 --- a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5EventsTest.java +++ b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5EventsTest.java @@ -47,14 +47,14 @@ public class JUnit5EventsTest { .finishWithFailure(multipleFailuresError); Assertions.assertEquals(""" - ##teamcity[enteredTheMatrix] - ##teamcity[rootName name = 'testClass' location = 'java:suite://testClass'] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/test1' metainfo=''] - ##teamcity[testStdOut id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' out = 'timestamp = ${timestamp}, key1 = value1, stdout = out1|n'] - ##teamcity[testFailed name='test1()' id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='message1|nComparison Failure: ' expected='expected1' actual='actual1' details='TRACE'] - ##teamcity[testFailed name='test1()' id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='message2|nComparison Failure: ' expected='expected2' actual='actual2' details='TRACE'] - ##teamcity[testFailed name='test1()' id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='2 errors (2 failures)|n\torg.opentest4j.AssertionFailedError: message1|n\torg.opentest4j.AssertionFailedError: message2' details='TRACE'] - ##teamcity[testFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0'] + ##TC[enteredTheMatrix] + ##TC[rootName name='testClass' location='java:suite://testClass'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/test1' metainfo=''] + ##TC[testStdOut id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' out='timestamp = ${timestamp}, key1 = value1, stdout = out1|n'] + ##TC[testFailed id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='message1|nComparison Failure: ' expected='expected1' actual='actual1' details='TRACE'] + ##TC[testFailed id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='message2|nComparison Failure: ' expected='expected2' actual='actual2' details='TRACE'] + ##TC[testFailed id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' message='2 errors (2 failures)|n\torg.opentest4j.AssertionFailedError: message1|n\torg.opentest4j.AssertionFailedError: message2' details='TRACE'] + ##TC[testFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='test1()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0'] """.replace("${timestamp}", reportEntry.getTimestamp().toString()), builder.getFormattedOutput()); } @@ -82,11 +82,11 @@ public class JUnit5EventsTest { Assertions.assertEquals( """ - ##teamcity[enteredTheMatrix] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test1|]' name='test1 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test1|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass' metainfo='110:221'] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test2|]' name='test2 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test2|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='file:///directory/test2.java:12'] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test3|]' name='test3 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test3|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://MyTestClass/test4Method' metainfo='java.lang.String,java.util.List'] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test4|]' name='test4 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test4|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:suite://com.intellij.junit5.JUnit5EventsTest'] + ##TC[enteredTheMatrix] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test1|]' name='test1 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test1|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass' metainfo='110:221'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test2|]' name='test2 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test2|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='file:///directory/test2.java:12'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test3|]' name='test3 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test3|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://MyTestClass/test4Method' metainfo='java.lang.String,java.util.List'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:test4|]' name='test4 display name' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:test4|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:suite://com.intellij.junit5.JUnit5EventsTest'] """, builder.getFormattedOutput()); } @@ -104,15 +104,14 @@ public class JUnit5EventsTest { testContext.startTestOnly().finishWithFailure(new IllegalStateException()); Assertions.assertEquals(""" - ##teamcity[enteredTheMatrix] - ##teamcity[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] - ##teamcity[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0'] - ##teamcity[treeEnded] - ##teamcity[rootName name = 'testMethod' location = 'java:suite://testMethod'] - ##teamcity[testStarted name='Class Configuration' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' ] - ##teamcity[testFailed name='Class Configuration' id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' error='true' message='' details='TRACE'] - ##teamcity[testFinished name='Class Configuration' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' ] - ##teamcity[testSuiteFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] + ##TC[enteredTheMatrix] + ##TC[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] + ##TC[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0'] + ##TC[treeEnded] + ##TC[rootName name='testMethod' location='java:suite://testMethod'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='Class Configuration' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/brokenStream' metainfo=''] + ##TC[testFailed id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='Class Configuration' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' error='true' message='' details='TRACE'] + ##TC[testFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='Class Configuration' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] """, builder.getFormattedOutput()); } @@ -131,14 +130,14 @@ public class JUnit5EventsTest { builder.getExecutionListener().executionFinished(testIdentifier, TestExecutionResult.successful()); Assertions.assertEquals(""" - ##teamcity[enteredTheMatrix] - ##teamcity[suiteTreeStarted id='|[engine:engine|]/|[suite:suiteClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[suite:suiteClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] - ##teamcity[suiteTreeStarted id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] - ##teamcity[suiteTreeEnded id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]'] - ##teamcity[suiteTreeEnded id='|[engine:engine|]/|[suite:suiteClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[suite:suiteClass|]' parentNodeId='0'] - ##teamcity[treeEnded] - ##teamcity[testSuiteStarted id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]'locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] - ##teamcity[testSuiteFinished id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]'] + ##TC[enteredTheMatrix] + ##TC[suiteTreeStarted id='|[engine:engine|]/|[suite:suiteClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[suite:suiteClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] + ##TC[suiteTreeStarted id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] + ##TC[suiteTreeEnded id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]'] + ##TC[suiteTreeEnded id='|[engine:engine|]/|[suite:suiteClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[suite:suiteClass|]' parentNodeId='0'] + ##TC[treeEnded] + ##TC[testSuiteStarted id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] + ##TC[testSuiteFinished id='|[engine:secondEngine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:secondEngine|]/|[class:testClass|]' parentNodeId='|[engine:engine|]/|[suite:suiteClass|]'] """, builder.getFormattedOutput()); } @@ -153,15 +152,15 @@ public class JUnit5EventsTest { testContext.startTestOnly().finishAborted(); Assertions.assertEquals(""" - ##teamcity[enteredTheMatrix] - ##teamcity[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] - ##teamcity[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/brokenStream' metainfo=''] - ##teamcity[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] - ##teamcity[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0'] - ##teamcity[treeEnded] - ##teamcity[testSuiteStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'locationHint='java:test://com.intellij.junit5.testData.MyTestClass/brokenStream' metainfo=''] - ##teamcity[testIgnored name='brokenStream()' id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] - ##teamcity[testSuiteFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] + ##TC[enteredTheMatrix] + ##TC[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0' locationHint='java:suite://com.intellij.junit5.testData.MyTestClass'] + ##TC[suiteTreeStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/brokenStream' metainfo=''] + ##TC[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] + ##TC[suiteTreeEnded id='|[engine:engine|]/|[class:testClass|]' name='MyTestClass' nodeId='|[engine:engine|]/|[class:testClass|]' parentNodeId='0'] + ##TC[treeEnded] + ##TC[testSuiteStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]' locationHint='java:test://com.intellij.junit5.testData.MyTestClass/brokenStream' metainfo=''] + ##TC[testIgnored id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] + ##TC[testSuiteFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='brokenStream()' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='|[engine:engine|]/|[class:testClass|]'] """, builder.getFormattedOutput()); } @@ -189,10 +188,10 @@ public class JUnit5EventsTest { testContext.startExecution().finish(); Assertions.assertEquals(""" - ##teamcity[enteredTheMatrix] - ##teamcity[rootName name = 'testClass' location = 'java:suite://testClass'] - ##teamcity[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='|[test|'s method|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' locationHint='java:test://com.intellij.junit5.testData.AnnotationsTestClass/test1' metainfo=''] - ##teamcity[testFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='|[test|'s method|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0'] + ##TC[enteredTheMatrix] + ##TC[rootName name='testClass' location='java:suite://testClass'] + ##TC[testStarted id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='|[test|'s method|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0' locationHint='java:test://com.intellij.junit5.testData.AnnotationsTestClass/test1' metainfo=''] + ##TC[testFinished id='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' name='|[test|'s method|]' nodeId='|[engine:engine|]/|[class:testClass|]/|[method:testMethod|]' parentNodeId='0'] """, builder.getFormattedOutput()); } } diff --git a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5NavigationTest.java b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5NavigationTest.java index da7c2ca4ace6..3242b12421ab 100644 --- a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5NavigationTest.java +++ b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5NavigationTest.java @@ -1,6 +1,7 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.junit5; +import com.intellij.junit5.report.LocationInfo; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -53,8 +54,7 @@ class JUnit5NavigationTest { myTestSource = anySupportedSource(); String locationHint = locationHint(); - Assertions.assertTrue(locationHint.startsWith("locationHint='"), locationHint); - Assertions.assertTrue(locationHint.endsWith("'"), locationHint); + Assertions.assertEquals("java:suite://java.lang.String", locationHint); } @Test @@ -115,7 +115,8 @@ class JUnit5NavigationTest { private String locationHint() { TestIdentifier testIdentifier = TestIdentifier.from(new ConfigurableTestDescriptor(myTestSource)); - return JUnit5TestExecutionListener.getLocationHint(testIdentifier, null); + LocationInfo info = new LocationInfo(testIdentifier.getSource().orElse(null), null, null); + return info.locationHint(); } private String locationHintValue() { @@ -124,7 +125,8 @@ class JUnit5NavigationTest { private static String locationHintValue(final ConfigurableTestDescriptor descriptor) { TestIdentifier testIdentifier = TestIdentifier.from(descriptor); - return JUnit5TestExecutionListener.getLocationHintValue(testIdentifier.getSource().orElseThrow(IllegalStateException::new), null); + LocationInfo info = new LocationInfo(testIdentifier.getSource().orElseThrow(IllegalStateException::new), null, null); + return info.locationHint(); } private static ClassSource anySupportedSource() { diff --git a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5TestRunnerBuilder.java b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5TestRunnerBuilder.java index d6118f747823..74582f0a8a1e 100644 --- a/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5TestRunnerBuilder.java +++ b/plugins/junit5_rt_tests/test/com/intellij/junit5/JUnit5TestRunnerBuilder.java @@ -87,17 +87,7 @@ public class JUnit5TestRunnerBuilder { public void write(int b) { myStringBuffer.append(new String(new byte[]{(byte)b}, StandardCharsets.UTF_8)); } - }, false, StandardCharsets.UTF_8)) { - @Override - protected long getDuration() { - return 0; - } - - @Override - protected String getTrace(Throwable ex) { - return "TRACE"; - } - }; + }, false, StandardCharsets.UTF_8)); } public TestDescriptorContext withTestMethod(Class testClass, String methodName) throws NoSuchMethodException { @@ -220,7 +210,12 @@ public class JUnit5TestRunnerBuilder { } public String getFormattedOutput() { - return StringUtil.convertLineSeparators(myStringBuffer.toString()).replaceAll("\\|r", ""); + String out = myStringBuffer.toString(); + return StringUtil.convertLineSeparators(out) + .replaceAll("\\|r", "") + .replaceAll("##teamcity\\[", "##TC[") + .replaceAll(" duration='[0-9]+'", "") + .replaceAll("details='.*?'\\]", "details='TRACE']"); } public JUnit5TestExecutionListener getExecutionListener() { diff --git a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6EventsTest.java b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6EventsTest.java index c2d21d9892d1..58d989cfbf0a 100644 --- a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6EventsTest.java +++ b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6EventsTest.java @@ -11,14 +11,15 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.testFramework.PlatformTestUtil; -import jetbrains.buildServer.messages.serviceMessages.*; +import jetbrains.buildServer.messages.serviceMessages.BaseTestMessage; +import jetbrains.buildServer.messages.serviceMessages.ServiceMessage; +import jetbrains.buildServer.messages.serviceMessages.TestStarted; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.maven.aether.ArtifactRepositoryManager; import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import static com.intellij.junit6.ServiceMessageUtil.replaceAttributes; @@ -44,39 +45,24 @@ public class JUnit6EventsTest extends AbstractTestFrameworkCompilingIntegrationT ProcessOutput output = doStartTestsProcess(createRunMethodConfiguration("com.intellij.junit6.testData.MyTestClass", "test1")); assertEmpty(output.err); - List messages = output.messages; - - Map tests = getStartedTests(messages); - // Ensure our test method started with a proper location hint - TestStarted started = tests.values().stream() - .filter(t -> "java:test://com.intellij.junit6.testData.MyTestClass/test1".equals(t.getAttributes().get("locationHint"))) - .findFirst().orElse(null); - assertNotNull(started); - assertEquals("java:test://com.intellij.junit6.testData.MyTestClass/test1", started.getAttributes().get("locationHint")); - String nodeId = started.getAttributes().get("nodeId"); - - // There should be multiple failures reported for the same test - List failed = messages.stream() - .filter(m -> nodeId.equals(m.getAttributes().get("nodeId"))) - .filter(m -> m instanceof TestFailed).toList(); - assertSize(3, failed); - - Set errors = failed.stream().filter(m -> m.getAttributes().get("expected") != null) - .map(m -> "expected='" + m.getAttributes().get("expected") + "', actual='" + m.getAttributes().get("actual") + "'") - .collect(Collectors.toSet()); - assertEquals(Set.of( - "expected='expected1', actual='actual1'", - "expected='expected2', actual='actual2'" - ), errors); - - // StdOut message should include our published entries - Set outs = messages.stream() - .filter(m -> nodeId.equals(m.getAttributes().get("nodeId"))) - .filter(m -> m instanceof TestStdOut) - .map(m -> m.getAttributes().get("out")) + String tests = output.messages.stream().filter(m -> m instanceof BaseTestMessage) + .map(m -> replaceAttributes(m, Map.of( + "timestamp", "##timestamp##", + "duration", "##duration##", + "details", "##details##" + ))) + .map(m -> m.replaceAll("##teamcity\\[", "##TC[")) .map(s -> s.replaceAll("timestamp = [0-9\\-:.T]+", "timestamp = ##timestamp##")) - .collect(Collectors.toSet()); - assertEquals(Set.of("timestamp = ##timestamp##, key1 = value1, stdout = out1\n"), outs); + .collect(Collectors.joining("\n")); + + assertEquals(""" + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/test1' metainfo='org.junit.jupiter.api.TestReporter'] + ##TC[testStdOut id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' out='timestamp = ##timestamp##, key1 = value1, stdout = out1|n'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='message1 ==> expected: but was: |nComparison Failure: ' expected='expected1' actual='actual1' details='##details##'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='message2 ==> expected: but was: |nComparison Failure: ' expected='expected2' actual='actual2' details='##details##'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='2 errors (2 failures)|n org.opentest4j.AssertionFailedError: message1 ==> expected: but was: |n org.opentest4j.AssertionFailedError: message2 ==> expected: but was: ' details='##details##'] + ##TC[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##']""", + tests); } public void testContainerFailure() throws Exception { @@ -84,46 +70,49 @@ public class JUnit6EventsTest extends AbstractTestFrameworkCompilingIntegrationT assertEmpty(output.err); // Expect a configuration failure - List test = output.messages.stream().filter(BaseTestMessage.class::isInstance) + String test = output.messages.stream().filter(BaseTestMessage.class::isInstance) .map(BaseTestMessage.class::cast) .filter(m -> m.getTestName().equals("Class Configuration")) .map(m -> replaceAttributes(m, Map.of("details", "##details##"))) - .toList(); - assertEquals(List.of( - "##teamcity[testStarted name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']", - "##teamcity[testFailed name='Class Configuration' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' error='true' message='java.lang.IllegalStateException: broken' details='##details##']", - "##teamcity[testFinished name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']" - ), test); + .map(m -> m.replaceAll("##teamcity\\[", "##TC[")) + .collect(Collectors.joining("\n")); + assertEquals(""" + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/brokenStream' metainfo=''] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' error='true' message='java.lang.IllegalStateException: broken' details='##details##'] + ##TC[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']""", + test); } public void testContainerDisabled() throws Exception { ProcessOutput output = doStartTestsProcess(createRunClassConfiguration("com.intellij.junit6.testData.MyTestClass")); assertEmpty(output.err); - List tests = output.messages.stream().filter(m -> m instanceof BaseTestMessage) + String tests = output.messages.stream().filter(m -> m instanceof BaseTestMessage) .map(m -> replaceAttributes(m, Map.of( "timestamp", "##timestamp##", "duration", "##duration##", "message", "##message##", "details", "##details##" ))) - .map(s -> s.replaceAll("timestamp = [0-9\\-:.T]+", "timestamp = ##timestamp##")).toList(); + .map(m -> m.replaceAll("##teamcity\\[", "##TC[")) + .map(s -> s.replaceAll("timestamp = [0-9\\-:.T]+", "timestamp = ##timestamp##")) + .collect(Collectors.joining("\n")); - assertEquals(List.of( - "##teamcity[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' name='disabledTest()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/disabledTest' metainfo='']", - "##teamcity[testIgnored name='disabledTest()' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0' message='##message##']", - "##teamcity[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' name='disabledTest()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0']", - "##teamcity[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/test1' metainfo='org.junit.jupiter.api.TestReporter']", - "##teamcity[testStdOut id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' out='timestamp = ##timestamp##, key1 = value1, stdout = out1|n']", - "##teamcity[testFailed name='test1(TestReporter)' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' expected='expected1' actual='actual1' details='##details##']", - "##teamcity[testFailed name='test1(TestReporter)' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' expected='expected2' actual='actual2' details='##details##']", - "##teamcity[testFailed name='test1(TestReporter)' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' details='##details##']", - "##teamcity[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##']", - "##teamcity[testIgnored name='brokenStreamDisabled()' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStreamDisabled()|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStreamDisabled()|]' parentNodeId='0' message='##message##']", - "##teamcity[testStarted name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']", - "##teamcity[testFailed name='Class Configuration' id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' error='true' message='##message##' details='##details##']", - "##teamcity[testFinished name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']" - ), tests); + assertEquals(""" + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' name='disabledTest()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/disabledTest' metainfo=''] + ##TC[testIgnored id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' name='disabledTest()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0' message='##message##'] + ##TC[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' name='disabledTest()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:disabledTest()|]' parentNodeId='0'] + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/test1' metainfo='org.junit.jupiter.api.TestReporter'] + ##TC[testStdOut id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' out='timestamp = ##timestamp##, key1 = value1, stdout = out1|n'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' expected='expected1' actual='actual1' details='##details##'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' expected='expected2' actual='actual2' details='##details##'] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##' message='##message##' details='##details##'] + ##TC[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' name='test1(TestReporter)' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[method:test1(org.junit.jupiter.api.TestReporter)|]' parentNodeId='0' duration='##duration##'] + ##TC[testIgnored id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStreamDisabled()|]' name='brokenStreamDisabled()' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStreamDisabled()|]' parentNodeId='0' message='##message##'] + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' locationHint='java:test://com.intellij.junit6.testData.MyTestClass/brokenStream' metainfo=''] + ##TC[testFailed id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0' error='true' message='##message##' details='##details##'] + ##TC[testFinished id='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' name='Class Configuration' nodeId='|[engine:junit-jupiter|]/|[class:com.intellij.junit6.testData.MyTestClass|]/|[test-factory:brokenStream()|]' parentNodeId='0']""", + tests); } public void testEscaping() throws Exception { diff --git a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6NavigationTest.java b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6NavigationTest.java index 07fb7e8f116a..31279eb24f26 100644 --- a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6NavigationTest.java +++ b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6NavigationTest.java @@ -15,8 +15,8 @@ import org.jetbrains.idea.maven.aether.ArtifactRepositoryManager; import org.jetbrains.jps.model.library.JpsMavenRepositoryLibraryDescriptor; import org.junit.jupiter.api.DisplayName; -import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static com.intellij.junit6.ServiceMessageUtil.replaceAttributes; @@ -33,26 +33,28 @@ public class JUnit6NavigationTest extends AbstractTestFrameworkCompilingIntegrat ModuleRootModificationUtil.updateModel(myModule, model -> model.addContentEntry(getTestContentRoot()) .addSourceFolder(getTestContentRoot() + "/test", true)); final ArtifactRepositoryManager repoManager = getRepoManager(); - addMavenLibs(myModule, new JpsMavenRepositoryLibraryDescriptor("org.junit.jupiter", "junit-jupiter-api", JUnit6Constants.VERSION), repoManager); + addMavenLibs(myModule, new JpsMavenRepositoryLibraryDescriptor("org.junit.jupiter", "junit-jupiter-api", JUnit6Constants.VERSION), + repoManager); } public void testNavigation() throws Exception { ProcessOutput output = doStartTestsProcess(createRunClassConfiguration("org.example.impl.NavTest")); assertEmpty(output.err); - List messages = output.messages.stream().filter(m -> m.getAttributes().containsKey("locationHint")) + String messages = output.messages.stream().filter(m -> m.getAttributes().containsKey("locationHint")) .map(m -> m.getAttributes().get("locationHint").startsWith("file://") ? replaceAttributes(m, Map.of("locationHint", "file://##path##")) : m.asString()) - .toList(); + .map(m -> m.replaceAll("##teamcity\\[", "##TC[")) + .collect(Collectors.joining("\n")); - assertEquals(List.of( - "##teamcity[suiteTreeStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' name='fileSourceDynamicTests()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/fileSourceDynamicTests' metainfo='']", - "##teamcity[suiteTreeNode id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' name='methodNavigation()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/methodNavigation' metainfo='']", - "##teamcity[testSuiteStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' name='fileSourceDynamicTests()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/fileSourceDynamicTests' metainfo='']", - "##teamcity[testStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]/|[dynamic-test:#1|]' name='fileSource' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]/|[dynamic-test:#1|]' parentNodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' locationHint='file://##path##']", - "##teamcity[testStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' name='methodNavigation()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/methodNavigation' metainfo='']" - ), messages); + assertEquals(""" + ##TC[suiteTreeStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' name='fileSourceDynamicTests()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/fileSourceDynamicTests' metainfo=''] + ##TC[suiteTreeNode id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' name='methodNavigation()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/methodNavigation' metainfo=''] + ##TC[testSuiteStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' name='fileSourceDynamicTests()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/fileSourceDynamicTests' metainfo=''] + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]/|[dynamic-test:#1|]' name='fileSource' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]/|[dynamic-test:#1|]' parentNodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[test-factory:fileSourceDynamicTests()|]' locationHint='file://##path##'] + ##TC[testStarted id='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' name='methodNavigation()' nodeId='|[engine:junit-jupiter|]/|[class:org.example.impl.NavTest|]/|[method:methodNavigation()|]' parentNodeId='0' locationHint='java:test://org.example.impl.NavTest/methodNavigation' metainfo='']""", + messages); } @NotNull diff --git a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6SuiteApiIntegrationTest.java b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6SuiteApiIntegrationTest.java index 72aef0801bfa..2dbe12d30021 100644 --- a/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6SuiteApiIntegrationTest.java +++ b/plugins/junit6_rt_tests/test/com/intellij/junit6/JUnit6SuiteApiIntegrationTest.java @@ -91,7 +91,7 @@ public class JUnit6SuiteApiIntegrationTest extends AbstractTestFrameworkCompilin private static Map getStartedTests(List messages) { return messages.stream().filter(TestStarted.class::isInstance).map(TestStarted.class::cast) - .collect(Collectors.toMap(t -> t.getAttributes().get("id"), t -> t)); + .collect(Collectors.toMap(t -> t.getAttributes().get("id"), t -> t, (existing, replacement) -> existing)); } private static Set getTestIds(Map tests, Set locationHints) { diff --git a/plugins/junit6_rt_tests/testData/integration/suiteApi/test/org/example/api/AllTests.java b/plugins/junit6_rt_tests/testData/integration/suiteApi/test/org/example/api/AllTests.java index e91bf7fdd94a..d75013464d72 100644 --- a/plugins/junit6_rt_tests/testData/integration/suiteApi/test/org/example/api/AllTests.java +++ b/plugins/junit6_rt_tests/testData/integration/suiteApi/test/org/example/api/AllTests.java @@ -1,10 +1,8 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.example.api; -import org.example.impl.FirstTest; -import org.example.impl.SecondTest; -import org.junit.platform.suite.api.SelectClasses; -import org.junit.platform.suite.api.Suite; +import org.example.impl.*; +import org.junit.platform.suite.api.*; @Suite @SelectClasses({FirstTest.class, SecondTest.class})