diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/Readme.md b/libraries/tools/kotlin-gradle-plugin-integration-tests/Readme.md index 7dc49518ae6..0d37b1de147 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/Readme.md +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/Readme.md @@ -85,6 +85,23 @@ plugins { A bunch of additional useful assertions available to use, such as file assertions, output assertions and task assertions. If you want to add a new assertion, add as a reviewer someone from Kotlin build tools team. +##### Additional test helpers + +Whenever you need to test combination of different JDKs and Gradle versions - you could use `@GradleWithJdkTest` instead of `@GradleTest`. +Then test method will receive requires JDKs as a second parameter: +```kotlin +@JdkVersions(version = [JavaVersion.VERSION_11, JavaVersion.VERSION_17]) +@GradleWithJdkTest +fun someTest( + gradleVersion: GradleVersion, + providedJdk: JdkVersions.ProvidedJdk +) { + project("simple", gradleVersion, buildJdk = providedJdk.location) { + build("assemble") + } +} +``` + ##### Deprecated tests setup When you create a new test, figure out which Gradle versions it is supposed to run on. Then, when you instantiate a test project, specify one of: diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleTest.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleTest.java index 2cfa42fe974..9cf42674f87 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleTest.java +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleTest.java @@ -17,8 +17,8 @@ import java.lang.annotation.Target; // Workaround for https://youtrack.jetbrains.com/issue/IDEA-265284 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) -@KGPBaseTest.GradleTestVersions +@GradleTestVersions @ParameterizedTest(name = "{0}: {displayName}") -@ArgumentsSource(KGPBaseTest.GradleArgumentsProvider.class) +@ArgumentsSource(GradleArgumentsProvider.class) public @interface GradleTest { } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleWithJdkTest.java b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleWithJdkTest.java new file mode 100644 index 00000000000..b0ea2e877d9 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/java/org/jetbrains/kotlin/gradle/testbase/GradleWithJdkTest.java @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.testbase; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +// Has to be java annotation +// Workaround for https://youtrack.jetbrains.com/issue/IDEA-265284 +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@GradleTestVersions +@JdkVersions +@ParameterizedTest(name = "{1} with {0}: {displayName}") +@ArgumentsSource(GradleAndJdkArgumentsProvider.class) +public @interface GradleWithJdkTest { +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/KGPBaseTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/KGPBaseTest.kt index 6a6abf78278..5bf9f79cba6 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/KGPBaseTest.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/KGPBaseTest.kt @@ -5,17 +5,11 @@ package org.jetbrains.kotlin.gradle.testbase -import org.gradle.util.GradleVersion import org.jetbrains.kotlin.test.WithMuteInDatabase import org.junit.jupiter.api.Tag import org.junit.jupiter.api.TestInstance -import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.io.TempDir -import org.junit.jupiter.params.provider.Arguments -import org.junit.jupiter.params.provider.ArgumentsProvider import java.nio.file.Path -import java.util.stream.Stream -import kotlin.streams.asStream /** * Base class for all Kotlin Gradle plugin integration tests. @@ -28,60 +22,4 @@ abstract class KGPBaseTest { @TempDir lateinit var workingDir: Path - - class GradleArgumentsProvider : ArgumentsProvider { - override fun provideArguments( - context: ExtensionContext - ): Stream { - var nextSuperclass: Class<*>? = context.testClass.get().superclass - val superClassSequence = if (nextSuperclass != null) { - generateSequence { - val currentSuperclass = nextSuperclass - nextSuperclass = nextSuperclass?.superclass - currentSuperclass - } - } else { - emptySequence() - } - - val versionsAnnotation = sequenceOf( - context.testMethod.get(), - context.testClass.get() - ) - .plus(superClassSequence) - .mapNotNull { declaration -> - declaration.annotations.firstOrNull { it is GradleTestVersions } - } - .firstOrNull() as GradleTestVersions? - ?: context.testMethod.get().annotations - .mapNotNull { annotation -> - annotation.annotationClass.annotations.firstOrNull { it is GradleTestVersions } - } - .first() as GradleTestVersions - - val minGradleVersion = GradleVersion.version(versionsAnnotation.minVersion) - val maxGradleVersion = GradleVersion.version(versionsAnnotation.maxVersion) - val additionalGradleVersions = versionsAnnotation - .additionalVersions - .map(GradleVersion::version) - additionalGradleVersions.forEach { - assert(it in minGradleVersion..maxGradleVersion) { - "Additional Gradle version ${it.version} should be between ${minGradleVersion.version} and ${maxGradleVersion.version}" - } - } - - return setOf(minGradleVersion, *additionalGradleVersions.toTypedArray(), maxGradleVersion) - .asSequence() - .map { Arguments.of(it) } - .asStream() - } - } - - @Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) - @Retention(AnnotationRetention.RUNTIME) - annotation class GradleTestVersions( - val minVersion: String = TestVersions.Gradle.MIN_SUPPORTED, - val maxVersion: String = TestVersions.Gradle.MAX_SUPPORTED, - val additionalVersions: Array = [] - ) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/arumentProviders.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/arumentProviders.kt new file mode 100644 index 00000000000..0d8604c454f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/arumentProviders.kt @@ -0,0 +1,142 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.testbase + +import org.gradle.api.JavaVersion +import org.gradle.util.GradleVersion +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import java.io.File +import java.util.stream.Stream +import kotlin.streams.asStream +import kotlin.streams.toList + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class GradleTestVersions( + val minVersion: String = TestVersions.Gradle.MIN_SUPPORTED, + val maxVersion: String = TestVersions.Gradle.MAX_SUPPORTED, + val additionalVersions: Array = [] +) + +open class GradleArgumentsProvider : ArgumentsProvider { + override fun provideArguments( + context: ExtensionContext + ): Stream { + val versionsAnnotation = findAnnotation(context) + + val minGradleVersion = GradleVersion.version(versionsAnnotation.minVersion) + val maxGradleVersion = GradleVersion.version(versionsAnnotation.maxVersion) + val additionalGradleVersions = versionsAnnotation + .additionalVersions + .map(GradleVersion::version) + additionalGradleVersions.forEach { + assert(it in minGradleVersion..maxGradleVersion) { + "Additional Gradle version ${it.version} should be between ${minGradleVersion.version} and ${maxGradleVersion.version}" + } + } + + return setOf(minGradleVersion, *additionalGradleVersions.toTypedArray(), maxGradleVersion) + .asSequence() + .map { Arguments.of(it) } + .asStream() + } + + inline fun findAnnotation(context: ExtensionContext): T { + var nextSuperclass: Class<*>? = context.testClass.get().superclass + val superClassSequence = if (nextSuperclass != null) { + generateSequence { + val currentSuperclass = nextSuperclass + nextSuperclass = nextSuperclass?.superclass + currentSuperclass + } + } else { + emptySequence() + } + + return sequenceOf( + context.testMethod.get(), + context.testClass.get() + ) + .plus(superClassSequence) + .mapNotNull { declaration -> + declaration.annotations.firstOrNull { it is T } + } + .firstOrNull() as T? + ?: context.testMethod.get().annotations + .mapNotNull { annotation -> + annotation.annotationClass.annotations.firstOrNull { it is T } + } + .first() as T + } +} + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class JdkVersions( + val versions: Array = [JavaVersion.VERSION_1_8, JavaVersion.VERSION_17], + val compatibleWithGradle: Boolean = true +) { + class ProvidedJdk( + val version: JavaVersion, + val location: File + ) { + override fun toString(): String { + return "JDK $version" + } + } +} + +class GradleAndJdkArgumentsProvider : GradleArgumentsProvider() { + override fun provideArguments( + context: ExtensionContext + ): Stream { + val jdkAnnotation = findAnnotation(context) + val providedJdks = jdkAnnotation + .versions + .map { + JdkVersions.ProvidedJdk( + it, + File(System.getProperty("jdk${it.majorVersion}Home")) + ) + } + + val gradleVersions = super.provideArguments(context).map { it.get().first() as GradleVersion }.toList() + + return providedJdks + .flatMap { providedJdk -> + val minSupportedGradleVersion = jdkGradleCompatibilityMatrix[providedJdk.version] + gradleVersions + .run { + if (jdkAnnotation.compatibleWithGradle && minSupportedGradleVersion != null) { + val initialVersionsCount = count() + val filteredVersions = filter { it >= minSupportedGradleVersion } + if (initialVersionsCount > filteredVersions.count()) { + (filteredVersions + minSupportedGradleVersion).toSet() + } else { + filteredVersions + } + } else this + } + .map { it to providedJdk } + } + .asSequence() + .map { + Arguments.of(it.first, it.second) + } + .asStream() + } + + companion object { + private val jdkGradleCompatibilityMatrix = mapOf( + JavaVersion.VERSION_14 to GradleVersion.version(TestVersions.Gradle.G_6_3), + JavaVersion.VERSION_15 to GradleVersion.version(TestVersions.Gradle.G_6_7), + JavaVersion.VERSION_16 to GradleVersion.version(TestVersions.Gradle.G_7_0), + JavaVersion.VERSION_17 to GradleVersion.version(TestVersions.Gradle.G_7_3) + ) + } +}