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

Add virtual thread support #3443

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions .github/actions/run-gradle/action.yml
Expand Up @@ -13,6 +13,12 @@ runs:
with:
distribution: temurin
java-version: 17
- uses: oracle-actions/setup-java@v1
with:
website: jdk.java.net
release: 21
- shell: bash
run: echo "JDK21=$JAVA_HOME" >> $GITHUB_ENV
- uses: gradle/gradle-build-action@v2
env:
JAVA_HOME: ${{ steps.setup-gradle-jdk.outputs.path }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/reproducible-build.yml
Expand Up @@ -26,6 +26,8 @@ jobs:
with:
arguments: --quiet
- name: Build and compare checksums
env:
JAVA_HOME: ${{env.JAVA_HOME_17_X64}}
shell: bash
run: |
./gradle/scripts/checkBuildReproducibility.sh
2 changes: 2 additions & 0 deletions gradle/config/checkstyle/suppressions.xml
Expand Up @@ -4,4 +4,6 @@
files="junit-platform-commons[\\/]src[\\/]main[\\/]java.+?[\\/]org[\\/]junit[\\/]platform[\\/]commons[\\/]util[\\/]*"/>
<suppress checks="JavadocPackage"
files="junit-platform-console[\\/]src[\\/]main[\\/]java.+?[\\/]org[\\/]junit[\\/]platform[\\/]console[\\/]*"/>
<suppress checks="JavadocPackage"
files="junit-platform-engine[\\/]src[\\/]main[\\/]java.+[\\/]org[\\/]junit[\\/]platform[\\/]engine[\\/]support[\\/]hierarchical[\\/]*"/>
</suppressions>
Expand Up @@ -147,9 +147,8 @@ val allMainClasses by tasks.registering {

val prepareModuleSourceDir by tasks.registering(Sync::class) {
from(moduleSourceDir)
from(sourceSets.matching { it.name.startsWith("main") }.map { it.allJava })
from(sourceSets.main.map { it.allJava })
into(combinedModuleSourceDir.map { it.dir(javaModuleName) })
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

val compileModule by tasks.registering(JavaCompile::class) {
Expand Down
Expand Up @@ -6,7 +6,7 @@ plugins {

val mavenizedProjects: List<Project> by rootProject.extra

listOf(9, 17).forEach { javaVersion ->
listOf(9, 17, 21).forEach { javaVersion ->
val sourceSet = sourceSets.register("mainRelease${javaVersion}") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
Expand All @@ -27,6 +27,11 @@ listOf(9, 17).forEach { javaVersion ->

named<JavaCompile>(sourceSet.get().compileJavaTaskName).configure {
options.release = javaVersion
if (javaVersion == 21) {
javaCompiler.set(javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(javaVersion))
})
}
}

named<Checkstyle>("checkstyle${sourceSet.name.capitalized()}").configure {
Expand Down

This file was deleted.

Expand Up @@ -25,6 +25,9 @@ abstract class RunConsoleLauncher @Inject constructor(private val execOperations
@get:Classpath
abstract val runtimeClasspath: ConfigurableFileCollection

@get:Input
abstract val jvmArgs: ListProperty<String>

marcphilipp marked this conversation as resolved.
Show resolved Hide resolved
@get:Input
abstract val args: ListProperty<String>

Expand Down Expand Up @@ -62,6 +65,7 @@ abstract class RunConsoleLauncher @Inject constructor(private val execOperations
val output = ByteArrayOutputStream()
val result = execOperations.javaexec {
executable = javaLauncher.get().executablePath.asFile.absolutePath
jvmArgs(this@RunConsoleLauncher.jvmArgs.get())
classpath = runtimeClasspath
mainClass.set("org.junit.platform.console.ConsoleLauncher")
args(this@RunConsoleLauncher.args.get())
Expand All @@ -82,6 +86,12 @@ abstract class RunConsoleLauncher @Inject constructor(private val execOperations
result.rethrowFailure().assertNormalExitValue()
}

@Suppress("unused")
@Option(option = "jvm-args", description = "JVM args for the console launcher")
fun setVMArgs(args: String) {
jvmArgs.set(Commandline.translateCommandline(args).toList())
}

@Suppress("unused")
@Option(option = "args", description = "Additional command line arguments for the console launcher")
fun setCliArgs(args: String) {
Expand Down

This file was deleted.

@@ -0,0 +1,37 @@
package junitbuild.java

import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.internal.file.archive.ZipCopyAction
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.process.ExecOperations
import java.time.Instant
import javax.inject.Inject

abstract class UpdateJarAction @Inject constructor(private val operations: ExecOperations) : Action<Task> {

abstract val javaLauncher: Property<JavaLauncher>

abstract val args: ListProperty<String>

abstract val date: Property<Instant>

init {
date.convention(Instant.ofEpochMilli(ZipCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES))
}

override fun execute(t: Task) {
operations.exec {
executable = javaLauncher.get()
.metadata.installationPath.file("bin/jar").asFile.absolutePath
args = listOf(
"--update",
"--file", (t as AbstractArchiveTask).archiveFile.get().asFile.absolutePath,
"--date=${date.get()}"
) + this@UpdateJarAction.args.get()
}
}
}
Expand Up @@ -124,6 +124,8 @@ public final class Constants {
@API(status = STABLE, since = "5.10")
public static final String PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME = JupiterConfiguration.PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME;

public static final String PARALLEL_EXECUTOR_PROPERTY_NAME = JupiterConfiguration.PARALLEL_EXECUTOR_PROPERTY_NAME;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ needs an @API annotation and Javadoc


/**
* Property name used to set the default test execution mode: {@value}
*
Expand Down
Expand Up @@ -11,6 +11,7 @@
package org.junit.jupiter.engine;

import static org.apiguardian.api.API.Status.INTERNAL;
import static org.junit.jupiter.engine.config.JupiterConfiguration.ParallelExecutor.VIRTUAL;

import java.util.Optional;

Expand All @@ -22,6 +23,7 @@
import org.junit.jupiter.engine.discovery.DiscoverySelectorResolver;
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
import org.junit.jupiter.engine.support.JupiterThrowableCollectorFactory;
import org.junit.platform.engine.ConfigurationParameters;
import org.junit.platform.engine.EngineDiscoveryRequest;
import org.junit.platform.engine.ExecutionRequest;
import org.junit.platform.engine.TestDescriptor;
Expand All @@ -31,6 +33,7 @@
import org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine;
import org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutorService;
import org.junit.platform.engine.support.hierarchical.ThrowableCollector;
import org.junit.platform.engine.support.hierarchical.VirtualThreadHierarchicalTestExecutorServiceFactory;

/**
* The JUnit Jupiter {@link org.junit.platform.engine.TestEngine TestEngine}.
Expand Down Expand Up @@ -74,8 +77,12 @@ public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId
protected HierarchicalTestExecutorService createExecutorService(ExecutionRequest request) {
JupiterConfiguration configuration = getJupiterConfiguration(request);
if (configuration.isParallelExecutionEnabled()) {
return new ForkJoinPoolHierarchicalTestExecutorService(new PrefixedConfigurationParameters(
request.getConfigurationParameters(), Constants.PARALLEL_CONFIG_PREFIX));
ConfigurationParameters configurationParameters = new PrefixedConfigurationParameters(
request.getConfigurationParameters(), Constants.PARALLEL_CONFIG_PREFIX);
if (configuration.getParallelExecutor() == VIRTUAL) {
return VirtualThreadHierarchicalTestExecutorServiceFactory.create(configurationParameters);
}
return new ForkJoinPoolHierarchicalTestExecutorService(configurationParameters);
}
return super.createExecutorService(request);
}
Expand Down
Expand Up @@ -67,6 +67,12 @@ public boolean isExtensionAutoDetectionEnabled() {
key -> delegate.isExtensionAutoDetectionEnabled());
}

@Override
public ParallelExecutor getParallelExecutor() {
return (ParallelExecutor) cache.computeIfAbsent(PARALLEL_EXECUTOR_PROPERTY_NAME,
key -> delegate.getParallelExecutor());
}

@Override
public ExecutionMode getDefaultExecutionMode() {
return (ExecutionMode) cache.computeIfAbsent(DEFAULT_EXECUTION_MODE_PROPERTY_NAME,
Expand Down
Expand Up @@ -41,6 +41,9 @@
@API(status = INTERNAL, since = "5.4")
public class DefaultJupiterConfiguration implements JupiterConfiguration {

private static final EnumConfigurationParameterConverter<ParallelExecutor> parallelExecutorConverter = //
new EnumConfigurationParameterConverter<>(ParallelExecutor.class, "parallel executor");

private static final EnumConfigurationParameterConverter<ExecutionMode> executionModeConverter = //
new EnumConfigurationParameterConverter<>(ExecutionMode.class, "parallel execution mode");

Expand Down Expand Up @@ -89,6 +92,12 @@ public boolean isExtensionAutoDetectionEnabled() {
return configurationParameters.getBoolean(EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME).orElse(false);
}

@Override
public ParallelExecutor getParallelExecutor() {
return parallelExecutorConverter.get(configurationParameters, PARALLEL_EXECUTOR_PROPERTY_NAME,
ParallelExecutor.FORK_JOIN_POOL);
}

@Override
public ExecutionMode getDefaultExecutionMode() {
return executionModeConverter.get(configurationParameters, DEFAULT_EXECUTION_MODE_PROPERTY_NAME,
Expand Down
Expand Up @@ -36,6 +36,7 @@ public interface JupiterConfiguration {

String DEACTIVATE_CONDITIONS_PATTERN_PROPERTY_NAME = "junit.jupiter.conditions.deactivate";
String PARALLEL_EXECUTION_ENABLED_PROPERTY_NAME = "junit.jupiter.execution.parallel.enabled";
String PARALLEL_EXECUTOR_PROPERTY_NAME = "junit.jupiter.execution.parallel.executor";
String DEFAULT_EXECUTION_MODE_PROPERTY_NAME = Execution.DEFAULT_EXECUTION_MODE_PROPERTY_NAME;
String DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME = Execution.DEFAULT_CLASSES_EXECUTION_MODE_PROPERTY_NAME;
String EXTENSIONS_AUTODETECTION_ENABLED_PROPERTY_NAME = "junit.jupiter.extensions.autodetection.enabled";
Expand All @@ -52,6 +53,8 @@ public interface JupiterConfiguration {

boolean isExtensionAutoDetectionEnabled();

ParallelExecutor getParallelExecutor();

ExecutionMode getDefaultExecutionMode();

ExecutionMode getDefaultClassesExecutionMode();
Expand All @@ -70,4 +73,7 @@ public interface JupiterConfiguration {

Supplier<TempDirFactory> getDefaultTempDirFactorySupplier();

enum ParallelExecutor {
FORK_JOIN_POOL, VIRTUAL
}
}
7 changes: 1 addition & 6 deletions junit-platform-commons/junit-platform-commons.gradle.kts
@@ -1,9 +1,6 @@
import junitbuild.java.ExecJarAction

plugins {
id("junitbuild.java-library-conventions")
id("junitbuild.java-multi-release-sources")
id("junitbuild.java-repackage-jars")
`java-test-fixtures`
}

Expand All @@ -18,11 +15,9 @@ dependencies {
tasks.jar {
val release9ClassesDir = sourceSets.mainRelease9.get().output.classesDirs.singleFile
inputs.dir(release9ClassesDir).withPathSensitivity(PathSensitivity.RELATIVE)
doLast(objects.newInstance(ExecJarAction::class).apply {
doLast(objects.newInstance(junitbuild.java.UpdateJarAction::class).apply {
javaLauncher = javaToolchains.launcherFor(java.toolchain)
args.addAll(
"--update",
"--file", archiveFile.get().asFile.absolutePath,
"--release", "9",
"-C", release9ClassesDir.absolutePath, "."
)
Expand Down
5 changes: 1 addition & 4 deletions junit-platform-console/junit-platform-console.gradle.kts
Expand Up @@ -2,7 +2,6 @@ plugins {
id("junitbuild.java-library-conventions")
id("junitbuild.shadow-conventions")
id("junitbuild.java-multi-release-sources")
id("junitbuild.java-repackage-jars")
}

description = "JUnit Platform Console"
Expand Down Expand Up @@ -40,11 +39,9 @@ tasks {
into("META-INF")
}
from(sourceSets.mainRelease9.get().output.classesDirs)
doLast(objects.newInstance(junitbuild.java.ExecJarAction::class).apply {
doLast(objects.newInstance(junitbuild.java.UpdateJarAction::class).apply {
javaLauncher = project.javaToolchains.launcherFor(java.toolchain)
args.addAll(
"--update",
"--file", archiveFile.get().asFile.absolutePath,
"--main-class", "org.junit.platform.console.ConsoleLauncher",
"--release", "17",
"-C", release17ClassesDir.absolutePath, "."
Expand Down
23 changes: 23 additions & 0 deletions junit-platform-engine/junit-platform-engine.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("junitbuild.java-library-conventions")
id("junitbuild.java-multi-release-sources")
`java-test-fixtures`
}

Expand All @@ -17,3 +18,25 @@ dependencies {
osgiVerification(projects.junitJupiterEngine)
osgiVerification(projects.junitPlatformLauncher)
}

tasks.jar {
val release21ClassesDir = project.sourceSets.mainRelease21.get().output.classesDirs.singleFile
inputs.dir(release21ClassesDir).withPathSensitivity(PathSensitivity.RELATIVE)
doLast(objects.newInstance(junitbuild.java.UpdateJarAction::class).apply {
javaLauncher.set(project.javaToolchains.launcherFor {
languageVersion.set(java.toolchain.languageVersion.map {
if (it.canCompileOrRun(21)) it else JavaLanguageVersion.of(21)
})
})
args.addAll(
"--release", "21",
"-C", release21ClassesDir.absolutePath, "."
)
})
}

eclipse {
classpath {
sourceSets -= project.sourceSets.mainRelease21.get()
}
}