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

Allow JUnit5 runner to be extended #295

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
import java.util.stream.Collectors;
import org.junit.jupiter.engine.Constants;
import org.junit.platform.engine.DiscoverySelector;
import org.junit.platform.engine.Filter;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherConstants;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TagFilter;
import org.junit.platform.launcher.core.LauncherConfig;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
Expand Down Expand Up @@ -55,70 +57,17 @@ public boolean run(String testClassName) {
FailFastExtension failFastExtension = new FailFastExtension();

LauncherConfig config =
LauncherConfig.builder()
getLauncherConfigBuilder()
.addTestExecutionListeners(bazelJUnitXml, summary, failFastExtension)
.addPostDiscoveryFilters(TestSharding.makeShardFilter())
.build();

final Class<?> testClass;
try {
testClass = Class.forName(testClassName, false, getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to find testClass", e);
}

// We only allow for one level of nesting at the moment
boolean enclosed = isRunWithEnclosed(testClass);
List<DiscoverySelector> classSelectors =
enclosed
? new ArrayList<>()
: Arrays.stream(testClass.getDeclaredClasses())
.filter(clazz -> Modifier.isStatic(clazz.getModifiers()))
.map(DiscoverySelectors::selectClass)
.collect(Collectors.toList());

classSelectors.add(DiscoverySelectors.selectClass(testClassName));

LauncherDiscoveryRequestBuilder request =
LauncherDiscoveryRequestBuilder.request()
.selectors(classSelectors)
.configurationParameter(LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME, "true")
.configurationParameter(LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME, "true")
.configurationParameter(
Constants.EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME, "true");

String filter = System.getenv("TESTBRIDGE_TEST_ONLY");
request.filters(new PatternFilter(filter));

String includeTags = System.getProperty("JUNIT5_INCLUDE_TAGS");
if (includeTags != null && !includeTags.isEmpty()) {
request.filters(TagFilter.includeTags(includeTags.split(",")));
}

String excludeTags = System.getProperty("JUNIT5_EXCLUDE_TAGS");
if (excludeTags != null && !excludeTags.isEmpty()) {
request.filters(TagFilter.excludeTags(excludeTags.split(",")));
}

List<String> includeEngines =
System.getProperty("JUNIT5_INCLUDE_ENGINES") == null
? null
: Arrays.asList(System.getProperty("JUNIT5_INCLUDE_ENGINES").split(","));
List<String> excludeEngines =
System.getProperty("JUNIT5_EXCLUDE_ENGINES") == null
? null
: Arrays.asList(System.getProperty("JUNIT5_EXCLUDE_ENGINES").split(","));
if (includeEngines != null) {
request.filters(includeEngines(includeEngines));
}
if (excludeEngines != null) {
request.filters(excludeEngines(excludeEngines));
}
LauncherDiscoveryRequest request = getRequest(testClassName);

File exitFile = getExitFile();

Launcher launcher = LauncherFactory.create(config);
launcher.execute(request.build());
launcher.execute(request);

deleteExitFile(exitFile);

Expand All @@ -130,6 +79,88 @@ public boolean run(String testClassName) {
}
}

/** Create and LauncherConfig.Builder instance for use in the launcher */
protected LauncherConfig.Builder getLauncherConfigBuilder() {
return LauncherConfig.builder();
}

/**
* Creates a LauncherDiscoveryRequest for the test class provided from bazel
*
* @param testClassName the fqn of the test class provided from the test_class argument
* @return the LauncherDiscoveryRequest for the run
*/
protected LauncherDiscoveryRequest getRequest(String testClassName) {
final Class<?> testClass;
try {
testClass = Class.forName(testClassName, false, getClass().getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to find testClass", e);
}

// We only allow for one level of nesting at the moment
boolean enclosed = isRunWithEnclosed(testClass);
List<DiscoverySelector> classSelectors =
enclosed
? new ArrayList<>()
: Arrays.stream(testClass.getDeclaredClasses())
.filter(clazz -> Modifier.isStatic(clazz.getModifiers()))
.map(DiscoverySelectors::selectClass)
.collect(Collectors.toList());

classSelectors.add(DiscoverySelectors.selectClass(testClassName));

LauncherDiscoveryRequestBuilder request =
LauncherDiscoveryRequestBuilder.request()
.selectors(classSelectors)
.configurationParameter(LauncherConstants.CAPTURE_STDERR_PROPERTY_NAME, "true")
.configurationParameter(LauncherConstants.CAPTURE_STDOUT_PROPERTY_NAME, "true")
.configurationParameter(
Constants.EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME, "true")
.filters(getFilters());

return request.build();
}

/**
* Returns an array of filters to filter tests for the run. This implementation returns filters
* constructed from bazel --test_filter option in addition to include/exclude arguments part of
* java_junit5_test
*/
protected Filter<?>[] getFilters() {
List<Filter<?>> filters = new ArrayList<>();

String filter = System.getenv("TESTBRIDGE_TEST_ONLY");
filters.add(new PatternFilter(filter));

String includeTags = System.getProperty("JUNIT5_INCLUDE_TAGS");
if (includeTags != null && !includeTags.isEmpty()) {
filters.add(TagFilter.includeTags(includeTags.split(",")));
}

String excludeTags = System.getProperty("JUNIT5_EXCLUDE_TAGS");
if (excludeTags != null && !excludeTags.isEmpty()) {
filters.add(TagFilter.excludeTags(excludeTags.split(",")));
}

List<String> includeEngines =
System.getProperty("JUNIT5_INCLUDE_ENGINES") == null
? null
: Arrays.asList(System.getProperty("JUNIT5_INCLUDE_ENGINES").split(","));
List<String> excludeEngines =
System.getProperty("JUNIT5_EXCLUDE_ENGINES") == null
? null
: Arrays.asList(System.getProperty("JUNIT5_EXCLUDE_ENGINES").split(","));
if (includeEngines != null) {
filters.add(includeEngines(includeEngines));
}
if (excludeEngines != null) {
filters.add(excludeEngines(excludeEngines));
}

return filters.toArray(new Filter[0]);
}

/**
* Checks if the test class is annotation with `@RunWith(Enclosed.class)`. We deliberately avoid
* using types here to avoid polluting the classpath with junit4 deps.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public class JUnit5Runner {
"com.github.bazel_contrib.contrib_rules_jvm.junit5.Java17SystemExitToggle";

public static void main(String[] args) {
new JUnit5Runner().run();
}

public void run() {
String testSuite = System.getProperty("bazel.test_suite");

SystemExitToggle systemExitToggle = getSystemExitToggle();
Expand All @@ -33,9 +37,7 @@ public static void main(String[] args) {
systemExitToggle.prevent();

try {
Constructor<? extends RunsTest> constructor =
Class.forName(JUNIT5_RUNNER_CLASS).asSubclass(RunsTest.class).getConstructor();
RunsTest runsTest = constructor.newInstance();
RunsTest runsTest = getRunner();
if (!runsTest.run(testSuite)) {
exit(systemExitToggle, 2);
}
Expand All @@ -50,7 +52,14 @@ public static void main(String[] args) {
exit(systemExitToggle, 0);
}

private static SystemExitToggle getSystemExitToggle() {
/** Returns an instance of RunsTest which handles creating and invoking the test runner */
protected RunsTest getRunner() throws ReflectiveOperationException {
Constructor<? extends RunsTest> constructor =
Class.forName(JUNIT5_RUNNER_CLASS).asSubclass(RunsTest.class).getConstructor();
return constructor.newInstance();
}

protected SystemExitToggle getSystemExitToggle() {
// In Java 8 and lower, the first part of the version is a 1.
// In Java 9 and higher, the first part of the version is the feature version.
// Major versions of early Access builds have an "-ea" suffix.
Expand Down
Loading