Skip to content

Commit c65d0f2

Browse files
authored
Allow whitebox tests to define requires in 'java9/module-info.java' (#31)
1 parent 61d9356 commit c65d0f2

File tree

6 files changed

+136
-0
lines changed

6 files changed

+136
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Java Module Testing Gradle Plugin - Changelog
22

3+
## Version 1.3
4+
* [#18](https://github.com/gradlex-org/java-module-testing/issues/18) Allow whitebox tests to define requires in `java9/module-info.java` (Thanks [brianoliver](https://github.com/brianoliver) for suggesting!)
5+
36
## Version 1.2.2
47
* No duplicated '--add-opens' runtime args
58

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,27 @@ javaModuleTesting.whitebox(testing.suites["test"]) {
8080

8181
See documentation on [JVM Test Suites](https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html#sec:jvm_test_suite_configuration) for more details on creating and configuring test suites.
8282

83+
Alternatively, you can put the `requires` into a `module-info.java` file using the same notation that you would use for blackbox tests.
84+
For this, you need to create the file in `<src-set-location>/java9/module-info.java`. For example:
85+
86+
```
87+
src
88+
├── main
89+
│ └── java
90+
│ ├── module-info.java
91+
│ └── ...
92+
└── test
93+
├── java
94+
│ └── ...
95+
└── java9
96+
└── module-info.java
97+
| module org.example.app.test {
98+
| requires org.example.app; // 'main' module into which the tests are patched
99+
| requires org.junit.jupiter.api;
100+
| }
101+
}
102+
```
103+
83104
A whitebox _test source set_ does **not** have a `module-info.java`.
84105
Instead, the _main_ and _test_ classes will be patched together and the test will run in the _main_ module which now includes the test classes as well.
85106
Additional `requires` for the test are defined as shown above.

src/main/java/org/gradlex/javamodule/testing/JavaModuleTestingExtension.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.gradle.api.artifacts.ConfigurationContainer;
2323
import org.gradle.api.artifacts.dsl.DependencyHandler;
2424
import org.gradle.api.file.FileCollection;
25+
import org.gradle.api.file.RegularFile;
2526
import org.gradle.api.plugins.jvm.JvmTestSuite;
2627
import org.gradle.api.provider.Provider;
2728
import org.gradle.api.tasks.SourceSet;
@@ -35,12 +36,16 @@
3536
import org.gradle.testing.base.TestSuite;
3637
import org.gradle.testing.base.TestingExtension;
3738
import org.gradlex.javamodule.testing.internal.ModuleInfoParser;
39+
import org.gradlex.javamodule.testing.internal.ModuleInfoRequiresParser;
3840
import org.gradlex.javamodule.testing.internal.bridges.JavaModuleDependenciesBridge;
3941
import org.gradlex.javamodule.testing.internal.provider.WhiteboxTestCompileArgumentProvider;
4042
import org.gradlex.javamodule.testing.internal.provider.WhiteboxTestRuntimeArgumentProvider;
4143

4244
import javax.inject.Inject;
4345
import java.io.File;
46+
import java.util.Collections;
47+
import java.util.List;
48+
import java.util.stream.Collectors;
4449

4550
@SuppressWarnings("UnstableApiUsage")
4651
public abstract class JavaModuleTestingExtension {
@@ -114,11 +119,31 @@ public void whitebox(TestSuite jvmTestSuite, Action<WhiteboxJvmTestSuite> conf)
114119
if (jvmTestSuite instanceof JvmTestSuite) {
115120
WhiteboxJvmTestSuite whiteboxJvmTestSuite = project.getObjects().newInstance(WhiteboxJvmTestSuite.class);
116121
whiteboxJvmTestSuite.getSourcesUnderTest().convention(project.getExtensions().getByType(SourceSetContainer.class).getByName(SourceSet.MAIN_SOURCE_SET_NAME));
122+
whiteboxJvmTestSuite.getRequires().addAll(requiresFromModuleInfo((JvmTestSuite) jvmTestSuite, whiteboxJvmTestSuite.getSourcesUnderTest()));
117123
conf.execute(whiteboxJvmTestSuite);
118124
configureJvmTestSuiteForWhitebox((JvmTestSuite) jvmTestSuite, whiteboxJvmTestSuite);
119125
}
120126
}
121127

128+
private Provider<List<String>> requiresFromModuleInfo(JvmTestSuite jvmTestSuite, Provider<SourceSet> sourcesUnderTest) {
129+
RegularFile moduleInfoFile = project.getLayout().getProjectDirectory().file(whiteboxModuleInfo(jvmTestSuite).getAbsolutePath());
130+
Provider<String> moduleInfoContent = project.getProviders().fileContents(moduleInfoFile).getAsText();
131+
return moduleInfoContent.map(c -> {
132+
ModuleInfoParser moduleInfoParser = new ModuleInfoParser(project.getLayout(), project.getProviders());
133+
String mainModuleName = moduleInfoParser.moduleName(sourcesUnderTest.get().getAllJava().getSrcDirs());
134+
List<String> requires = ModuleInfoRequiresParser.parse(moduleInfoContent.get());
135+
if (requires.stream().anyMatch(r -> r.equals(mainModuleName))) {
136+
return requires.stream().filter(r -> !r.equals(mainModuleName)).collect(Collectors.toList());
137+
}
138+
return Collections.<String>emptyList();
139+
}).orElse(Collections.emptyList());
140+
}
141+
142+
private File whiteboxModuleInfo(JvmTestSuite jvmTestSuite) {
143+
File sourceSetDir = jvmTestSuite.getSources().getJava().getSrcDirs().iterator().next().getParentFile();
144+
return new File(sourceSetDir, "java9/module-info.java");
145+
}
146+
122147
private void configureJvmTestSuiteForBlackbox(JvmTestSuite jvmTestSuite) {
123148
ConfigurationContainer configurations = project.getConfigurations();
124149
TaskContainer tasks = project.getTasks();
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.javamodule.testing.internal;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
23+
public class ModuleInfoRequiresParser {
24+
25+
public static List<String> parse(String moduleInfoFileContent) {
26+
List<String> requires = new ArrayList<>();
27+
boolean insideComment = false;
28+
for(String line: moduleInfoFileContent.split("\n")) {
29+
insideComment = parseLine(line, insideComment, requires);
30+
}
31+
return requires;
32+
}
33+
34+
/**
35+
* @return true, if we are inside a multi-line comment after this line
36+
*/
37+
private static boolean parseLine(String moduleLine, boolean insideComment, List<String> requires) {
38+
if (insideComment) {
39+
return !moduleLine.contains("*/");
40+
}
41+
42+
List<String> tokens = Arrays.asList(moduleLine
43+
.replace(";", "")
44+
.replace("{", "")
45+
.replaceAll("/\\*.*?\\*/", " ")
46+
.trim().split("\\s+"));
47+
int singleLineCommentStartIndex = tokens.indexOf("//");
48+
if (singleLineCommentStartIndex >= 0) {
49+
tokens = tokens.subList(0, singleLineCommentStartIndex);
50+
}
51+
52+
if (tokens.size() > 1 && tokens.get(0).equals("requires")) {
53+
if (tokens.size() > 3 && tokens.contains("static") && tokens.contains("transitive")) {
54+
requires.add(tokens.get(3));
55+
} else if (tokens.size() > 2 && tokens.contains("transitive")) {
56+
requires.add(tokens.get(2));
57+
} else if (tokens.size() > 2 && tokens.contains("static")) {
58+
requires.add(tokens.get(2));
59+
} else {
60+
requires.add(tokens.get(1));
61+
}
62+
}
63+
return moduleLine.lastIndexOf("/*") > moduleLine.lastIndexOf("*/");
64+
}
65+
}

src/test/groovy/org/gradlex/javamodule/testing/test/CustomizationTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ class CustomizationTest extends Specification {
3131
result.task(":app:test").outcome == TaskOutcome.SUCCESS
3232
}
3333

34+
def "can define whitebox test suite requires in module-info file"() {
35+
given:
36+
appModuleInfoFile << '''
37+
module org.example.app {
38+
}
39+
'''
40+
appWhiteboxTestModuleInfoFile << '''
41+
module org.example.app.test {
42+
requires org.example.app;
43+
requires org.junit.jupiter.api;
44+
}
45+
'''
46+
47+
when:
48+
def result = runTests()
49+
50+
then:
51+
result.task(":app:test").outcome == TaskOutcome.SUCCESS
52+
}
53+
3454
def "repetitive blackbox calls on the same test suite have no effect"() {
3555
given:
3656
appBuildFile << '''

src/test/groovy/org/gradlex/javamodule/testing/test/fixture/GradleBuild.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class GradleBuild {
1313
final File appBuildFile
1414
final File appModuleInfoFile
1515
final File appTestModuleInfoFile
16+
final File appWhiteboxTestModuleInfoFile
1617
final File libBuildFile
1718
final File libModuleInfoFile
1819

@@ -24,6 +25,7 @@ class GradleBuild {
2425
this.appBuildFile = file("app/build.gradle.kts")
2526
this.appModuleInfoFile = file("app/src/main/java/module-info.java")
2627
this.appTestModuleInfoFile = file("app/src/test/java/module-info.java")
28+
this.appWhiteboxTestModuleInfoFile = file("app/src/test/java9/module-info.java")
2729
this.libBuildFile = file("lib/build.gradle.kts")
2830
this.libModuleInfoFile = file("lib/src/main/java/module-info.java")
2931

0 commit comments

Comments
 (0)