Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discover tests with ClassLoader other than ImageClassLoader #445

Merged
merged 1 commit into from Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -42,38 +42,21 @@
package org.graalvm.junit.platform;

import org.graalvm.junit.platform.config.core.PluginConfigProvider;
import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.junit.platform.engine.DiscoverySelector;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.engine.discovery.UniqueIdSelector;
import org.junit.platform.engine.support.descriptor.ClassSource;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.UniqueIdTrackingListener;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

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

public final boolean debug = System.getProperty("debug") != null;
public static final boolean debug = System.getProperty(TestsDiscoveryHelper.DEBUG) != null;

private static final NativeImageConfigurationImpl nativeImageConfigImpl = new NativeImageConfigurationImpl();
private final ServiceLoader<PluginConfigProvider> extensionConfigProviders = ServiceLoader.load(PluginConfigProvider.class);
Expand All @@ -86,65 +69,28 @@ public void duringSetup(DuringSetupAccess access) {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
RuntimeClassInitialization.initializeAtBuildTime(NativeImageJUnitLauncher.class);

List<Path> classpathRoots = access.getApplicationClassPath();
List<? extends DiscoverySelector> selectors = getSelectors(classpathRoots);

Launcher launcher = LauncherFactory.create();
TestPlan testplan = discoverTestsAndRegisterTestClassesForReflection(launcher, selectors);
ImageSingletons.add(NativeImageJUnitLauncher.class, new NativeImageJUnitLauncher(launcher, testplan));
}

private List<? extends DiscoverySelector> getSelectors(List<Path> classpathRoots) {
try {
Path outputDir = Paths.get(System.getProperty(UniqueIdTrackingListener.OUTPUT_DIR_PROPERTY_NAME));
String prefix = System.getProperty(UniqueIdTrackingListener.OUTPUT_FILE_PREFIX_PROPERTY_NAME,
UniqueIdTrackingListener.DEFAULT_OUTPUT_FILE_PREFIX);
List<UniqueIdSelector> selectors = readAllFiles(outputDir, prefix)
.map(DiscoverySelectors::selectUniqueId)
.collect(Collectors.toList());
if (!selectors.isEmpty()) {
System.out.printf(
"[junit-platform-native] Running in 'test listener' mode using files matching pattern [%s*] "
+ "found in folder [%s] and its subfolders.%n",
prefix, outputDir.toAbsolutePath());
return selectors;
List<Class<?>> discoveredTests;
if (Boolean.parseBoolean(System.getProperty("isolateTestDiscovery"))) {
List<String> discoveredTestNames = TestsDiscoveryHelper.launchTestDiscovery(debug, classpathRoots);
discoveredTests = new ArrayList<>();
for (String discoveredTestName : discoveredTestNames) {
try {
discoveredTests.add(Class.forName(discoveredTestName, false, access.getApplicationClassLoader()));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
} catch (Exception ex) {
debug("Failed to read UIDs from UniqueIdTrackingListener output files: " + ex.getMessage());
} else {
TestsDiscoveryHelper helper = new TestsDiscoveryHelper(debug, classpathRoots);
discoveredTests = helper.discoverTests();
}

System.out.println("[junit-platform-native] Running in 'test discovery' mode. Note that this is a fallback mode.");
if (debug) {
classpathRoots.forEach(entry -> debug("Selecting classpath root: " + entry));
for (Class<?> discoveredTest : discoveredTests) {
registerTestClassForReflection(discoveredTest);
}
return DiscoverySelectors.selectClasspathRoots(new HashSet<>(classpathRoots));
}

/**
* Use the JUnit Platform Launcher to discover tests and register classes
* for reflection.
*/
private TestPlan discoverTestsAndRegisterTestClassesForReflection(Launcher launcher,
List<? extends DiscoverySelector> selectors) {

LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectors)
.build();

TestPlan testPlan = launcher.discover(request);

testPlan.getRoots().stream()
.flatMap(rootIdentifier -> testPlan.getDescendants(rootIdentifier).stream())
.map(TestIdentifier::getSource)
.filter(Optional::isPresent)
.map(Optional::get)
.filter(ClassSource.class::isInstance)
.map(ClassSource.class::cast)
.map(ClassSource::getJavaClass)
.forEach(this::registerTestClassForReflection);

return testPlan;
ImageSingletons.add(NativeImageJUnitLauncher.class,
new NativeImageJUnitLauncher(new TestsDiscoveryHelper(debug, classpathRoots)));
}

private void registerTestClassForReflection(Class<?> clazz) {
Expand All @@ -170,26 +116,11 @@ public static void debug(String format, Object... args) {
}

public static boolean debug() {
return ImageSingletons.lookup(JUnitPlatformFeature.class).debug;
}

private Stream<String> readAllFiles(Path dir, String prefix) throws IOException {
return findFiles(dir, prefix).map(outputFile -> {
try {
return Files.readAllLines(outputFile);
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}).flatMap(List::stream);
}

private static Stream<Path> findFiles(Path dir, String prefix) throws IOException {
if (!Files.exists(dir)) {
return Stream.empty();
if (!ImageInfo.inImageCode()) {
return debug;
} else {
return ImageSingletons.lookup(JUnitPlatformFeature.class).debug;
}
return Files.find(dir, Integer.MAX_VALUE,
(path, basicFileAttributes) -> (basicFileAttributes.isRegularFile()
&& path.getFileName().toString().startsWith(prefix)));
}

}
Expand Up @@ -43,6 +43,8 @@

import org.graalvm.nativeimage.ImageInfo;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;
Expand All @@ -59,11 +61,17 @@ public class NativeImageJUnitLauncher {
static final String DEFAULT_OUTPUT_FOLDER = Paths.get("test-results-native").resolve("test").toString();

final Launcher launcher;
final TestPlan testPlan;
TestPlan testPlan;
final TestsDiscoveryHelper testsDiscoveryHelper;

public NativeImageJUnitLauncher(Launcher launcher, TestPlan testPlan) {
this.launcher = launcher;
this.testPlan = testPlan;
@Platforms(Platform.HOSTED_ONLY.class)
public NativeImageJUnitLauncher(TestsDiscoveryHelper testsDiscoveryHelper) {
this.testsDiscoveryHelper = testsDiscoveryHelper;
launcher = testsDiscoveryHelper.getLauncher();
}

private void discoverTests() {
testPlan = testsDiscoveryHelper.discoverTestPlan();
}

public void registerTestExecutionListeners(TestExecutionListener testExecutionListener) {
Expand Down Expand Up @@ -115,7 +123,8 @@ public static void main(String... args) {

PrintWriter out = new PrintWriter(System.out);
NativeImageJUnitLauncher launcher = ImageSingletons.lookup(NativeImageJUnitLauncher.class);

//Discover the test plan at runtime.
launcher.discoverTests();
if (!silent) {
out.println("JUnit Platform on Native Image - report");
out.println("----------------------------------------\n");
Expand Down