Skip to content

Commit 675f9e8

Browse files
author
Aleksandar Gradinac
committed
Introduce and migrate to a plugin configuration mechanism
1 parent aa4b041 commit 675f9e8

25 files changed

+1833
-100
lines changed

common/junit-platform-native/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ dependencies {
2323
implementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: junit_jupiter_version
2424
testImplementation(platform('org.junit:junit-bom:' + junit_jupiter_version))
2525
testImplementation('org.junit.jupiter:junit-jupiter')
26+
testImplementation('org.junit.vintage:junit-vintage-engine')
2627
}
2728

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

common/junit-platform-native/gradle/native-image-testing.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ afterEvaluate {
3737
"-cp", classpath,
3838
"--no-fallback",
3939
"--features=org.graalvm.junit.platform.JUnitPlatformFeature",
40-
"-H:Name=native-image-tests.bin",
41-
"-H:Class=org.graalvm.junit.platform.NativeImageJUnitLauncher"
40+
"-H:Name=native-image-tests",
41+
"-H:Class=org.graalvm.junit.platform.NativeImageJUnitLauncher",
4242
]
4343
if (project.hasProperty("agent")) {
4444
if (!new File("${buildDir}/agentOutput/").exists()) {
4545
throw new GradleException("Agent output missing when -Pagent is set.\n" +
46-
"You need to run `./gradlew -Pagent test` first.")
46+
"You need to run `gradle -Pagent test` first.")
4747
}
4848

