diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc index 4f40bc523b81..7496f59a759b 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-6.1.0-M2.adoc @@ -45,8 +45,8 @@ repository on GitHub. [[release-notes-6.1.0-M2-junit-jupiter-new-features-and-improvements]] ==== New Features and Improvements -* ❓ - +* Trim internal stack frames from `AssertionFailedError` stack traces. +* Introduce new `trimStacktrace(Class)` method for `AssertionFailureBuilder`. It allows user defined assertions to trim their stacktrace. [[release-notes-6.1.0-M2-junit-vintage]] === JUnit Vintage diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java index 35bc69437fae..56c707d7c489 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertArrayEquals.java @@ -483,6 +483,7 @@ private static AssertionFailedError expectedArrayIsNullFailure(@Nullable Deque" + formatIndexes(indexes)) // + .trimStacktrace(Assertions.class) // .build(); } @@ -495,6 +496,7 @@ private static AssertionFailedError actualArrayIsNullFailure(@Nullable Deque" + formatIndexes(indexes)) // + .trimStacktrace(Assertions.class) // .build(); } @@ -507,6 +509,7 @@ private static void assertArraysHaveSameLength(int expected, int actual, @Nullab .reason("array lengths differ" + formatIndexes(indexes)) // .expected(expected) // .actual(actual) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } } @@ -519,6 +522,7 @@ private static void failArraysNotEqual(@Nullable Object expected, @Nullable Obje .reason("array contents differ" + formatIndexes(indexes)) // .expected(expected) // .actual(actual) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java index 82e15c85a41a..68882f795208 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertDoesNotThrow.java @@ -87,6 +87,7 @@ public static AssertionFailedError createAssertionFailedError(@Nullable Object m .message(messageOrSupplier) // .reason("Unexpected exception thrown: " + t.getClass().getName() + buildSuffix(t.getMessage())) // .cause(t) // + .trimStacktrace(Assertions.class) // .build(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java index 2e38bdb6f1c6..d862bbdb1a6b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertEquals.java @@ -198,6 +198,7 @@ private static void failNotEqual(@Nullable Object expected, @Nullable Object act .message(messageOrSupplier) // .expected(expected) // .actual(actual) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java index d0e308d3be26..4dac4ca3810b 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertFalse.java @@ -66,6 +66,7 @@ private static void failNotFalse(@Nullable Object messageOrSupplier) { .message(messageOrSupplier) // .expected(false) // .actual(true) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java index 92ae88a5d9e6..d9a1c51eb6c7 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertInstanceOf.java @@ -56,6 +56,7 @@ private static T assertInstanceOf(Class expectedType, @Nullable Object ac .expected(expectedType) // .actual(actualValue == null ? null : actualValue.getClass()) // .cause(actualValue instanceof Throwable t ? t : null) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } return expectedType.cast(actualValue); diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java index b9e70a58844d..9523ecd19124 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertIterableEquals.java @@ -153,6 +153,7 @@ private static AssertionFailedError expectedIterableIsNullFailure(Deque return assertionFailure() // .message(messageOrSupplier) // .reason("expected iterable was " + formatIndexes(indexes)) // + .trimStacktrace(Assertions.class) // .build(); } @@ -165,6 +166,7 @@ private static AssertionFailedError actualIterableIsNullFailure(Deque i return assertionFailure() // .message(messageOrSupplier) // .reason("actual iterable was " + formatIndexes(indexes)) // + .trimStacktrace(Assertions.class) // .build(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java index 990d12b94f29..722aaec87ee4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertLinesMatch.java @@ -193,6 +193,7 @@ void fail(String format, Object... args) { .expected(join(newLine, expectedLines)) // .actual(join(newLine, actualLines)) // .includeValuesInMessage(false) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java index e78a32eb4aa8..52e253f101f3 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotEquals.java @@ -279,6 +279,7 @@ private static void failEqual(@Nullable Object actual, @Nullable Object messageO assertionFailure() // .message(messageOrSupplier) // .reason("expected: not equal but was: <" + actual + ">") // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java index a03079f49341..c5b25ce39bed 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotNull.java @@ -52,6 +52,7 @@ private static void failNull(@Nullable Object messageOrSupplier) { assertionFailure() // .message(messageOrSupplier) // .reason("expected: not ") // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java index d46c8f711fe1..ed46b9dc1f52 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNotSame.java @@ -49,6 +49,7 @@ private static void failSame(@Nullable Object actual, @Nullable Object messageOr assertionFailure() // .message(messageOrSupplier) // .reason("expected: not same but was: <" + actual + ">") // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java index 137fb1e83aee..d2213d1836d6 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertNull.java @@ -53,6 +53,7 @@ private static void failNotNull(@Nullable Object actual, @Nullable Object messag .message(messageOrSupplier) // .expected(null) // .actual(actual) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java index 2cf1476f3742..9c7f1be7b2fb 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertSame.java @@ -51,6 +51,7 @@ private static void failNotSame(@Nullable Object expected, @Nullable Object actu .message(messageOrSupplier) // .expected(expected) // .actual(actual) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java index d198486087a4..7c29a2af7edf 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrows.java @@ -65,12 +65,14 @@ private static T assertThrows(Class expectedType, Execu .actual(actualException.getClass()) // .reason("Unexpected exception type thrown") // .cause(actualException) // + .trimStacktrace(Assertions.class) // .build(); } } throw assertionFailure() // .message(messageOrSupplier) // .reason("Expected %s to be thrown, but nothing was thrown.".formatted(getCanonicalName(expectedType))) // + .trimStacktrace(Assertions.class) // .build(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java index 59e72305be9e..865bd1945d04 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertThrowsExactly.java @@ -65,6 +65,7 @@ private static T assertThrowsExactly(Class expectedType .actual(actualException.getClass()) // .reason("Unexpected exception type thrown") // .cause(actualException) // + .trimStacktrace(Assertions.class) // .build(); } } @@ -72,6 +73,7 @@ private static T assertThrowsExactly(Class expectedType throw assertionFailure() // .message(messageOrSupplier) // .reason("Expected %s to be thrown, but nothing was thrown.".formatted(getCanonicalName(expectedType))) // + .trimStacktrace(Assertions.class) // .build(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java index d60b065be1bb..baefebfe50b4 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeout.java @@ -82,6 +82,7 @@ static void assertTimeout(Duration timeout, Executable executable, Supplier<@Nul .message(messageOrSupplier) // .reason("execution exceeded timeout of " + timeoutInMillis + " ms by " + (timeElapsed - timeoutInMillis) + " ms") // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } return result; diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java index f6c6afd42a12..72d7742a1c7a 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTimeoutPreemptively.java @@ -73,6 +73,7 @@ private static AssertionFailedError createAssertionFailure(Duration timeout, .message(messageSupplier) // .reason("execution timed out after " + timeout.toMillis() + " ms") // .cause(cause) // + .trimStacktrace(Assertions.class) // .build(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java index cb92e2278419..d56237c75b72 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertTrue.java @@ -66,6 +66,7 @@ private static void failNotTrue(@Nullable Object messageOrSupplier) { .message(messageOrSupplier) // .expected(true) // .actual(false) // + .trimStacktrace(Assertions.class) // .buildAndThrow(); } diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java index a8fd354ce2a4..3b71149c49c2 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionFailureBuilder.java @@ -10,9 +10,11 @@ package org.junit.jupiter.api; +import static org.apiguardian.api.API.Status.EXPERIMENTAL; import static org.apiguardian.api.API.Status.STABLE; import static org.junit.jupiter.api.AssertionUtils.getCanonicalName; +import java.util.Arrays; import java.util.function.Supplier; import org.apiguardian.api.API; @@ -46,6 +48,8 @@ public class AssertionFailureBuilder { private boolean includeValuesInMessage = true; + private @Nullable Class trimStackTraceTo; + /** * Create a new {@code AssertionFailureBuilder}. */ @@ -130,6 +134,21 @@ public AssertionFailureBuilder includeValuesInMessage(boolean includeValuesInMes return this; } + /** + * Set class to trim from the stacktrace. + * + *

