From 3cedd0ee6f104c113262cbde37d358aad810e696 Mon Sep 17 00:00:00 2001 From: Oleg Golberg Date: Thu, 7 May 2026 12:41:45 -0400 Subject: [PATCH 1/2] Add @GradleVersion sub-annotation with per-version properties Introduce @GradleVersion(version, properties = [@Property(...)]) so tests can attach metadata (e.g. a minimum Kotlin version) to each parameterized Gradle version. Both @TestKit and @ParameterizedWithGradleVersions accept the richer form alongside the existing Array path, and TestProject.property(key) exposes the values to test bodies. Generated with Claude Code --- .../toasttab/gradle/testkit/GradleVersion.kt | 25 +++++++++++ .../gradle/testkit/GradleVersionArgument.kt | 17 +++++-- .../ParameterizedWithGradleVersions.kt | 5 ++- .../com/toasttab/gradle/testkit/TestKit.kt | 1 + .../toasttab/gradle/testkit/TestProject.kt | 5 ++- .../gradle/testkit/TestProjectExtension.kt | 19 ++++---- .../GradleVersionPropertiesIntegrationTest.kt | 44 +++++++++++++++++++ .../build.gradle.kts | 5 +++ .../settings.gradle.kts | 1 + 9 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersion.kt create mode 100644 junit5/src/test/kotlin/com/toasttab/gradle/testkit/GradleVersionPropertiesIntegrationTest.kt create mode 100644 junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/build.gradle.kts create mode 100644 junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/settings.gradle.kts diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersion.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersion.kt new file mode 100644 index 0000000..dd17231 --- /dev/null +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersion.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2026 Toast Inc. + * + * 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 + * + * 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 com.toasttab.gradle.testkit + +@Retention(AnnotationRetention.RUNTIME) +annotation class GradleVersion( + val version: String, + val properties: Array = [] +) + +@Retention(AnnotationRetention.RUNTIME) +annotation class Property(val key: String, val value: String) diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersionArgument.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersionArgument.kt index 9b36f93..97557a6 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersionArgument.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/GradleVersionArgument.kt @@ -16,12 +16,23 @@ package com.toasttab.gradle.testkit class GradleVersionArgument private constructor( - val version: String? + val version: String?, + val properties: Map = emptyMap() ) { - override fun toString() = version ?: "default" + fun property(key: String): String? = properties[key] + + override fun toString() = when { + version == null -> "default" + properties.isEmpty() -> version + else -> "$version${properties.entries.joinToString(prefix = " [", postfix = "]") { "${it.key}=${it.value}" }}" + } companion object { - fun of(version: String) = GradleVersionArgument(version) + fun of(version: String, properties: Map = emptyMap()) = + GradleVersionArgument(version, properties) + + fun of(spec: GradleVersion) = + GradleVersionArgument(spec.version, spec.properties.associate { it.key to it.value }) val DEFAULT = GradleVersionArgument(null) } diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/ParameterizedWithGradleVersions.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/ParameterizedWithGradleVersions.kt index a7ff241..b746a24 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/ParameterizedWithGradleVersions.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/ParameterizedWithGradleVersions.kt @@ -20,4 +20,7 @@ import org.junit.jupiter.params.provider.ArgumentsSource @ArgumentsSource(TestProjectExtension::class) @ParameterizedTest -annotation class ParameterizedWithGradleVersions(val value: Array = []) +annotation class ParameterizedWithGradleVersions( + val value: Array = [], + val versions: Array = [] +) diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestKit.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestKit.kt index b3c689c..d9f1613 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestKit.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestKit.kt @@ -26,5 +26,6 @@ import kotlin.reflect.KClass annotation class TestKit( val locator: KClass = SimpleNameProjectLocator::class, val gradleVersions: Array = [], + val versions: Array = [], val cleanup: Boolean = true ) diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt index 8a48535..dfe8902 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt @@ -25,10 +25,13 @@ import kotlin.io.path.deleteRecursively class TestProject( val dir: Path, - private val gradleVersion: GradleVersionArgument, + val gradleVersion: GradleVersionArgument, private val cleanup: Boolean, private val initArgs: List = emptyList() ) { + fun property(key: String): String? = gradleVersion.property(key) + + companion object { private val LOGGER = LoggerFactory.getLogger(TestProject::class.java) } diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProjectExtension.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProjectExtension.kt index 47b8b13..fa7ffc6 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProjectExtension.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProjectExtension.kt @@ -42,9 +42,10 @@ private const val LOCATOR = "locator" private const val PROJECTS = "PROJECTS" data class ProjectKey( - val gradleVersion: String? + val gradleVersion: String?, + val properties: Map = emptyMap() ) { - constructor(gradleVersion: GradleVersionArgument) : this(gradleVersion.version) + constructor(gradleVersion: GradleVersionArgument) : this(gradleVersion.version, gradleVersion.properties) } class TestProjects : CloseableResource { @@ -87,16 +88,16 @@ class TestProjectExtension : ParameterResolver, BeforeAllCallback, AfterTestExec override fun provideArguments(context: ExtensionContext): Stream { val methodAnn = context.requiredTestMethod.getAnnotation(ParameterizedWithGradleVersions::class.java) + val classAnn = context.requiredTestClass.getAnnotation(TestKit::class.java) - val versions = if (methodAnn.value.isNotEmpty()) { - methodAnn.value - } else { - context.requiredTestClass.getAnnotation(TestKit::class.java).gradleVersions + val specs: List = when { + methodAnn.versions.isNotEmpty() -> methodAnn.versions.map(GradleVersionArgument::of) + methodAnn.value.isNotEmpty() -> methodAnn.value.map { GradleVersionArgument.of(it) } + classAnn.versions.isNotEmpty() -> classAnn.versions.map(GradleVersionArgument::of) + else -> classAnn.gradleVersions.map { GradleVersionArgument.of(it) } } - return versions.map { - Arguments.of(context.project(GradleVersionArgument.of(it))) - }.stream() + return specs.map { Arguments.of(context.project(it)) }.stream() } companion object { diff --git a/junit5/src/test/kotlin/com/toasttab/gradle/testkit/GradleVersionPropertiesIntegrationTest.kt b/junit5/src/test/kotlin/com/toasttab/gradle/testkit/GradleVersionPropertiesIntegrationTest.kt new file mode 100644 index 0000000..b5270f4 --- /dev/null +++ b/junit5/src/test/kotlin/com/toasttab/gradle/testkit/GradleVersionPropertiesIntegrationTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Toast Inc. + * + * 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 + * + * 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 com.toasttab.gradle.testkit + +import strikt.api.expectThat +import strikt.assertions.isEqualTo + +@TestKit( + versions = [ + GradleVersion( + version = "8.6", + properties = [Property(key = "kotlin", value = "1.9.24")] + ), + GradleVersion( + version = "8.7", + properties = [Property(key = "kotlin", value = "2.0.0")] + ) + ] +) +class GradleVersionPropertiesIntegrationTest { + @ParameterizedWithGradleVersions + fun `per-version properties are attached to the test project`(project: TestProject) { + val expectedKotlin = when (project.gradleVersion.version) { + "8.6" -> "1.9.24" + "8.7" -> "2.0.0" + else -> error("unexpected gradle version ${project.gradleVersion.version}") + } + + expectThat(project.property("kotlin")).isEqualTo(expectedKotlin) + } +} diff --git a/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/build.gradle.kts b/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/build.gradle.kts new file mode 100644 index 0000000..2ced2f7 --- /dev/null +++ b/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/build.gradle.kts @@ -0,0 +1,5 @@ +plugins { + java +} + +println("gradle version = ${gradle.gradleVersion}") diff --git a/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/settings.gradle.kts b/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/settings.gradle.kts new file mode 100644 index 0000000..20308c4 --- /dev/null +++ b/junit5/src/test/projects/GradleVersionPropertiesIntegrationTest/per-version properties are attached to the test project/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "test" From 7b4bfd22f7993f4e604ccbd8993d51f9d4859492 Mon Sep 17 00:00:00 2001 From: Oleg Golberg Date: Fri, 8 May 2026 09:25:42 -0400 Subject: [PATCH 2/2] Apply spotless formatting Generated with Claude Code --- .../src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt index dfe8902..1633877 100644 --- a/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt +++ b/junit5/src/main/kotlin/com/toasttab/gradle/testkit/TestProject.kt @@ -31,7 +31,6 @@ class TestProject( ) { fun property(key: String): String? = gradleVersion.property(key) - companion object { private val LOGGER = LoggerFactory.getLogger(TestProject::class.java) }