Skip to content
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.
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 @@ -3,7 +3,9 @@

import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.gradle.api.Action;
Expand Down Expand Up @@ -39,6 +41,9 @@ public abstract class JavaModuleTestingExtension {

private final Project project;

// for late 'extendsFrom' configuration
private final Map<SourceSet, SourceSet> whiteboxOrClasspathTestSuites = new HashMap<>();

@Inject
public JavaModuleTestingExtension(Project project) {
this.project = project;
Expand All @@ -57,6 +62,7 @@ public JavaModuleTestingExtension(Project project) {
whitebox(jvmTestSuite, conf -> conf.getOpensTo().add("org.junit.platform.commons"));
}
});
project.afterEvaluate(this::lateConfigureExtendsFrom);
}

/**
Expand Down Expand Up @@ -148,6 +154,8 @@ public void whitebox(TestSuite jvmTestSuite, Action<WhiteboxJvmTestSuite> conf)
.convention(project.getExtensions()
.getByType(SourceSetContainer.class)
.getByName(SourceSet.MAIN_SOURCE_SET_NAME));

// Improve: requiresFromModuleInfo is done multiple times when 'whitebox' is called multiple times
whiteboxJvmTestSuite
.getRequires()
.addAll(requiresFromModuleInfo(
Expand All @@ -156,6 +164,7 @@ public void whitebox(TestSuite jvmTestSuite, Action<WhiteboxJvmTestSuite> conf)
.getRequiresRuntime()
.addAll(requiresFromModuleInfo(
(JvmTestSuite) jvmTestSuite, whiteboxJvmTestSuite.getSourcesUnderTest(), true));

conf.execute(whiteboxJvmTestSuite);
configureJvmTestSuiteForWhitebox((JvmTestSuite) jvmTestSuite, whiteboxJvmTestSuite);
}
Expand Down Expand Up @@ -224,24 +233,17 @@ private void configureJvmTestSuiteForBlackbox(JvmTestSuite jvmTestSuite) {
private void configureJvmTestSuiteForWhitebox(
JvmTestSuite jvmTestSuite, WhiteboxJvmTestSuite whiteboxJvmTestSuite) {
ConfigurationContainer configurations = project.getConfigurations();
DependencyHandler dependencies = project.getDependencies();
TaskContainer tasks = project.getTasks();
ModuleInfoParser moduleInfoParser = new ModuleInfoParser(project.getLayout(), project.getProviders());

SourceSet testSources = jvmTestSuite.getSources();
SourceSet sourcesUnderTest = whiteboxJvmTestSuite.getSourcesUnderTest().get();
whiteboxOrClasspathTestSuites.put(testSources, sourcesUnderTest);

JavaModuleDependenciesBridge.addRequiresRuntimeSupport(
project, whiteboxJvmTestSuite.getSourcesUnderTest().get(), jvmTestSuite.getSources());

tasks.named(testSources.getCompileJavaTaskName(), JavaCompile.class, compileJava -> {
SourceSet sourcesUnderTest =
whiteboxJvmTestSuite.getSourcesUnderTest().get();

Configuration compileOnly = configurations.getByName(sourcesUnderTest.getCompileOnlyConfigurationName());
Configuration testCompileOnly = configurations.getByName(testSources.getCompileOnlyConfigurationName());
if (!testCompileOnly.getExtendsFrom().contains(compileOnly)) {
testCompileOnly.extendsFrom(compileOnly);
}

compileJava.setClasspath(sourcesUnderTest
.getOutput()
.plus(configurations.getByName(testSources.getCompileClasspathConfigurationName())));
Expand All @@ -250,29 +252,12 @@ private void configureJvmTestSuiteForWhitebox(
compileJava.getOptions().getCompilerArgumentProviders().stream()
.filter(p -> p instanceof WhiteboxTestCompileArgumentProvider)
.findFirst()
.orElseGet(() -> {
WhiteboxTestCompileArgumentProvider newProvider =
new WhiteboxTestCompileArgumentProvider(
testSources.getJava().getSrcDirs(),
moduleInfoParser,
project.getObjects());
compileJava
.getOptions()
.getCompilerArgumentProviders()
.add(newProvider);
compileJava.doFirst(
project.getObjects().newInstance(JavaCompileSetModulePathAction.class));
return newProvider;
});
.orElseGet(() -> initCompileArgProvider(compileJava, testSources, moduleInfoParser));
argumentProvider.setMainSourceFolders(sourcesUnderTest.getJava().getSrcDirs());
argumentProvider.testRequires(
JavaModuleDependenciesBridge.getCompileClasspathModules(project, testSources));
argumentProvider.testRequires(whiteboxJvmTestSuite.getRequires());
});

tasks.named(testSources.getName(), Test.class, test -> {
SourceSet sourcesUnderTest =
whiteboxJvmTestSuite.getSourcesUnderTest().get();
test.setClasspath(configurations
.getByName(testSources.getRuntimeClasspathConfigurationName())
.plus(sourcesUnderTest.getOutput())
Expand All @@ -288,49 +273,61 @@ private void configureJvmTestSuiteForWhitebox(
(WhiteboxTestRuntimeArgumentProvider) test.getJvmArgumentProviders().stream()
.filter(p -> p instanceof WhiteboxTestRuntimeArgumentProvider)
.findFirst()
.orElseGet(() -> {
WhiteboxTestRuntimeArgumentProvider newProvider =
new WhiteboxTestRuntimeArgumentProvider(
testSources.getJava().getClassesDirectory(),
testSources.getOutput().getResourcesDir(),
moduleInfoParser,
project.getObjects());
test.getJvmArgumentProviders().add(newProvider);
return newProvider;
});
.orElseGet(() -> initRuntimeArgProvider(test, testSources, moduleInfoParser));
argumentProvider.setMainSourceFolders(sourcesUnderTest.getJava().getSrcDirs());
argumentProvider.setResourcesUnderTest(sourcesUnderTest.getOutput().getResourcesDir());
argumentProvider.testRequires(
JavaModuleDependenciesBridge.getRuntimeClasspathModules(project, testSources));
argumentProvider.testRequires(whiteboxJvmTestSuite.getRequires());
argumentProvider.testOpensTo(JavaModuleDependenciesBridge.getOpensToModules(project, testSources));
argumentProvider.testOpensTo(whiteboxJvmTestSuite.getOpensTo());
argumentProvider.testExportsTo(JavaModuleDependenciesBridge.getExportsToModules(project, testSources));
argumentProvider.testExportsTo(whiteboxJvmTestSuite.getExportsTo());
});

Configuration implementation = configurations.getByName(testSources.getImplementationConfigurationName());
implementation.withDependencies(d -> {
for (String requiresModuleName : whiteboxJvmTestSuite.getRequires().get()) {
Provider<?> dependency = JavaModuleDependenciesBridge.create(
project,
requiresModuleName,
whiteboxJvmTestSuite.getSourcesUnderTest().get());
if (dependency != null) {
dependencies.addProvider(implementation.getName(), dependency);
}
}
});
Configuration runtimeOnly = configurations.getByName(testSources.getRuntimeOnlyConfigurationName());
runtimeOnly.withDependencies(d -> {
for (String requiresModuleName :
whiteboxJvmTestSuite.getRequiresRuntime().get()) {
addDependencyForRequires(
whiteboxJvmTestSuite,
testSources.getImplementationConfigurationName(),
whiteboxJvmTestSuite.getRequires());
addDependencyForRequires(
whiteboxJvmTestSuite,
testSources.getRuntimeOnlyConfigurationName(),
whiteboxJvmTestSuite.getRequiresRuntime());
}

private WhiteboxTestCompileArgumentProvider initCompileArgProvider(
JavaCompile compileJava, SourceSet testSources, ModuleInfoParser moduleInfoParser) {
WhiteboxTestCompileArgumentProvider newProvider = new WhiteboxTestCompileArgumentProvider(
testSources.getJava().getSrcDirs(), moduleInfoParser, project.getObjects());
newProvider.testRequires(JavaModuleDependenciesBridge.getCompileClasspathModules(project, testSources));
compileJava.getOptions().getCompilerArgumentProviders().add(newProvider);
compileJava.doFirst(project.getObjects().newInstance(JavaCompileSetModulePathAction.class));
return newProvider;
}

private WhiteboxTestRuntimeArgumentProvider initRuntimeArgProvider(
Test test, SourceSet testSources, ModuleInfoParser moduleInfoParser) {
WhiteboxTestRuntimeArgumentProvider newProvider = new WhiteboxTestRuntimeArgumentProvider(
testSources.getJava().getClassesDirectory(),
testSources.getOutput().getResourcesDir(),
moduleInfoParser,
project.getObjects());
newProvider.testRequires(JavaModuleDependenciesBridge.getRuntimeClasspathModules(project, testSources));
newProvider.testOpensTo(JavaModuleDependenciesBridge.getOpensToModules(project, testSources));
newProvider.testExportsTo(JavaModuleDependenciesBridge.getExportsToModules(project, testSources));
test.getJvmArgumentProviders().add(newProvider);
return newProvider;
}

private void addDependencyForRequires(
WhiteboxJvmTestSuite whiteboxJvmTestSuite, String scope, Provider<List<String>> requires) {
ConfigurationContainer configurations = project.getConfigurations();
DependencyHandler dependencies = project.getDependencies();

configurations.getByName(scope).withDependencies(d -> {
for (String requiresModuleName : requires.get()) {
Provider<?> dependency = JavaModuleDependenciesBridge.create(
project,
requiresModuleName,
whiteboxJvmTestSuite.getSourcesUnderTest().get());
if (dependency != null) {
dependencies.addProvider(runtimeOnly.getName(), dependency);
dependencies.addProvider(scope, dependency);
}
}
});
Expand Down Expand Up @@ -363,4 +360,30 @@ private void revertJvmTestSuiteForWhitebox(JvmTestSuite jvmTestSuite) {
test.getJvmArgumentProviders().removeIf(p -> p instanceof WhiteboxTestRuntimeArgumentProvider);
});
}

private void lateConfigureExtendsFrom(Project project) {
ConfigurationContainer configurations = project.getConfigurations();
whiteboxOrClasspathTestSuites.forEach((testSources, sourcesUnderTest) -> {
extendsFrom(
configurations,
testSources.getImplementationConfigurationName(),
sourcesUnderTest.getImplementationConfigurationName());
extendsFrom(
configurations,
testSources.getRuntimeOnlyConfigurationName(),
sourcesUnderTest.getRuntimeOnlyConfigurationName());
extendsFrom(
configurations,
testSources.getCompileOnlyConfigurationName(),
sourcesUnderTest.getCompileOnlyConfigurationName());
});
}

private void extendsFrom(ConfigurationContainer configurations, String testScope, String underTestScope) {
Configuration testScopeConf = configurations.getByName(testScope);
Configuration underTestScopeConf = configurations.getByName(underTestScope);
if (!testScopeConf.getExtendsFrom().contains(underTestScopeConf)) {
testScopeConf.extendsFrom(underTestScopeConf);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import java.util.Set;
import java.util.stream.Collectors;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.SetProperty;
import org.gradle.process.CommandLineArgumentProvider;
import org.gradlex.javamodule.testing.internal.ModuleInfoParser;
import org.jspecify.annotations.NullMarked;
Expand All @@ -21,13 +21,13 @@ public class WhiteboxTestCompileArgumentProvider implements CommandLineArgumentP
@SuppressWarnings("NotNullFieldNotInitialized")
private Set<File> mainSourceFolders;

private final ListProperty<String> allTestRequires;
private final SetProperty<String> allTestRequires;

public WhiteboxTestCompileArgumentProvider(
Set<File> testSourceFolders, ModuleInfoParser moduleInfoParser, ObjectFactory objects) {
this.testSourceFolders = testSourceFolders;
this.moduleInfoParser = moduleInfoParser;
this.allTestRequires = objects.listProperty(String.class);
this.allTestRequires = objects.setProperty(String.class);
}

public void setMainSourceFolders(Set<File> mainSourceFolders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import java.util.TreeSet;
import org.gradle.api.file.Directory;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Provider;
import org.gradle.api.provider.SetProperty;
import org.gradle.process.CommandLineArgumentProvider;
import org.gradlex.javamodule.testing.internal.ModuleInfoParser;
import org.jspecify.annotations.NullMarked;
Expand All @@ -26,9 +26,9 @@ public class WhiteboxTestRuntimeArgumentProvider implements CommandLineArgumentP
@SuppressWarnings("NotNullFieldNotInitialized")
private File resourcesUnderTest;

private final ListProperty<String> allTestRequires;
private final ListProperty<String> allTestOpensTo;
private final ListProperty<String> allTestExportsTo;
private final SetProperty<String> allTestRequires;
private final SetProperty<String> allTestOpensTo;
private final SetProperty<String> allTestExportsTo;

public WhiteboxTestRuntimeArgumentProvider(
Provider<Directory> testClassesFolders,
Expand All @@ -38,9 +38,9 @@ public WhiteboxTestRuntimeArgumentProvider(
this.testClassesFolders = testClassesFolders;
this.testResources = testResources;
this.moduleInfoParser = moduleInfoParser;
this.allTestRequires = objects.listProperty(String.class);
this.allTestOpensTo = objects.listProperty(String.class);
this.allTestExportsTo = objects.listProperty(String.class);
this.allTestRequires = objects.setProperty(String.class);
this.allTestOpensTo = objects.setProperty(String.class);
this.allTestExportsTo = objects.setProperty(String.class);
}

public void setMainSourceFolders(Set<File> mainSourceFolders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,42 @@ class CoreFunctionalityTest {

@Test
void testCompileOnly_extends_compileOnly_for_whitebox_test_suites() {
build.appBuildFile.appendText(
"""
javaModuleTesting.whitebox(testing.suites["test"]) {
requires.add("org.junit.jupiter.api")
}
dependencies {
compileOnly("jakarta.servlet:jakarta.servlet-api:6.1.0")
}""");
build.file("app/src/main/java/org/example/app/ServletImpl.java")
.writeText(
"""
package org.example.app;
public abstract class ServletImpl implements jakarta.servlet.Servlet { }
""");
build.file("app/src/test/java/org/example/app/test/ServletMock.java")
.writeText(
"""
package org.example.app.test;
public abstract class ServletMock extends org.example.app.ServletImpl { }
""");
build.appModuleInfoFile.writeText(
"""
module org.example.app {
requires static jakarta.servlet;
}
""");

var result = build.runner("compileTestJava").build();
var compileTestResult = result.task(":app:compileTestJava");

assertThat(compileTestResult).isNotNull();
assertThat(compileTestResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}

@Test
void testCompileOnly_extends_compileOnly_for_classpath_test_suites() {
build.appBuildFile.appendText(
"""
javaModuleTesting.classpath(testing.suites["test"])
Expand Down Expand Up @@ -44,4 +80,48 @@ public abstract class ServletMock extends org.example.app.ServletImpl { }
assertThat(compileTestResult).isNotNull();
assertThat(compileTestResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}

@Test
void testImplementation_extends_implementation_for_whitebox_test_suites() {
build.useTestFixturesPlugin();
build.appBuildFile.appendText(
"""
javaModuleTesting.whitebox(testing.suites["test"]) {
requires.add("org.junit.jupiter.api")
sourcesUnderTest.set(sourceSets.testFixtures)
}
dependencies {
testFixturesImplementation("jakarta.servlet:jakarta.servlet-api:6.1.0")
}""");
build.file("app/src/testFixtures/java/org/example/app/Main.java")
.writeText("""
package org.example.app;
public class Main {}
""");
build.file("app/src/testFixtures/java/org/example/app/ServletImpl.java")
.writeText(
"""
package org.example.app;
public abstract class ServletImpl implements jakarta.servlet.Servlet { }
""");
build.file("app/src/test/java/org/example/app/test/ServletMock.java")
.writeText(
"""
package org.example.app.test;
public abstract class ServletMock extends org.example.app.ServletImpl { }
""");
build.file("app/src/testFixtures/java/module-info.java")
.writeText(
"""
module org.example.fixtures {
requires static jakarta.servlet;
}
""");

var result = build.runner("compileTestJava").build();
var compileTestResult = result.task(":app:compileTestJava");

assertThat(compileTestResult).isNotNull();
assertThat(compileTestResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
}
}
Loading