diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndGradleDaemon.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndGradleDaemon.kt new file mode 100644 index 00000000000..d4f4d5b84b4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3AndGradleDaemon.kt @@ -0,0 +1,103 @@ +/* + * 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 + +import org.gradle.testkit.runner.BuildResult +import org.gradle.tooling.internal.consumer.ConnectorServices +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.DisplayName + +@DisplayName("Kapt caching inside Gradle daemon") +@DaemonsGradlePluginTests +class Kapt3AndGradleDaemon : KGPBaseTest() { + + override val defaultBuildOptions: BuildOptions = super.defaultBuildOptions + .copy( + kaptOptions = BuildOptions.KaptOptions( + verbose = true, + useWorkers = true, + includeCompileClasspath = false + ) + ) + + @AfterEach + internal fun tearDown() { + // Stops Gradle and Kotlin daemon, so new run will pick up new jvm arguments + ConnectorServices.reset() + } + + @DisplayName("Javac should be loaded only once") + @GradleTest + fun testJavacIsLoadedOnce(gradleVersion: GradleVersion) { + project("javacIsLoadedOnce".withPrefix, gradleVersion) { + build("assemble") { + val loadsCount = "Loaded com.sun.tools.javac.util.Context from" + .toRegex(RegexOption.LITERAL) + .findAll(output) + .count() + + assert(loadsCount == 1) { + """ + |${printBuildOutput()} + | + | 'javac' is loaded more than once + """.trimMargin() + } + } + } + } + + @DisplayName("Annotation processor class should be loaded only once") + @GradleTest + fun testAnnotationProcessorClassIsLoadedOnce(gradleVersion: GradleVersion) { + project( + "javacIsLoadedOnce".withPrefix, + gradleVersion, + buildOptions = defaultBuildOptions.copy( + kaptOptions = defaultBuildOptions.kaptOptions!!.copy( + classLoadersCacheSize = 10 + ) + ) + ) { + val loadPattern = ("Loaded example.ExampleAnnotationProcessor from").toRegex(RegexOption.LITERAL) + fun BuildResult.classLoadingCount() = loadPattern.findAll(output).count() + + build("build") { + assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin") + assert(classLoadingCount() == 1) { + """ + |${printBuildOutput()} + | + |AP class is loaded not once: ${classLoadingCount()} times. + """.trimMargin() + } + } + + listOf( + subProject("module1").kotlinSourcesDir().resolve("module1/Module1Class.kt"), + subProject("module2").kotlinSourcesDir().resolve("module2/Module2Class.kt") + ).forEach { + it.append("\n fun touch() = null") + } + + build("build") { + assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin") + assert(classLoadingCount() == 0) { + """ + | ${printBuildOutput()} + | + |AP class shouldn't be loaded on the second build, actually loaded ${classLoadingCount()} times. + | + """.trimMargin() + } + } + } + } + + private val String.withPrefix get() = "kapt2/$this" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt index 763551e184e..02acaca2665 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kapt3IT.kt @@ -73,27 +73,6 @@ open class Kapt3WorkersIT : Kapt3IT() { override fun kaptOptions(): BuildOptions.KaptOptions = super.kaptOptions().copy(useWorkers = true) - @DisplayName("Javac should be loaded only once") - @GradleTest - fun testJavacIsLoadedOnce(gradleVersion: GradleVersion) { - project("javacIsLoadedOnce".withPrefix, gradleVersion) { - build("assemble") { - val loadsCount = "Loaded com.sun.tools.javac.util.Context from" - .toRegex(RegexOption.LITERAL) - .findAll(output) - .count() - - assert(loadsCount == 1) { - """ - |${printBuildOutput()} - | - | 'javac' is loaded more than once - """.trimMargin() - } - } - } - } - @DisplayName("Kapt is skipped when no annotation processors are added") @GradleTest fun testKaptSkipped(gradleVersion: GradleVersion) { @@ -133,8 +112,16 @@ class Kapt3ClassLoadersCacheIT : Kapt3WorkersIT() { ) @Disabled("classloaders cache is incompatible with AP discovery in classpath") - override fun testDisableDiscoveryInCompileClasspath(gradleVersion: GradleVersion) { - } + override fun testDisableDiscoveryInCompileClasspath(gradleVersion: GradleVersion) {} + + @Disabled("classloaders cache is leaking file descriptors that prevents cleaning test project") + override fun testChangesInLocalAnnotationProcessor(gradleVersion: GradleVersion) {} + + @Disabled("classloaders cache is leaking file descriptors that prevents cleaning test project") + override fun testKt19179andKt37241(gradleVersion: GradleVersion) {} + + @Disabled("classloaders cache is leaking file descriptors that prevents cleaning test project") + override fun testChangesToKaptConfigurationDoNotTriggerStubGeneration(gradleVersion: GradleVersion) {} override fun testAnnotationProcessorAsFqName(gradleVersion: GradleVersion) { project("annotationProcessorAsFqName".withPrefix, gradleVersion) { @@ -155,32 +142,6 @@ class Kapt3ClassLoadersCacheIT : Kapt3WorkersIT() { } } } - - @DisplayName("Annotation processor class should be loaded only once") - @GradleTest - fun testAnnotationProcessorClassIsLoadedOnce(gradleVersion: GradleVersion) { - project("javacIsLoadedOnce".withPrefix, gradleVersion) { - val loadPattern = Pattern.quote("Loaded example.ExampleAnnotationProcessor from").toRegex() - fun BuildResult.classLoadingCount() = loadPattern.findAll(output).count() - - build("build") { - assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin") - assertTrue(classLoadingCount() == 1, "AP class is loaded more than once") - } - - listOf( - subProject("module1").kotlinSourcesDir().resolve("module1/Module1Class.kt"), - subProject("module2").kotlinSourcesDir().resolve("module2/Module2Class.kt") - ).forEach { - it.append("\n fun touch() = null") - } - - build("build") { - assertTasksExecuted(":module1:kaptKotlin", ":module2:kaptKotlin") - assertTrue(classLoadingCount() == 0, "AP class shouldn't be loaded on the second build") - } - } - } } @DisplayName("Kapt without workers") @@ -554,7 +515,7 @@ open class Kapt3IT : Kapt3BaseIT() { @DisplayName("Should re-run kapt on changes in local annotation processor") @GradleTest - fun testChangesInLocalAnnotationProcessor(gradleVersion: GradleVersion) { + open fun testChangesInLocalAnnotationProcessor(gradleVersion: GradleVersion) { project("localAnnotationProcessor".withPrefix, gradleVersion) { build("build") @@ -701,7 +662,7 @@ open class Kapt3IT : Kapt3BaseIT() { @DisplayName("KT19179 and KT37241: kapt is not skipped and does not generate stubs for non-existent entries") @GradleTest - fun testKt19179andKt37241(gradleVersion: GradleVersion) { + open fun testKt19179andKt37241(gradleVersion: GradleVersion) { project("kt19179".withPrefix, gradleVersion) { build("build") { @@ -901,7 +862,7 @@ open class Kapt3IT : Kapt3BaseIT() { @DisplayName("KT-47347: kapt processors should not be an input files for stub generation") @GradleTest - fun testChangesToKaptConfigurationDoNotTriggerStubGeneration(gradleVersion: GradleVersion) { + open fun testChangesToKaptConfigurationDoNotTriggerStubGeneration(gradleVersion: GradleVersion) { project("localAnnotationProcessor".withPrefix, gradleVersion) { build("assemble")