diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a7fce5a79..962ebc7c0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,7 +30,7 @@ on: jobs: publish: name: Publish to Maven repositories - uses: SpineEventEngine/config/.github/workflows/publishing.yml@5ddff23286f7f6353d4714b0399ab89109c56c79 + uses: SpineEventEngine/config/.github/workflows/publishing.yml@3cc29d4e51eb6de7da4234d2677973cead2cea84 permissions: contents: read packages: write diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt index b75c0f1ef..385a12179 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt @@ -60,7 +60,7 @@ object Compiler : Dependency() { * The version of the Compiler dependencies. */ override val version: String - private const val fallbackVersion = "2.0.0-SNAPSHOT.081" + private const val fallbackVersion = "2.0.0-SNAPSHOT.083" /** * The distinct version of the Compiler used by other build tools. @@ -69,7 +69,7 @@ object Compiler : Dependency() { * transitive dependencies, this is the version used to build the project itself. */ val dogfoodingVersion: String - private const val fallbackDfVersion = "2.0.0-SNAPSHOT.081" + private const val fallbackDfVersion = "2.0.0-SNAPSHOT.083" /** * The artifact for the Compiler Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt index 94f3d9d68..597049f49 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt @@ -30,7 +30,7 @@ typealias CoreJava = CoreJvm ) object CoreJvm { const val group = Spine.group - const val version = "2.0.0-SNAPSHOT.577" + const val version = "2.0.0-SNAPSHOT.611" const val coreArtifact = "spine-core" const val clientArtifact = "spine-client" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 8fc131eaa..ef58eb0a6 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -34,12 +34,12 @@ object CoreJvmCompiler { /** * The version used in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.099" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.104" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.099" + const val version = "2.0.0-SNAPSHOT.104" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt index b9e6f18ec..a7b277c26 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/ProtoTap.kt @@ -26,7 +26,7 @@ package io.spine.dependency.local ) object ProtoTap { const val group = Spine.toolsGroup - const val version = "0.17.4" + const val version = "0.17.5" const val gradlePluginId = "io.spine.prototap" const val api = "$group:prototap-api:$version" const val gradlePlugin = "$group:prototap-gradle-plugin:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt index 7bd22c472..52b0f2a58 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt @@ -32,8 +32,8 @@ package io.spine.dependency.local ) object ToolBase { const val group = Spine.toolsGroup - const val version = "2.0.0-SNAPSHOT.428" - const val dogfoodingVersion = "2.0.0-SNAPSHOT.428" + const val version = "2.0.0-SNAPSHOT.451" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.451" const val archive = "$group:archive:$version" const val code = "$group:code:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 1be9aff47..1b4d5cf49 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -27,7 +27,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.469" + const val version = "2.0.0-SNAPSHOT.470" const val group = Spine.toolsGroup private const val prefix = "validation" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitCoverage.kt b/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitCoverage.kt index 52f663236..96c0753e3 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitCoverage.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitCoverage.kt @@ -35,8 +35,8 @@ import org.gradle.kotlin.dsl.withType * dedicated [AGENT_CONFIGURATION] configuration. * 2. Passes the agent JAR path and a per-module exec directory * (`build/`[TESTKIT_COVERAGE_DIR]) to the test JVM as system properties. - * The `plugin-testlib` harness reads these and writes a `gradle.properties` - * into the worker's Gradle user home that adds `-javaagent:…` to the worker JVM. + * The `plugin-testlib` harness reads these and attaches `-javaagent:…` to the + * worker JVM. * 3. Wipes the exec directory at most once per build invocation, from the * `doFirst` of the first `Test` task that actually executes, so stale worker * coverage from a previous run does not accumulate. Two failure modes are @@ -58,6 +58,11 @@ import org.gradle.kotlin.dsl.withType * coverage. An up-to-date (non-cache) run is unaffected: the previous run's * files remain on disk and the guarded `doFirst` never deletes them. * + * The method does not choose the TestKit directory of the worker builds. + * The `module-testing` and `kmp-module` script plugins point the `Test` tasks + * of a module at a shared one (see [useSharedTestKitDir]), which a harness + * that passes its own directory to `withTestKitDir(...)` does not use. + * * The produced `.exec` files are merged into the Kover reports by * [io.spine.gradle.report.coverage.KoverConfig]. The agent emits binary * execution data rather than an XML report because that is its only file output, diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitDir.kt b/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitDir.kt new file mode 100644 index 000000000..d9c60ef97 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/testing/TestKitDir.kt @@ -0,0 +1,84 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.testing + +import org.gradle.api.Project +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.withType + +/** + * Configures the `Test` tasks of this project so that the Gradle TestKit builds + * they run use the TestKit directory [shared by all modules][SHARED_TESTKIT_DIR]. + * + * A TestKit directory is the Gradle user home of the builds that + * [GradleRunner][org.gradle.testkit.runner.GradleRunner] runs. It holds mostly + * Gradle's instrumented copies of the JARs on the classpath of the plugin under + * test, and the compiled build scripts of the test projects. When the directory + * is empty, the TestKit builds recreate them, which is slow on a CI runner. + * + * Without the [`org.gradle.testkit.dir`][TESTKIT_DIR_PROPERTY] system property, + * TestKit keeps the directory at `build/tmp//work/.gradle-test-kit`, in + * the temporary directory of the `Test` task. So each `Test` task of each module + * has its own, which is empty in a fresh checkout and after `clean`. This + * function passes the shared directory to the test JVM as that property, which + * `GradleRunner.create()` reads. A runner given a directory with + * `withTestKitDir(...)` still uses that one. + * + * The property is set from a `doFirst` action rather than at configuration time, + * so it is not a task input. Where TestKit builds keep their caches does not + * affect the test results, while the absolute path differs between machines. + * As an input, it would only break the reuse of the `Test` task results from + * the build cache across machines. A `Test` task that already has the property + * keeps its value, so a repeated call has no further effect. + * + * The function applies to all the `Test` tasks of the project, whether they + * run TestKit builds or not, and whether the project collects the coverage of + * those builds or not (see [enableTestKitCoverage]). + */ +fun Project.useSharedTestKitDir() { + val testKitDir = gradle.gradleUserHomeDir.resolve(SHARED_TESTKIT_DIR) + tasks.withType().configureEach { + doFirst { + if (TESTKIT_DIR_PROPERTY !in systemProperties) { + systemProperty(TESTKIT_DIR_PROPERTY, testKitDir.absolutePath) + } + } + } +} + +/** + * The name of the system property carrying the absolute path to the TestKit + * directory, which `GradleRunner.create()` reads. + * + * A runner given a directory with `withTestKitDir(...)` uses that one instead. + * + * @see org.gradle.testkit.runner.GradleRunner.withTestKitDir + */ +private const val TESTKIT_DIR_PROPERTY: String = "org.gradle.testkit.dir" + +/** + * The path, relative to the Gradle user home of the build, of the TestKit + * directory shared by the TestKit builds of all modules. + * + * The directory is placed under `caches/`, which `gradle/actions/setup-gradle` + * restores on every run but saves only on the default branch. So a pull request + * run starts with a warm directory once a run on the default branch has saved it. + * Locally, the TestKit builds of all the repositories that use this `buildSrc` + * share it. + * + * Unlike a directory under `build/`, it is not reset by `clean`. Delete it to make + * the TestKit builds start from an empty one. + */ +private const val SHARED_TESTKIT_DIR: String = "caches/spine-testkit" diff --git a/buildSrc/src/main/kotlin/kmp-module.gradle.kts b/buildSrc/src/main/kotlin/kmp-module.gradle.kts index 0e84f04ac..0574f09dc 100644 --- a/buildSrc/src/main/kotlin/kmp-module.gradle.kts +++ b/buildSrc/src/main/kotlin/kmp-module.gradle.kts @@ -27,6 +27,7 @@ import io.spine.gradle.kotlin.setFreeCompilerArgs import io.spine.gradle.publish.IncrementGuard import io.spine.gradle.report.license.LicenseReporter import io.spine.gradle.testing.configureLogging +import io.spine.gradle.testing.useSharedTestKitDir /** * Configures this [Project] as a Kotlin Multiplatform module. @@ -167,6 +168,12 @@ tasks { } } +/** + * Gives all the `Test` tasks the TestKit directory shared by all modules, + * as `module-testing` does (see [useSharedTestKitDir]). + */ +project.useSharedTestKitDir() + /** * Overrides the default location of Kotlin sources. * diff --git a/buildSrc/src/main/kotlin/module-testing.gradle.kts b/buildSrc/src/main/kotlin/module-testing.gradle.kts index add017838..ec256e1cf 100644 --- a/buildSrc/src/main/kotlin/module-testing.gradle.kts +++ b/buildSrc/src/main/kotlin/module-testing.gradle.kts @@ -20,10 +20,14 @@ import io.spine.dependency.test.Kotest import io.spine.dependency.test.Truth import io.spine.gradle.testing.configureLogging import io.spine.gradle.testing.registerTestTasks +import io.spine.gradle.testing.useSharedTestKitDir /** * This convention plugin applies test dependencies and configures test-related tasks. * + * All the `Test` tasks of the module get the TestKit directory shared by all + * modules (see [useSharedTestKitDir]). + * * The version of the [JUnit] platform must be applied via the [BomsPlugin][io.spine.dependency.boms.BomsPlugin]: * * ```kotlin @@ -40,6 +44,7 @@ plugins { project.run { setupTests() forceTestDependencies() + useSharedTestKitDir() } dependencies { diff --git a/config b/config index 5ddff2328..3cc29d4e5 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit 5ddff23286f7f6353d4714b0399ab89109c56c79 +Subproject commit 3cc29d4e51eb6de7da4234d2677973cead2cea84 diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index ceba616e9..cd7ba025f 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:archive:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:archive:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -832,14 +832,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:code:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:code:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -1655,14 +1655,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:fixtures:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:fixtures:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.android. **Name** : annotations. **Version** : 4.1.1.4. @@ -2593,14 +2593,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:fs:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:fs:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -3416,14 +3416,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:gradle-plugin-api:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.22. @@ -4345,14 +4345,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:gradle-plugin-api-test-fixtures:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-annotations. **Version** : 2.22. @@ -4865,14 +4865,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:gradle-root-plugin:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -5736,14 +5736,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:intellij-platform:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -6840,14 +6840,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:47 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:intellij-platform-java:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -8719,14 +8719,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:37 WET 2026** using +This report was generated on **Tue Nov 03 21:49:47 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:java-code:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:java-code:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9596,14 +9596,14 @@ This report was generated on **Tue Nov 03 02:04:37 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:jvm-tool-plugins:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10459,14 +10459,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:jvm-tools:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 26.1.0. @@ -11233,14 +11233,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:kotlin-code:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:kotlin-code:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -12056,14 +12056,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:plugin-base:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -12927,14 +12927,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:plugin-testlib:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.auto.value. **Name** : auto-value-annotations. **Version** : 1.11.1. @@ -13901,14 +13901,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:proto-code:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:proto-code:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -14794,14 +14794,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:36 WET 2026** using +This report was generated on **Tue Nov 03 21:49:46 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:protobuf-setup-plugins:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -15677,14 +15677,14 @@ This report was generated on **Tue Nov 03 02:04:36 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:37 WET 2026** using +This report was generated on **Tue Nov 03 21:49:47 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:psi:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -16812,14 +16812,14 @@ This report was generated on **Tue Nov 03 02:04:37 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:37 WET 2026** using +This report was generated on **Tue Nov 03 21:49:47 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.451` +# Dependencies of `io.spine.tools:psi-java:2.0.0-SNAPSHOT.460` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -18738,6 +18738,6 @@ This report was generated on **Tue Nov 03 02:04:37 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Nov 03 02:04:37 WET 2026** using +This report was generated on **Tue Nov 03 21:49:47 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index bbb31acf6..3dc21609b 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools tool-base -2.0.0-SNAPSHOT.451 +2.0.0-SNAPSHOT.460 2015 diff --git a/plugin-testlib/build.gradle.kts b/plugin-testlib/build.gradle.kts index 607796b5e..db1e1d329 100644 --- a/plugin-testlib/build.gradle.kts +++ b/plugin-testlib/build.gradle.kts @@ -1,36 +1,29 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ import io.spine.dependency.local.TestLib import io.spine.dependency.test.JUnit +import io.spine.gradle.testing.enableTestKitCoverage plugins { module } +// Credit coverage of the code executed in TestKit worker JVMs by the +// `GradleProjectSpec` tests. See `io.spine.gradle.testing.enableTestKitCoverage`. +enableTestKitCoverage() + dependencies { api(gradleApi()) api(gradleTestKit()) diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/Gradle.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/Gradle.kt index a36003572..9f09c747b 100644 --- a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/Gradle.kt +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/Gradle.kt @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.gradle.testing @@ -94,8 +82,7 @@ public fun runGradleBuild( val runner = GradleRunner.create() .withProjectDir(projectDir) .withPluginClasspath() - .withArguments(arguments) - runner.enableTestKitCoverage() + .withArguments(arguments.withTestKitCoverage()) stdOutput?.let { runner.forwardStdOutput(it) } diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProject.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProject.kt index ff007dc85..730a25e17 100644 --- a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProject.kt +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProject.kt @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.gradle.testing @@ -48,23 +36,25 @@ import org.gradle.testkit.runner.UnexpectedBuildFailure * Plugin code exercised through Gradle TestKit runs in a separate worker JVM, which * JaCoCo/Kover do not instrument by default, so that out-of-process execution is * otherwise not credited to coverage. This library attaches the JaCoCo agent to the - * worker JVM automatically — for projects run via this class as well as via - * [runGradleBuild] — but only when the test JVM is started with two system - * properties, which the consuming build is responsible for setting: + * worker JVM automatically — for builds started by [executeTask] and [executeAndFail] + * of this class as well as by [runGradleBuild] — but only when the test JVM is started + * with two system properties, which the consuming build is responsible for setting: * - * - `io.spine.tools.gradle.testkit.coverage.agent` — the absolute path to the - * standalone JaCoCo agent JAR, that is, the artifact - * `org.jacoco:org.jacoco.agent::runtime`. - * - `io.spine.tools.gradle.testkit.coverage.execDir` — the directory into which the - * worker should write its execution data. The harness creates a single - * per-module `testkit.exec` file inside it. + * - [TestKitCoverage.AGENT_PROPERTY] — the absolute path to the standalone JaCoCo + * agent JAR, that is, the artifact `org.jacoco:org.jacoco.agent::runtime`. + * - [TestKitCoverage.EXEC_DIR_PROPERTY] — the directory into which the worker should + * write its execution data. The harness creates a single per-module `testkit.exec` + * file inside it. * - * When the properties are absent the harness is a no-op, so ordinary runs and - * consumers that do not opt in are unaffected. + * When either of these properties is absent, the harness is a no-op, so ordinary + * runs and consumers that do not opt in are unaffected. A build started from + * [runner] directly gets no agent. * * The snippet below is a self-contained Gradle (Kotlin DSL) setup that a module * running TestKit-based tests can copy into its `build.gradle.kts`. Pin the agent - * to the same JaCoCo version your coverage tooling uses. + * to the same JaCoCo version your coverage tooling uses. The snippet spells out the + * property names held by [TestKitCoverage], because a build script does not see + * the classes of this library. * * ```kotlin * import java.util.concurrent.atomic.AtomicBoolean @@ -116,6 +106,16 @@ import org.gradle.testkit.runner.UnexpectedBuildFailure * `config`'s `buildSrc`, and the produced files are merged into Kover reports by * `KoverConfig`. Consumers outside that setup can use the snippet above instead. * + * ## TestKit directory + * + * The TestKit directory is the Gradle user home of the worker builds. A project set + * up with [GradleProjectSetup.withSharedTestKitDirectory] uses the directory given + * there, or `caches/spine-testkit` under the Gradle user home if given none. Other + * builds started by this class and by [runGradleBuild] use the directory TestKit + * picks: the one named by Gradle's `org.gradle.testkit.dir` system property of the + * test JVM, or one under the temporary directory of the `Test` task without it. + * This choice is independent of the coverage setup above. + * * @see GradleProjectSetup */ public class GradleProject internal constructor(setup: GradleProjectSetup) { @@ -139,20 +139,17 @@ public class GradleProject internal constructor(setup: GradleProjectSetup) { /** * The runner for executing tasks. + * + * A build started from the runner directly, rather than by [executeTask] or + * [executeAndFail], runs without the TestKit coverage agent. */ public val runner: GradleRunner by lazy { val runner = GradleRunner.create() .withProjectDir(setup.projectDir) .withDebug(setup.debug) - val sharedTestKitDir = if (setup.useSharedTestKit) { - RootProject.testKitTempDir().toFile().apply { mkdirs() } - } else { - null - } - if (sharedTestKitDir != null) { - runner.withTestKitDir(sharedTestKitDir) + setup.testKitDir()?.let { dir -> + runner.withTestKitDir(dir.apply { mkdirs() }) } - runner.enableTestKitCoverage(sharedTestKitDir) runner } @@ -264,7 +261,7 @@ public class GradleProject internal constructor(setup: GradleProjectSetup) { private fun prepareRun(taskName: TaskName): GradleRunner { val args = arguments.forTask(taskName) - return runner.withArguments(args) + return runner.withArguments(args.withTestKitCoverage()) } /** diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProjectSetup.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProjectSetup.kt index 98f42b346..391bc1ddf 100644 --- a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProjectSetup.kt +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleProjectSetup.kt @@ -1,28 +1,17 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ + package io.spine.tools.gradle.testing import io.spine.annotation.VisibleForTesting @@ -93,10 +82,16 @@ public class GradleProjectSetup internal constructor( internal var debug = false private set - /** Whether a [shared Gradle TestKit folder][RootProject.testKitTempDir] should be used. */ + /** Whether a [shared Gradle TestKit directory][withSharedTestKitDirectory] should be used. */ internal var useSharedTestKit = false private set + /** + * The shared TestKit directory [given][withSharedTestKitDirectory] explicitly, + * or `null` if none was given. + */ + private var sharedTestKitDir: File? = null + /** * Determines whether the plugin under test classpath is defined and * should be added to the Gradle execution classpath. @@ -364,17 +359,37 @@ public class GradleProjectSetup internal constructor( } /** - * Tells the TestKit runner to use a [single shared folder][RootProject.testKitTempDir] - * for all tests. + * Tells the TestKit runner to use a TestKit directory shared by the tests. + * + * The TestKit directory is the Gradle user home of the test builds. When it is + * shared, a build can reuse what earlier builds left there, such as Gradle's + * instrumented copies of the JARs on the classpath of the plugin under test. + * The directory is not deleted by `clean`. * - * The idea is that this folder is not deleted on `gradlew clean` phase, - * and therefore may be somewhat re-used to speed up the test execution. + * @param dir The directory to use. If `null`, the runner uses `caches/spine-testkit` + * under the Gradle user home. The Gradle user home is looked up in the order + * Gradle uses: the `gradle.user.home` system property of the test JVM, then the + * `GRADLE_USER_HOME` environment variable, then `.gradle` in the home directory + * of the user. */ - public fun withSharedTestKitDirectory() : GradleProjectSetup { + @JvmOverloads + public fun withSharedTestKitDirectory(dir: File? = null): GradleProjectSetup { this.useSharedTestKit = true + this.sharedTestKitDir = dir return this } + /** + * Obtains the TestKit directory to give the runner, or `null` to leave the choice + * to TestKit. + */ + internal fun testKitDir(): File? = + if (useSharedTestKit) { + sharedTestKitDir ?: defaultSharedTestKitDir() + } else { + null + } + /** * Creates a new project on the file system. */ diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleUserHome.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleUserHome.kt new file mode 100644 index 000000000..5ce961b43 --- /dev/null +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/GradleUserHome.kt @@ -0,0 +1,104 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.tools.gradle.testing + +import java.io.File + +/** + * Returns the TestKit directory that a project set up with + * [GradleProjectSetup.withSharedTestKitDirectory] uses when given none: + * `caches/spine-testkit` under the [Gradle user home][gradleUserHome]. + * + * @param property Returns the value of the system property with the given name. + * @param environment Returns the value of the environment variable with the given name. + */ +internal fun defaultSharedTestKitDir( + property: (String) -> String? = System::getProperty, + environment: (String) -> String? = System::getenv +): File = gradleUserHome(property, environment) + .resolve(CACHES_DIR) + .resolve(SHARED_TESTKIT_DIR) + +/** + * Returns the Gradle user home, looked up in the order Gradle uses: + * + * 1. The `gradle.user.home` system property. + * 2. The `GRADLE_USER_HOME` environment variable. + * 3. The `.gradle` directory in the home directory of the user (`user.home`). + * + * A blank value counts as not set. The path is composed with [File], so it gets + * the separators of the platform, and a Windows path such as `D:\a\.gradle` is + * kept as given. + * + * The lookup runs in the test JVM. That JVM does not see a Gradle user home passed + * to the outer build only with the `-g` option, so its result is then the default. + * + * @param property Returns the value of the system property with the given name. + * @param environment Returns the value of the environment variable with the given name. + */ +internal fun gradleUserHome( + property: (String) -> String?, + environment: (String) -> String? +): File { + val given = property(GRADLE_USER_HOME_PROPERTY).nonBlank() + ?: environment(GRADLE_USER_HOME_VARIABLE).nonBlank() + if (given != null) { + return File(given) + } + val userHome = checkNotNull(property(USER_HOME_PROPERTY)) { + "The `$USER_HOME_PROPERTY` system property is not set." + } + return File(userHome, GRADLE_DIR) +} + +private fun String?.nonBlank(): String? = this?.takeIf { it.isNotBlank() } + +/** + * The name of the system property with which Gradle can be given its user home, + * as by `-Dgradle.user.home=`. + */ +private const val GRADLE_USER_HOME_PROPERTY: String = "gradle.user.home" + +/** + * The name of the environment variable with which Gradle can be given its user home. + * + * It is consulted when the [GRADLE_USER_HOME_PROPERTY] system property is not set. + */ +private const val GRADLE_USER_HOME_VARIABLE: String = "GRADLE_USER_HOME" + +/** + * The name of the standard Java system property carrying the home directory of + * the user, which holds the [default Gradle user home][GRADLE_DIR]. + */ +private const val USER_HOME_PROPERTY: String = "user.home" + +/** + * The name of the directory, in the home directory of the user, that serves as + * the Gradle user home when neither [GRADLE_USER_HOME_PROPERTY] nor + * [GRADLE_USER_HOME_VARIABLE] names another one. + */ +private const val GRADLE_DIR: String = ".gradle" + +/** + * The name of the directory under the Gradle user home that holds the caches + * of Gradle and the [shared TestKit directory][SHARED_TESTKIT_DIR]. + */ +private const val CACHES_DIR: String = "caches" + +/** + * The name of the TestKit directory shared by the test builds, placed in + * the [caches directory][CACHES_DIR] of the Gradle user home. + */ +private const val SHARED_TESTKIT_DIR: String = "spine-testkit" diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/RootProject.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/RootProject.kt index de5138f3c..26dd32672 100644 --- a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/RootProject.kt +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/RootProject.kt @@ -1,28 +1,17 @@ /* - * Copyright 2022, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ + package io.spine.tools.gradle.testing import java.io.File @@ -80,16 +69,4 @@ internal object RootProject { fun dir(): File { return path().toFile() } - - /** - * Returns a path to a conventionally established temp directory named `.gradle-test-kit`, - * for Gradle TestKit runners. - * - * It may be used to configure TestKit runners, so that just a single folder - * is created for all integration tests. - */ - @JvmStatic - fun testKitTempDir() : Path { - return path().resolve(".gradle-test-kit") - } } diff --git a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/TestKitCoverage.kt b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/TestKitCoverage.kt index 98fb482e9..95f1c3550 100644 --- a/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/TestKitCoverage.kt +++ b/plugin-testlib/src/main/kotlin/io/spine/tools/gradle/testing/TestKitCoverage.kt @@ -1,105 +1,111 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.gradle.testing import java.io.File -import org.gradle.testkit.runner.GradleRunner /** - * Attaches the JaCoCo coverage agent to the worker JVM spawned by this runner, - * so that plugin code executed out-of-process via TestKit is credited to coverage. - * - * Does nothing unless the test JVM was started with the [AGENT_PROPERTY] and - * [EXEC_DIR_PROPERTY] system properties. This keeps the harness a no-op for - * ordinary runs and for consumers that do not opt in. - * - * Supplying those two properties on the test JVM is the responsibility of the - * consuming build. Consumer-facing setup instructions, including a copy-paste - * Gradle recipe, live in the [GradleProject] KDoc (this function is `internal`, - * so its own KDoc is not part of the published API documentation). + * The names of the system properties with which the consuming build enables + * the coverage of Gradle TestKit worker JVMs. * - * When enabled, the method writes a `gradle.properties` carrying a `-javaagent:…` - * argument into the TestKit directory (the Gradle user home of the worker build), - * and points the runner at that directory. The JaCoCo agent appends to a single - * per-module [execution-data file][EXEC_FILE]; on worker daemon shutdown — which - * happens after the tests complete and before the coverage report task runs — the - * data is flushed to disk. + * The consuming build sets them on the test JVM, as described in [GradleProject]. * - * @param preferredTestKitDir The TestKit directory the caller already intends to - * use (e.g. a [shared one][GradleProjectSetup.withSharedTestKitDirectory]), or - * `null` to let this method pick the per-module [TESTKIT_HOME_DIR]. + * The property names are part of the contract with consuming builds and must not + * change: a build sets the properties by name, and is versioned independently of + * this library. */ -internal fun GradleRunner.enableTestKitCoverage(preferredTestKitDir: File? = null) { - val agentJar = System.getProperty(AGENT_PROPERTY) ?: return - val execDirPath = System.getProperty(EXEC_DIR_PROPERTY) ?: return +public object TestKitCoverage { - val execDir = File(execDirPath).apply { mkdirs() } - val execFile = File(execDir, EXEC_FILE) - val testKitDir = (preferredTestKitDir ?: File(execDir.parentFile, TESTKIT_HOME_DIR)) - .apply { mkdirs() } + // The `io.spine.gradle.testing.enableTestKitCoverage` extension in `config`'s + // `buildSrc` sets these properties. It repeats the names, because it does not + // depend on this library. - val jvmArg = "-javaagent:${agentJar.forProperties()}=" + - "destfile=${execFile.invariantSeparatorsPath}," + - "append=true,dumponexit=true,output=file,jmx=false" - File(testKitDir, "gradle.properties") - .writeText("org.gradle.jvmargs=$jvmArg\n") + /** + * The prefix shared by the names of the properties. + */ + private const val PREFIX: String = "io.spine.tools.gradle.testkit.coverage" - withTestKitDir(testKitDir) -} + /** + * The name of the system property carrying the absolute path to the JaCoCo + * agent JAR to attach to a Gradle TestKit worker JVM. The JAR is the artifact + * `org.jacoco:org.jacoco.agent::runtime`. + */ + public const val AGENT_PROPERTY: String = "$PREFIX.agent" -/** - * Normalizes a file-system path for safe inclusion into a `*.properties` file - * by using forward slashes regardless of the host platform. - */ -private fun String.forProperties(): String = File(this).invariantSeparatorsPath + /** + * The name of the system property carrying the absolute path to the directory + * where the TestKit worker should write its JaCoCo execution data. + */ + public const val EXEC_DIR_PROPERTY: String = "$PREFIX.execDir" +} /** - * The name of the system property carrying the absolute path to the JaCoCo - * agent JAR to attach to a Gradle TestKit worker JVM. + * Returns these arguments with the `-Dorg.gradle.jvmargs` argument that attaches + * the JaCoCo agent to the worker JVM appended, or the same arguments when TestKit + * coverage is not enabled. + * + * TestKit coverage is enabled when the test JVM was started with the + * [TestKitCoverage.AGENT_PROPERTY] and [TestKitCoverage.EXEC_DIR_PROPERTY] system + * properties. Without them, the harness is a no-op for ordinary runs and for + * consumers that do not opt in. Supplying the properties is the responsibility of + * the consuming build; the setup instructions live in the [GradleProject] KDoc. + * + * The agent appends to a single per-module [execution-data file][EXEC_FILE]; on + * worker daemon shutdown — which happens after the tests complete and before the + * coverage report task runs — the data is flushed to disk. * - * The consuming build is expected to set this property on the test JVM. Within - * the SpineEventEngine organisation it is set by the - * `io.spine.gradle.testing.enableTestKitCoverage` extension in `config`'s - * `buildSrc`, where the constant is duplicated (the `buildSrc` and the production - * source sets cannot share code). Keep the two values in sync. + * The agent is passed on the command line rather than in a `gradle.properties` file + * in the TestKit directory, so that coverage does not depend on which TestKit + * directory the runner uses. The argument replaces the `org.gradle.jvmargs` the + * worker build would use otherwise, such as the one of the `gradle.properties` of + * the test project, or one passed earlier in these arguments. */ -private const val AGENT_PROPERTY: String = - "io.spine.tools.gradle.testkit.coverage.agent" +internal fun List.withTestKitCoverage(): List { + val coverage = CoverageSetup.fromSystemProperties() ?: return this + val execFile = File(coverage.execDir, EXEC_FILE) + // Forward slashes work on every platform and need no escaping. + val jvmArg = "-javaagent:${coverage.agentJar.invariantSeparatorsPath}=" + + "destfile=${execFile.invariantSeparatorsPath}," + + "append=true,dumponexit=true,output=file,jmx=false" + return this + "-Dorg.gradle.jvmargs=$jvmArg" +} /** - * The name of the system property carrying the absolute path to the directory - * where the TestKit worker should write its JaCoCo execution data. + * The TestKit coverage setup the consuming build passed to this test JVM. * - * Set by the consuming build on the test JVM. Within the SpineEventEngine - * organisation this is done by the `io.spine.gradle.testing.enableTestKitCoverage` - * extension in `config`'s `buildSrc`; keep the two values in sync. + * @property agentJar The JaCoCo agent JAR. + * @property execDir The directory for the execution data of the worker JVMs. */ -private const val EXEC_DIR_PROPERTY: String = - "io.spine.tools.gradle.testkit.coverage.execDir" +private class CoverageSetup(val agentJar: File, val execDir: File) { + + companion object { + + /** + * Reads the setup from the system properties of this test JVM, + * or returns `null` when TestKit coverage is not enabled for it. + */ + fun fromSystemProperties(): CoverageSetup? { + val agentJar = System.getProperty(TestKitCoverage.AGENT_PROPERTY) + val execDir = System.getProperty(TestKitCoverage.EXEC_DIR_PROPERTY) + if (agentJar == null || execDir == null) { + return null + } + return CoverageSetup(File(agentJar), File(execDir)) + } + } +} /** * The name of the JaCoCo execution-data file written by TestKit workers. @@ -110,13 +116,3 @@ private const val EXEC_DIR_PROPERTY: String = * into the Kover reports. */ private const val EXEC_FILE: String = "testkit.exec" - -/** - * The name of the directory, placed next to the [execution-data directory][EXEC_DIR_PROPERTY], - * that serves as the dedicated Gradle user home (the TestKit directory) for the - * coverage-enabled worker builds. - * - * Reusing one TestKit directory per module lets a single TestKit daemon serve all - * the test cases, keeping the agent attached with a stable `destfile`. - */ -private const val TESTKIT_HOME_DIR: String = "testkit-home" diff --git a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleProjectSetupSpec.kt b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleProjectSetupSpec.kt index 458aa35c8..36f2d7a66 100644 --- a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleProjectSetupSpec.kt +++ b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleProjectSetupSpec.kt @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.gradle.testing @@ -114,6 +102,29 @@ class GradleProjectSetupSpec { setup.useSharedTestKit shouldBe true } + @Nested + inner class `choose the TestKit directory` { + + @Test + fun `leaving it to TestKit by default`() { + setup.testKitDir() shouldBe null + } + + @Test + fun `taking the one given`(@TempDir dir: File) { + setup.withSharedTestKitDirectory(dir) + + setup.testKitDir() shouldBe dir + } + + @Test + fun `taking the shared one under the Gradle user home when given none`() { + setup.withSharedTestKitDirectory() + + setup.testKitDir() shouldBe defaultSharedTestKitDir() + } + } + @Test fun `copy 'buildSrc' without caching`() { setup.copyBuildSrc(cachingEnabled = false) diff --git a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleUserHomeSpec.kt b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleUserHomeSpec.kt new file mode 100644 index 000000000..48f53ab6e --- /dev/null +++ b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/GradleUserHomeSpec.kt @@ -0,0 +1,121 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.tools.gradle.testing + +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS + +@DisplayName("Gradle user home lookup should") +internal class GradleUserHomeSpec { + + @Test + fun `prefer the system property of the Gradle user home`() { + val home = gradleUserHome( + systemProperties("gradle.user.home" to "/opt/gradle", "user.home" to "/home/me"), + environment("GRADLE_USER_HOME" to "/var/gradle") + ) + + home shouldBe File("/opt/gradle") + } + + @Test + fun `use the 'GRADLE_USER_HOME' environment variable when the property is not set`() { + val home = gradleUserHome( + systemProperties("user.home" to "/home/me"), + environment("GRADLE_USER_HOME" to "/var/gradle") + ) + + home shouldBe File("/var/gradle") + } + + @Test + fun `default to the dot-gradle directory in the home directory of the user`() { + val home = gradleUserHome(systemProperties("user.home" to "/home/me"), environment()) + + home shouldBe File("/home/me", ".gradle") + } + + @Test + fun `treat blank values as not set`() { + val home = gradleUserHome( + systemProperties("gradle.user.home" to " ", "user.home" to "/home/me"), + environment("GRADLE_USER_HOME" to "") + ) + + home shouldBe File("/home/me", ".gradle") + } + + @Test + fun `fail when the home directory of the user is not known`() { + val error = assertThrows { + gradleUserHome(systemProperties(), environment()) + } + + error.message shouldContain "user.home" + } + + @Test + fun `put the shared TestKit directory under 'caches' of the Gradle user home`() { + val dir = defaultSharedTestKitDir( + systemProperties("user.home" to "/home/me"), + environment("GRADLE_USER_HOME" to "/var/gradle") + ) + + dir shouldBe File("/var/gradle/caches/spine-testkit") + } + + @Nested + @EnabledOnOs(OS.WINDOWS) + inner class `keep a Windows path` { + + @Test + fun `given with backslashes`() { + val fromVariable = defaultSharedTestKitDir( + systemProperties("user.home" to "C:\\Users\\runneradmin"), + environment("GRADLE_USER_HOME" to "D:\\a\\.gradle") + ) + val fromUserHome = defaultSharedTestKitDir( + systemProperties("user.home" to "C:\\Users\\runneradmin"), + environment() + ) + + fromVariable.path shouldBe "D:\\a\\.gradle\\caches\\spine-testkit" + fromUserHome.path shouldBe "C:\\Users\\runneradmin\\.gradle\\caches\\spine-testkit" + } + + @Test + fun `given with forward slashes`() { + val dir = defaultSharedTestKitDir( + systemProperties("user.home" to "C:\\Users\\runneradmin"), + environment("GRADLE_USER_HOME" to "D:/a/.gradle") + ) + + dir.path shouldBe "D:\\a\\.gradle\\caches\\spine-testkit" + } + } +} + +private fun systemProperties(vararg values: Pair): (String) -> String? = + mapOf(*values)::get + +private fun environment(vararg values: Pair): (String) -> String? = + mapOf(*values)::get diff --git a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/RootProjectSpec.kt b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/RootProjectSpec.kt index d439fe424..065e9101f 100644 --- a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/RootProjectSpec.kt +++ b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/RootProjectSpec.kt @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.gradle.testing @@ -37,11 +25,4 @@ class RootProjectSpec { fun `locate the project root`() { RootProject.dir().exists() shouldBe true } - - @Test - fun `provide the shared TestKit temp directory under the project root`() { - val testKitDir = RootProject.testKitTempDir() - testKitDir.startsWith(RootProject.path()) shouldBe true - testKitDir.fileName.toString() shouldBe ".gradle-test-kit" - } } diff --git a/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/TestKitCoverageSpec.kt b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/TestKitCoverageSpec.kt new file mode 100644 index 000000000..fa9402d8f --- /dev/null +++ b/plugin-testlib/src/test/kotlin/io/spine/tools/gradle/testing/TestKitCoverageSpec.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.tools.gradle.testing + +import io.kotest.matchers.shouldBe +import io.spine.tools.gradle.testing.TestKitCoverage.AGENT_PROPERTY +import io.spine.tools.gradle.testing.TestKitCoverage.EXEC_DIR_PROPERTY +import java.io.File +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("TestKit coverage harness should") +internal class TestKitCoverageSpec { + + private val savedProperties = mutableMapOf() + + @BeforeEach + fun clearProperties() { + COVERAGE_PROPERTIES.forEach { name -> + savedProperties[name] = System.getProperty(name) + System.clearProperty(name) + } + } + + @AfterEach + fun restoreProperties() { + savedProperties.forEach { (name, value) -> + if (value == null) { + System.clearProperty(name) + } else { + System.setProperty(name, value) + } + } + } + + @Test + fun `leave the arguments intact when coverage is not enabled`() { + val args = listOf("build") + + args.withTestKitCoverage() shouldBe args + } + + @Test + fun `stay disabled when only the agent property is set`(@TempDir dir: File) { + System.setProperty(AGENT_PROPERTY, dir.resolve("agent.jar").path) + val args = listOf("build") + + args.withTestKitCoverage() shouldBe args + } + + @Nested + inner class `when coverage is enabled` { + + @TempDir + lateinit var dir: File + + private val agentJar by lazy { dir.resolve("jacoco/agent.jar") } + private val execDir by lazy { dir.resolve("build/jacoco-testkit") } + + @BeforeEach + fun enableCoverage() { + System.setProperty(AGENT_PROPERTY, agentJar.path) + System.setProperty(EXEC_DIR_PROPERTY, execDir.path) + } + + @Test + fun `append the argument that attaches the agent to the worker JVM`() { + val args = listOf("build", "--stacktrace").withTestKitCoverage() + + val execFile = execDir.resolve("testkit.exec") + args shouldBe listOf( + "build", + "--stacktrace", + "-Dorg.gradle.jvmargs=-javaagent:${agentJar.invariantSeparatorsPath}=" + + "destfile=${execFile.invariantSeparatorsPath}," + + "append=true,dumponexit=true,output=file,jmx=false" + ) + } + } +} + +private val COVERAGE_PROPERTIES = listOf(AGENT_PROPERTY, EXEC_DIR_PROPERTY) diff --git a/version.gradle.kts b/version.gradle.kts index 0faf741b6..bb97fa199 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -12,4 +12,4 @@ * and limitations under the License. */ -extra.set("versionToPublish", "2.0.0-SNAPSHOT.451") +extra.set("versionToPublish", "2.0.0-SNAPSHOT.460")