Add @GradleWithJdkTest annotation.

Adding this annotation to the test will provide requested JDKs + Gradle
versions matrix as test parameter.

It is also possible to filter out supported by Gradle version JDKs.

^KT-45745 In Progress
This commit is contained in:
Yahor Berdnikau
2021-10-22 16:31:51 +02:00
committed by Space
parent ca3e1b18dc
commit 98791000bf
5 changed files with 186 additions and 64 deletions
@@ -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:
@@ -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 {
}
@@ -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 {
}
@@ -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<out Arguments> {
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<String> = []
)
}
@@ -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<String> = []
)
open class GradleArgumentsProvider : ArgumentsProvider {
override fun provideArguments(
context: ExtensionContext
): Stream<out Arguments> {
val versionsAnnotation = findAnnotation<GradleTestVersions>(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 <reified T : Annotation> 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> = [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<out Arguments> {
val jdkAnnotation = findAnnotation<JdkVersions>(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)
)
}
}