Skip to content

Commit b5e1ed5

Browse files
committed
Fix IllegalAccessError for Kotlin-specific assertDoesNotThrow (#5063)
* Trigger `IllegalAccessError` by moving tests to different package * Fix `IllegalAccessError` by inlining referenced method Resolves #5062.
1 parent d37e1f3 commit b5e1ed5

File tree

9 files changed

+92
-50
lines changed

9 files changed

+92
-50
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-6.0.1.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ repository on GitHub.
4141
option to change it. To resolve this, a new `commentCharacter` attribute has been added
4242
to both annotations. Its default value remains `+++#+++`, but it can now be customized
4343
to avoid conflicts with other control characters.
44+
* Fix `IllegalAccessError` thrown when using the Kotlin-specific `assertDoesNotThrow`
45+
assertion.
4446

4547
[[release-notes-6.0.1-junit-jupiter-deprecations-and-breaking-changes]]
4648
==== Deprecations and Breaking Changes

junit-jupiter-api/src/main/kotlin/org/junit/jupiter/api/Assertions.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package org.junit.jupiter.api
1414
import org.apiguardian.api.API
1515
import org.apiguardian.api.API.Status.MAINTAINED
1616
import org.apiguardian.api.API.Status.STABLE
17+
import org.junit.jupiter.api.AssertionFailureBuilder.assertionFailure
1718
import org.junit.jupiter.api.function.Executable
1819
import org.junit.platform.commons.util.UnrecoverableExceptions.rethrowIfUnrecoverable
1920
import java.time.Duration
@@ -347,7 +348,6 @@ inline fun <reified T : Throwable> assertThrows(
347348
* @see Assertions.assertDoesNotThrow
348349
* @param R the result type of the [executable]
349350
*/
350-
@Suppress("LESS_VISIBLE_TYPE_ACCESS_IN_INLINE_WARNING")
351351
@OptIn(ExperimentalContracts::class)
352352
@API(status = STABLE, since = "5.11")
353353
inline fun <R> assertDoesNotThrow(executable: () -> R): R {
@@ -359,7 +359,11 @@ inline fun <R> assertDoesNotThrow(executable: () -> R): R {
359359
return executable()
360360
} catch (t: Throwable) {
361361
rethrowIfUnrecoverable(t)
362-
throw AssertDoesNotThrow.createAssertionFailedError(null, t)
362+
val suffix = t.message?.let { if (it.isNotBlank()) ": ${t.message}" else null } ?: ""
363+
throw assertionFailure()
364+
.reason("Unexpected exception thrown: ${t.javaClass.getName()}$suffix")
365+
.cause(t)
366+
.build()
363367
}
364368
}
365369

@@ -396,7 +400,6 @@ inline fun <R> assertDoesNotThrow(
396400
* @see Assertions.assertDoesNotThrow
397401
* @param R the result type of the [executable]
398402
*/
399-
@Suppress("LESS_VISIBLE_TYPE_ACCESS_IN_INLINE_WARNING")
400403
@OptIn(ExperimentalContracts::class)
401404
@API(status = STABLE, since = "5.11")
402405
inline fun <R> assertDoesNotThrow(
@@ -412,7 +415,12 @@ inline fun <R> assertDoesNotThrow(
412415
return executable()
413416
} catch (t: Throwable) {
414417
rethrowIfUnrecoverable(t)
415-
throw AssertDoesNotThrow.createAssertionFailedError(message(), t)
418+
val suffix = t.message?.let { if (it.isNotBlank()) ": ${t.message}" else null } ?: ""
419+
throw assertionFailure()
420+
.message(message())
421+
.reason("Unexpected exception thrown: ${t.javaClass.getName()}$suffix")
422+
.cause(t)
423+
.build()
416424
}
417425
}
418426

jupiter-tests/src/test/java/org/junit/jupiter/api/AssertionTestUtils.java renamed to junit-jupiter-api/src/testFixtures/java/org/junit/jupiter/api/AssertionTestUtils.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,44 +10,48 @@
1010

1111
package org.junit.jupiter.api;
1212

13+
import static org.junit.jupiter.api.Assertions.assertEquals;
14+
import static org.junit.jupiter.api.Assertions.assertNotNull;
15+
1316
import java.io.Serializable;
17+
import java.util.List;
1418
import java.util.Objects;
1519

16-
import org.apache.groovy.parser.antlr4.util.StringUtils;
1720
import org.jspecify.annotations.Nullable;
1821
import org.opentest4j.AssertionFailedError;
22+
import org.opentest4j.MultipleFailuresError;
1923
import org.opentest4j.ValueWrapper;
2024

21-
class AssertionTestUtils {
25+
public class AssertionTestUtils {
2226

2327
private AssertionTestUtils() {
2428
/* no-op */
2529
}
2630

27-
static void expectAssertionFailedError() {
31+
public static void expectAssertionFailedError() {
2832
throw new AssertionError("Should have thrown an " + AssertionFailedError.class.getName());
2933
}
3034

31-
static void assertEmptyMessage(Throwable ex) throws AssertionError {
32-
if (!StringUtils.isEmpty(ex.getMessage())) {
35+
public static void assertEmptyMessage(Throwable ex) throws AssertionError {
36+
if (!(ex.getMessage() == null || ex.getMessage().isEmpty())) {
3337
throw new AssertionError("Exception message should be empty, but was [" + ex.getMessage() + "].");
3438
}
3539
}
3640

37-
static void assertMessageEquals(Throwable ex, String msg) throws AssertionError {
41+
public static void assertMessageEquals(Throwable ex, String msg) throws AssertionError {
3842
if (!msg.equals(ex.getMessage())) {
3943
throw new AssertionError("Exception message should be [" + msg + "], but was [" + ex.getMessage() + "].");
4044
}
4145
}
4246

43-
static void assertMessageMatches(Throwable ex, String regex) throws AssertionError {
47+
public static void assertMessageMatches(Throwable ex, String regex) throws AssertionError {
4448
if (ex.getMessage() == null || !ex.getMessage().matches(regex)) {
4549
throw new AssertionError("Exception message should match regular expression [" + regex + "], but was ["
4650
+ ex.getMessage() + "].");
4751
}
4852
}
4953

50-
static void assertMessageStartsWith(@Nullable Throwable ex, String msg) throws AssertionError {
54+
public static void assertMessageStartsWith(@Nullable Throwable ex, String msg) throws AssertionError {
5155
if (ex == null) {
5256
throw new AssertionError("Cause should not have been null");
5357
}
@@ -57,14 +61,14 @@ static void assertMessageStartsWith(@Nullable Throwable ex, String msg) throws A
5761
}
5862
}
5963

60-
static void assertMessageEndsWith(Throwable ex, String msg) throws AssertionError {
64+
public static void assertMessageEndsWith(Throwable ex, String msg) throws AssertionError {
6165
if (ex.getMessage() == null || !ex.getMessage().endsWith(msg)) {
6266
throw new AssertionError(
6367
"Exception message should end with [" + msg + "], but was [" + ex.getMessage() + "].");
6468
}
6569
}
6670

67-
static void assertMessageContains(@Nullable Throwable ex, String msg) throws AssertionError {
71+
public static void assertMessageContains(@Nullable Throwable ex, String msg) throws AssertionError {
6872
if (ex == null) {
6973
throw new AssertionError("Cause should not have been null");
7074
}
@@ -74,7 +78,7 @@ static void assertMessageContains(@Nullable Throwable ex, String msg) throws Ass
7478
}
7579
}
7680

77-
static void assertExpectedAndActualValues(AssertionFailedError ex, @Nullable Object expected,
81+
public static void assertExpectedAndActualValues(AssertionFailedError ex, @Nullable Object expected,
7882
@Nullable Object actual) throws AssertionError {
7983
if (!wrapsEqualValue(ex.getExpected(), expected)) {
8084
throw new AssertionError("Expected value in AssertionFailedError should equal ["
@@ -86,7 +90,7 @@ static void assertExpectedAndActualValues(AssertionFailedError ex, @Nullable Obj
8690
}
8791
}
8892

89-
static boolean wrapsEqualValue(ValueWrapper wrapper, @Nullable Object value) {
93+
public static boolean wrapsEqualValue(ValueWrapper wrapper, @Nullable Object value) {
9094
if (value == null || value instanceof Serializable) {
9195
return Objects.equals(value, wrapper.getValue());
9296
}
@@ -95,14 +99,32 @@ static boolean wrapsEqualValue(ValueWrapper wrapper, @Nullable Object value) {
9599
&& Objects.equals(wrapper.getType(), value.getClass());
96100
}
97101

98-
static void recurseIndefinitely() {
102+
public static void recurseIndefinitely() {
99103
// simulate infinite recursion
100104
throw new StackOverflowError();
101105
}
102106

103-
static void runOutOfMemory() {
107+
public static void runOutOfMemory() {
104108
// simulate running out of memory
105109
throw new OutOfMemoryError("boom");
106110
}
107111

112+
@SafeVarargs
113+
public static void assertExpectedExceptionTypes(MultipleFailuresError multipleFailuresError,
114+
Class<? extends Throwable>... exceptionTypes) {
115+
116+
assertNotNull(multipleFailuresError, "MultipleFailuresError");
117+
List<Throwable> failures = multipleFailuresError.getFailures();
118+
assertEquals(exceptionTypes.length, failures.size(), "number of failures");
119+
120+
// Verify that exceptions are also present as suppressed exceptions.
121+
// https://github.com/junit-team/junit-framework/issues/1602
122+
Throwable[] suppressed = multipleFailuresError.getSuppressed();
123+
assertEquals(exceptionTypes.length, suppressed.length, "number of suppressed exceptions");
124+
125+
for (int i = 0; i < exceptionTypes.length; i++) {
126+
assertEquals(exceptionTypes[i], failures.get(i).getClass(), "exception type [" + i + "]");
127+
assertEquals(exceptionTypes[i], suppressed[i].getClass(), "suppressed exception type [" + i + "]");
128+
}
129+
}
108130
}

jupiter-tests/src/test/java/org/junit/jupiter/api/AssertAllAssertionsTests.java

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@
1212

1313
import static java.util.Arrays.asList;
1414
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.junit.jupiter.api.AssertionTestUtils.assertExpectedExceptionTypes;
1516
import static org.junit.jupiter.api.Assertions.assertAll;
1617
import static org.junit.jupiter.api.Assertions.assertEquals;
1718
import static org.junit.jupiter.api.Assertions.assertFalse;
18-
import static org.junit.jupiter.api.Assertions.assertNotNull;
1919
import static org.junit.jupiter.api.Assertions.assertThrows;
2020
import static org.junit.jupiter.api.Assertions.assertTrue;
2121
import static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullFor;
2222
import static org.junit.platform.commons.test.PreconditionAssertions.assertPreconditionViolationNotNullOrEmptyFor;
2323

2424
import java.io.IOException;
2525
import java.util.Collection;
26-
import java.util.List;
2726
import java.util.stream.Stream;
2827

2928
import org.junit.jupiter.api.function.Executable;
@@ -198,25 +197,6 @@ void assertAllWithParallelStream() {
198197
assertThat(multipleFailuresError.getFailures()).hasSize(100).doesNotContainNull();
199198
}
200199

201-
@SafeVarargs
202-
static void assertExpectedExceptionTypes(MultipleFailuresError multipleFailuresError,
203-
Class<? extends Throwable>... exceptionTypes) {
204-
205-
assertNotNull(multipleFailuresError, "MultipleFailuresError");
206-
List<Throwable> failures = multipleFailuresError.getFailures();
207-
assertEquals(exceptionTypes.length, failures.size(), "number of failures");
208-
209-
// Verify that exceptions are also present as suppressed exceptions.
210-
// https://github.com/junit-team/junit-framework/issues/1602
211-
Throwable[] suppressed = multipleFailuresError.getSuppressed();
212-
assertEquals(exceptionTypes.length, suppressed.length, "number of suppressed exceptions");
213-
214-
for (int i = 0; i < exceptionTypes.length; i++) {
215-
assertEquals(exceptionTypes[i], failures.get(i).getClass(), "exception type [" + i + "]");
216-
assertEquals(exceptionTypes[i], suppressed[i].getClass(), "suppressed exception type [" + i + "]");
217-
}
218-
}
219-
220200
@SuppressWarnings("serial")
221201
private static class EnigmaThrowable extends Throwable {
222202
}

jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertTimeoutAssertionsTests.kt renamed to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinAssertTimeoutAssertionsTests.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,26 @@
77
*
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
10-
package org.junit.jupiter.api
10+
package org.junit.jupiter.api.kotlin
1111

1212
import org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals
1313
import org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith
1414
import org.junit.jupiter.api.Assertions.assertEquals
1515
import org.junit.jupiter.api.Assertions.assertFalse
1616
import org.junit.jupiter.api.Assertions.assertTrue
17+
import org.junit.jupiter.api.Test
18+
import org.junit.jupiter.api.assertThrows
19+
import org.junit.jupiter.api.assertTimeout
20+
import org.junit.jupiter.api.assertTimeoutPreemptively
21+
import org.junit.jupiter.api.fail
1722
import org.junit.platform.commons.util.ExceptionUtils
1823
import org.opentest4j.AssertionFailedError
1924
import java.time.Duration.ofMillis
2025
import java.util.concurrent.CountDownLatch
2126
import java.util.concurrent.atomic.AtomicBoolean
2227

2328
/**
24-
* Unit tests for JUnit Jupiter [Assertions].
29+
* Unit tests for Kotlin-specific `assertTimeout*` assertions.
2530
*
2631
* @since 5.5
2732
*/

jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinAssertionsTests.kt renamed to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinAssertionsTests.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,27 @@
77
*
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
10-
package org.junit.jupiter.api
10+
package org.junit.jupiter.api.kotlin
1111

1212
import kotlinx.coroutines.runBlocking
13+
import org.junit.jupiter.api.AssertionTestUtils
1314
import org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals
1415
import org.junit.jupiter.api.AssertionTestUtils.assertMessageStartsWith
1516
import org.junit.jupiter.api.AssertionTestUtils.expectAssertionFailedError
1617
import org.junit.jupiter.api.Assertions.assertEquals
1718
import org.junit.jupiter.api.Assertions.assertFalse
1819
import org.junit.jupiter.api.Assertions.assertTrue
1920
import org.junit.jupiter.api.DynamicContainer.dynamicContainer
21+
import org.junit.jupiter.api.DynamicNode
2022
import org.junit.jupiter.api.DynamicTest.dynamicTest
23+
import org.junit.jupiter.api.Test
24+
import org.junit.jupiter.api.TestFactory
25+
import org.junit.jupiter.api.assertAll
26+
import org.junit.jupiter.api.assertDoesNotThrow
27+
import org.junit.jupiter.api.assertInstanceOf
28+
import org.junit.jupiter.api.assertNull
29+
import org.junit.jupiter.api.assertThrows
30+
import org.junit.jupiter.api.fail
2131
import org.opentest4j.AssertionFailedError
2232
import org.opentest4j.MultipleFailuresError
2333
import java.util.stream.Stream
@@ -70,7 +80,7 @@ class KotlinAssertionsTests {
7080
}
7181

7282
@TestFactory
73-
fun assertDoesNotThrow(): Stream<out DynamicNode> =
83+
fun `assertDoesNotThrow behaves as expected`(): Stream<out DynamicNode> =
7484
Stream.of(
7585
dynamicContainer(
7686
"succeeds when no exception thrown",
@@ -223,7 +233,7 @@ class KotlinAssertionsTests {
223233
}
224234

225235
@Test
226-
fun assertInstanceOf() {
236+
fun `assertInstanceOf succeeds`() {
227237
assertInstanceOf<RandomAccess>(listOf("whatever"))
228238
assertInstanceOf<RandomAccess>(listOf("whatever"), "No random access")
229239
assertInstanceOf<RandomAccess>(listOf("whatever")) { "No random access" }
@@ -336,7 +346,7 @@ class KotlinAssertionsTests {
336346
fun assertExpectedExceptionTypes(
337347
multipleFailuresError: MultipleFailuresError,
338348
vararg exceptionTypes: KClass<out Throwable>
339-
) = AssertAllAssertionsTests.assertExpectedExceptionTypes(
349+
) = AssertionTestUtils.assertExpectedExceptionTypes(
340350
multipleFailuresError,
341351
*exceptionTypes.map { it.java }.toTypedArray()
342352
)

jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinDynamicTests.kt renamed to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinDynamicTests.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@
77
*
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
10-
package org.junit.jupiter.api
10+
package org.junit.jupiter.api.kotlin
1111

1212
import org.junit.jupiter.api.Assertions.assertEquals
13+
import org.junit.jupiter.api.DynamicTest
1314
import org.junit.jupiter.api.DynamicTest.dynamicTest
15+
import org.junit.jupiter.api.Nested
16+
import org.junit.jupiter.api.TestFactory
1417
import java.math.BigDecimal
1518
import java.math.BigDecimal.ONE
1619
import java.math.MathContext
1720
import java.math.BigInteger as BigInt
1821
import java.math.RoundingMode as Rounding
1922

2023
/**
21-
* Unit tests for JUnit Jupiter [TestFactory] use in kotlin classes.
24+
* Unit tests for JUnit Jupiter [org.junit.jupiter.api.TestFactory] use in kotlin classes.
2225
*
2326
* @since 5.12
2427
*/

jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinFailAssertionsTests.kt renamed to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinFailAssertionsTests.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
*
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
10-
package org.junit.jupiter.api
10+
package org.junit.jupiter.api.kotlin
1111

12-
import org.junit.jupiter.api.AssertEquals.assertEquals
1312
import org.junit.jupiter.api.AssertionTestUtils.assertEmptyMessage
1413
import org.junit.jupiter.api.AssertionTestUtils.assertMessageContains
1514
import org.junit.jupiter.api.AssertionTestUtils.assertMessageEquals
15+
import org.junit.jupiter.api.Assertions.assertEquals
16+
import org.junit.jupiter.api.Test
17+
import org.junit.jupiter.api.assertThrows
18+
import org.junit.jupiter.api.fail
1619
import org.opentest4j.AssertionFailedError
1720
import java.util.stream.Stream
1821

jupiter-tests/src/test/kotlin/org/junit/jupiter/api/KotlinSuspendFunctionsTests.kt renamed to jupiter-tests/src/test/kotlin/org/junit/jupiter/api/kotlin/KotlinSuspendFunctionsTests.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@
77
*
88
* https://www.eclipse.org/legal/epl-v20.html
99
*/
10-
package org.junit.jupiter.api
10+
package org.junit.jupiter.api.kotlin
1111

1212
import kotlinx.coroutines.runBlocking
1313
import org.assertj.core.api.Assertions.assertThat
14+
import org.junit.jupiter.api.AfterAll
15+
import org.junit.jupiter.api.AfterEach
16+
import org.junit.jupiter.api.BeforeAll
17+
import org.junit.jupiter.api.BeforeEach
18+
import org.junit.jupiter.api.DynamicTest
1419
import org.junit.jupiter.api.DynamicTest.dynamicTest
20+
import org.junit.jupiter.api.Test
21+
import org.junit.jupiter.api.TestFactory
22+
import org.junit.jupiter.api.TestInstance
1523
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
24+
import org.junit.jupiter.api.TestReporter
1625
import org.junit.jupiter.engine.AbstractJupiterTestEngineTests
1726
import org.junit.jupiter.params.AfterParameterizedClassInvocation
1827
import org.junit.jupiter.params.BeforeParameterizedClassInvocation

0 commit comments

Comments
 (0)