Skip to content

Commit 4e3a9c8

Browse files
dnestoromelix
andauthored
Avoid Adding Duplicated JUnit Entries on Classpath (#712)
* Avoid adding duplicated junit entries on classpath * Provide support for earlier JUnit versions without JUnit duplicates on claspath * Add missing dependencies to gradle samples * Add missing dependencies to maven samples * Add missing dependencies to fix failing gradle tests * Infer missing test dependencies This commit fixes the fact that users have to declare dependencies on `junit-platform-launcher` and `junit-platform-console` in order for the native tests to run. This was caused by the fact that now, these dependencies are compile only dependencies of junit platform native. This was done so that there's no conflict between versions declared by a user and the versions used by Native Build Tools or, it could also cause duplicate entries on classpath. However, this broke the simple cases where users didn't declare the junit-platform-launcher dependency. While Gradle users are encouraged to do so, for Maven, the surefire plugin infers which dependency to use. This now does the same for both Maven and Gradle. The implementation is not fast, because this imples resolving a dependency graph with the current set of dependencies, as declared by the user, then figuring out if dependencies are missing, inferring their versions from dependencies present in the graph. * Do not test config cache on older Gradle releases These releases are buggy. * Fix Gradle versions * Bump minimal requirement to Gradle 8.3 Since there are bugs in previous Gradle releases (it doesn't track dependencies properly) --------- Co-authored-by: Cedric Champeau <[email protected]>
1 parent 424126c commit 4e3a9c8

File tree

19 files changed

+271
-39
lines changed

19 files changed

+271
-39
lines changed

build-logic/common-plugins/src/main/groovy/org.graalvm.build.github-actions-helper.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ def matrixDefault = [
77

88
// # Following versions are disabled temporarily in order to speed up PR testing "7.3.3", "7.2", "7.1", "6.8.3",
99
def gradleVersions = [
10-
"gradle-version": ["current", "7.4", "8.13"],
10+
"gradle-version": ["current", "8.4", "8.14.2"],
1111
]
1212

1313
def gradleCachedVersions = [
14-
"gradle-config-cache-version": ["current", "8.0.1"]
14+
"gradle-config-cache-version": ["current", "8.14.2"]
1515
]
1616

1717
sourceSets.configureEach { sourceSet ->

build-logic/gradle-functional-testing/src/main/groovy/org.graalvm.build.functional-testing.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ def graalVm = javaToolchains.launcherFor {
8383

8484
def fullFunctionalTest = tasks.register("fullFunctionalTest")
8585

86-
['functionalTest', 'configCacheFunctionalTest'].each { baseName ->
87-
["current", "7.4", "7.6.2", "8.0.1", "8.2.1", "8.13"].each { gradleVersion ->
86+
['functionalTest': ["current", "8.4", "8.14.2"],
87+
'configCacheFunctionalTest': ['current', "8.14.2"]
88+
].each { baseName, gradleVersions ->
89+
gradleVersions.each { gradleVersion ->
8890
String taskName = gradleVersion == 'current' ? baseName : "gradle${gradleVersion}${baseName.capitalize()}"
8991
// Add a task to run the functional tests
9092
def testTask = tasks.register(taskName, Test) {

common/junit-platform-native/build.gradle

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,19 @@ maven {
5252
}
5353
dependencies {
5454
compileOnly libs.graalvm.svm
55-
implementation(platform(libs.test.junit.bom))
56-
implementation libs.test.junit.platform.console
57-
implementation libs.test.junit.platform.launcher
58-
implementation libs.test.junit.jupiter.core
55+
compileOnly(platform(libs.test.junit.bom))
56+
compileOnly libs.test.junit.platform.console
57+
compileOnly libs.test.junit.platform.launcher
58+
compileOnly libs.test.junit.jupiter.core
5959
testImplementation libs.test.junit.vintage
60+
61+
testImplementation libs.test.junit.jupiter.core
62+
63+
testRuntimeOnly(platform(libs.test.junit.bom))
64+
testCompileOnly(platform(libs.test.junit.bom))
65+
testRuntimeOnly libs.test.junit.platform.console
6066
testRuntimeOnly libs.test.junit.platform.launcher
67+
testRuntimeOnly libs.test.junit.jupiter.core
6168
}
6269

6370
apply from: "gradle/native-image-testing.gradle"

common/junit-platform-native/src/main/java/org/graalvm/junit/platform/JUnitPlatformFeature.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,13 @@ private static void initializeClassesForJDK21OrEarlier() {
226226
try (InputStream is = JUnitPlatformFeature.class.getResourceAsStream("/initialize-at-buildtime")) {
227227
if (is != null) {
228228
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
229-
br.lines().forEach(RuntimeClassInitialization::initializeAtBuildTime);
229+
br.lines().forEach(cls -> {
230+
try {
231+
RuntimeClassInitialization.initializeAtBuildTime(cls);
232+
} catch (NoClassDefFoundError e) {
233+
// if users use older JUnit versions some classes might not be available
234+
}
235+
});
230236
}
231237
}
232238
} catch (IOException e) {

common/junit-platform-native/src/main/java/org/graalvm/junit/platform/NativeImageJUnitLauncher.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,12 @@ public static void main(String... args) {
123123
out.println("JUnit Platform on Native Image - report");
124124
out.println("----------------------------------------\n");
125125
out.flush();
126-
launcher.registerTestExecutionListeners(new PrintTestExecutionListener(out));
126+
configurePrintTestExecutionListener(launcher, out);
127127
}
128128

129129
SummaryGeneratingListener summaryListener = new SummaryGeneratingListener();
130130
launcher.registerTestExecutionListeners(summaryListener);
131-
launcher.registerTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get(xmlOutput), out));
131+
configureLegacyXMLReport(launcher, xmlOutput, out);
132132
launcher.execute(testPlan);
133133

134134
TestExecutionSummary summary = summaryListener.getSummary();
@@ -271,4 +271,21 @@ private static boolean testIdsDirectoryExists(Path directory) {
271271
return directory != null && Files.exists(directory);
272272
}
273273

274+
275+
private static void configurePrintTestExecutionListener(Launcher launcher, PrintWriter out) {
276+
try {
277+
Class.forName("org.junit.platform.reporting.legacy.LegacyReportingUtils");
278+
launcher.registerTestExecutionListeners(new PrintTestExecutionListener(out));
279+
} catch (NoClassDefFoundError | ClassNotFoundException e) {
280+
// intentionally ignored
281+
}
282+
}
283+
284+
private static void configureLegacyXMLReport(Launcher launcher, String xmlOutput, PrintWriter out) {
285+
try {
286+
launcher.registerTestExecutionListeners(new LegacyXmlReportGeneratingListener(Paths.get(xmlOutput), out));
287+
} catch (NoClassDefFoundError e) {
288+
// intentionally ignored
289+
}
290+
}
274291
}

common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/jupiter/JupiterConfigProvider.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@
5454
import org.junit.jupiter.params.converter.ConvertWith;
5555
import org.junit.jupiter.params.provider.ArgumentsSource;
5656
import org.junit.jupiter.params.provider.EnumSource;
57-
import org.junit.jupiter.params.provider.MethodSource;
5857
import org.junit.jupiter.params.provider.FieldSource;
58+
import org.junit.jupiter.params.provider.MethodSource;
5959

6060
import java.lang.reflect.Method;
6161
import java.util.ArrayList;
@@ -86,9 +86,15 @@ public void onTestClassRegistered(Class<?> testClass, NativeImageConfiguration r
8686
AnnotationUtils.forEachAnnotatedMethodParameter(testClass, AggregateWith.class, annotation -> registry.registerAllClassMembersForReflection(annotation.value()));
8787
AnnotationUtils.forEachAnnotatedMethod(testClass, EnumSource.class, (m, annotation) -> handleEnumSource(m, annotation, registry));
8888
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, MethodSource.class, JupiterConfigProvider::handleMethodSource);
89-
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, FieldSource.class, JupiterConfigProvider::handleFieldSource);
9089
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, EnabledIf.class, JupiterConfigProvider::handleEnabledIf);
9190
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, DisabledIf.class, JupiterConfigProvider::handleDisabledIf);
91+
92+
try {
93+
AnnotationUtils.registerClassesFromAnnotationForReflection(testClass, registry, FieldSource.class, JupiterConfigProvider::handleFieldSource);
94+
} catch (NoClassDefFoundError e) {
95+
// if users use JUnit version older than 5.11, FieldSource class won't be available
96+
}
97+
9298
}
9399

94100
private static Class<?>[] handleMethodSource(MethodSource annotation) {

common/junit-platform-native/src/main/resources/initialize-at-buildtime

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ org.junit.jupiter.engine.discovery.MethodSelectorResolver$MethodType$3
4646
org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests
4747
org.junit.jupiter.engine.discovery.predicates.IsTestFactoryMethod
4848
org.junit.jupiter.engine.execution.ConditionEvaluator
49+
org.junit.jupiter.engine.execution.ExecutableInvoker
4950
org.junit.jupiter.engine.execution.InterceptingExecutableInvoker
5051
org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall
5152
org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall$VoidMethodInterceptorCall
@@ -78,6 +79,7 @@ org.junit.platform.launcher.core.DiscoveryIssueNotifier
7879
org.junit.platform.launcher.core.EngineDiscoveryOrchestrator
7980
org.junit.platform.launcher.core.EngineExecutionOrchestrator
8081
org.junit.platform.launcher.core.EngineFilterer
82+
org.junit.platform.launcher.core.EngineIdValidator
8183
org.junit.platform.launcher.core.HierarchicalOutputDirectoryProvider
8284
org.junit.platform.launcher.core.InternalTestPlan
8385
org.junit.platform.launcher.core.LauncherConfig
@@ -91,6 +93,8 @@ org.junit.platform.launcher.core.LauncherDiscoveryResult$EngineResultInfo
9193
org.junit.platform.launcher.core.LauncherListenerRegistry
9294
org.junit.platform.launcher.core.LauncherPhase
9395
org.junit.platform.launcher.core.ListenerRegistry
96+
org.junit.platform.launcher.core.ServiceLoaderRegistry
97+
org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry
9498
org.junit.platform.launcher.core.SessionPerRequestLauncher
9599
org.junit.platform.launcher.EngineDiscoveryResult
96100
org.junit.platform.launcher.LauncherSessionListener$1
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2003-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.graalvm.buildtools.utils;
17+
18+
import java.util.List;
19+
20+
public abstract class JUnitPlatformNativeDependenciesHelper {
21+
22+
private static final DependencyNotation JUNIT_PLATFORM_LAUNCHER = new DependencyNotation(
23+
"org.junit.platform", "junit-platform-launcher", ""
24+
);
25+
private static final DependencyNotation JUNIT_PLATFORM_ENGINE = new DependencyNotation(
26+
"org.junit.platform", "junit-platform-engine", ""
27+
);
28+
private static final DependencyNotation JUNIT_PLATFORM_CONSOLE = new DependencyNotation(
29+
"org.junit.platform", "junit-platform-console", ""
30+
);
31+
private static final DependencyNotation JUNIT_PLATFORM_REPORTING = new DependencyNotation(
32+
"org.junit.platform", "junit-platform-reporting", ""
33+
);
34+
35+
private static final List<DependencyNotation> JUNIT_PLATFORM_DEPENDENCIES = List.of(
36+
JUNIT_PLATFORM_LAUNCHER,
37+
JUNIT_PLATFORM_CONSOLE,
38+
JUNIT_PLATFORM_REPORTING
39+
);
40+
41+
private JUnitPlatformNativeDependenciesHelper() {
42+
43+
}
44+
45+
/**
46+
* Returns the list of dependencies which should be added to the
47+
* native test classpath in order for tests to execute.
48+
* @param input the current list of dependencies
49+
* @return a list of dependencies which need to be added
50+
*/
51+
public static List<DependencyNotation> inferMissingDependenciesForTestRuntime(
52+
List<DependencyNotation> input
53+
) {
54+
var junitPlatformVersion = input.stream()
55+
.filter(d -> d.equalsIgnoreVersion(JUNIT_PLATFORM_ENGINE))
56+
.findFirst()
57+
.map(DependencyNotation::version)
58+
.orElse("");
59+
var list = JUNIT_PLATFORM_DEPENDENCIES.stream()
60+
.filter(d -> input.stream().noneMatch(o -> o.equalsIgnoreVersion(d)))
61+
.map(d -> d.withVersion(junitPlatformVersion))
62+
.toList();
63+
return list;
64+
}
65+
66+
67+
public record DependencyNotation(
68+
String groupId,
69+
String artifactId,
70+
String version
71+
) {
72+
public boolean equalsIgnoreVersion(DependencyNotation other) {
73+
return other.groupId.equals(groupId) &&
74+
other.artifactId.equals(artifactId);
75+
}
76+
77+
public DependencyNotation withVersion(String version) {
78+
return new DependencyNotation(groupId, artifactId, version);
79+
}
80+
}
81+
}

docs/src/docs/asciidoc/changelog.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
== Release 0.11.0
55

6-
This version introduces a breaking change: the plugin now requires Java 17 to run.
6+
This version introduces a breaking change: the plugin now requires Gradle 8.3+ and Java 17 to run.
77

88
=== Gradle plugin
99

native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JUnitFunctionalTests.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ package org.graalvm.buildtools.gradle
4444
import org.graalvm.buildtools.gradle.fixtures.AbstractFunctionalTest
4545

4646
class JUnitFunctionalTests extends AbstractFunctionalTest {
47-
def "test if JUint support works with various annotations, reflection and resources"() {
47+
def "test if JUnit support works with various annotations, reflection and resources"() {
4848
debug=true
4949
given:
5050
withSample("junit-tests")

0 commit comments

Comments
 (0)