Migrate KaptIncrementalWithIsolatingApt to new test DSL

^KT-45745 In Progress
This commit is contained in:
Yahor Berdnikau
2022-01-27 16:51:11 +01:00
committed by TeamCityServer
parent 9ca2ce6647
commit baa3610c7d
5 changed files with 352 additions and 393 deletions
@@ -6,6 +6,7 @@ import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import java.io.File
import java.nio.file.Path
import java.util.*
import kotlin.io.path.createDirectories
@@ -31,10 +32,23 @@ open class KaptIncrementalIT : KGPBaseTest() {
kaptOptions = BuildOptions.KaptOptions(incrementalKapt = true)
)
protected open fun KGPBaseTest.kaptProject(
gradleVersion: GradleVersion,
buildOptions: BuildOptions = defaultBuildOptions,
buildJdk: File? = null,
test: TestProject.() -> Unit
): TestProject = project(
PROJECT_NAME,
gradleVersion,
buildOptions = buildOptions,
buildJdk = buildJdk,
test = test
)
@DisplayName("After adding new line compilation is incremental")
@GradleTest
fun testAddNewLine(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("clean", "build")
javaSourcesDir().resolve("bar/useB.kt").modify { "\n$it" }
@@ -49,8 +63,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("On rebuild without changes tasks should be UP-TO-DATE")
@GradleTest
fun testBasic(gradleVersion: GradleVersion) {
project(
PROJECT_NAME,
kaptProject(
gradleVersion,
buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)
) {
@@ -77,7 +90,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Successfully rebuild after error")
@GradleTest
fun testCompileError(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val bKt = javaSourcesDir().resolve("bar/B.kt")
@@ -100,7 +113,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Change in the function body without changing the signature")
@GradleTest
fun testChangeFunctionBodyWithoutChangingSignature(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("build", buildOptions = buildOptions.copy(logLevel = LogLevel.DEBUG)) {
checkGenerated(kaptGeneratedToPath, *annotatedElements)
checkNotGenerated(kaptGeneratedToPath, "notAnnotatedFun")
@@ -123,7 +136,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Adding new annotated element")
@GradleTest
fun testAddAnnotatedElement(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val utilKt = javaSourcesDir().resolve("baz/util.kt")
@@ -148,7 +161,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Adding new annotation triggers kapt run")
@GradleTest
fun testAddAnnotation(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val utilKt = javaSourcesDir().resolve("baz/util.kt")
@@ -167,7 +180,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Kapt run is incremental after source file was removed")
@GradleTest
fun testRemoveSourceFile(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
val kapt3IncDataPath = "build/tmp/kapt3/incrementalData/main"
val kapt3StubsPath = "build/tmp/kapt3/stubs/main"
@@ -214,7 +227,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Incremental kapt run is correct after removing all Kotlin sources")
@GradleTest
fun testRemoveAllKotlinSources(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble") {
assertFileInProjectExists("$KAPT3_STUBS_PATH/bar/UseBKt.java")
}
@@ -259,7 +272,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("On all annotations remove kapt and compile runs incremenatally")
@GradleTest
fun testRemoveAnnotations(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val bKt = javaSourcesDir().resolve("bar/B.kt")
@@ -294,7 +307,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Changing annotated property type is handled correctly")
@GradleTest
fun testChangeAnnotatedPropertyType(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val bKt = javaSourcesDir().resolve("bar/B.kt")
@@ -315,7 +328,7 @@ open class KaptIncrementalIT : KGPBaseTest() {
@DisplayName("Change in inline delegate is handled correctly")
@GradleTest
fun testChangeInlineDelegate(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
kaptProject(gradleVersion) {
build("assemble")
val file = javaSourcesDir().resolve("delegate/Usage.kt")
@@ -5,299 +5,313 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.JavaVersion
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.incapt.IncrementalAggregatingReferencingClasspathProcessor
import org.jetbrains.kotlin.gradle.incapt.IncrementalBinaryIsolatingProcessor
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessor
import org.jetbrains.kotlin.gradle.incapt.IncrementalProcessorReferencingClasspath
import org.jetbrains.kotlin.gradle.testbase.TestProject
import org.jetbrains.kotlin.gradle.util.AGPVersion
import org.jetbrains.kotlin.gradle.util.modify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assume
import org.junit.Test
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.DisabledOnOs
import org.junit.jupiter.api.condition.OS
import test.kt33617.MyClass
import java.io.File
import java.nio.file.Path
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.readText
import kotlin.io.path.writeText
import kotlin.io.path.*
import kotlin.test.assertEquals
@DisplayName("Kapt incremental tests with isolating apt")
class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
override fun getProject() =
Project(
"kaptIncrementalCompilationProject",
GradleVersionRequired.None
).apply {
override val defaultBuildOptions = super.defaultBuildOptions.copy(
incremental = true,
kaptOptions = super.defaultBuildOptions.kaptOptions!!.copy(
verbose = true,
useWorkers = true,
incrementalKapt = true,
includeCompileClasspath = false
)
)
override fun KGPBaseTest.kaptProject(
gradleVersion: GradleVersion,
buildOptions: BuildOptions,
buildJdk: File?,
test: TestProject.() -> Unit
): TestProject {
return project(PROJECT_NAME, gradleVersion, buildOptions = buildOptions, buildJdk = buildJdk) {
setupIncrementalAptProject("ISOLATING")
}
override fun defaultBuildOptions(): BuildOptions =
super.defaultBuildOptions().copy(
incremental = true,
kaptOptions = KaptOptions(
verbose = true,
useWorkers = true,
incrementalKapt = true,
includeCompileClasspath = false
)
)
@Test
fun testIncrementalChanges() {
val project = getProject()
project.build("clean", "build") {
assertSuccessful()
}
project.projectFile("useB.kt").modify { current -> "$current\nfun otherFunction() {}" }
project.build("build") {
assertSuccessful()
assertEquals(
setOf(
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").canonicalPath,
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
), getProcessedSources(output)
)
}
project.projectFile("B.kt").modify { current ->
val lastBrace = current.lastIndexOf("}")
current.substring(0, lastBrace) + "fun anotherFun() {}\n }"
}
project.build("build") {
assertSuccessful()
assertEquals(
setOf(
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/B.java").canonicalPath,
fileInWorkingDir("build/tmp/kapt3/stubs/main/bar/UseBKt.java").canonicalPath,
fileInWorkingDir("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
),
getProcessedSources(output)
)
test(this)
}
}
@Test
fun testChangingAnnotationProcessorClasspath() {
val project = getProject()
@DisplayName("Incremental changes")
@GradleTest
fun testIncrementalChanges(gradleVersion: GradleVersion) {
kaptProject(gradleVersion) {
build("clean", "assemble")
project.build("clean", "build") {
assertSuccessful()
}
project.gradleBuildScript().appendText(
"""
dependencies {
kapt 'com.google.guava:guava:12.0'
javaSourcesDir().resolve("bar/useB.kt").modify { current -> "$current\nfun otherFunction() {}" }
build("assemble") {
assertEquals(
setOf(
"$KAPT3_STUBS_PATH/bar/UseBKt.java",
"$KAPT3_STUBS_PATH/error/NonExistentClass.java"
)
.map { projectPath.resolve(it).toRealPath().toString() }
.toSet(),
getProcessedSources(output)
)
}
javaSourcesDir().resolve("bar/B.kt").modify { current ->
val lastBrace = current.lastIndexOf("}")
current.substring(0, lastBrace) + "fun anotherFun() {}\n }"
}
build("assemble") {
assertEquals(
setOf(
"$KAPT3_STUBS_PATH/bar/B.java",
"$KAPT3_STUBS_PATH/bar/UseBKt.java",
"$KAPT3_STUBS_PATH/error/NonExistentClass.java",
)
.map { projectPath.resolve(it).toRealPath().toString() }
.toSet(),
getProcessedSources(output)
)
}
""".trimIndent()
)
project.build("build") {
assertSuccessful()
assertContains("The input changes require a full rebuild for incremental task ':kaptKotlin'.")
}
}
@Test
fun testUnchangedAnnotationProcessorClasspathButContentChanged() {
val project = getProject()
val processorJar = project.projectDir.resolve("processor.jar").also {
ZipOutputStream(it.outputStream()).use {
// create an empty jar
}
}
project.gradleBuildScript().appendText(
"""
dependencies {
kapt files("processor.jar")
}
""".trimIndent()
)
@DisplayName("On changing annotation processor classpath")
@GradleTest
fun testChangingAnnotationProcessorClasspath(gradleVersion: GradleVersion) {
kaptProject(gradleVersion) {
build("clean", "assemble")
project.build("clean", "build") {
assertSuccessful()
}
buildGradle.append(
"""
dependencies {
kapt 'com.google.guava:guava:12.0'
}
""".trimIndent()
)
ZipOutputStream(processorJar.outputStream()).use {
it.putNextEntry(ZipEntry("resource.txt"))
it.closeEntry()
}
project.build("build") {
assertSuccessful()
assertContains("The input changes require a full rebuild for incremental task ':kaptKotlin'.")
build("assemble") {
assertOutputContains("The input changes require a full rebuild for incremental task ':kaptKotlin'.")
}
}
}
@Test
fun testNonIncrementalWithUnrecognizedInputs() {
val project = getProject()
val additionalInputs = project.projectDir.resolve("additionalInputs").also { it.mkdirs() }
project.gradleBuildScript().appendText(
"""
tasks.whenTaskAdded {
if (it.name == "kaptKotlin") {
it.getInputs().files("${additionalInputs.invariantSeparatorsPath}")
@DisplayName("On unchanged annotation processor classpath, but processor itself is updated")
@GradleTest
fun testUnchangedAnnotationProcessorClasspathButContentChanged(gradleVersion: GradleVersion) {
kaptProject(gradleVersion) {
val processorJar = projectPath.resolve("processor.jar").also {
ZipOutputStream(it.outputStream()).use {
// create an empty jar
}
}
""".trimIndent()
)
project.build("clean", "build") {
assertSuccessful()
}
additionalInputs.resolve("layout.xml").createNewFile()
project.build("build") {
assertSuccessful()
assertContains("Incremental annotation processing (apt mode): false")
}
}
/** Regression test for https://youtrack.jetbrains.com/issue/KT-33617. */
@Test
fun testSourcesInCompileClasspathJars() {
val javaHome = File(System.getProperty("jdk9Home")!!)
Assume.assumeTrue("JDK 9 isn't available", javaHome.isDirectory)
val options = defaultBuildOptions().copy(javaHome = javaHome)
val project = getProject()
// create jar with .class and .java file for the same type
ZipOutputStream(project.projectDir.resolve("lib-with-sources.jar").outputStream()).use {
it.putNextEntry(ZipEntry("test/kt33617/MyClass.class"))
MyClass::class.java.classLoader.getResourceAsStream("test/kt33617/MyClass.class").use { input ->
it.write(input!!.readBytes())
}
it.closeEntry()
it.putNextEntry(ZipEntry("test/kt33617/MyClass.java"))
it.write(
buildGradle.append(
"""
package test.kt33617;
public class MyClass {}
""".trimIndent().toByteArray(Charsets.UTF_8)
dependencies {
kapt files("processor.jar")
}
""".trimIndent()
)
it.closeEntry()
}
project.gradleBuildScript().appendText(
"""
dependencies {
implementation files('lib-with-sources.jar')
}
""".trimIndent()
)
project.projectFile("useB.kt").modify { current -> "$current\nfun otherFunction(param: test.kt33617.MyClass) {}" }
project.build("clean", "kaptKotlin", options = options) {
assertSuccessful()
build("clean", "assemble")
ZipOutputStream(processorJar.outputStream()).use {
it.putNextEntry(ZipEntry("resource.txt"))
it.closeEntry()
}
build("assemble") {
assertOutputContains("The input changes require a full rebuild for incremental task ':kaptKotlin'.")
}
}
}
/** Regression test for https://youtrack.jetbrains.com/issue/KT-42182. */
@Test
fun testGeneratedSourcesImpactedByClasspathChanges() {
val project = Project(
"kaptIncrementalCompilationProject",
GradleVersionRequired.None
).apply {
setupIncrementalAptProject("ISOLATING", procClass = IncrementalProcessorReferencingClasspath::class.java)
}
project.gradleSettingsScript().appendText("\ninclude ':', ':lib'")
val classpathTypeSource = project.projectDir.resolve("lib").run {
mkdirs()
resolve("build.gradle").writeText(
@DisplayName("Runs non-incrementally on unrecognized inputs")
@GradleTest
fun testNonIncrementalWithUnrecognizedInputs(gradleVersion: GradleVersion) {
kaptProject(gradleVersion) {
val additionalInputs = projectPath.resolve("additionalInputs").also { it.createDirectories() }
buildGradle.appendText(
"""
plugins {
id 'java'
tasks.whenTaskAdded {
if (it.name == "kaptKotlin") {
it.getInputs().files("${additionalInputs.invariantSeparatorsPathString}")
}
}
""".trimIndent()
)
build("clean", "assemble")
additionalInputs.resolve("layout.xml").createFile()
build("assemble") {
assertOutputContains("Incremental annotation processing (apt mode): false")
}
}
}
@DisplayName("KT-33617: sources in compile classpath jars")
@JdkVersions(versions = [JavaVersion.VERSION_1_9])
@GradleWithJdkTest
fun testSourcesInCompileClasspathJars(gradleVersion: GradleVersion, jdk: JdkVersions.ProvidedJdk) {
kaptProject(gradleVersion, buildJdk = jdk.location) {
// create jar with .class and .java file for the same type
ZipOutputStream(projectPath.resolve("lib-with-sources.jar").outputStream()).use {
it.putNextEntry(ZipEntry("test/kt33617/MyClass.class"))
MyClass::class.java.classLoader.getResourceAsStream("test/kt33617/MyClass.class").use { input ->
it.write(input!!.readBytes())
}
it.closeEntry()
it.putNextEntry(ZipEntry("test/kt33617/MyClass.java"))
it.write(
"""
package test.kt33617;
public class MyClass {}
""".trimIndent().toByteArray(Charsets.UTF_8)
)
it.closeEntry()
}
buildGradle.append(
"""
dependencies {
implementation files('lib-with-sources.jar')
}
""".trimIndent()
)
javaSourcesDir().resolve("bar/useB.kt").modify { current ->
"$current\nfun otherFunction(param: test.kt33617.MyClass) {}"
}
build("clean", "kaptKotlin")
}
}
@DisplayName("KT-42182: generated sources are updated on classpath changes")
@GradleTest
fun testGeneratedSourcesImpactedByClasspathChanges(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
setupIncrementalAptProject(
"ISOLATING",
procClass = IncrementalProcessorReferencingClasspath::class.java
)
settingsGradle.append("\ninclude ':', ':lib'")
val classpathTypeSource = subProject("lib").run {
projectPath.createDirectory()
buildGradle.writeText(
"""
plugins {
id 'java'
}
""".trimIndent()
)
val source = javaSourcesDir()
.resolve(
IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE
.replace(".", "/") + ".java"
)
source.parent.createDirectories()
source.writeText(
"""
package ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringBeforeLast(".")};
public class ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringAfterLast(".")} {}
""".trimIndent()
)
return@run source
}
buildGradle.appendText(
"""
dependencies {
implementation project(':lib')
}
""".trimIndent()
)
val source = resolve("src/main/java/" + IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.replace(".", "/") + ".java")
source.parentFile.mkdirs()
source.writeText(
"""
package ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringBeforeLast(".")};
public class ${IncrementalProcessorReferencingClasspath.CLASSPATH_TYPE.substringAfterLast(".")} {}
""".trimIndent()
val annotatedKotlinStubs = listOf(
"$KAPT3_STUBS_PATH/foo/A.java",
"$KAPT3_STUBS_PATH/bar/B.java",
"$KAPT3_STUBS_PATH/bar/UseBKt.java",
"$KAPT3_STUBS_PATH/baz/UtilKt.java",
"$KAPT3_STUBS_PATH/baz/UtilKt.java",
"$KAPT3_STUBS_PATH/error/NonExistentClass.java"
)
return@run source
}
project.gradleBuildScript().appendText(
"""
dependencies {
implementation project(':lib')
val allKotlinStubs = annotatedKotlinStubs + listOf(
"$KAPT3_STUBS_PATH/delegate/Delegate.java",
"$KAPT3_STUBS_PATH/delegate/Usage.java"
)
build("clean", "assemble") {
assertEquals(
allKotlinStubs
.plus("src/main/java/foo/JavaClass.java")
.map { projectPath.resolve(it).toRealPath().toString() }.toSet(),
getProcessedSources(output)
)
}
""".trimIndent()
)
val annotatedKotlinStubs = setOf(
project.projectDir.resolve("build/tmp/kapt3/stubs/main/foo/A.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/bar/B.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/bar/UseBKt.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/baz/UtilKt.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/baz/UtilKt.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
)
val allKotlinStubs = annotatedKotlinStubs + setOf(
project.projectDir.resolve("build/tmp/kapt3/stubs/main/delegate/Delegate.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/delegate/Usage.java").canonicalPath
)
project.build("clean", "build") {
assertSuccessful()
assertEquals(allKotlinStubs + fileInWorkingDir("src/main/java/foo/JavaClass.java").canonicalPath, getProcessedSources(output))
}
// change type that all generated sources reference
classpathTypeSource.writeText(classpathTypeSource.readText().replace("}", "int i = 10;\n}"))
project.build("build") {
assertSuccessful()
assertEquals(annotatedKotlinStubs, getProcessedSources(output))
// change type that all generated sources reference
classpathTypeSource.writeText(
classpathTypeSource.readText().replace("}", "int i = 10;\n}")
)
build("assemble") {
assertEquals(
annotatedKotlinStubs.map { projectPath.resolve(it).toRealPath().toString() }.toSet(),
getProcessedSources(output)
)
}
}
}
/** Regression test for KT-34340. */
@Test
fun testIsolatingWithOriginsInClasspath() {
//https://youtrack.jetbrains.com/issue/KTI-405
if (System.getProperty("os.name")?.toLowerCase()?.contains("windows") == true) return
@DisplayName("KT-34340: origins in classpath")
@GradleTest
@DisabledOnOs(OS.WINDOWS, disabledReason = "https://youtrack.jetbrains.com/issue/KTI-405")
fun testIsolatingWithOriginsInClasspath(gradleVersion: GradleVersion) {
project(
"kaptIncrementalWithParceler",
gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = TestVersions.AGP.AGP_42)
) {
build("clean", ":mylibrary:assembleDebug")
val project = Project(
projectName = "kaptIncrementalWithParceler"
).apply {
setupWorkingDir()
}
val options = defaultBuildOptions().copy(androidGradlePluginVersion = AGPVersion.v4_2_0)
project.build("clean", ":mylibrary:assembleDebug", options = options) {
assertSuccessful()
}
subProject("baseLibrary")
.javaSourcesDir()
.resolve("com/example/lib2/basemodule/BaseClassParcel.java")
.modify { current ->
current.replace("protected FieldClassParcel", "private FieldClassParcel")
}
project.projectFile("BaseClassParcel.java").modify { current ->
current.replace("protected FieldClassParcel", "private FieldClassParcel")
}
project.build(":mylibrary:assembleDebug", options = options) {
assertSuccessful()
assertEquals(
setOf(
fileInWorkingDir("mylibrary/src/main/java/com/example/lib/ExampleParcel.java").canonicalPath,
fileInWorkingDir("baseLibrary/src/main/java/com/example/lib2/basemodule/BaseClassParcel.java").canonicalPath,
),
getProcessedSources(output)
)
build(":mylibrary:assembleDebug") {
assertEquals(
listOf(
"mylibrary/src/main/java/com/example/lib/ExampleParcel.java",
"baseLibrary/src/main/java/com/example/lib2/basemodule/BaseClassParcel.java",
).map { projectPath.resolve(it).toRealPath().toString() }.toSet(),
getProcessedSources(output)
)
}
}
}
@@ -305,54 +319,56 @@ class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
* Make sure that changes to classpath can cause types to be reprocessed (i.e types in generated .class files that contain annotations
* claimed by annotation processors).
*/
@Test
fun testClasspathChangesCauseTypesToBeReprocessed() {
val project = Project(
"kaptIncrementalCompilationProject",
GradleVersionRequired.None
).apply {
@DisplayName("Changed types causing incremental compilation")
@GradleTest
fun testClasspathChangesCauseTypesToBeReprocessed(gradleVersion: GradleVersion) {
project(PROJECT_NAME, gradleVersion) {
setupIncrementalAptProject(
Pair("ISOLATING", IncrementalBinaryIsolatingProcessor::class.java),
Pair("AGGREGATING", IncrementalAggregatingReferencingClasspathProcessor::class.java),
)
}
project.gradleSettingsScript().appendText("\ninclude ':', ':lib'\n")
val classpathTypeSource = project.projectDir.resolve("lib").run {
mkdirs()
resolve("build.gradle").writeText(
settingsGradle.append("\ninclude ':', ':lib'\n")
val classpathTypeSource = subProject("lib").run {
projectPath.createDirectories()
buildGradle.writeText(
"""
plugins {
id 'java'
}
""".trimIndent()
)
val source = javaSourcesDir()
.resolve(
IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE
.replace(".", "/") + ".java"
)
source.parent.createDirectories()
source.writeText(
"""
package ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringBeforeLast(".")};
public class ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringAfterLast(".")} {}
""".trimIndent()
)
return@run source
}
buildGradle.append(
"""
plugins {
id 'java'
dependencies {
implementation project(':lib')
}
""".trimIndent()
)
val source =
resolve("src/main/java/" + IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.replace(".", "/") + ".java")
source.parentFile.mkdirs()
source.writeText(
"""
package ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringBeforeLast(".")};
public class ${IncrementalAggregatingReferencingClasspathProcessor.CLASSPATH_TYPE.substringAfterLast(".")} {}
""".trimIndent()
)
return@run source
}
project.gradleBuildScript().appendText(
"""
dependencies {
implementation project(':lib')
}
""".trimIndent()
)
// Remove all sources, and add only 1 source file
project.projectDir.resolve("src").let {
it.deleteRecursively()
with(it.resolve("main/java/example/A.kt")) {
parentFile.mkdirs()
// Remove all sources, and add only 1 source file
javaSourcesDir().deleteRecursively()
with(javaSourcesDir().resolve("example/A.kt")) {
parent.createDirectories()
writeText(
"""
package example
@@ -360,37 +376,31 @@ class KaptIncrementalWithIsolatingApt : KaptIncrementalIT() {
annotation class ExampleAnnotation
@ExampleAnnotation
class A
""".trimIndent()
""".trimIndent()
)
}
}
val allKotlinStubs = setOf(
project.projectDir.resolve("build/tmp/kapt3/stubs/main/example/ExampleAnnotation.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/example/A.java").canonicalPath,
project.projectDir.resolve("build/tmp/kapt3/stubs/main/error/NonExistentClass.java").canonicalPath
)
project.build("clean", "build") {
assertSuccessful()
assertEquals(allKotlinStubs, getProcessedSources(output))
assertTrue(
"Aggregating sources exists",
fileInWorkingDir("build/generated/source/kapt/main/com/example/AggGenerated.java").exists()
val allKotlinStubs = setOf(
"build/tmp/kapt3/stubs/main/example/ExampleAnnotation.java",
"build/tmp/kapt3/stubs/main/example/A.java",
"build/tmp/kapt3/stubs/main/error/NonExistentClass.java"
)
}
// change type that the aggregated generated source reference
classpathTypeSource.writeText(classpathTypeSource.readText().replace("}", "int i = 10;\n}"))
project.build("build") {
assertSuccessful()
assertEquals(emptySet<String>(), getProcessedSources(output))
assertEquals(setOf("example.AGenerated"), getProcessedTypes(output))
assertTrue(
"Aggregating sources exists",
fileInWorkingDir("build/generated/source/kapt/main/com/example/AggGenerated.java").exists()
)
build("clean", "assemble") {
assertEquals(
allKotlinStubs.map { projectPath.resolve(it).toRealPath().toString() }.toSet(),
getProcessedSources(output)
)
assertFileInProjectExists("build/generated/source/kapt/main/com/example/AggGenerated.java")
}
// change type that the aggregated generated source reference
classpathTypeSource.writeText(classpathTypeSource.readText().replace("}", "int i = 10;\n}"))
build("assemble") {
assertEquals(emptySet(), getProcessedSources(output))
assertEquals(setOf("example.AGenerated"), getProcessedTypes(output))
assertFileInProjectExists("build/generated/source/kapt/main/com/example/AggGenerated.java")
}
}
}
}
@@ -399,7 +409,7 @@ private const val patternApt = "Processing java sources with annotation processo
fun getProcessedSources(output: String): Set<String> {
return output.lines().filter { it.contains(patternApt) }.flatMapTo(HashSet()) { logging ->
val indexOf = logging.indexOf(patternApt) + patternApt.length
logging.drop(indexOf).split(",").map { it.trim() }.filter { !it.isEmpty() }.toSet()
logging.drop(indexOf).split(",").map { it.trim() }.filter { it.isNotEmpty() }.toSet()
}
}
@@ -407,59 +417,10 @@ private const val patternClassesApt = "Processing types with annotation processo
fun getProcessedTypes(output: String): Set<String> {
return output.lines().filter { it.contains(patternClassesApt) }.flatMapTo(HashSet()) { logging ->
val indexOf = logging.indexOf(patternClassesApt) + patternClassesApt.length
logging.drop(indexOf).split(",").map { it.trim() }.filter { !it.isEmpty() }.toSet()
logging.drop(indexOf).split(",").map { it.trim() }.filter { it.isNotEmpty() }.toSet()
}
}
fun BaseGradleIT.Project.setupIncrementalAptProject(
procType: String,
buildFile: File = projectDir.resolve("build.gradle"),
procClass: Class<*> = IncrementalProcessor::class.java
) {
setupIncrementalAptProject(procType to procClass, buildFile = buildFile)
}
fun BaseGradleIT.Project.setupIncrementalAptProject(
vararg processors: Pair<String, Class<*>>,
buildFile: File = projectDir.resolve("build.gradle")
) {
setupWorkingDir()
val content = buildFile.readText()
val processorPath = generateProcessor(*processors)
val updatedContent = content.replace(
Regex("^\\s*kapt\\s\"org\\.jetbrains\\.kotlin.*$", RegexOption.MULTILINE),
" kapt files(\"${processorPath.invariantSeparatorsPath}\")"
)
buildFile.writeText(updatedContent)
}
fun BaseGradleIT.Project.generateProcessor(vararg processors: Pair<String, Class<*>>): File {
val processorPath = projectDir.resolve("incrementalProcessor.jar")
ZipOutputStream(processorPath.outputStream()).use {
for ((_, procClass) in processors) {
val path = procClass.name.replace(".", "/") + ".class"
procClass.classLoader.getResourceAsStream(path).use { inputStream ->
it.putNextEntry(ZipEntry(path))
it.write(inputStream.readBytes())
it.closeEntry()
}
}
it.putNextEntry(ZipEntry("META-INF/gradle/incremental.annotation.processors"))
it.write(processors.joinToString("\n") { (procType, procClass) ->
"${procClass.name},$procType"
}.toByteArray())
it.closeEntry()
it.putNextEntry(ZipEntry("META-INF/services/javax.annotation.processing.Processor"))
it.write(processors.joinToString("\n") { (_, procClass) ->
procClass.name
}.toByteArray())
it.closeEntry()
}
return processorPath
}
fun TestProject.setupIncrementalAptProject(
procType: String,
buildFile: Path = buildGradle,
@@ -493,7 +454,7 @@ fun TestProject.generateProcessor(
val path = procClass.name.replace(".", "/") + ".class"
procClass.classLoader.getResourceAsStream(path).use { inputStream ->
it.putNextEntry(ZipEntry(path))
it.write(inputStream.readBytes())
it.write(inputStream!!.readBytes())
it.closeEntry()
}
}
@@ -1,7 +1,7 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.kapt'
}
android {
@@ -1,22 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenLocal()
maven { url 'https://maven.google.com' }
mavenCentral()
maven { url = uri("https://jcenter.bintray.com/") }
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.android.tools.build:gradle:$android_tools_version"
}
}
allprojects {
repositories {
mavenLocal()
maven { url 'https://maven.google.com' }
google()
mavenCentral()
maven { url = uri("https://jcenter.bintray.com/") }
}
}
@@ -1,7 +1,7 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'kotlin-kapt'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.kapt'
}
android {