KAPT: add tests for processed types, remove dead code, simplify logic

Add integration test which checks if only types can be
reprocessed in an incremental round. Also, remove unused
`invalidateTypesForFiles` method.

Furthermore, clarify that types that are reprocessed
(i.e types from .class files) are not necessarily
aggregating types, but simply types that should be reprocessed.

Test: KaptIncrementalWithIsolatingApt.testClasspathChangesCauseTypesToBeReprocessed
This commit is contained in:
Ivan Gavrilovic
2020-12-02 12:19:34 +00:00
committed by Mikhael Bogdanov
parent 08a2b47c77
commit 11673bd09c
7 changed files with 164 additions and 18 deletions
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2019 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.incapt;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
public class IncrementalAggregatingReferencingClasspathProcessor extends AbstractProcessor {
// Type that the generated source will extend.
public static final String CLASSPATH_TYPE = "com.example.FromClasspath";
private Set<String> values = new TreeSet<String>();
@Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton("example.KotlinFilerGenerated");
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
if (element instanceof TypeElement || element instanceof ExecutableElement || element instanceof VariableElement) {
values.add(element.getSimpleName().toString());
}
}
}
if (roundEnv.processingOver() && !values.isEmpty()) {
try (Writer writer = processingEnv.getFiler().createSourceFile("com.example.AggGenerated").openWriter()) {
writer.append("package ").append("com.example").append(";");
writer.append("\npublic class ").append("AggGenerated").append(" extends ").append(CLASSPATH_TYPE).append(" {}");
}
catch (IOException e) {
throw new RuntimeException(e);
}
values.clear();
}
return true;
}
}
@@ -5,11 +5,14 @@
package org.jetbrains.kotlin.gradle
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.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 test.kt33617.MyClass
@@ -275,6 +278,92 @@ 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 {
setupIncrementalAptProject(
Pair("ISOLATING", IncrementalBinaryIsolatingProcessor::class.java),
Pair("AGGREGATING", IncrementalAggregatingReferencingClasspathProcessor::class.java),
)
}
project.gradleSettingsScript().writeText("include ':', ':lib'")
val classpathTypeSource = project.projectDir.resolve("lib").run {
mkdirs()
resolve("build.gradle").writeText("apply plugin: 'java'")
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()
writeText(
"""
package example
annotation class ExampleAnnotation
@ExampleAnnotation
class A
""".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()
)
}
// 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()
)
}
}
}
private const val patternApt = "Processing java sources with annotation processors:"
@@ -285,6 +374,14 @@ fun getProcessedSources(output: String): Set<String> {
}
}
private const val patternClassesApt = "Processing types with annotation processors: "
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()
}
}
fun BaseGradleIT.Project.setupIncrementalAptProject(
procType: String,
buildFile: File = projectDir.resolve("build.gradle"),
@@ -15,11 +15,15 @@ apply plugin: "kotlin-kapt"
repositories {
jcenter()
mavenLocal()
maven {
url "https://jetbrains.bintray.com/intellij-third-party-dependencies/"
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
kapt "org.jetbrains.kotlin:annotation-processor-example:$kotlin_version"
kapt "org.jetbrains.intellij.deps:asm-all:9.0"
testImplementation 'junit:junit:4.12'
}