Integration tests for output dependencies
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt
new file mode 100644
index 0000000..14bb1a6
--- /dev/null
+++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt
@@ -0,0 +1,57 @@
+package com.google.devtools.ksp.test
+
+import Artifact
+import org.gradle.testkit.runner.GradleRunner
+import org.gradle.testkit.runner.TaskOutcome
+import org.junit.Assert
+import org.junit.Rule
+import org.junit.Test
+import java.io.File
+
+class OutputDepsIt {
+    @Rule
+    @JvmField
+    val project: TemporaryTestProject = TemporaryTestProject("output-deps")
+
+    val src2Dirty = listOf(
+            "workload/src/main/java/p1/J1.java" to setOf(
+                    "w: [ksp] p1/J1.java",
+                    "w: [ksp] p1/K1.kt",
+                    "w: [ksp] p1/K2.kt",
+            ),
+            "workload/src/main/java/p1/J2.java" to setOf(
+                    "w: [ksp] p1/J2.java",
+            ),
+            "workload/src/main/kotlin/p1/K1.kt" to setOf(
+                    "w: [ksp] p1/J1.java",
+                    "w: [ksp] p1/K1.kt",
+                    "w: [ksp] p1/K2.kt",
+            ),
+            "workload/src/main/kotlin/p1/K2.kt" to setOf(
+                    "w: [ksp] p1/J1.java",
+                    "w: [ksp] p1/K1.kt",
+                    "w: [ksp] p1/K2.kt",
+            ),
+    )
+
+    @Test
+    fun testOutputDeps() {
+        val gradleRunner = GradleRunner.create().withProjectDir(project.root)
+
+        gradleRunner.withArguments("assemble").build().let { result ->
+            Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome)
+        }
+        val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+
+        src2Dirty.forEach { (src, expectedDirties) ->
+            File(project.root, src).appendText("\n\n")
+            gradleRunner.withArguments("assemble").build().let { result ->
+                Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome)
+                val dirties = result.output.split("\n").filter { it.startsWith("w: [ksp]") }.toSet()
+                Assert.assertEquals(expectedDirties, dirties)
+            }
+        }
+        val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar"))
+        Assert.assertEquals(cleanArtifact, incrementalArtifact)
+    }
+}
diff --git a/integration-tests/src/test/resources/output-deps/build.gradle.kts b/integration-tests/src/test/resources/output-deps/build.gradle.kts
new file mode 100644
index 0000000..f557a53
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/build.gradle.kts
@@ -0,0 +1,9 @@
+plugins {
+    kotlin("jvm")
+}
+
+repositories {
+    mavenCentral()
+    google()
+}
+
diff --git a/integration-tests/src/test/resources/output-deps/gradle.properties b/integration-tests/src/test/resources/output-deps/gradle.properties
new file mode 100644
index 0000000..f932564
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/gradle.properties
@@ -0,0 +1,2 @@
+ksp.incremental=true
+ksp.incremental.log=true
\ No newline at end of file
diff --git a/integration-tests/src/test/resources/output-deps/settings.gradle.kts b/integration-tests/src/test/resources/output-deps/settings.gradle.kts
new file mode 100644
index 0000000..1c71fa2
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/settings.gradle.kts
@@ -0,0 +1,19 @@
+pluginManagement {
+    val kspVersion: String by settings
+    val kotlinVersion: String by settings
+    val testRepo: String by settings
+    plugins {
+        id("com.google.devtools.ksp") version kspVersion
+        kotlin("jvm") version kotlinVersion
+    }
+    repositories {
+        maven(testRepo)
+        gradlePluginPortal()
+        google()
+    }
+}
+
+rootProject.name = "incremental-test"
+
+include(":workload")
+include(":test-processor")
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts b/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts
new file mode 100644
index 0000000..ec59e67
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts
@@ -0,0 +1,25 @@
+val kspVersion: String by project
+val testRepo: String by project
+
+plugins {
+    kotlin("jvm")
+}
+
+group = "com.example"
+version = "1.0-SNAPSHOT"
+
+repositories {
+    maven(testRepo)
+    mavenCentral()
+    google()
+}
+
+dependencies {
+    implementation(kotlin("stdlib"))
+    implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
+}
+
+sourceSets.main {
+    java.srcDirs("src/main/kotlin")
+}
+
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt
new file mode 100644
index 0000000..5ef052e
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt
@@ -0,0 +1,43 @@
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.validate
+import com.google.devtools.ksp.visitor.KSDefaultVisitor
+import java.io.File
+import java.io.OutputStream
+import java.io.OutputStreamWriter
+
+class TestProcessor : SymbolProcessor {
+    lateinit var codeGenerator: CodeGenerator
+    lateinit var logger: KSPLogger
+
+    override fun init(options: Map<String, String>, kotlinVersion: KotlinVersion, codeGenerator: CodeGenerator, logger: KSPLogger) {
+        this.codeGenerator = codeGenerator
+        this.logger = logger
+    }
+
+    override fun finish() = Unit
+
+    override fun process(resolver: Resolver) {
+        fun outputForAnno(anno: String) {
+            val annoFiles = resolver.getSymbolsWithAnnotation(anno).map { (it as KSDeclaration).containingFile!! }
+            codeGenerator.createNewFile(Dependencies(false, *annoFiles.toTypedArray()), "", anno, "log").use { output ->
+                OutputStreamWriter(output).use { writer ->
+                    writer.write(annoFiles.map { it.fileName }.joinToString(", "))
+                }
+            }
+        }
+
+        outputForAnno("p1.Anno1")
+        outputForAnno("p1.Anno2")
+
+        resolver.getAllFiles().forEach { file ->
+            logger.warn("${file.packageName.asString()}/${file.fileName}")
+        }
+    }
+}
+
+
diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessor b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessor
new file mode 100644
index 0000000..7dcc953
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessor
@@ -0,0 +1 @@
+TestProcessor
diff --git a/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts b/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts
new file mode 100644
index 0000000..e34f0bf
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts
@@ -0,0 +1,20 @@
+val testRepo: String by project
+
+plugins {
+    id("com.google.devtools.ksp")
+    kotlin("jvm")
+}
+
+version = "1.0-SNAPSHOT"
+
+repositories {
+    maven(testRepo)
+    mavenCentral()
+    google()
+}
+
+dependencies {
+    implementation(kotlin("stdlib"))
+    ksp(project(":test-processor"))
+}
+
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java
new file mode 100644
index 0000000..e5e1bc0
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J1.java
@@ -0,0 +1,5 @@
+package p1;
+
+@Anno2
+public class J1 {
+}
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java
new file mode 100644
index 0000000..7de0687
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/java/p1/J2.java
@@ -0,0 +1,4 @@
+package p1;
+
+public class J2 {
+}
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt
new file mode 100644
index 0000000..badbcbd
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno1.kt
@@ -0,0 +1,2 @@
+package p1
+annotation class Anno1
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt
new file mode 100644
index 0000000..1c51771
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/Anno2.kt
@@ -0,0 +1,2 @@
+package p1
+annotation class Anno2
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt
new file mode 100644
index 0000000..4330181
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K1.kt
@@ -0,0 +1,4 @@
+package p1
+
+@Anno1
+open class K1
diff --git a/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt
new file mode 100644
index 0000000..506ed47
--- /dev/null
+++ b/integration-tests/src/test/resources/output-deps/workload/src/main/kotlin/p1/K2.kt
@@ -0,0 +1,5 @@
+package p1
+
+@Anno1
+@Anno2
+open class K2