To improve the readability of assertion failures, stack-frames up to + * and including any frames from {@code to} are trimmed from the stacktrace. + * + * @param to class to prune from the stacktrace. + * @return this builder for method chaining + */ + @API(status = EXPERIMENTAL, since = "6.1") + public AssertionFailureBuilder trimStacktrace(@Nullable Class to) { + this.trimStackTraceTo = to; + return this; + } + /** * Build the {@link AssertionFailedError AssertionFailedError} and throw it. * @@ -154,9 +173,37 @@ public AssertionFailedError build() { if (reason != null) { message = buildPrefix(message) + reason; } - return mismatch // + + var assertionFailedError = mismatch // ? new AssertionFailedError(message, expected, actual, cause) // : new AssertionFailedError(message, cause); + + maybeTrimStackTrace(assertionFailedError); + return assertionFailedError; + } + + private void maybeTrimStackTrace(Throwable throwable) { + if (trimStackTraceTo == null) { + return; + } + + var pruneTargetClassName = trimStackTraceTo.getName(); + var stackTrace = throwable.getStackTrace(); + + int lastIndexOf = -1; + for (int i = 0; i < stackTrace.length; i++) { + var element = stackTrace[i]; + var className = element.getClassName(); + if (className.equals(pruneTargetClassName)) { + lastIndexOf = i; + } + } + + if (lastIndexOf != -1) { + int from = Math.min(lastIndexOf + 1, stackTrace.length); + var pruned = Arrays.copyOfRange(stackTrace, from, stackTrace.length); + throwable.setStackTrace(pruned); + } } private static @Nullable String nullSafeGet(@Nullable Object messageOrSupplier) { diff --git a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java index f5dae75d401f..6ae0c48536a7 100644 --- a/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java +++ b/junit-jupiter-api/src/main/java/org/junit/jupiter/api/AssertionUtils.java @@ -11,6 +11,7 @@ package org.junit.jupiter.api; import static java.util.stream.Collectors.joining; +import static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure; import java.util.Deque; import java.util.function.Supplier; @@ -18,7 +19,6 @@ import org.jspecify.annotations.Nullable; import org.junit.platform.commons.annotation.Contract; import org.junit.platform.commons.util.UnrecoverableExceptions; -import org.opentest4j.AssertionFailedError; /** * {@code AssertionUtils} is a collection of utility methods that are common to @@ -34,27 +34,42 @@ private AssertionUtils() { @Contract(" -> fail") static void fail() { - throw new AssertionFailedError(); + throw assertionFailure() // + .trimStacktrace(Assertions.class) // + .build(); } @Contract("_ -> fail") static void fail(@Nullable String message) { - throw new AssertionFailedError(message); + throw assertionFailure() // + .message(message) // + .trimStacktrace(Assertions.class) // + .build(); } @Contract("_, _ -> fail") static void fail(@Nullable String message, @Nullable Throwable cause) { - throw new AssertionFailedError(message, cause); + throw assertionFailure() // + .message(message) // + .cause(cause) // + .trimStacktrace(Assertions.class) // + .build(); } @Contract("_ -> fail") static void fail(@Nullable Throwable cause) { - throw new AssertionFailedError(null, cause); + throw assertionFailure() // + .cause(cause) // + .trimStacktrace(Assertions.class) // + .build(); } @Contract("_ -> fail") static void fail(Supplier<@Nullable String> messageSupplier) { - throw new AssertionFailedError(nullSafeGet(messageSupplier)); + throw assertionFailure() // + .message(nullSafeGet(messageSupplier)) // + .trimStacktrace(Assertions.class) // + .build(); } static @Nullable String nullSafeGet(@Nullable Supplier<@Nullable String> messageSupplier) { diff --git a/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt b/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt index 932c281ac982..d6e4ea0f67d3 100644 --- a/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt +++ b/junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt @@ -363,6 +363,7 @@ inline fun assertDoesNotThrow(executable: () -> R): R { throw assertionFailure() .reason("Unexpected exception thrown: ${t.javaClass.getName()}$suffix") .cause(t) + .trimStacktrace(AssertionFailureBuilder::class.java) .build() } } @@ -420,6 +421,7 @@ inline fun assertDoesNotThrow( .message(message()) .reason("Unexpected exception thrown: ${t.javaClass.getName()}$suffix") .cause(t) + .trimStacktrace(AssertionFailureBuilder::class.java) .build() } } diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionFailureBuilderTest.java b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionFailureBuilderTest.java new file mode 100644 index 000000000000..9062fbaf34f5 --- /dev/null +++ b/jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionFailureBuilderTest.java @@ -0,0 +1,98 @@ +/* + * Copyright 2015-2025 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.api; + +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure; +import static org.junit.jupiter.api.Assertions.assertLinesMatch; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; + +import org.opentest4j.AssertionFailedError; + +class AssertionFailureBuilderTest { + + @Test + void doesNotTrimByDefault() { + var error = AssertionsFacade.fail(); + assertStackTraceMatch(error, + """ + \\Qorg.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:\\E.+ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.fail(AssertionFailureBuilderTest.java:\\E.+ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.doesNotTrimByDefault(AssertionFailureBuilderTest.java:\\E.+ + >>>> + """); + } + + @Test + void trimsUpToAssertionsFacade() { + var error = AssertionsFacade.failWithTrimmedStacktrace(AssertionsFacade.class); + assertStackTraceMatch(error, + """ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.trimsUpToAssertionsFacade(AssertionFailureBuilderTest.java:\\E.+ + >>>> + """); + } + + @Test + void trimsUpToAssertionFailureBuilder() { + var error = AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilder.class); + assertStackTraceMatch(error, + """ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilderTest.java:\\E.+ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.trimsUpToAssertionFailureBuilder(AssertionFailureBuilderTest.java:\\E.+ + >>>> + """); + } + + @Test + void ignoresClassNotInStackTrace() { + var error = AssertionsFacade.failWithTrimmedStacktrace(String.class); + assertStackTraceMatch(error, + """ + \\Qorg.junit.jupiter.api.AssertionFailureBuilder.build(AssertionFailureBuilder.java:\\E.+ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest$AssertionsFacade.failWithTrimmedStacktrace(AssertionFailureBuilderTest.java:\\E.+ + \\Qorg.junit.jupiter.api.AssertionFailureBuilderTest.ignoresClassNotInStackTrace(AssertionFailureBuilderTest.java:\\E.+ + >>>> + """); + } + + @Test + void canTrimToEmptyStacktrace() throws ExecutionException, InterruptedException { + try (ExecutorService service = newSingleThreadExecutor()) { + // Ensure that the stacktrace starts at Thread. + var error = service.submit(() -> AssertionsFacade.failWithTrimmedStacktrace(Thread.class)).get(); + assertThat(error.getStackTrace()).isEmpty(); + } + } + + private static void assertStackTraceMatch(AssertionFailedError assertionFailedError, String expectedLines) { + List stackStraceAsLines = Arrays.stream(assertionFailedError.getStackTrace()) // + .map(StackTraceElement::toString) // + .toList(); + assertLinesMatch(expectedLines.lines().toList(), stackStraceAsLines); + } + + static class AssertionsFacade { + static AssertionFailedError fail() { + return assertionFailure().build(); + } + + static AssertionFailedError failWithTrimmedStacktrace(Class to) { + return assertionFailure().trimStacktrace(to).build(); + } + + } +} diff --git a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java index 83e56373d509..6259b7b8de45 100644 --- a/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java +++ b/platform-tests/src/test/java/org/junit/platform/StackTracePruningTests.java @@ -75,7 +75,7 @@ void shouldNotPruneStackTraceWhenDisabled() { List stackTrace = extractStackTrace(results); assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.fail(StackTracePruningTests.java:\\E.+ >>>> \\Qorg.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:\\E.+ >>>> @@ -83,7 +83,7 @@ void shouldNotPruneStackTraceWhenDisabled() { } @Test - void shouldAlwaysKeepJupiterAssertionStackTraceElement() { + void shouldAlwaysKeepJupiterTestStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // @@ -92,13 +92,12 @@ void shouldAlwaysKeepJupiterAssertionStackTraceElement() { List stackTrace = extractStackTrace(results); assertStackTraceMatch(stackTrace, """ - >>>> - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.fail(StackTracePruningTests.java:\\E.+ >>>> """); } - @Test + @Test //TODO: void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // @@ -115,18 +114,16 @@ void shouldAlwaysKeepJupiterAssumptionStackTraceElement() { } @Test - void shouldKeepExactlyEverythingAfterTestCall() { + void shouldKeepExactlyEverythingBeforeAssertionsCall() { EngineExecutionResults results = EngineTestKit.engine("junit-jupiter") // .configurationParameter("junit.platform.stacktrace.pruning.enabled", "true") // .selectors(selectMethod(FailingTestTestCase.class, "failingAssertion")) // .execute(); List stackTrace = extractStackTrace(results); - assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.fail(StackTracePruningTests.java:\\E.+ \\Qorg.junit.platform.StackTracePruningTests$FailingTestTestCase.failingAssertion(StackTracePruningTests.java:\\E.+ """); } @@ -145,8 +142,7 @@ void shouldKeepExactlyEverythingAfterLifecycleMethodCall(Class methodClass) { assertStackTraceMatch(stackTrace, """ - \\Qorg.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:\\E.+ - \\Qorg.junit.jupiter.api.Assertions.fail(Assertions.java:\\E.+ + \\Qorg.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase.fail(StackTracePruningTests.java:\\E.+ \\Qorg.junit.platform.StackTracePruningTests$FailingBeforeEachTestCase.setUp(StackTracePruningTests.java:\\E.+ """); } @@ -198,12 +194,20 @@ static class FailingTestTestCase { @Test void failingAssertion() { + fail(); + } + + private static void fail() { Assertions.fail(); } @Test void multipleFailingAssertions() { - Assertions.assertAll(Assertions::fail, Assertions::fail); + failMultiple(); + } + + private void failMultiple() { + Assertions.assertAll(FailingTestTestCase::fail, FailingTestTestCase::fail); } @Test @@ -212,7 +216,6 @@ void failingAssumption() { throw new RuntimeException(); }); } - } @SuppressWarnings("JUnitMalformedDeclaration") @@ -220,6 +223,10 @@ static class FailingBeforeEachTestCase { @BeforeEach void setUp() { + fail(); + } + + private static void fail() { Assertions.fail(); } @@ -240,11 +247,7 @@ class NestedNestedTestCase { @Test void test() { } - } - } - } - }