Skip to content

Commit 0921340

Browse files
committed
Stop reporting discovery issues for synthetic methods (#5103)
Prior to this commit, _all_ methods of a potential test class were checked for being test methods, including synthetic ones. However, when resolving a test class only non-synthetic methods were taken into account. Since the Kotlin compiler generates a synthetic, static method suffixed with `$suspendImpl` for every open (i.e. non-final) method and copies its annotations, such methods were included in the check. In addition to filtering out synthetic methods from being checked, `KotlinReflectionUtils` no longer recognizes them as suspend functions. Fixes #5102.
1 parent 34d541d commit 0921340

File tree

6 files changed

+75
-3
lines changed

6 files changed

+75
-3
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
@@ -43,6 +43,8 @@ repository on GitHub.
4343
to avoid conflicts with other control characters.
4444
* Fix `IllegalAccessError` thrown when using the Kotlin-specific `assertDoesNotThrow`
4545
assertion.
46+
* Stop reporting discovery issues for synthetic methods, particularly in conjunction with
47+
Kotlin suspend functions.
4648

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

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/predicates/IsTestableMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ abstract class IsTestableMethod implements Predicate<Method> {
4545

4646
@Override
4747
public boolean test(Method candidate) {
48-
if (isAnnotated(candidate, this.annotationType)) {
48+
if (!candidate.isSynthetic() && isAnnotated(candidate, this.annotationType)) {
4949
return condition.check(candidate) && isNotAbstract(candidate);
5050
}
5151
return false;

junit-platform-commons/src/main/java/org/junit/platform/commons/util/KotlinReflectionUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private static Try<Class<? extends Annotation>> tryToLoadKotlinMetadataClass() {
6767
*/
6868
@API(status = INTERNAL, since = "6.0")
6969
public static boolean isKotlinSuspendingFunction(Method method) {
70-
if (kotlinCoroutineContinuation != null && isKotlinType(method.getDeclaringClass())) {
70+
if (!method.isSynthetic() && kotlinCoroutineContinuation != null && isKotlinType(method.getDeclaringClass())) {
7171
int parameterCount = method.getParameterCount();
7272
return parameterCount > 0 //
7373
&& method.getParameterTypes()[parameterCount - 1] == kotlinCoroutineContinuation;

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ class KotlinSuspendFunctionsTests : AbstractJupiterTestEngineTests() {
4141
assertThat(getPublishedEvents(results)).containsExactly("test")
4242
}
4343

44+
@Test
45+
fun suspendingOpenTestMethodsAreSupported() {
46+
val results = executeTestsForClass(OpenTestMethodTestCase::class)
47+
assertAllTestsPassed(results, 1)
48+
assertThat(getPublishedEvents(results)).containsExactly("test")
49+
}
50+
4451
@Test
4552
fun suspendingTestTemplateMethodsAreSupported() {
4653
val results = executeTestsForClass(TestTemplateTestCase::class)
@@ -187,6 +194,14 @@ class KotlinSuspendFunctionsTests : AbstractJupiterTestEngineTests() {
187194
}
188195
}
189196

197+
@Suppress("JUnitMalformedDeclaration")
198+
open class OpenTestMethodTestCase {
199+
@Test // https://github.com/junit-team/junit-framework/issues/5102
200+
open suspend fun `check getRandomPositiveInt`(reporter: TestReporter) {
201+
suspendingPublish(reporter, "test")
202+
}
203+
}
204+
190205
@Suppress("RedundantSuspendModifier")
191206
companion object {
192207
suspend fun suspendingPublish(

platform-tests/platform-tests.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import org.gradle.plugins.ide.eclipse.model.Classpath
55
import org.gradle.plugins.ide.eclipse.model.SourceFolder
66

77
plugins {
8-
id("junitbuild.java-library-conventions")
8+
id("junitbuild.kotlin-library-conventions")
99
id("junitbuild.java-nullability-conventions")
1010
id("junitbuild.junit4-compatibility")
1111
id("junitbuild.testing-conventions")
@@ -55,6 +55,7 @@ dependencies {
5555
testImplementation(libs.openTestReporting.tooling.core)
5656
testImplementation(libs.picocli)
5757
testImplementation(libs.bundles.xmlunit)
58+
testImplementation(kotlin("stdlib"))
5859
testImplementation(testFixtures(projects.junitJupiterApi))
5960
testImplementation(testFixtures(projects.junitPlatformReporting))
6061
testImplementation(projects.platformTests) {
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
package org.junit.platform.commons.util
11+
12+
import org.junit.jupiter.api.Assertions.assertFalse
13+
import org.junit.jupiter.api.Assertions.assertTrue
14+
import org.junit.jupiter.api.Test
15+
import org.junit.platform.commons.support.ModifierSupport
16+
import kotlin.coroutines.Continuation
17+
18+
class KotlinReflectionUtilsTests {
19+
@Test
20+
fun recognizesSuspendFunction() {
21+
val method =
22+
OpenTestMethodTestCase::class.java.getDeclaredMethod(
23+
"test",
24+
Continuation::class.java
25+
)
26+
27+
assertFalse(ModifierSupport.isStatic(method))
28+
assertFalse(method.isSynthetic)
29+
30+
assertTrue(KotlinReflectionUtils.isKotlinSuspendingFunction(method))
31+
}
32+
33+
@Test
34+
fun doesNotRecognizeSyntheticMethodAsSuspendFunction() {
35+
val method =
36+
OpenTestMethodTestCase::class.java.getDeclaredMethod(
37+
"test\$suspendImpl",
38+
OpenTestMethodTestCase::class.java,
39+
Continuation::class.java
40+
)
41+
42+
assertTrue(ModifierSupport.isStatic(method))
43+
assertTrue(method.isSynthetic)
44+
45+
assertFalse(KotlinReflectionUtils.isKotlinSuspendingFunction(method))
46+
}
47+
48+
@Suppress("JUnitMalformedDeclaration")
49+
open class OpenTestMethodTestCase {
50+
@Test
51+
open suspend fun test() {
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)