diff --git a/build.gradle.kts b/build.gradle.kts index 4d019ca..70c906f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -16,11 +16,3 @@ publishingConventions { } testingConventions { testGradleVersions("7.4", "7.6.5", "8.0.2", "8.14.2") } - -// === the following custom configuration should be removed once tests are migrated to Java -apply(plugin = "groovy") - -tasks.named("compileTestGroovy") { targetCompatibility = "11" } // allow tests to run against 6.x - -dependencies { testImplementation("org.spockframework:spock-core:2.3-groovy-4.0") } // -// ==================================================================================== diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index 5eb0fa2..f832d5a 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -1568,133 +1568,3 @@ rXfNOAUjWjYEM11ek3EKUScDr+hyP7IW6pqf6Le1rHjDQt3PT7zL4LogfL3Rt6GP Fu69UuSeTPOL =LH1+ -----END PGP PUBLIC KEY BLOCK----- - -pub BEDE11EAF1164480 -sub 4BE257B370130000 ------BEGIN PGP PUBLIC KEY BLOCK----- - -xsDNBFv1EEwBDAC61jyEM99KH18hI3zlfuqvGoNjTLIh0wge5vXAH8VxMR0ndOID -HYSBT2+L6OeiqKlyhCgF1km48F/dMzyJdTASkNO1Ni+B2Ric1sBxjsSPufkjl4en -yMOl/FuQOB2myht1fCXhlynmOoiRia5J6xzCsCNVGOVYfSru8vpoT9QKcD1OlwoD -WhfyBx/bXsoRvD1CMjQdalcGxv1aJRWfhRumXQwhMPZlFeARAzeDmWNpglqrMnuG -/VADZXZsbLv8VWaequ4wEWiwTOeA6YYElx648OTSv7NjMM7iyPPPWbbUvkVbA3Em -lLBLlGYZTx2nI0B/322SsREcEDwaBzO53GStIzP1XvaRosM/98/Y9ITwB+Oh7ZwZ -dYmmabxN6F5O3v+TNndEW7wgP0lkbsOWZ6YNmFhvoEtd1RxZiSNov5CxokYUrug1 -cS+/vsa9oIecUwxYOG2D1v/pwYhQnr3qasYz4nEEBWHnnkhyr1BbUSuen7w2SiK+ -64cQn6V9aeZYi6cAEQEAAc7AzQRb9RBMAQwA7UCAsQ8KxX8nYO4Sy2pzlh9W5FMP -wGluuokPA2A6g2Fz3vF62RqeaE4HrRQMpijQCsN3JTJVwDid41X84XCMItkdAxMj -mn5zeF/yCcRuHe2Ci/+ae5BzrBaKE/VWRAkaZSZWJ1MoDdpSxJhLHNFnVrwTkM/S -eSNUBk9ZDEC+43b0hciefX9bFlc6XPHgV+yr5ohhwcNcrZ/gbAhhN3/xIVmvKoib -mb+ZIajhiCP1OOH+GpZAPT93w9qZWq3+2gvP4ZZ7bO+8N8Gmz24GL3/0eYI6aMUM -wWGjy5J+iRiFjb6E+Iv/zToyZFWm2VOuOUqy5t4u+Vyk5bl0hATpJICmKa5OFtQw -G5Uvfztk6rujjat90xv8yzsBvoEUqKqzIzjHdN36qop5hLMnBljdLdFY+Rk9CHdF -7MW8Nf0YWbP/3uUk19utGW686Lolt8gvBQc4B5N7VtNoXFCKM/I3ufgnHQvDlf8p -gdJOcyx/a90V/DpUI1ANlwg6IsmFZXbBQw7tABEBAAHCwPwEGAEKACYWIQTjqflQ -eehM4gH3z2C+3hHq8RZEgAUCW/UQTAIbDAUJA8JnAAAKCRC+3hHq8RZEgEy+C/4l -sgrKCmq2Nc7eTdN1AxwMkj28XQFmkqO8orfJm1hAtVK1KRizkX52RNeRN6QX3pX9 -s1e3DjJi3Hpa1UWqeicPA0kKTi2ytUlxR/iZDkaQkLyCCZtWnGHr/eRBdOjblprl -5O+v/tcyrmQGC04TqOntMumuk7JNjZ0QAVkZUxdmfi9bHaF5W5vlcaFYT5gdWpkO -Q0YaWXXw5ynh6Ookjhq0g4pZNjl2rdWWyTC59YIvC9THx0+vuyN7xnSWIb8J1IjE -EYvPqRfpd8s1Vf2AA0JRPjUG2UV8MZqu8k8x4iC2gbdji/vyg/ycdlRT/ULyNprz -1nTLMfhBT0Wmy8B5lFVme3URmld8T90RPln6Dy+c+IKb/79z3FPujuSbipXzx3Qv -GwVYyP80JFn7CJluOl/u8vxi2EVFN6aVqdzwoswFE3+0W0AfbpHUUT4oeBW5OBTJ -5i1Qb0DT6WXk3Y2j1Z08xxhY1RITnc2C33wjXAW0h+qq7/7Yq3w3/7ncv9sWIzU= -=+T4J ------END PGP PUBLIC KEY BLOCK----- - -pub 6A65176A0FB1CD0B -uid Paul King - -sub EA8543C570FAF804 -sub CA890A5FA09CFD80 ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -=g/bF ------END PGP PUBLIC KEY BLOCK----- - -pub 72FEFD1572EB75E1 -uid Spockframework Robot - -sub E95B8FF73F6B84E3 ------BEGIN PGP PUBLIC KEY BLOCK----- - -xsBNBFt1xhcBCADDofPZYtlG1I5NRG67j5Fn8KArIzKK9L3bH3MfJC+XYYPY2N11 -ckiF3JQ0kXH3BPL6r0+dOQm/9SGJYsRVRYYoz2HEf/bzLSxFvKeEdEsWh66IcEzp -xlxV7rXc3oeGCZ+EvobG7JVoRyyLegx/VareBwtBhW39lLGfBjGBzFuXEMVG4H4U -v304N7D4sEIBYWT8c2z5Q5/Iviiyu/VgHxczDn0H6Sl1kXP1vVhWff+OBlbO7JAF -TcW4ET3K/ASWL/CirLbyyVkO8DO5pQyuJqci7pJFWWp/56CpJBmxuyQRc4SEpPjC -ZUOs335j1+MxAJh6z1L/xZHF9OV0GgT8cs67ABEBAAG0M1Nwb2NrZnJhbWV3b3Jr -IFJvYm90IDxkZXZAZm9ydW0uc3BvY2tmcmFtZXdvcmsub3JnPs7ATQRbdcYXAQgA -xh+deGqkK22ydYsUjGkRe8BGN9QQfdY3KfuTIugU9nQBTtXetx2wGD2T0Oz0vLnG -FEVVR3wDOWvLBJ4BD9tJX76JeHf3Eurj3g8ctBMbla7GDwfDbW0p7vT2I7Mlryvo -I7XSyP0l8KfnNaKWo5tzFw2MpyYY7BN+giWbyCJV+10w2B7XMSwlDddNm0w1I0ec -rcCn8ju0gFPH+nYB2AUo4vvejC2pAgFemWZGv0AesSLCri+zHCHfdLf0ex8TcoKZ -VYkQin0JuK00aaidv7lwW1rGEQSGeamZekBu5Bw2yMGA6JZchR5ynuJOQ8ygX98L -MmAHMdXHfghX9cHOLhm5mQARAQABwsB8BBgBCAAmFiEEdulOj/CrWvO2+DZpcv79 -FXLrdeEFAlt1xhcCGwwFCQPCZwAACgkQcv79FXLrdeGvyggAvNhz/LfzuQKaEkSc -xaJ5Ww5xhDilQuPbJNhT2lhFCbEd0EjALxmRvE+WMVm+Cs+yd1Gce5VlS7/BNJWM -7JTRr1QDueirU+nmwj7DPEpJgx3cI9rPFytA8SmeokWJUOZrU0BddNARLNImcKei -B/SeaDiy+BVs1qY5+ZqwGKvPAJuEUOgvTUbYd6EWCYnxGttZtrAa/Dx2kShNOgNz -kLBXkjgSJxFIbaRjrOFvsH/wcSG3Teyv5g/kOXb6stVtoUi9RoyQCEe9JbxeH4pA -j4u9f5+Fcg4SfYIub6ONQy61an6LPty07DADRtYREyn+rmgzopI9KSB2iSZiwhrt -1RHXCQ== -=mM4w ------END PGP PUBLIC KEY BLOCK----- diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6f6be5b..a1d7255 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -129,15 +129,6 @@ - - - - - - - - - diff --git a/src/test/groovy/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.groovy b/src/test/groovy/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.groovy deleted file mode 100644 index 681e6d4..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.groovy +++ /dev/null @@ -1,78 +0,0 @@ -package org.gradlex.javamodule.testing.internal - -import spock.lang.Specification - -class ModuleInfoParseTest extends Specification { - - def "ignores single line comments"() { - given: - def nameFromFile = ModuleInfoParser.moduleName(''' - // module some.thing.else - module some.thing { - requires transitive foo.bar.la; - } - ''') - - expect: - nameFromFile == 'some.thing' - } - - def "ignores single line comments late in line"() { - given: - def nameFromFile = ModuleInfoParser.moduleName(''' - module some.thing { // module some.thing.else - requires transitive foo.bar.la; - } - ''') - - expect: - nameFromFile == 'some.thing' - } - - def "ignores multi line comments"() { - given: - def nameFromFile = ModuleInfoParser.moduleName(''' - /* - module some.thing.else; - */ - module some.thing { - requires static foo.bar.la; - } - ''') - - expect: - nameFromFile == 'some.thing' - } - - def "ignores multi line comments between keywords"() { - given: - def nameFromFile = ModuleInfoParser.moduleName(''' - module /*module some.other*/ some.thing { /* module - odd comment*/ requires transitive foo.bar.la; - requires/* weird comment*/ static foo.bar.lo; - requires /*something to say*/foo.bar.li; /* - requires only.a.comment - */ - } - ''') - - expect: - nameFromFile == 'some.thing' - } - - def "finds module name when open keyword is used"() { - given: - def nameFromFile = ModuleInfoParser.moduleName(''' - open module /*module some.other*/ some.thing { /* module - odd comment*/ requires transitive foo.bar.la; - requires/* weird comment*/ static foo.bar.lo; - requires /*something to say*/foo.bar.li; /* - requires only.a.comment - */ - } - ''') - - expect: - nameFromFile == 'some.thing' - } -} diff --git a/src/test/groovy/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.groovy b/src/test/groovy/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.groovy deleted file mode 100644 index fae2f31..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package org.gradlex.javamodule.testing.test - -import org.gradle.testkit.runner.TaskOutcome -import org.gradlex.javamodule.testing.test.fixture.GradleBuild -import spock.lang.Specification - -class ClasspathSuiteTest extends Specification { - - @Delegate - GradleBuild build = new GradleBuild() - - def "can configure classpath test suite"() { - given: - appBuildFile << ''' - javaModuleTesting.classpath(testing.suites["test"]) - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - - when: - def result = runTests() - - then: - result.output.contains('Main Module: null') - result.output.contains('Test Module: null') - result.task(':app:test').outcome == TaskOutcome.SUCCESS - } -} diff --git a/src/test/groovy/org/gradlex/javamodule/testing/test/CoreFunctionailtyTest.groovy b/src/test/groovy/org/gradlex/javamodule/testing/test/CoreFunctionailtyTest.groovy deleted file mode 100644 index 1149f8f..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/test/CoreFunctionailtyTest.groovy +++ /dev/null @@ -1,40 +0,0 @@ -package org.gradlex.javamodule.testing.test - -import org.gradle.testkit.runner.TaskOutcome -import org.gradlex.javamodule.testing.test.fixture.GradleBuild -import spock.lang.Specification - -class CoreFunctionailtyTest extends Specification { - - @Delegate - GradleBuild build = new GradleBuild() - - def "testCompileOnly extends compileOnly for whitebox test suites"() { - given: - appBuildFile << ''' - javaModuleTesting.classpath(testing.suites["test"]) - dependencies { - compileOnly("jakarta.servlet:jakarta.servlet-api:6.1.0") - } - ''' - file("app/src/main/java/org/example/app/ServletImpl.java") << ''' - package org.example.app; - public abstract class ServletImpl implements jakarta.servlet.Servlet { } - ''' - file("app/src/test/java/org/example/app/test/ServletMock.java") << ''' - package org.example.app.test; - public abstract class ServletMock extends org.example.app.ServletImpl { } - ''' - appModuleInfoFile << ''' - module org.example.app { - requires static jakarta.servlet; - } - ''' - - when: - def result = runner('compileTestJava').build() - - then: - result.task(':app:compileTestJava').outcome == TaskOutcome.SUCCESS - } -} diff --git a/src/test/groovy/org/gradlex/javamodule/testing/test/CustomizationTest.groovy b/src/test/groovy/org/gradlex/javamodule/testing/test/CustomizationTest.groovy deleted file mode 100644 index e4ac0c9..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/test/CustomizationTest.groovy +++ /dev/null @@ -1,172 +0,0 @@ -package org.gradlex.javamodule.testing.test - -import org.gradle.testkit.runner.TaskOutcome -import org.gradlex.javamodule.testing.test.fixture.GradleBuild -import spock.lang.Specification - -class CustomizationTest extends Specification { - - @Delegate - GradleBuild build = new GradleBuild() - - def "can customize whitebox test suites in multiple steps"() { - given: - appBuildFile << ''' - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - } - javaModuleTesting.whitebox(testing.suites["test"]) { - opensTo.add("org.junit.platform.commons") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - - when: - def result = runTests() - - then: - result.output.contains('Main Module: org.example.app') - result.output.contains('Test Module: org.example.app') - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "can define whitebox test suite requires in module-info file"() { - given: - appModuleInfoFile << ''' - module org.example.app { - } - ''' - appWhiteboxTestModuleInfoFile << ''' - module org.example.app.test { - requires org.example.app; - requires org.junit.jupiter.api; - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "can customize whitebox test suites with exportsTo"() { - given: - def mainTest = file("app/src/test/java/org/example/app/test/MainTest.java") - // make test public, so that 'exportsTo org.junit.platform.commons' is sufficient - mainTest.text = mainTest.text.replace('void testApp()' , 'public void testApp()') - - appBuildFile << ''' - javaModuleTesting.classpath(testing.suites["test"]) // reset default setup - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - exportsTo.add("org.junit.platform.commons") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - - when: - def result = runTests() - - then: - result.output.contains('Main Module: org.example.app') - result.output.contains('Test Module: org.example.app') - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - - def "repetitive blackbox calls on the same test suite have no effect"() { - given: - appBuildFile << ''' - javaModuleTesting.blackbox(testing.suites["test"]) - javaModuleTesting.blackbox(testing.suites["test"]) - dependencies { testImplementation(project(path)) } - ''' - appModuleInfoFile << ''' - module org.example.app { - exports org.example.app; - } - ''' - appTestModuleInfoFile << ''' - open module org.example.app.test { - requires org.example.app; - requires org.junit.jupiter.api; - } - ''' - - when: - def result = runTests() - - then: - result.output.contains('Main Module: org.example.app') - result.output.contains('Test Module: org.example.app.test') - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "can use task lock service"() { - given: - appBuildFile.text = 'import org.gradlex.javamodule.testing.TaskLockService\n\n' + appBuildFile.text - appBuildFile << ''' - javaModuleTesting.whitebox(testing.suites.getByName("test") { - targets.all { - testTask { - usesService(gradle.sharedServices.registerIfAbsent(TaskLockService.NAME, TaskLockService::class) { maxParallelUsages.set(1) }) - } - } - }) { - requires.add("org.junit.jupiter.api") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "build does not fail when JUnit has no version and the test folder is empty"() { - given: - appTestModuleInfoFile.parentFile.deleteDir() - appBuildFile << ''' - testing.suites.withType().all { - useJUnitJupiter("") // <- no version, we want to manage that ourselves - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.NO_SOURCE - } - - def "build does not fail when JUnit has no version, the test folder is empty and whitebox was manually configured"() { - given: - appTestModuleInfoFile.parentFile.deleteDir() - appBuildFile << ''' - testing.suites.withType().all { - useJUnitJupiter("") // <- no version, we want to manage that ourselves - } - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.NO_SOURCE - } -} diff --git a/src/test/groovy/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.groovy b/src/test/groovy/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.groovy deleted file mode 100644 index 4f8b420..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.groovy +++ /dev/null @@ -1,135 +0,0 @@ -package org.gradlex.javamodule.testing.test - -import org.gradle.testkit.runner.TaskOutcome -import org.gradlex.javamodule.testing.test.fixture.GradleBuild -import spock.lang.Specification - -class JavaModuleDependenciesBridgeTest extends Specification { - - @Delegate - GradleBuild build = new GradleBuild() - - def setup() { - useJavaModuleDependenciesPlugin() - } - - def "respects moduleNameToGA mappings"() { - given: - appBuildFile << ''' - javaModuleDependencies { - moduleNameToGA.put("org.example.lib", "org.example:lib") - } - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - requires.add("org.example.lib") - opensTo.add("org.junit.platform.commons") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - libModuleInfoFile << ''' - module org.example.lib { - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "respects moduleNamePrefixToGroup mappings"() { - given: - appBuildFile << ''' - javaModuleDependencies { - moduleNamePrefixToGroup.put("org.example.", "org.example") - } - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - requires.add("org.example.lib") - opensTo.add("org.junit.platform.commons") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - } - ''' - libModuleInfoFile << ''' - module org.example.lib { - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } - - def "compiles with provides runtime directives"() { - given: - appBuildFile << ''' - dependencies.constraints { - javaModuleDependencies { - implementation(gav("org.slf4j", "2.0.3")) - implementation(gav("org.slf4j.simple", "2.0.3")) - } - } - javaModuleDependencies { - moduleNameToGA.put("org.example.lib", "org.example:lib") - } - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - requires.add("org.example.lib") - opensTo.add("org.junit.platform.commons") - } - ''' - appModuleInfoFile << ''' - module org.example.app { - requires org.slf4j; - requires /*runtime*/ org.slf4j.simple; - } - ''' - libModuleInfoFile << ''' - module org.example.lib { - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:compileTestJava").outcome == TaskOutcome.SUCCESS - } - - def "can be combined with test-fixtures plugins"() { - given: - useTestFixturesPlugin() - appModuleInfoFile << ''' - module org.example.app { - } - ''' - file("app/src/testFixtures/java/module-info.java") << ''' - open module org.example.app.test.fixtures { - requires org.example.app; - } - ''' - appBuildFile << ''' - javaModuleTesting.whitebox(testing.suites["test"]) { - requires.add("org.junit.jupiter.api") - requires.add("org.example.app.test.fixtures") - } - javaModuleDependencies { - } - ''' - - when: - def result = runTests() - - then: - result.task(":app:test").outcome == TaskOutcome.SUCCESS - } -} diff --git a/src/test/groovy/org/gradlex/javamodule/testing/test/fixture/GradleBuild.groovy b/src/test/groovy/org/gradlex/javamodule/testing/test/fixture/GradleBuild.groovy deleted file mode 100644 index bda9d6e..0000000 --- a/src/test/groovy/org/gradlex/javamodule/testing/test/fixture/GradleBuild.groovy +++ /dev/null @@ -1,145 +0,0 @@ -package org.gradlex.javamodule.testing.test.fixture - -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner - -import java.lang.management.ManagementFactory -import java.nio.file.Files - -class GradleBuild { - - final File projectDir - final File settingsFile - final File appBuildFile - final File appModuleInfoFile - final File appTestModuleInfoFile - final File appWhiteboxTestModuleInfoFile - final File libBuildFile - final File libModuleInfoFile - - final String gradleVersionUnderTest = System.getProperty("gradleVersionUnderTest") - boolean canUseProjectIsolation = gradleVersionUnderTest == null - - GradleBuild(File projectDir = Files.createTempDirectory("gradle-build").toFile()) { - this.projectDir = projectDir - this.settingsFile = file("settings.gradle.kts") - this.appBuildFile = file("app/build.gradle.kts") - this.appModuleInfoFile = file("app/src/main/java/module-info.java") - this.appTestModuleInfoFile = file("app/src/test/java/module-info.java") - this.appWhiteboxTestModuleInfoFile = file("app/src/test/java9/module-info.java") - this.libBuildFile = file("lib/build.gradle.kts") - this.libModuleInfoFile = file("lib/src/main/java/module-info.java") - - def launcherDependency = gradleVersionUnderTest == '7.4' ? - 'testRuntimeOnly("org.junit.platform:junit-platform-launcher")' : '' - - settingsFile << ''' - pluginManagement { - plugins { id("org.gradlex.java-module-dependencies") version "1.8" } - } - dependencyResolutionManagement { repositories.mavenCentral() } - includeBuild(".") - rootProject.name = "test-project" - include("app", "lib") - ''' - appBuildFile << """ - plugins { - id("org.gradlex.java-module-testing") - id("application") - } - group = "org.example" - dependencies { - testImplementation(platform("org.junit:junit-bom:5.9.0")) - $launcherDependency - } - application { - mainModule.set("org.example.app") - mainClass.set("org.example.app.Main") - } - tasks.test { - testLogging.showStandardStreams = true - } - tasks.withType().configureEach { options.release.set(11) } - """ - file("app/src/main/java/org/example/app/Main.java") << ''' - package org.example.app; - - public class Main { - public void main(String... args) { - } - } - ''' - file("app/src/test/java/org/example/app/test/MainTest.java") << ''' - package org.example.app.test; - - import org.junit.jupiter.api.Test; - import org.example.app.Main; - - public class MainTest { - - @Test - void testApp() { - new Main(); - System.out.println("Main Module: " + Main.class.getModule().getName()); - System.out.println("Test Module: " + MainTest.class.getModule().getName()); - } - } - ''' - - libBuildFile << ''' - plugins { - id("org.gradlex.java-module-testing") - id("java-library") - } - group = "org.example" - tasks.withType().configureEach { options.release.set(11) } - ''' - } - - void useJavaModuleDependenciesPlugin() { - canUseProjectIsolation = false // 'java-module-dependencies' not yet fully compatible - appBuildFile.text = appBuildFile.text.replace('plugins {', 'plugins { id("org.gradlex.java-module-dependencies")') - libBuildFile.text = libBuildFile.text.replace('plugins {', 'plugins { id("org.gradlex.java-module-dependencies")') - } - - def useTestFixturesPlugin() { - appBuildFile.text = appBuildFile.text.replace('plugins {', 'plugins { id("java-test-fixtures");') - libBuildFile.text = libBuildFile.text.replace('plugins {', 'plugins { id("java-test-fixtures");') - } - - File file(String path) { - new File(projectDir, path).tap { - it.getParentFile().mkdirs() - } - } - - BuildResult build() { - runner('build').build() - } - - BuildResult run() { - runner('run').build() - } - - BuildResult runTests() { - runner(':app:test').build() - } - - BuildResult fail() { - runner('build').buildAndFail() - } - - GradleRunner runner(String... args) { - List latestFeaturesArgs = canUseProjectIsolation ? [ - '-Dorg.gradle.unsafe.isolated-projects=true' - ] : [] - GradleRunner.create() - .forwardOutput() - .withPluginClasspath() - .withProjectDir(projectDir) - .withArguments(Arrays.asList(args) + latestFeaturesArgs + '-s' + '--configuration-cache') - .withDebug(ManagementFactory.getRuntimeMXBean().getInputArguments().toString().contains("-agentlib:jdwp")).with { - gradleVersionUnderTest ? it.withGradleVersion(gradleVersionUnderTest) : it - } - } -} diff --git a/src/test/java/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.java b/src/test/java/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.java new file mode 100644 index 0000000..173f7ea --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/internal/ModuleInfoParseTest.java @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.internal; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ModuleInfoParseTest { + + @Test + void ignores_single_line_comments() { + var nameFromFile = ModuleInfoParser.moduleName( + """ + // module some.thing.else + module some.thing { + requires transitive foo.bar.la; + } + """); + + assertThat(nameFromFile).isEqualTo("some.thing"); + } + + @Test + void ignores_single_line_comments_late_in_line() { + var nameFromFile = ModuleInfoParser.moduleName( + """ + module some.thing { // module some.thing.else + requires transitive foo.bar.la; + } + """); + + assertThat(nameFromFile).isEqualTo("some.thing"); + } + + @Test + void ignores_multi_line_comments() { + var nameFromFile = ModuleInfoParser.moduleName( + """ + /* + module some.thing.else; + */ + module some.thing { + requires static foo.bar.la; + } + """); + + assertThat(nameFromFile).isEqualTo("some.thing"); + } + + @Test + void ignores_multi_line_comments_between_keywords() { + var nameFromFile = ModuleInfoParser.moduleName( + """ + module /*module some.other*/ some.thing { /* module + odd comment*/ requires transitive foo.bar.la; + requires/* weird comment*/ static foo.bar.lo; + requires /*something to say*/foo.bar.li; /* + requires only.a.comment + */ + } + """); + + assertThat(nameFromFile).isEqualTo("some.thing"); + } + + @Test + void finds_module_name_when_open_keyword_is_used() { + var nameFromFile = ModuleInfoParser.moduleName( + """ + open module /*module some.other*/ some.thing { /* module + odd comment*/ requires transitive foo.bar.la; + requires/* weird comment*/ static foo.bar.lo; + requires /*something to say*/foo.bar.li; /* + requires only.a.comment + */ + } + """); + + assertThat(nameFromFile).isEqualTo("some.thing"); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.java b/src/test/java/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.java new file mode 100644 index 0000000..8dfa3b0 --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/ClasspathSuiteTest.java @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.gradle.testkit.runner.TaskOutcome; +import org.gradlex.javamodule.testing.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + +class ClasspathSuiteTest { + + GradleBuild build = new GradleBuild(); + + @Test + void can_configure_classpath_test_suite() { + build.appBuildFile.appendText( + """ + javaModuleTesting.classpath(testing.suites["test"]) + """); + build.appModuleInfoFile.appendText(""" + module org.example.app { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(result.getOutput()).contains("Main Module: null"); + assertThat(result.getOutput()).contains("Test Module: null"); + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/CoreFunctionalityTest.java b/src/test/java/org/gradlex/javamodule/testing/test/CoreFunctionalityTest.java new file mode 100644 index 0000000..3d7a1e9 --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/CoreFunctionalityTest.java @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.gradle.testkit.runner.TaskOutcome; +import org.gradlex.javamodule.testing.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + +class CoreFunctionalityTest { + + GradleBuild build = new GradleBuild(); + + @Test + void testCompileOnly_extends_compileOnly_for_whitebox_test_suites() { + build.appBuildFile.appendText( + """ + javaModuleTesting.classpath(testing.suites["test"]) + 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); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/CustomizationTest.java b/src/test/java/org/gradlex/javamodule/testing/test/CustomizationTest.java new file mode 100644 index 0000000..0cc7b94 --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/CustomizationTest.java @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.gradle.testkit.runner.TaskOutcome; +import org.gradlex.javamodule.testing.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + +class CustomizationTest { + + GradleBuild build = new GradleBuild(); + + @Test + void can_customize_whitebox_test_suites_in_multiple_steps() { + build.appBuildFile.appendText( + """ + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + } + javaModuleTesting.whitebox(testing.suites["test"]) { + opensTo.add("org.junit.platform.commons") + } + """); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(result.getOutput()).contains("Main Module: org.example.app"); + assertThat(result.getOutput()).contains("Test Module: org.example.app"); + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void can_define_whitebox_test_suite_requires_in_moduleinfo_file() { + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + build.appWhiteboxTestModuleInfoFile.writeText( + """ + module org.example.app.test { + requires org.example.app; + requires org.junit.jupiter.api; + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void can_customize_whitebox_test_suites_with_exportsTo() { + var mainTest = build.file("app/src/test/java/org/example/app/test/MainTest.java"); + // make test public, so that 'exportsTo org.junit.platform.commons' is sufficient + mainTest.writeText(mainTest.text().replace("void testApp()", "public void testApp()")); + + build.appBuildFile.appendText( + """ + javaModuleTesting.classpath(testing.suites["test"]) // reset default setup + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + exportsTo.add("org.junit.platform.commons") + } + """); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(result.getOutput()).contains("Main Module: org.example.app"); + assertThat(result.getOutput()).contains("Test Module: org.example.app"); + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void repetitive_blackbox_calls_on_the_same_test_suite_have_no_effect() { + build.appBuildFile.appendText( + """ + javaModuleTesting.blackbox(testing.suites["test"]) + javaModuleTesting.blackbox(testing.suites["test"]) + dependencies { testImplementation(project(path)) } + """); + build.appModuleInfoFile.writeText( + """ + module org.example.app { + exports org.example.app; + } + """); + build.appTestModuleInfoFile.writeText( + """ + open module org.example.app.test { + requires org.example.app; + requires org.junit.jupiter.api; + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(result.getOutput()).contains("Main Module: org.example.app"); + assertThat(result.getOutput()).contains("Test Module: org.example.app"); + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void can_use_task_lock_service() { + build.appBuildFile.writeText( + "import org.gradlex.javamodule.testing.TaskLockService\n\n" + build.appBuildFile.text()); + build.appBuildFile.appendText( + """ + javaModuleTesting.whitebox(testing.suites.getByName("test") { + targets.all { + testTask { + usesService(gradle.sharedServices.registerIfAbsent(TaskLockService.NAME, TaskLockService::class) { maxParallelUsages.set(1) }) + } + } + }) { + requires.add("org.junit.jupiter.api") + } + """); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void build_does_not_fail_when_JUnit_has_no_version_and_the_test_folder_is_empty() { + build.appTestModuleInfoFile.parent().delete(); + build.appBuildFile.appendText( + """ + testing.suites.withType().all { + useJUnitJupiter("") // <- no version, we want to manage that ourselves + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.NO_SOURCE); + } + + @Test + void + build_does_not_fail_when_JUnit_has_no_version_and_the_test_folder_is_empty_and_whitebox_was_manually_configured() { + build.appTestModuleInfoFile.parent().delete(); + build.appBuildFile.appendText( + """ + testing.suites.withType().all { + useJUnitJupiter("") // <- no version, we want to manage that ourselves + } + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.NO_SOURCE); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.java b/src/test/java/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.java new file mode 100644 index 0000000..d8f96bb --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/JavaModuleDependenciesBridgeTest.java @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.gradle.testkit.runner.TaskOutcome; +import org.gradlex.javamodule.testing.test.fixture.GradleBuild; +import org.junit.jupiter.api.Test; + +class JavaModuleDependenciesBridgeTest { + + GradleBuild build = new GradleBuild().useJavaModuleDependenciesPlugin(); + + @Test + void respects_moduleNameToGA_mappings() { + build.appBuildFile.appendText( + """ + javaModuleDependencies { + moduleNameToGA.put("org.example.lib", "org.example:lib") + } + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + requires.add("org.example.lib") + opensTo.add("org.junit.platform.commons") + } + """); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + build.libModuleInfoFile.writeText(""" + module org.example.lib { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void respects_moduleNamePrefixToGroup_mappings() { + build.appBuildFile.appendText( + """ + javaModuleDependencies { + moduleNamePrefixToGroup.put("org.example.", "org.example") + } + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + requires.add("org.example.lib") + opensTo.add("org.junit.platform.commons") + } + """); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + build.libModuleInfoFile.writeText(""" + module org.example.lib { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void compiles_with_provides_runtime_directives() { + build.appBuildFile.appendText( + """ + dependencies.constraints { + javaModuleDependencies { + implementation(gav("org.slf4j", "2.0.3")) + implementation(gav("org.slf4j.simple", "2.0.3")) + } + } + javaModuleDependencies { + moduleNameToGA.put("org.example.lib", "org.example:lib") + } + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + requires.add("org.example.lib") + opensTo.add("org.junit.platform.commons") + } + """); + build.appModuleInfoFile.writeText( + """ + module org.example.app { + requires org.slf4j; + requires /*runtime*/ org.slf4j.simple; + } + """); + build.libModuleInfoFile.writeText(""" + module org.example.lib { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } + + @Test + void can_be_combined_with_testfixtures_plugin() { + build.useTestFixturesPlugin(); + build.appModuleInfoFile.writeText(""" + module org.example.app { + } + """); + build.file("app/src/testFixtures/java/module-info.java") + .writeText( + """ + open module org.example.app.test.fixtures { + requires org.example.app; + } + """); + build.appBuildFile.appendText( + """ + javaModuleTesting.whitebox(testing.suites["test"]) { + requires.add("org.junit.jupiter.api") + requires.add("org.example.app.test.fixtures") + } + javaModuleDependencies { + } + """); + + var result = build.runTests(); + var testResult = result.task(":app:test"); + + assertThat(testResult).isNotNull(); + assertThat(testResult.getOutcome()).isEqualTo(TaskOutcome.SUCCESS); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/fixture/Directory.java b/src/test/java/org/gradlex/javamodule/testing/test/fixture/Directory.java new file mode 100644 index 0000000..d5b583f --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/fixture/Directory.java @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test.fixture; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Comparator; +import java.util.stream.Stream; + +public class Directory { + + private final Path directory; + + Directory(Path directory) { + this.directory = directory; + Io.unchecked(() -> Files.createDirectories(directory)); + } + + public WritableFile file(String path) { + Path file = directory.resolve(path); + Io.unchecked(() -> Files.createDirectories(file.getParent())); + return new WritableFile(file); + } + + public Directory dir(String path) { + Path dir = directory.resolve(path); + return new Directory(dir); + } + + public void delete() { + try (Stream walk = Files.walk(directory)) { + walk.sorted(Comparator.reverseOrder()).forEach(p -> Io.unchecked(p, Files::delete)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public Path getAsPath() { + return directory; + } + + public String canonicalPath() { + return Io.unchecked(() -> getAsPath().toFile().getCanonicalPath()); + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/fixture/GradleBuild.java b/src/test/java/org/gradlex/javamodule/testing/test/fixture/GradleBuild.java new file mode 100644 index 0000000..76d85a1 --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/fixture/GradleBuild.java @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test.fixture; + +import static java.util.function.Function.identity; + +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.GradleRunner; + +public class GradleBuild { + public final Directory projectDir; + public final WritableFile settingsFile; + public final WritableFile appBuildFile; + public final WritableFile appModuleInfoFile; + public final WritableFile appTestModuleInfoFile; + public final WritableFile appWhiteboxTestModuleInfoFile; + public final WritableFile libBuildFile; + public final WritableFile libModuleInfoFile; + + public static final String GRADLE_VERSION_UNDER_TEST = System.getProperty("gradleVersionUnderTest"); + + public GradleBuild() { + this(createBuildTmpDir()); + } + + public GradleBuild(Path dir) { + this.projectDir = new Directory(dir); + this.settingsFile = new WritableFile(projectDir, "settings.gradle.kts"); + this.appBuildFile = new WritableFile(projectDir.dir("app"), "build.gradle.kts"); + this.libBuildFile = new WritableFile(projectDir.dir("lib"), "build.gradle.kts"); + this.appModuleInfoFile = new WritableFile(projectDir.dir("app/src/main/java"), "module-info.java"); + this.libModuleInfoFile = new WritableFile(projectDir.dir("lib/src/main/java"), "module-info.java"); + this.appTestModuleInfoFile = new WritableFile(projectDir, "app/src/test/java/module-info.java"); + this.appWhiteboxTestModuleInfoFile = new WritableFile(projectDir, "app/src/test/java9/module-info.java"); + + var launcherDependency = Objects.equals(GRADLE_VERSION_UNDER_TEST, "7.4") + ? "testRuntimeOnly(\"org.junit.platform:junit-platform-launcher\")" + : ""; + + settingsFile.writeText( + """ + pluginManagement { + plugins { id("org.gradlex.java-module-dependencies") version "1.10" } + } + dependencyResolutionManagement { repositories.mavenCentral() } + includeBuild(".") + rootProject.name = "test-project" + include("app", "lib") + """); + appBuildFile.writeText( + """ + plugins { + id("org.gradlex.java-module-testing") + id("application") + } + group = "org.example" + dependencies { + testImplementation(platform("org.junit:junit-bom:5.9.0")) + %s + } + application { + mainModule.set("org.example.app") + mainClass.set("org.example.app.Main") + } + tasks.test { + testLogging.showStandardStreams = true + } + tasks.withType().configureEach { options.release.set(11) } + """ + .formatted(launcherDependency)); + file("app/src/main/java/org/example/app/Main.java") + .writeText( + """ + package org.example.app; + + public class Main { + public void main(String... args) { + } + } + """); + file("app/src/test/java/org/example/app/test/MainTest.java") + .writeText( + """ + package org.example.app.test; + + import org.junit.jupiter.api.Test; + import org.example.app.Main; + + public class MainTest { + + @Test + void testApp() { + new Main(); + System.out.println("Main Module: " + Main.class.getModule().getName()); + System.out.println("Test Module: " + MainTest.class.getModule().getName()); + } + } + """); + + libBuildFile.writeText( + """ + plugins { + id("org.gradlex.java-module-testing") + id("java-library") + } + group = "org.example" + tasks.withType().configureEach { options.release.set(11) } + """); + } + + public GradleBuild useJavaModuleDependenciesPlugin() { + appBuildFile.writeText( + appBuildFile.text().replace("plugins {", "plugins { id(\"org.gradlex.java-module-dependencies\")")); + libBuildFile.writeText( + libBuildFile.text().replace("plugins {", "plugins { id(\"org.gradlex.java-module-dependencies\")")); + return this; + } + + public GradleBuild useTestFixturesPlugin() { + appBuildFile.writeText(appBuildFile.text().replace("plugins {", "plugins { id(\"java-test-fixtures\");")); + libBuildFile.writeText(libBuildFile.text().replace("plugins {", "plugins { id(\"java-test-fixtures\");")); + return this; + } + + public WritableFile file(String path) { + return new WritableFile(projectDir, path); + } + + public BuildResult build() { + return runner("build").build(); + } + + public BuildResult run() { + return runner("run").build(); + } + + public BuildResult runTests() { + return runner(":app:test").build(); + } + + public BuildResult fail() { + return runner("build").buildAndFail(); + } + + public GradleRunner runner(String... args) { + return runner(true, args); + } + + public GradleRunner runner(boolean projectIsolation, String... args) { + boolean debugMode = ManagementFactory.getRuntimeMXBean() + .getInputArguments() + .toString() + .contains("-agentlib:jdwp"); + List latestFeaturesArgs = GRADLE_VERSION_UNDER_TEST != null || !projectIsolation + ? List.of() + : List.of( + "--configuration-cache", + "-Dorg.gradle.unsafe.isolated-projects=true", + // "getGroup" in "JavaModuleDependenciesExtension.create" + "--configuration-cache-problems=warn", + "-Dorg.gradle.configuration-cache.max-problems=4"); + Stream standardArgs = Stream.of("-s", "--warning-mode=fail"); + GradleRunner runner = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withDebug(debugMode) + .withProjectDir(projectDir.getAsPath().toFile()) + .withArguments(Stream.of(Arrays.stream(args), latestFeaturesArgs.stream(), standardArgs) + .flatMap(identity()) + .collect(Collectors.toList())); + if (GRADLE_VERSION_UNDER_TEST != null) { + runner.withGradleVersion(GRADLE_VERSION_UNDER_TEST); + } + return runner; + } + + private static Path createBuildTmpDir() { + try { + return Files.createTempDirectory("gradle-build"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/fixture/Io.java b/src/test/java/org/gradlex/javamodule/testing/test/fixture/Io.java new file mode 100644 index 0000000..afd2e09 --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/fixture/Io.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test.fixture; + +import java.io.IOException; +import java.io.UncheckedIOException; + +class Io { + + private Io() {} + + static T unchecked(IoSupplier supplier) { + try { + return supplier.get(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + static void unchecked(T t, IoConsumer consumer) { + try { + consumer.accept(t); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @FunctionalInterface + interface IoSupplier { + T get() throws IOException; + } + + @FunctionalInterface + interface IoConsumer { + void accept(T t) throws IOException; + } +} diff --git a/src/test/java/org/gradlex/javamodule/testing/test/fixture/WritableFile.java b/src/test/java/org/gradlex/javamodule/testing/test/fixture/WritableFile.java new file mode 100644 index 0000000..292885c --- /dev/null +++ b/src/test/java/org/gradlex/javamodule/testing/test/fixture/WritableFile.java @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 +package org.gradlex.javamodule.testing.test.fixture; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + +public class WritableFile { + + private final Path file; + + public WritableFile(Path file) { + this.file = file; + } + + public WritableFile(Directory parent, String filePath) { + this.file = Io.unchecked(() -> Files.createDirectories( + parent.getAsPath().resolve(filePath).getParent())) + .resolve(Path.of(filePath).getFileName()); + } + + public WritableFile writeText(String text) { + Io.unchecked(() -> Files.writeString( + file, text, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)); + return this; + } + + public WritableFile appendText(String text) { + Io.unchecked(() -> Files.writeString(file, text, StandardOpenOption.CREATE, StandardOpenOption.APPEND)); + return this; + } + + public WritableFile delete() { + Io.unchecked(file, Files::delete); + return this; + } + + public Directory parent() { + return new Directory(file.getParent()); + } + + public boolean exists() { + return Files.exists(file); + } + + public Path getAsPath() { + return file; + } + + public String text() { + return Io.unchecked(() -> Files.readString(file)); + } +}