4949
args << "-H:ConfigurationFileDirectories=${buildDir}/agentOutput"
@@ -59,5 +59,5 @@ afterEvaluate {
5959
tasks.register("nativeTest", Exec) {
6060
dependsOn nativeTestBuild
6161
workingDir = "${buildDir}"
62-
executable = "./native-image-tests.bin"
62+
executable = "${buildDir}/native-image-tests"
6363
}

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

Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
*/
4141
package org.graalvm.junit.platform;
4242

43+
import org.graalvm.junit.platform.config.core.PluginConfigProvider;
4344
import org.graalvm.nativeimage.ImageSingletons;
4445
import org.graalvm.nativeimage.hosted.Feature;
4546
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
46-
import org.graalvm.nativeimage.hosted.RuntimeReflection;
4747
import org.junit.platform.engine.DiscoverySelector;
4848
import org.junit.platform.engine.discovery.DiscoverySelectors;
4949
import org.junit.platform.engine.support.descriptor.ClassSource;
@@ -60,58 +60,29 @@
6060
import java.io.IOException;
6161
import java.io.InputStream;
6262
import java.io.InputStreamReader;
63-
import java.lang.reflect.Field;
64-
import java.lang.reflect.Method;
6563
import java.nio.file.Path;
6664
import java.util.HashSet;
6765
import java.util.List;
6866
import java.util.Optional;
67+
import java.util.ServiceLoader;
68+
import java.util.function.Consumer;
6969
import java.util.stream.Collectors;
7070

7171
@SuppressWarnings("unused")
7272
public final class JUnitPlatformFeature implements Feature {
7373

74-
final boolean debug = System.getProperty("debug") != null;
74+
public final boolean debug = System.getProperty("debug") != null;
75+
76+
private static final NativeImageConfigurationImpl nativeImageConfigImpl = new NativeImageConfigurationImpl();
77+
private final ServiceLoader<PluginConfigProvider> extensionConfigProviders = ServiceLoader.load(PluginConfigProvider.class);
7578

7679
@Override
7780
public void duringSetup(DuringSetupAccess access) {
78-
try {
79-
RuntimeReflection.register(org.junit.platform.commons.annotation.Testable.class.getMethods());
80-
RuntimeReflection.register(Class.forName("org.junit.jupiter.params.ParameterizedTestExtension").getDeclaredMethods());
81-
RuntimeReflection.registerForReflectiveInstantiation(Class.forName("org.junit.jupiter.params.ParameterizedTestExtension"));
82-
RuntimeReflection.register(Class.forName("org.junit.jupiter.params.provider.CsvArgumentsProvider").getMethods());
83-
RuntimeReflection.register(Class.forName("org.junit.jupiter.params.provider.CsvArgumentsProvider").getDeclaredMethods());
84-
RuntimeReflection.registerForReflectiveInstantiation(Class.forName("org.junit.jupiter.params.provider.CsvArgumentsProvider"));
85-
} catch (ClassNotFoundException e) {
86-
throw new RuntimeException("Missing some JUnit Platform classes for runtime reflection configuration. \n" +
87-
"Check if JUnit Platform is on your classpath or if that version is supported. \n" +
88-
"Original error: " + e);
89-
}
81+
forEachProvider(p -> p.onLoad(nativeImageConfigImpl));
9082
}
9183

9284
@Override
9385
public void beforeAnalysis(BeforeAnalysisAccess access) {
94-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.vintage.engine.support.UniqueIdReader");
95-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.vintage.engine.support.UniqueIdStringifier");
96-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.launcher.core.InternalTestPlan");
97-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.commons.util.StringUtils");
98-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.launcher.core.TestExecutionListenerRegistry");
99-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.commons.logging.LoggerFactory$DelegatingLogger");
100-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.launcher.core.EngineDiscoveryOrchestrator");
101-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.launcher.core.LauncherConfigurationParameters");
102-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.commons.logging.LoggerFactory");
103-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.config.EnumConfigurationParameterConverter");
104-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.ClassTestDescriptor");
105-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor");
106-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.TestFactoryTestDescriptor");
107-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.engine.UniqueIdFormat");
108-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.JupiterTestDescriptor");
109-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.MethodBasedTestDescriptor");
110-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor");
111-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.platform.commons.util.ReflectionUtils");
112-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.descriptor.TestTemplateTestDescriptor");
113-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.execution.ConditionEvaluator");
114-
RuntimeClassInitialization.initializeAtBuildTime("org.junit.jupiter.engine.execution.ExecutableInvoker");
11586
RuntimeClassInitialization.initializeAtBuildTime(NativeImageJUnitLauncher.class);
11687

11788
Launcher launcher = LauncherFactory.create();
@@ -136,7 +107,7 @@ private List<? extends DiscoverySelector> getSelectors(List<Path> classpath) {
136107

137108
// Run a a junit launcher to discover tests and register classes for reflection
138109
if (debug) {
139-
classpath.forEach(path -> System.out.println("[Debug] Found classpath: " + path));
110+
classpath.forEach(path -> debug("Found classpath: " + path));
140111
}
141112
return DiscoverySelectors.selectClasspathRoots(new HashSet<>(classpath));
142113

@@ -163,16 +134,28 @@ private TestPlan registerTestPlan(Launcher launcher, List<Path> classpath) {
163134
}
164135

165136
private void registerTestClassForReflection(Class<?> clazz) {
166-
if (debug) {
167-
System.out.println("[Debug] Registering test class for reflection: " + clazz.getName());
137+
debug("Registering test class for reflection: %s", clazz.getName());
138+
nativeImageConfigImpl.registerAllClassMembersForReflection(clazz);
139+
forEachProvider(p -> p.onTestClassRegistered(clazz, nativeImageConfigImpl));
140+
Class<?> superClass = clazz.getSuperclass();
141+
if (superClass != null && superClass != Object.class) {
142+
registerTestClassForReflection(superClass);
168143
}
169-
RuntimeReflection.register(clazz.getDeclaredConstructors());
144+
}
170145

171-
for (Field field : clazz.getDeclaredFields()) {
172-
RuntimeReflection.register(field);
146+
private void forEachProvider(Consumer<PluginConfigProvider> consumer) {
147+
for (PluginConfigProvider provider : extensionConfigProviders) {
148+
consumer.accept(provider);
173149
}
174-
for (Method method : clazz.getDeclaredMethods()) {
175-
RuntimeReflection.register(method);
150+
}
151+
152+
public static void debug(String format, Object... args) {
153+
if (debug()) {
154+
System.out.printf("[Debug] " + format + "%n", args);
176155
}
177156
}
157+
158+
public static boolean debug() {
159+
return ImageSingletons.lookup(JUnitPlatformFeature.class).debug;
160+
}
178161
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) 2021, 2021 Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.junit.platform;
42+
43+
import org.graalvm.junit.platform.config.core.NativeImageConfiguration;
44+
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
45+
import org.graalvm.nativeimage.hosted.RuntimeReflection;
46+
47+
import java.lang.reflect.Executable;
48+
import java.lang.reflect.Field;
49+
50+
class NativeImageConfigurationImpl implements NativeImageConfiguration {
51+
52+
@Override
53+
public void registerForReflection(Class<?>... classes) {
54+
RuntimeReflection.register(classes);
55+
}
56+
57+
@Override
58+
public void registerForReflection(Executable... methods) {
59+
RuntimeReflection.register(methods);
60+
}
61+
62+
@Override
63+
public void registerForReflection(Field... fields) {
64+
RuntimeReflection.register(fields);
65+
}
66+
67+
@Override
68+
public void initializeAtBuildTime(String... classNames) {
69+
for (String className : classNames) {
70+
Class<?> clazz;
71+
try {
72+
clazz = Class.forName(className);
73+
initializeAtBuildTime(clazz);
74+
} catch (ClassNotFoundException e) {
75+
JUnitPlatformFeature.debug("[Native Image Configuration] Failed to register class for build time initialization: %s Reason: %s", className, e);
76+
}
77+
}
78+
}
79+
80+
@Override
81+
public void initializeAtBuildTime(Class<?>... classes) {
82+
RuntimeClassInitialization.initializeAtBuildTime(classes);
83+
}
84+
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,19 @@ public void testPlanExecutionStarted(TestPlan testPlan) {
6767
this.testPlan = testPlan;
6868
}
6969

70+
@Override
71+
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
72+
printTest(testIdentifier, "SKIPPED: " + reason);
73+
}
74+
7075
@Override
7176
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
77+
printTest(testIdentifier, testExecutionResult.getStatus().name());
78+
}
79+
80+
private void printTest(TestIdentifier testIdentifier, String status) {
7281
if (testIdentifier.getParentId().isPresent() && !testIdentifier.isContainer()) {
73-
out.println(LegacyReportingUtils.getClassName(testPlan, testIdentifier) + " > " + testIdentifier.getDisplayName() + " " + testExecutionResult.getStatus().name() + "\n");
82+
out.println(LegacyReportingUtils.getClassName(testPlan, testIdentifier) + " > " + testIdentifier.getDisplayName() + " " + status + "\n");
7483
}
7584
}
7685
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
* that were executed and generates a file (currently hard coded to
5959
* {@code ./build/test_ids.txt} with Gradle or {@code ./target/test_ids.txt} with Maven)
6060
* that contains test IDs which can be passed to custom launcher to select exactly those test classes.
61-
*
61+
* <p>
6262
* This file should be replaced with org.junit.platform.launcher.listeners.UniqueIdTrackingListener,
6363
* once junit-platform-launcher version 1.8 gets released.
6464
*
@@ -71,8 +71,17 @@ public class UniqueIdTrackingTestExecutionListener implements TestExecutionListe
7171

7272
private final List<String> uniqueIds = new ArrayList<>();
7373

74+
@Override
75+
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
76+
addTest(testIdentifier);
77+
}
78+
7479
@Override
7580
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
81+
addTest(testIdentifier);
82+
}
83+
84+
private void addTest(TestIdentifier testIdentifier) {
7685
if (testIdentifier.isTest()) {
7786
this.uniqueIds.add(testIdentifier.getUniqueId());
7887
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2021, 2021 Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.junit.platform.config.core;
42+
43+
import org.graalvm.junit.platform.JUnitPlatformFeature;
44+
45+
import java.lang.reflect.Executable;
46+
import java.lang.reflect.Field;
47+
48+
public interface NativeImageConfiguration {
49+
50+
void registerForReflection(Class<?>... classes);
51+
52+
void registerForReflection(Executable... methods);
53+
54+
void registerForReflection(Field... fields);
55+
56+
default void registerAllClassMembersForReflection(Class<?>... classes) {
57+
for (Class<?> clazz : classes) {
58+
JUnitPlatformFeature.debug("[Native Image Configuration] Registering for reflection: %s", clazz.getName());
59+
registerForReflection(clazz);
60+
registerForReflection(clazz.getDeclaredConstructors());
61+
registerForReflection(clazz.getDeclaredMethods());
62+
registerForReflection(clazz.getDeclaredFields());
63+
}
64+
}
65+
66+
void initializeAtBuildTime(String... classNames);
67+
68+
void initializeAtBuildTime(Class<?>... classes);
69+
}

common/junit-platform-native/src/test/java/com/example/project/Calculator.java renamed to common/junit-platform-native/src/main/java/org/graalvm/junit/platform/config/core/PluginConfigProvider.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2021 Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -38,12 +38,12 @@
3838
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3939
* SOFTWARE.
4040
*/
41-
package com.example.project;
41+
package org.graalvm.junit.platform.config.core;
4242

43-
public class Calculator {
43+
public interface PluginConfigProvider {
4444

45-
public int add(int a, int b) {
46-
return a + b;
47-
}
45+
void onLoad(NativeImageConfiguration config);
46+
47+
void onTestClassRegistered(Class<?> testClass, NativeImageConfiguration registry);
4848

4949
}

0 commit comments

Comments
 (0)