diff options
Diffstat (limited to 'integration-tests/src')
262 files changed, 6156 insertions, 0 deletions
diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt new file mode 100644 index 00000000..7c485212 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIT.kt @@ -0,0 +1,84 @@ +package com.google.devtools.ksp.test + +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 AndroidIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground-android", "playground") + + @Test + fun testPlaygroundAndroid() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + // Disabling configuration cache. See https://github.com/google/ksp/issues/299 for details + gradleRunner.withArguments( + "clean", "build", "minifyReleaseWithR8", "--configuration-cache-problems=warn", "--info", "--stacktrace" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:build")?.outcome) + val mergedConfiguration = File(project.root, "workload/build/outputs/mapping/release/configuration.txt") + assert(mergedConfiguration.exists()) { + "Merged configuration file not found!\n${printDirectoryTree(project.root)}" + } + val configurationText = mergedConfiguration.readText() + assert("-keep class com.example.AClassBuilder { *; }" in configurationText) { + "Merged configuration did not contain generated proguard rules!\n$configurationText" + } + } + } +} + +/** + * Pretty print the directory tree and its file names. + * + * @param folder + * must be a folder. + * @return + */ +fun printDirectoryTree(folder: File): String? { + require(folder.isDirectory) { "folder is not a Directory" } + val indent = 0 + val sb = StringBuilder() + printDirectoryTree(folder, indent, sb) + return sb.toString() +} + +private fun printDirectoryTree( + folder: File, + indent: Int, + sb: StringBuilder +) { + require(folder.isDirectory) { "folder is not a Directory" } + sb.append(getIndentString(indent)) + sb.append("+--") + sb.append(folder.name) + sb.append("/") + sb.append("\n") + for (file in folder.listFiles()) { + if (file.isDirectory) { + printDirectoryTree(file, indent + 1, sb) + } else { + printFile(file, indent + 1, sb) + } + } +} + +private fun printFile(file: File, indent: Int, sb: StringBuilder) { + sb.append(getIndentString(indent)) + sb.append("+--") + sb.append(file.name) + sb.append("\n") +} + +private fun getIndentString(indent: Int): String? { + val sb = StringBuilder() + for (i in 0 until indent) { + sb.append("| ") + } + return sb.toString() +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt new file mode 100644 index 00000000..3cd01c14 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/AndroidIncrementalIT.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.ksp.test + +import com.google.devtools.ksp.test.fixtures.BuildResultFixture +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 AndroidIncrementalIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground-android-multi", "playground") + + @Test + fun testPlaygroundAndroid() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "clean", ":application:compileDebugKotlin", "--configuration-cache-problems=warn", "--debug", "--stacktrace" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:compileDebugKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":application:compileDebugKotlin")?.outcome) + } + + project.root.resolve("workload/src/main/java/com/example/A.kt").also { + it.appendText( + """ + + class Unused + """.trimIndent() + ) + } + + gradleRunner.withArguments( + ":application:compileDebugKotlin", "--configuration-cache-problems=warn", "--debug", "--stacktrace" + ).build().let { result -> + Assert.assertEquals( + setOf("workload/src/main/java/com/example/A.kt".replace('/', File.separatorChar)), + BuildResultFixture(result).compiledKotlinSources, + ) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt new file mode 100644 index 00000000..0b09a07a --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/Artifact.kt @@ -0,0 +1,24 @@ +import org.junit.* +import java.io.File +import java.util.zip.ZipFile + +// A snapshot of the digest of output jar. +class Artifact(file: File) { + private fun getCRCs(file: File): Map<String, Long> { + Assert.assertTrue(file.exists()) + return ZipFile(file).use { + it.entries().asSequence().map { + it.name to it.crc + }.toMap() + } + } + + val crcs: Map<String, Long> = getCRCs(file) + + override fun equals(other: Any?): Boolean { + if (other !is Artifact) + return false + + return crcs == other.crcs + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt new file mode 100644 index 00000000..c1c0c498 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/BuildCacheIT.kt @@ -0,0 +1,41 @@ +package com.google.devtools.ksp.test + +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 BuildCacheIT { + @Rule + @JvmField + val project1: TemporaryTestProject = TemporaryTestProject("buildcache", "playground") + + @Rule + @JvmField + val project2: TemporaryTestProject = TemporaryTestProject("buildcache", "playground") + + @Test + fun testBuildCache() { + val buildCacheDir = File(project1.root, "build-cache").absolutePath.replace(File.separatorChar, '/') + File(project1.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir") + File(project2.root, "gradle.properties").appendText("\nbuildCacheDir=$buildCacheDir") + + GradleRunner.create().withProjectDir(project1.root).withArguments( + "--build-cache", + ":workload:clean", + "build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload:kspKotlin")?.outcome) + } + + GradleRunner.create().withProjectDir(project2.root).withArguments( + "--build-cache", + ":workload:clean", + "build" + ).build().let { + Assert.assertEquals(TaskOutcome.FROM_CACHE, it.task(":workload:kspKotlin")?.outcome) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt new file mode 100644 index 00000000..30a8f01c --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedRefsIncIT.kt @@ -0,0 +1,69 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class GeneratedRefsIncIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("refs-gen", "test-processor") + + @Test + fun testGeneratedRefsInc() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val expected = listOf( + "w: [ksp] 1: [File: Bar.kt, File: Baz.kt]", + "w: [ksp] 2: [File: Foo.kt]", + "w: [ksp] 3: [File: Goo.kt]" + ) + + val expectedBar = listOf( + "w: [ksp] 1: [File: Bar.kt]", + "w: [ksp] 2: [File: Foo.kt]", + "w: [ksp] 3: [File: Goo.kt]" + ) + + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + + File(project.root, "workload/src/main/kotlin/com/example/Baz.kt").appendText("\n\n") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + + // Baz doesn't depend on Bar, so touching Bar won't invalidate Baz. + File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\n\n") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expectedBar, outputs) + } + + // Make Baz depends on Bar; Bar will be invalidated. + File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\nclass List<T>\n") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + + // Baz depended on Bar, so Baz should be invalidated. + project.restore("workload/src/main/kotlin/com/example/Bar.kt") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + + // Baz doesn't depend on Bar, so touching Bar won't invalidate Baz. + File(project.root, "workload/src/main/kotlin/com/example/Bar.kt").appendText("\n\n") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expectedBar, outputs) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt new file mode 100644 index 00000000..df9fc89f --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GeneratedSrcsIncIT.kt @@ -0,0 +1,34 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class GeneratedSrcsIncIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("srcs-gen", "test-processor") + + @Test + fun testGeneratedSrcsInc() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val expected = listOf( + "w: [ksp] 1: [File: Bar.kt, File: Baz.kt]", + "w: [ksp] 2: [File: Foo.kt]", + "w: [ksp] 3: [File: FooBar.kt, File: FooBaz.kt]" + ) + + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + File(project.root, "workload/src/main/kotlin/com/example/Baz.kt").appendText(System.lineSeparator()) + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected, outputs) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt new file mode 100644 index 00000000..29f56108 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/GetSealedSubclassesIncIT.kt @@ -0,0 +1,56 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class GetSealedSubclassesIncIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("sealed-subclasses", "test-processor") + + @Test + fun testGetSealedSubclassesInc() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val expected2 = listOf( + "w: [ksp] Processing Impl1.kt", + "w: [ksp] Impl1 : []", + "w: [ksp] Processing Impl2.kt", + "w: [ksp] Impl2 : []", + "w: [ksp] Processing Sealed.kt", + "w: [ksp] Sealed : [Impl1, Impl2]", + ) + + val expected3 = listOf( + "w: [ksp] Processing Impl1.kt", + "w: [ksp] Impl1 : []", + "w: [ksp] Processing Impl2.kt", + "w: [ksp] Impl2 : []", + "w: [ksp] Processing Impl3.kt", + "w: [ksp] Impl3 : []", + "w: [ksp] Processing Sealed.kt", + "w: [ksp] Sealed : [Impl1, Impl2, Impl3]", + ) + + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected2, outputs) + } + + File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").appendText("package com.example\n\n") + File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").appendText("class Impl3 : Sealed()\n") + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected3, outputs) + } + + File(project.root, "workload/src/main/kotlin/com/example/Impl3.kt").delete() + gradleRunner.withArguments("assemble").build().let { result -> + val outputs = result.output.lines().filter { it.startsWith("w: [ksp]") } + Assert.assertEquals(expected2, outputs) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt new file mode 100644 index 00000000..9da354ed --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/HmppIT.kt @@ -0,0 +1,83 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test + +class HmppIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("hmpp") + + val taskToFilesTraditional = mapOf( + ":workload:kspCommonMainKotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain", + ":workload:kspJvmJsKotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain_JvmJs", + ":workload:kspJvmLinuxX64KotlinMetadata" to "w: [ksp] EchoProcessor: CommonMain_JvmLinuxX64", + ":workload:kspKotlinJvm" to "w: [ksp] EchoProcessor: CommonMain_JvmJs_JvmLinuxX64_JvmMain_JvmOnly", + ":workload:kspKotlinJs" to "w: [ksp] EchoProcessor: CommonMain_JsMain_JvmJs", + ":workload:kspKotlinLinuxX64" to "w: [ksp] EchoProcessor: CommonMain_JvmLinuxX64_LinuxX64Main", + ) + + @Test + fun testTraditional() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + taskToFilesTraditional.forEach { (task, expected) -> + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + task, + ).build().let { result -> + val logs = result.output.lines().filter { it.startsWith("w: [ksp] EchoProcessor: ") }.toSet() + Assert.assertTrue(expected in logs) + } + } + } + + val taskToFilesHmpp = mapOf( + ":workload:kspCommonMainKotlinMetadata" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + ), + ":workload:kspJvmJsKotlinMetadata" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + "w: [ksp] EchoProcessor: (CommonMain)_JvmJs", + ), + ":workload:kspJvmLinuxX64KotlinMetadata" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64", + ), + ":workload:kspKotlinJvm" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + "w: [ksp] EchoProcessor: (CommonMain)_JvmJs", + "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64", + "w: [ksp] EchoProcessor: ((CommonMain)_JvmJs)_((CommonMain)_JvmLinuxX64)_(CommonMain)_JvmMain_JvmOnly", + ), + ":workload:kspKotlinJs" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + "w: [ksp] EchoProcessor: (CommonMain)_JvmJs", + "w: [ksp] EchoProcessor: ((CommonMain)_JvmJs)_(CommonMain)_JsMain", + ), + ":workload:kspKotlinLinuxX64" to setOf( + "w: [ksp] EchoProcessor: CommonMain", + "w: [ksp] EchoProcessor: (CommonMain)_JvmLinuxX64", + "w: [ksp] EchoProcessor: ((CommonMain)_JvmLinuxX64)_(CommonMain)_LinuxX64Main", + ), + ) + + @Test + fun testHmpp() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + taskToFilesHmpp.forEach { (task, expected) -> + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "--rerun-tasks", + "-Pksp.experimental.processing.model=hierarchical", + task, + ).build().let { result -> + val logs = result.output.lines().filter { it.startsWith("w: [ksp] EchoProcessor: ") }.toSet() + Assert.assertTrue(logs == expected) + } + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt new file mode 100644 index 00000000..cf07dd66 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalCPIT.kt @@ -0,0 +1,130 @@ +package com.google.devtools.ksp.test + +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 IncrementalCPIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("incremental-classpath") + + val src2Dirty = listOf( + "l1/src/main/kotlin/p1/L1.kt" to setOf( + "w: [ksp] p1/K1.kt", + "w: [ksp] processing done", + ), + "l2/src/main/kotlin/p1/L2.kt" to setOf( + "w: [ksp] p1/K1.kt", + "w: [ksp] p1/K2.kt", + "w: [ksp] processing done", + ), + "l3/src/main/kotlin/p1/L3.kt" to setOf( + "w: [ksp] p1/K3.kt", + "w: [ksp] processing done", + ), + "l4/src/main/kotlin/p1/L4.kt" to setOf( + "w: [ksp] p1/K3.kt", + "w: [ksp] processing done", + ), + "l5/src/main/kotlin/p1/L5.kt" to setOf( + "w: [ksp] processing done", + ), + ) + + val emptyMessage = setOf("w: [ksp] processing done") + + @Test + fun testCPChanges() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + src2Dirty.forEach { (src, expectedDirties) -> + File(project.root, src).appendText("\n\n") + gradleRunner.withArguments("assemble").build().let { result -> + // Trivial changes should not result in re-processing. + Assert.assertEquals(TaskOutcome.UP_TO_DATE, result.task(":workload:kspKotlin")?.outcome) + } + } + + var i = 100 + src2Dirty.forEach { (src, expectedDirties) -> + File(project.root, src).appendText("\n{ val v$i = ${i++} }\n") + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + Assert.assertEquals(expectedDirties, dirties) + } + } + + src2Dirty.forEach { (src, expectedDirties) -> + File(project.root, src).appendText("\n\nclass C${i++}\n") + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + // Non-signature changes should not affect anything. + Assert.assertEquals(emptyMessage, dirties) + } + } + } + + private fun toggleFlags(vararg extras: String) { + val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true) + + gradleRunner.withArguments( + *extras, + "--rerun-tasks", + "-Pksp.incremental=true", + "-Pksp.incremental.intermodule=true", + "assemble" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + } + + gradleRunner.withArguments( + *extras, + "--rerun-tasks", + "-Pksp.incremental=false", + "-Pksp.incremental.intermodule=true", + "assemble" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + } + + gradleRunner.withArguments( + *extras, + "--rerun-tasks", + "-Pksp.incremental=true", + "-Pksp.incremental.intermodule=false", + "assemble" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + } + + gradleRunner.withArguments( + *extras, + "--rerun-tasks", + "-Pksp.incremental=false", + "-Pksp.incremental.intermodule=false", + "assemble" + ).build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + } + } + + @Test + fun toggleIncrementalFlagsWithoutConfigurationCache() { + toggleFlags("--no-configuration-cache") + } + + @Test + fun toggleIncrementalFlagsWithConfigurationCache() { + toggleFlags("--configuration-cache") + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt new file mode 100644 index 00000000..880598bf --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalIT.kt @@ -0,0 +1,262 @@ +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 IncrementalIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("incremental") + + val src2Dirty = listOf( + "workload/src/main/java/p1/J1.java" to setOf( + "w: [ksp] p1/TestK2J.kt", + "w: [ksp] p1/TestJ2J.java", + "w: [ksp] p1/J1.java", + ), + "workload/src/main/java/p1/J2.java" to setOf( + "w: [ksp] p1/J2.java", + ), + "workload/src/main/java/p1/TestJ2J.java" to setOf( + "w: [ksp] p1/TestJ2J.java", + ), + "workload/src/main/java/p1/TestJ2K.java" to setOf( + "w: [ksp] p1/TestJ2K.java", + ), + "workload/src/main/java/p2/J2.java" to setOf( + "w: [ksp] p1/TestK2J.kt", + "w: [ksp] p2/J2.java", + "w: [ksp] p1/TestJ2J.java", + ), + "workload/src/main/java/p3/J1.java" to setOf( + "w: [ksp] p3/J1.java", + ), + "workload/src/main/java/p3/J2.java" to setOf( + "w: [ksp] p3/J2.java", + ), + "workload/src/main/java/p3/J3.java" to setOf( + "w: [ksp] p1/TestK2J.kt", + "w: [ksp] p1/TestJ2J.java", + "w: [ksp] p3/J3.java", + ), + "workload/src/main/kotlin/p1/K1.kt" to setOf( + "w: [ksp] p1/TestK2K.kt", + "w: [ksp] p1/K1.kt", + "w: [ksp] p1/TestJ2K.java", + ), + "workload/src/main/kotlin/p1/K2.kt" to setOf( + "w: [ksp] p1/K2.kt", + ), + "workload/src/main/kotlin/p1/TestK2J.kt" to setOf( + "w: [ksp] p1/TestK2J.kt", + ), + "workload/src/main/kotlin/p1/TestK2K.kt" to setOf( + "w: [ksp] p1/TestK2K.kt", + ), + "workload/src/main/kotlin/p2/K2.kt" to setOf( + "w: [ksp] p1/TestK2K.kt", + "w: [ksp] p2/K2.kt", + "w: [ksp] p1/TestJ2K.java", + ), + "workload/src/main/kotlin/p3/K1.kt" to setOf( + "w: [ksp] p3/K1.kt", + ), + "workload/src/main/kotlin/p3/K2.kt" to setOf( + "w: [ksp] p3/K2.kt", + ), + "workload/src/main/kotlin/p3/K3.kt" to setOf( + "w: [ksp] p1/TestK2K.kt", + "w: [ksp] p3/K3.kt", + "w: [ksp] p1/TestJ2K.java", + ) + ) + + @Test + fun testUpToDate() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.UP_TO_DATE, result.task(":workload:kspKotlin")?.outcome) + } + } + + @Test + fun testIsolating() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "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.lines().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) + } + + val changeSets = listOf( + listOf(7, 5), + listOf(0, 12), + listOf(13, 14), + listOf(8, 10), + listOf(11, 4), + listOf(3, 15), + listOf(6, 9), + listOf(2, 1), + listOf(3, 1, 12), + listOf(13, 0, 11), + listOf(6, 8, 4), + listOf(10, 9, 15), + listOf(2, 14, 5, 7), + listOf(5, 0, 13, 15), + listOf(3, 2, 6, 7), + listOf(4, 14, 10, 1), + listOf(12, 9, 8, 11), + listOf(12, 13, 5, 14, 7), + listOf(11, 2, 8, 8, 9), + listOf(11, 2, 8, 8, 9), + listOf(4, 0, 15, 1, 10), + ) + + @Test + fun testMultipleChanges() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + changeSets.forEach { changeSet -> + changeSet.forEach { + File(project.root, src2Dirty[it].first).appendText("\n\n") + } + val expectedDirties = changeSet.flatMapTo(mutableSetOf()) { + src2Dirty[it].second + } + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + Assert.assertEquals(expectedDirties, dirties) + } + } + } + + @Test + fun testMultipleDeletes() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + val srcs = src2Dirty.map { it.first } + + changeSets.forEach { changeSet -> + val notChanged = IntRange(0, srcs.size - 1).filter { it !in changeSet } + + // Touch a file so that Gradle won't UP_TO_DATE for us. + notChanged.first().let { + File(project.root, srcs[it]).appendText("\n\n") + } + + // Delete files + changeSet.forEach { + File(project.root, srcs[it]).delete() + } + + // in: "workload/src/main/kotlin/p1/K2.kt" + // out: "p1/K2.kt" + val expectedOutputs = notChanged.map() { + srcs[it].split("/").subList(4, 6).joinToString(File.separator) + ".log" + }.sorted() + + gradleRunner.withArguments(":workload:kspKotlin").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val outputRoot = File(project.root, "workload/build/generated/ksp/main/resources") + val outputs = outputRoot.walk().filter { it.isFile() }.map { + it.toRelativeString(outputRoot) + }.sorted().toList() + + Assert.assertEquals(expectedOutputs, outputs) + } + + changeSet.forEach { + project.restore(srcs[it]) + } + } + } + + @Test + fun testArgChange() { + 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")) + + val expectedDirties = src2Dirty.map { it.second }.flatten().toSet() + fun buildAndCheck() { + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + Assert.assertEquals(dirties, expectedDirties) + } + val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")) + Assert.assertEquals(cleanArtifact, incrementalArtifact) + } + + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option1\", \"value1\") }\n") + buildAndCheck() + + project.restore("workload/build.gradle.kts") + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option1\", \"value2\") }\n") + buildAndCheck() + + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"option2\", \"value2\") }\n") + buildAndCheck() + + project.restore("workload/build.gradle.kts") + buildAndCheck() + } + + @Test + fun testProcessorChange() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.withArguments("build").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspTestKotlin")?.outcome) + } + val cleanArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")) + + val expectedDirties = src2Dirty.map { it.second }.flatten().toSet() + fun buildAndCheck() { + gradleRunner.withArguments("build").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspTestKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + Assert.assertEquals(dirties, expectedDirties) + } + val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")) + Assert.assertEquals(cleanArtifact, incrementalArtifact) + } + + File(project.root, "validator/src/main/kotlin/Validator.kt").appendText("\n") + buildAndCheck() + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt new file mode 100644 index 00000000..33680be3 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalMultiChainIT.kt @@ -0,0 +1,49 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class IncrementalMultiChainIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("incremental-multi-chain") + + @Test + fun testMultiChain() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val k2 = File(project.root, "workload/src/main/kotlin/K2.kt") + + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("validating K1.kt")) + Assert.assertTrue(result.output.contains("validating Main.kt")) + Assert.assertTrue(result.output.contains("validating K1Impl.kt")) + Assert.assertTrue(result.output.contains("validating AllImpls.kt")) + Assert.assertTrue(result.output.contains("[K1Impl]")) + } + + k2.writeText( + "@NeedsImpl\ninterface K2\n" + ) + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("validating K1.kt")) + Assert.assertTrue(result.output.contains("validating K2.kt")) + Assert.assertTrue(result.output.contains("validating Main.kt")) + Assert.assertTrue(result.output.contains("validating K1Impl.kt")) + Assert.assertTrue(result.output.contains("validating K2Impl.kt")) + Assert.assertTrue(result.output.contains("validating AllImpls.kt")) + Assert.assertTrue(result.output.contains("[K1Impl, K2Impl]")) + } + + k2.delete() + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("validating K1.kt")) + Assert.assertTrue(result.output.contains("validating Main.kt")) + Assert.assertTrue(result.output.contains("validating K1Impl.kt")) + Assert.assertTrue(result.output.contains("validating AllImpls.kt")) + Assert.assertTrue(result.output.contains("[K1Impl]")) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt new file mode 100644 index 00000000..78de3c04 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/IncrementalRemovalIT.kt @@ -0,0 +1,35 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class IncrementalRemovalIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("incremental-removal") + + @Test + fun testRemoveOutputs() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val k1 = "workload/src/main/kotlin/p1/K1.kt" + + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("result: generated")) + } + + File(project.root, k1).writeText( + "package p1\n\nclass K1\n\nclass Foo : Bar { override fun s() = \"crafted\" }\n" + ) + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("result: crafted")) + } + + project.restore(k1) + gradleRunner.withArguments("run").build().let { result -> + Assert.assertTrue(result.output.contains("result: generated")) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt new file mode 100644 index 00000000..31b7cb58 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/InitPlusProviderIT.kt @@ -0,0 +1,33 @@ +package com.google.devtools.ksp.test + +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 +import java.util.jar.JarFile + +class InitPlusProviderIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("init-plus-provider") + + @Test + fun testInitPlusProvider() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val resultCleanBuild = gradleRunner.withArguments("clean", "build").build() + + Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome) + + val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar") + Assert.assertTrue(artifact.exists()) + + JarFile(artifact).use { jarFile -> + Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0) + Assert.assertTrue(jarFile.getEntry("HelloFromProvider.class").size > 0) + Assert.assertTrue(jarFile.getEntry("GeneratedFromProvider.class").size > 0) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt new file mode 100644 index 00000000..14cd0903 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaNestedClassIT.kt @@ -0,0 +1,22 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Rule +import org.junit.Test + +class JavaNestedClassIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("javaNestedClass") + + @Test + fun testJavaNestedClass() { + + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val resultCleanBuild = gradleRunner.withArguments("clean", "build").build() + Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome) + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt new file mode 100644 index 00000000..bf94e522 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/JavaOnlyIT.kt @@ -0,0 +1,34 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Assume +import org.junit.Rule +import org.junit.Test +import java.io.File + +class JavaOnlyIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("java-only", "test-processor") + + @Test + fun testJavaOnly() { + // FIXME: `clean` fails to delete files on windows. + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + File(project.root, "workload/src/main/java/com/example/Foo.java").delete() + + gradleRunner.withArguments("clean", "assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.NO_SOURCE, result.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt new file mode 100644 index 00000000..7c9c2dcd --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KMPImplementedIT.kt @@ -0,0 +1,369 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Assume +import org.junit.Rule +import org.junit.Test +import java.io.File +import java.util.jar.* + +class KMPImplementedIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("kmp") + + private fun verify(jarName: String, contents: List<String>) { + val artifact = File(project.root, jarName) + Assert.assertTrue(artifact.exists()) + + JarFile(artifact).use { jarFile -> + contents.forEach { + Assert.assertTrue(jarFile.getEntry(it).size > 0) + } + } + } + + private fun verifyKexe(path: String) { + val artifact = File(project.root, path) + Assert.assertTrue(artifact.exists()) + Assert.assertTrue(artifact.readBytes().size > 0) + } + + private fun checkExecutionOptimizations(log: String) { + Assert.assertFalse( + "Execution optimizations have been disabled", + log.contains("Execution optimizations have been disabled") + ) + } + + @Test + fun testJvm() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-jvm:build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-jvm:build")?.outcome) + verify( + "workload-jvm/build/libs/workload-jvm-jvm-1.0-SNAPSHOT.jar", + listOf( + "com/example/Foo.class" + ) + ) + Assert.assertFalse(it.output.contains("kotlin scripting plugin:")) + Assert.assertTrue(it.output.contains("w: [ksp] platforms: [JVM")) + Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true")) + checkExecutionOptimizations(it.output) + } + } + + @Test + fun testJvmErrorLog() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload-jvm/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n") + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-jvm:build" + ).buildAndFail().let { + val errors = it.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first()) + } + project.restore("workload-jvm/build.gradle.kts") + } + + @Test + fun testJs() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-js:build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-js:build")?.outcome) + verify( + "workload-js/build/libs/workload-js-jslegacy-1.0-SNAPSHOT.jar", + listOf( + "playground-workload-js-js-legacy.js" + ) + ) + verify( + "workload-js/build/libs/workload-js-jsir-1.0-SNAPSHOT.klib", + listOf( + "default/ir/types.knt" + ) + ) + Assert.assertFalse(it.output.contains("kotlin scripting plugin:")) + Assert.assertTrue(it.output.contains("w: [ksp] platforms: [JS")) + Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true")) + checkExecutionOptimizations(it.output) + } + } + + @Test + fun testJsErrorLog() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload-js/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n") + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-js:build" + ).buildAndFail().let { + val errors = it.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first()) + } + project.restore("workload-js/build.gradle.kts") + } + + @Test + fun testJsFailWarning() { + File(project.root, "workload-js/build.gradle.kts") + .appendText("\nksp {\n allWarningsAsErrors = true\n}\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-js:build" + ).buildAndFail() + } + + @Test + fun testAndroidNative() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-androidNative:build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-androidNative:build")?.outcome) + verifyKexe( + "workload-androidNative/build/bin/androidNativeX64/debugExecutable/workload-androidNative.kexe" + ) + verifyKexe( + "workload-androidNative/build/bin/androidNativeX64/releaseExecutable/workload-androidNative.kexe" + ) + verifyKexe( + "workload-androidNative/build/bin/androidNativeArm64/debugExecutable/workload-androidNative.kexe" + ) + verifyKexe( + "workload-androidNative/build/bin/androidNativeArm64/releaseExecutable/workload-androidNative.kexe" + ) + Assert.assertFalse(it.output.contains("kotlin scripting plugin:")) + Assert.assertTrue(it.output.contains("w: [ksp] platforms: [Native")) + checkExecutionOptimizations(it.output) + } + } + + @Test + fun testLinuxX64() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val genDir = File(project.root, "workload-linuxX64/build/generated/ksp/linuxX64/linuxX64Main/kotlin") + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-linuxX64:build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:build")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:kspTestKotlinLinuxX64")?.outcome) + verifyKexe("workload-linuxX64/build/bin/linuxX64/debugExecutable/workload-linuxX64.kexe") + verifyKexe("workload-linuxX64/build/bin/linuxX64/releaseExecutable/workload-linuxX64.kexe") + + // TODO: Enable after CI's Xcode version catches up. + // Assert.assertTrue( + // result.task(":workload-linuxX64:kspKotlinIosArm64")?.outcome == TaskOutcome.SUCCESS || + // result.task(":workload-linuxX64:kspKotlinIosArm64")?.outcome == TaskOutcome.SKIPPED + // ) + // Assert.assertTrue( + // result.task(":workload-linuxX64:kspKotlinMacosX64")?.outcome == TaskOutcome.SUCCESS || + // result.task(":workload-linuxX64:kspKotlinMacosX64")?.outcome == TaskOutcome.SKIPPED + // ) + Assert.assertTrue( + it.task(":workload-linuxX64:kspKotlinMingwX64")?.outcome == TaskOutcome.SUCCESS || + it.task(":workload-linuxX64:kspKotlinMingwX64")?.outcome == TaskOutcome.SKIPPED + ) + Assert.assertFalse(it.output.contains("kotlin scripting plugin:")) + Assert.assertTrue(it.output.contains("w: [ksp] platforms: [Native")) + Assert.assertTrue(it.output.contains("w: [ksp] List has superTypes: true")) + Assert.assertTrue(File(genDir, "Main_dot_kt.kt").exists()) + Assert.assertTrue(File(genDir, "ToBeRemoved_dot_kt.kt").exists()) + checkExecutionOptimizations(it.output) + } + + File(project.root, "workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt").delete() + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + ":workload-linuxX64:build" + ).build().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:build")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload-linuxX64:kspTestKotlinLinuxX64")?.outcome) + verifyKexe("workload-linuxX64/build/bin/linuxX64/debugExecutable/workload-linuxX64.kexe") + verifyKexe("workload-linuxX64/build/bin/linuxX64/releaseExecutable/workload-linuxX64.kexe") + Assert.assertTrue(File(genDir, "Main_dot_kt.kt").exists()) + Assert.assertFalse(File(genDir, "ToBeRemoved_dot_kt.kt").exists()) + checkExecutionOptimizations(it.output) + } + } + + @Test + fun testNonEmbeddableArtifact() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "-Pkotlin.native.useEmbeddableCompilerJar=false", + ":workload-linuxX64:kspTestKotlinLinuxX64" + ).build() + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "-Pkotlin.native.useEmbeddableCompilerJar=true", + ":workload-linuxX64:kspTestKotlinLinuxX64" + ).build() + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + ":workload-linuxX64:kspTestKotlinLinuxX64" + ).build() + } + + @Test + fun testLinuxX64ErrorLog() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload-linuxX64/build.gradle.kts") + .appendText("\nksp { arg(\"exception\", \"process\") }\n") + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + ":workload-linuxX64:build" + ).buildAndFail().let { + val errors = it.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first()) + } + project.restore("workload-js/build.gradle.kts") + } + + private fun verifyAll(result: BuildResult) { + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:build")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspTestKotlinLinuxX64")?.outcome) + + verify( + "workload/build/libs/workload-jvm-1.0-SNAPSHOT.jar", + listOf( + "com/example/Foo.class" + ) + ) + + verify( + "workload/build/libs/workload-jslegacy-1.0-SNAPSHOT.jar", + listOf( + "playground-workload-js-legacy.js" + ) + ) + + verify( + "workload/build/libs/workload-jsir-1.0-SNAPSHOT.klib", + listOf( + "default/ir/types.knt" + ) + ) + + verifyKexe("workload/build/bin/linuxX64/debugExecutable/workload.kexe") + verifyKexe("workload/build/bin/linuxX64/releaseExecutable/workload.kexe") + verifyKexe("workload/build/bin/androidNativeX64/debugExecutable/workload.kexe") + verifyKexe("workload/build/bin/androidNativeX64/releaseExecutable/workload.kexe") + verifyKexe("workload/build/bin/androidNativeArm64/debugExecutable/workload.kexe") + verifyKexe("workload/build/bin/androidNativeArm64/releaseExecutable/workload.kexe") + + // TODO: Enable after CI's Xcode version catches up. + // Assert.assertTrue( + // result.task(":workload:kspKotlinIosArm64")?.outcome == TaskOutcome.SUCCESS || + // result.task(":workload:kspKotlinIosArm64")?.outcome == TaskOutcome.SKIPPED + // ) + // Assert.assertTrue( + // result.task(":workload:kspKotlinMacosX64")?.outcome == TaskOutcome.SUCCESS || + // result.task(":workload:kspKotlinMacosX64")?.outcome == TaskOutcome.SKIPPED + // ) + Assert.assertTrue( + result.task(":workload:kspKotlinMingwX64")?.outcome == TaskOutcome.SUCCESS || + result.task(":workload:kspKotlinMingwX64")?.outcome == TaskOutcome.SKIPPED + ) + + Assert.assertFalse(result.output.contains("kotlin scripting plugin:")) + Assert.assertTrue(result.output.contains("w: [ksp] platforms: [JVM")) + Assert.assertTrue(result.output.contains("w: [ksp] platforms: [JS")) + Assert.assertTrue(result.output.contains("w: [ksp] platforms: [Native")) + } + + @Test + fun testMainConfiguration() { + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val buildScript = File(project.root, "workload/build.gradle.kts") + val lines = buildScript.readLines().takeWhile { + it.trimEnd() != "dependencies {" + } + buildScript.writeText(lines.joinToString(System.lineSeparator())) + buildScript.appendText(System.lineSeparator()) + buildScript.appendText("dependencies {") + buildScript.appendText(System.lineSeparator()) + buildScript.appendText(" add(\"ksp\", project(\":test-processor\"))") + buildScript.appendText(System.lineSeparator()) + buildScript.appendText("}") + + val messages = listOf( + "The 'ksp' configuration is deprecated in Kotlin Multiplatform projects. ", + "Please use target-specific configurations like 'kspJvm' instead." + ) + + // KotlinNative doesn't support configuration cache yet. + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + "build", + "-Pksp.allow.all.target.configuration=false" + ).buildAndFail().apply { + Assert.assertTrue( + messages.all { + output.contains(it) + } + ) + checkExecutionOptimizations(output) + } + + // KotlinNative doesn't support configuration cache yet. + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "clean", + "build" + ).build().apply { + Assert.assertTrue( + messages.all { + output.contains(it) + } + ) + verifyAll(this) + checkExecutionOptimizations(output) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt new file mode 100644 index 00000000..80f8cce6 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KSPCmdLineOptionsIT.kt @@ -0,0 +1,85 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.net.URLClassLoader + +data class CompileResult(val exitCode: ExitCode, val output: String) + +class KSPCmdLineOptionsIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("cmd-options") + + private fun runCmdCompiler(pluginOptions: List<String>): CompileResult { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.withArguments("clean", ":processors:build").build() + val processorJar = File(project.root, "processors/build/libs/processors-1.0-SNAPSHOT.jar") + val classLoader = URLClassLoader(arrayOf(processorJar.toURI().toURL()), javaClass.classLoader) + val compiler = classLoader.loadClass(K2JVMCompiler::class.java.name).newInstance() as K2JVMCompiler + val repoPath = "../build/repos/test/com/google/devtools/ksp/" + val kspPluginId = "com.google.devtools.ksp.symbol-processing" + val kspPluginJar = File("$repoPath/symbol-processing-cmdline/2.0.255-SNAPSHOT").listFiles()!!.filter { + it.name.matches(Regex(".*-\\d.jar")) + }.maxByOrNull { it.lastModified() }!! + val kspApiJar = File("$repoPath/symbol-processing-api/2.0.255-SNAPSHOT").listFiles()!!.filter { + it.name.matches(Regex(".*-\\d.jar")) + }.maxByOrNull { it.lastModified() }!! + val compilerArgs = mutableListOf( + "-no-stdlib", + "-Xplugin=${kspPluginJar.absolutePath}", + "-Xplugin=${kspApiJar.absolutePath}", + "-P", "plugin:$kspPluginId:apclasspath=${processorJar.absolutePath}", + "-P", "plugin:$kspPluginId:projectBaseDir=${project.root}/build", + "-P", "plugin:$kspPluginId:classOutputDir=${project.root}/build", + "-P", "plugin:$kspPluginId:javaOutputDir=${project.root}/build/out", + "-P", "plugin:$kspPluginId:kotlinOutputDir=${project.root}/build/out", + "-P", "plugin:$kspPluginId:resourceOutputDir=${project.root}/build/out", + "-P", "plugin:$kspPluginId:kspOutputDir=${project.root}/build/out", + "-P", "plugin:$kspPluginId:cachesDir=${project.root}/build/out", + "-P", "plugin:$kspPluginId:incremental=false", + "-d", "${project.root}/build/out" + ) + pluginOptions.forEach { + compilerArgs.add("-P") + compilerArgs.add("plugin:$kspPluginId:$it") + } + compilerArgs.add(File(project.root, "workload/src/main/kotlin/com/example/A.kt").absolutePath) + val outStream = ByteArrayOutputStream() + val exitCode = compiler.exec(PrintStream(outStream), *compilerArgs.toTypedArray()) + return CompileResult(exitCode, outStream.toString()) + } + + @Test + fun testWithCompilationOnError() { + val result = runCmdCompiler(listOf("apoption=error=true", "withCompilation=true")) + val errors = result.output.lines().filter { it.startsWith("error: [ksp]") } + val exitCode = result.exitCode + Assert.assertTrue(exitCode == ExitCode.COMPILATION_ERROR) + Assert.assertTrue( + errors.any { + it.startsWith("error: [ksp] java.lang.IllegalStateException: Error on request") + } + ) + } + + @Test + fun testWithCompilationOnErrorOk() { + val result = runCmdCompiler(listOf("apoption=error=true", "returnOkOnError=true", "withCompilation=true")) + val errors = result.output.lines().filter { it.startsWith("error: [ksp]") } + val exitCode = result.exitCode + Assert.assertTrue(exitCode == ExitCode.OK) + Assert.assertTrue( + errors.any { + it.startsWith("error: [ksp] java.lang.IllegalStateException: Error on request") + } + ) + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt new file mode 100644 index 00000000..d8714bf7 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/KotlinConstsInJavaIT.kt @@ -0,0 +1,42 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Assume +import org.junit.Rule +import org.junit.Test +import java.io.File + +class KotlinConstsInJavaIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("kotlin-consts-in-java") + + private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) = + buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck) + + private fun GradleRunner.buildAndCheckOutcome( + vararg args: String, + outcome: TaskOutcome, + extraCheck: (BuildResult) -> Unit = {} + ) { + val result = this.withArguments(*args).build() + + Assert.assertEquals(outcome, result.task(":workload:kspKotlin")?.outcome) + + extraCheck(result) + } + + @Test + fun testKotlinConstsInJava() { + // FIXME: `clean` fails to delete files on windows. + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true) + gradleRunner.buildAndCheck(":workload:kspKotlin") + + File(project.root, "workload/src/main/java/com/example/JavaClass.java").appendText("\n") + gradleRunner.buildAndCheck(":workload:kspKotlin") + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt new file mode 100644 index 00000000..870b4e60 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MapAnnotationArgumentsIT.kt @@ -0,0 +1,40 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Rule +import org.junit.Test + +class MapAnnotationArgumentsIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("map-annotation-arguments", "test-processor") + + val expectedErrors = listOf( + "e: [ksp] unboxedChar: Char != Character\n", + "e: [ksp] boxedChar: (Char..Char?) != Character\n", + "e: Error occurred in KSP, check log for detail\n", + ) + + @Test + fun testMapAnnotationArguments() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("assemble", "-Pksp.map.annotation.arguments.in.java=true").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + gradleRunner.withArguments("clean", "assemble", "--rerun-tasks").buildAndFail().let { result -> + Assert.assertEquals(TaskOutcome.FAILED, result.task(":workload:kspKotlin")?.outcome) + Assert.assertTrue(expectedErrors.all { it in result.output }) + } + + gradleRunner.withArguments("clean", "assemble", "-Pksp.map.annotation.arguments.in.java=false", "--rerun-tasks") + .buildAndFail().let { result -> + Assert.assertEquals(TaskOutcome.FAILED, result.task(":workload:kspKotlin")?.outcome) + Assert.assertTrue(expectedErrors.all { it in result.output }) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt new file mode 100644 index 00000000..9d7cb56e --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/MultiplatformIT.kt @@ -0,0 +1,34 @@ +package com.google.devtools.ksp.test + +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 +import java.util.jar.* + +class MultiplatformIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground-mpp", "playground") + + @Test + fun testJVM() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + val resultCleanBuild = + gradleRunner.withArguments("--configuration-cache-problems=warn", "clean", "build").build() + + Assert.assertEquals(TaskOutcome.SUCCESS, resultCleanBuild.task(":workload:build")?.outcome) + + val artifact = File(project.root, "workload/build/libs/workload-jvm-1.0-SNAPSHOT.jar") + Assert.assertTrue(artifact.exists()) + + JarFile(artifact).use { jarFile -> + Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0) + Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0) + Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt new file mode 100644 index 00000000..d44740af --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnErrorIT.kt @@ -0,0 +1,111 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Rule +import org.junit.Test +import java.io.File + +class OnErrorIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("on-error") + + @Test + fun testOnError() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] Error processor: errored at 2", errors.first()) + Assert.assertEquals("e: [ksp] NormalProcessor called error on 2", errors.last()) + } + } + + @Test + fun testOnExceptionInInit() { + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"init\") }\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in init", errors.first()) + } + project.restore("workload/build.gradle.kts") + } + + @Test + fun testOnExceptionInProcess() { + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"process\") }\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in process", errors.first()) + } + project.restore("workload/build.gradle.kts") + } + + @Test + fun testOnExceptionInFinish() { + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"finish\") }\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in finish", errors.first()) + } + project.restore("workload/build.gradle.kts") + } + + @Test + fun testOnExceptionInOnError() { + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"error\") }\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + + Assert.assertEquals("e: [ksp] Error processor: errored at 2", errors.first()) + Assert.assertEquals("e: [ksp] java.lang.Exception: Test Exception in error", errors[1]) + } + project.restore("workload/build.gradle.kts") + } + + @Test + fun testCreateTwice() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true) + + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"createTwice\") }\n") + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + + Assert.assertTrue( + errors.any { + it.startsWith("e: [ksp] kotlin.io.FileAlreadyExistsException:") + } + ) + + Assert.assertFalse(result.output.contains("e: java.lang.IllegalStateException: Should not be called!")) + } + project.restore("workload/build.gradle.kts") + } + + @Test + fun testCreateTwiceNotOkOnError() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true) + + File(project.root, "workload/build.gradle.kts").appendText("\nksp { arg(\"exception\", \"createTwice\") }\n") + File(project.root, "gradle.properties").appendText("\nksp.return.ok.on.error=false") + gradleRunner.withArguments("clean", "assemble").buildAndFail().let { result -> + val errors = result.output.lines().filter { it.startsWith("e: [ksp]") } + + Assert.assertTrue( + errors.any { + it.startsWith("e: [ksp] kotlin.io.FileAlreadyExistsException:") + } + ) + } + project.restore("workload/build.gradle.kts") + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt new file mode 100644 index 00000000..21b182a5 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OnlyResourcesFileIT.kt @@ -0,0 +1,21 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.Test + +class OnlyResourcesFileIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("only-resources-file") + + @Test + fun test() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments( + "--configuration-cache-problems=warn", + "jvmJar", + ).build() + } +} 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 00000000..9c39e743 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/OutputDepsIt.kt @@ -0,0 +1,157 @@ +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.Assume +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", + ), + ) + + val src2Output = mapOf( + "workload/src/main/java/p1/J1.java" to setOf( + "kotlin/p1/J1Generated.kt", + "kotlin/p1/K1Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/java/p1/J2.java" to setOf( + "kotlin/p1/J2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/kotlin/p1/K1.kt" to setOf( + "kotlin/p1/J1Generated.kt", + "kotlin/p1/K1Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/kotlin/p1/K2.kt" to setOf( + "kotlin/p1/J1Generated.kt", + "kotlin/p1/K1Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + ) + + val deletedSrc2Output = listOf( + "workload/src/main/java/p1/J1.java" to listOf( + "kotlin/p1/Anno1Generated.kt", + "kotlin/p1/Anno2Generated.kt", + "kotlin/p1/J2Generated.kt", + "kotlin/p1/K1Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/java/p1/J2.java" to listOf( + "kotlin/p1/Anno1Generated.kt", + "kotlin/p1/Anno2Generated.kt", + "kotlin/p1/K1Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/kotlin/p1/K1.kt" to listOf( + "kotlin/p1/Anno1Generated.kt", + "kotlin/p1/Anno2Generated.kt", + "kotlin/p1/K2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + "workload/src/main/kotlin/p1/K2.kt" to listOf( + "kotlin/p1/Anno1Generated.kt", + "kotlin/p1/Anno2Generated.kt", + "resources/p1.Anno1.log", + "resources/p1.Anno2.log", + ), + ) + + @Test + fun testOutputDeps() { + // FIXME + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + 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) -> + val srcFile = File(project.root, src) + // In case that the test goes faster than the precision of timestamps. + // It's 1s on Github's CI. + Thread.sleep(1000) + srcFile.appendText("\n\n") + Thread.sleep(1000) + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val dirties = result.output.lines().filter { it.startsWith("w: [ksp]") }.toSet() + Assert.assertEquals(expectedDirties, dirties) + + val outputRoot = File(project.root, "workload/build/generated/ksp/main/") + outputRoot.walk().filter { it.isFile() }.forEach { + if (it.toRelativeString(outputRoot) in src2Output[src]!!) { + Assert.assertTrue(it.lastModified() > srcFile.lastModified()) + } else { + Assert.assertTrue(it.lastModified() < srcFile.lastModified()) + } + } + } + } + val incrementalArtifact = Artifact(File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar")) + Assert.assertEquals(cleanArtifact, incrementalArtifact) + } + + @Test + fun testDeletion() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:assemble")?.outcome) + } + + deletedSrc2Output.forEach { (src, expectedDirties) -> + File(project.root, src).delete() + gradleRunner.withArguments("assemble").build().let { result -> + Assert.assertEquals(TaskOutcome.SUCCESS, result.task(":workload:kspKotlin")?.outcome) + val outputRoot = File(project.root, "workload/build/generated/ksp/main/") + val outputs = outputRoot.walk().filter { it.isFile() }.map { + it.toRelativeString(outputRoot).replace(File.separatorChar, '/') + }.toList().sorted() + Assert.assertEquals(expectedDirties, outputs) + } + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt new file mode 100644 index 00000000..eeaaf502 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PlaygroundIT.kt @@ -0,0 +1,210 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.Assert +import org.junit.Assume +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import java.io.File +import java.util.jar.* + +class PlaygroundIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground") + + private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) = + buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck) + + private fun GradleRunner.buildAndCheckOutcome( + vararg args: String, + outcome: TaskOutcome, + extraCheck: (BuildResult) -> Unit = {} + ) { + val result = this.withArguments(*args).build() + + Assert.assertEquals(outcome, result.task(":workload:build")?.outcome) + + val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar") + Assert.assertTrue(artifact.exists()) + + JarFile(artifact).use { jarFile -> + Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0) + Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0) + Assert.assertTrue(jarFile.getEntry("g/G.class").size > 0) + Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0) + } + + extraCheck(result) + } + + @Test + fun testPlayground() { + // FIXME: `clean` fails to delete files on windows. + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.buildAndCheck("clean", "build") + gradleRunner.buildAndCheck("clean", "build") + } + + // TODO: add another plugin and see if it is blocked. + // Or use a project that depends on a builtin plugin like all-open and see if the build fails + @Test + fun testBlockOtherCompilerPlugins() { + // FIXME: `clean` fails to delete files on windows. + Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload/build.gradle.kts") + .appendText("\nksp {\n blockOtherCompilerPlugins = false\n}\n") + gradleRunner.buildAndCheck("clean", "build") + gradleRunner.buildAndCheck("clean", "build") + project.restore("workload/build.gradle.kts") + } + + @Test + fun testAllowSourcesFromOtherPlugins() { + fun checkGBuilder() { + val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar") + + JarFile(artifact).use { jarFile -> + Assert.assertTrue(jarFile.getEntry("g/GBuilder.class").size > 0) + } + } + + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload/build.gradle.kts") + .appendText("\nksp {\n allowSourcesFromOtherPlugins = true\n}\n") + gradleRunner.buildAndCheck("clean", "build") { checkGBuilder() } + gradleRunner.buildAndCheckOutcome("build", "--info", outcome = TaskOutcome.UP_TO_DATE) { + Assert.assertEquals(TaskOutcome.UP_TO_DATE, it.task(":workload:kspKotlin")?.outcome) + checkGBuilder() + } + project.restore("workload/build.gradle.kts") + } + + /** Regression test for https://github.com/google/ksp/issues/518. */ + @Test + fun testBuildWithConfigureOnDemand() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.buildAndCheck("--configure-on-demand", ":workload:build") + } + + @Test + fun testBuildCache() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + // The first build can be FROM_CACHE or SUCCESS, and we only care about the second build. + gradleRunner.buildAndCheck("--build-cache", ":workload:clean", "build") + gradleRunner.buildAndCheck("--build-cache", ":workload:clean", "build") { + Assert.assertEquals(TaskOutcome.FROM_CACHE, it.task(":workload:kspKotlin")?.outcome) + } + } + + @Test + fun testAllWarningsAsErrors() { + File(project.root, "workload/build.gradle.kts") + .appendText("\nksp {\n allWarningsAsErrors = true\n}\n") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.withArguments("build").buildAndFail().let { result -> + Assert.assertTrue(result.output.contains("This is a harmless warning.")) + } + } + + // Compiler's test infra report this kind of error before KSP, so it is not testable there. + @Test + fun testNoFunctionName() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + fun buildAndFileAndCheck() { + gradleRunner.withArguments("build").buildAndFail().let { result -> + Assert.assertTrue(result.output.contains("Function declaration must have a name")) + } + } + + File(project.root, "workload/src/main/java/com/example/A.kt").appendText("\n{}\n") + buildAndFileAndCheck() + project.restore("workload/src/main/java/com/example/A.kt") + + File(project.root, "workload/src/main/java/com/example/A.kt").appendText("\nfun() = {0}\n") + buildAndFileAndCheck() + project.restore("workload/src/main/java/com/example/A.kt") + } + + @Test + fun testRewriteFile() { + File( + project.root, + "test-processor/src/main/resources/META-INF/services/" + + "com.google.devtools.ksp.processing.SymbolProcessorProvider" + ).writeText("RewriteProcessorProvider") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.withArguments("build").buildAndFail().let { result -> + Assert.assertTrue(result.output.contains("kotlin.io.FileAlreadyExistsException")) + } + } + + // Disabled for now: ERROR: K2 does not support plugins yet, so please remove -Xuse-k2 flag + // Test -Xuse-fir for compilation; KSP still uses FE1.0 + @Ignore + @Test + fun testFirPreview() { + val gradleProperties = File(project.root, "gradle.properties") + gradleProperties.appendText("\nkotlin.useK2=true") + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.buildAndCheck("clean", "build") { result -> + Assert.assertTrue(result.output.contains("This build uses experimental K2 compiler")) + Assert.assertTrue(result.output.contains("-Xuse-k2")) + } + project.restore(gradleProperties.path) + } + + @Test + fun testVersions() { + val kotlinCompile = "org.jetbrains.kotlin.gradle.tasks.KotlinCompile" + val buildFile = File(project.root, "workload/build.gradle.kts") + buildFile.appendText("\ntasks.withType<$kotlinCompile> {") + buildFile.appendText("\n kotlinOptions.apiVersion = \"1.5\"") + buildFile.appendText("\n kotlinOptions.languageVersion = \"1.5\"") + buildFile.appendText("\n}") + + val kotlinVersion = System.getProperty("kotlinVersion").split('-').first() + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.buildAndCheck("clean", "build") { result -> + Assert.assertTrue(result.output.contains("language version: 1.5")) + Assert.assertTrue(result.output.contains("api version: 1.5")) + Assert.assertTrue(result.output.contains("compiler version: $kotlinVersion")) + } + project.restore(buildFile.path) + } + + @Test + fun testExcludeProcessor() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + File(project.root, "workload/build.gradle.kts") + .appendText("\nksp {\n excludeProcessor(\"TestProcessorProvider\")\n") + File(project.root, "workload/build.gradle.kts") + .appendText("\n excludeProcessor(\"NotMatchingAnything\")\n}\n") + gradleRunner.withArguments("build").buildAndFail().let { + Assert.assertEquals(TaskOutcome.SUCCESS, it.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.FAILED, it.task(":workload:compileKotlin")?.outcome) + Assert.assertTrue("Unresolved reference: AClassBuilder" in it.output) + } + gradleRunner.withArguments("build").buildAndFail().let { + Assert.assertEquals(TaskOutcome.UP_TO_DATE, it.task(":workload:kspKotlin")?.outcome) + Assert.assertEquals(TaskOutcome.FAILED, it.task(":workload:compileKotlin")?.outcome) + Assert.assertTrue("Unresolved reference: AClassBuilder" in it.output) + } + + project.restore("workload/build.gradle.kts") + File(project.root, "workload/build.gradle.kts") + .appendText("\nksp {\n excludeProcessor(\"DoNotMatch\")\n}\n") + gradleRunner.buildAndCheck("build") + + project.restore("workload/build.gradle.kts") + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt new file mode 100644 index 00000000..9f3a46dd --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/PsiCacheIT.kt @@ -0,0 +1,18 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.Test + +class PsiCacheIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("psi-cache", "test-processor") + + @Test + fun testPsiCache() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + + gradleRunner.withArguments("assemble").build() + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt new file mode 100644 index 00000000..fea748f0 --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/TemporaryTestProject.kt @@ -0,0 +1,41 @@ +package com.google.devtools.ksp.test + +import org.junit.rules.TemporaryFolder +import java.io.File + +class TemporaryTestProject(projectName: String, baseProject: String? = null) : TemporaryFolder() { + private val testProjectSrc = File("src/test/resources", projectName) + private val baseProjectSrc = baseProject?.let { File("src/test/resources", baseProject) } + + override fun before() { + super.before() + + baseProjectSrc?.copyRecursively(root) + testProjectSrc.copyRecursively(root, true) + + val kotlinVersion = System.getProperty("kotlinVersion") + val kspVersion = System.getProperty("kspVersion") + val agpVersion = System.getProperty("agpVersion") + val testRepo = System.getProperty("testRepo").replace(File.separator, "/") + val gradleProperties = File(root, "gradle.properties") + gradleProperties.appendText("\nkotlinVersion=$kotlinVersion") + gradleProperties.appendText("\nkspVersion=$kspVersion") + gradleProperties.appendText("\nagpVersion=$agpVersion") + gradleProperties.appendText("\ntestRepo=$testRepo") + gradleProperties.appendText("\norg.gradle.unsafe.configuration-cache=true") + gradleProperties.appendText("\nkotlin.jvm.target.validation.mode=warning") + // Uncomment this to debug compiler and compiler plugin. + // gradleProperties.appendText("\nsystemProp.kotlin.compiler.execution.strategy=in-process") + } + + fun restore(file: String) { + fun copySafe(src: File, dst: File) { + if (src.exists()) + src.copyTo(dst, true) + } + baseProjectSrc?.let { + copySafe(File(baseProjectSrc, file), File(root, file)) + } + copySafe(File(testProjectSrc, file), File(root, file)) + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt new file mode 100644 index 00000000..935cd39b --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VerboseIT.kt @@ -0,0 +1,67 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.BuildResult +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 +import java.util.jar.JarFile + +class VerboseIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground") + + private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) = + buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck) + + private fun GradleRunner.buildAndCheckOutcome( + vararg args: String, + outcome: TaskOutcome, + extraCheck: (BuildResult) -> Unit = {} + ) { + val result = this.withArguments(*args).build() + + Assert.assertEquals(outcome, result.task(":workload:build")?.outcome) + + val artifact = File(project.root, "workload/build/libs/workload-1.0-SNAPSHOT.jar") + Assert.assertTrue(artifact.exists()) + + JarFile(artifact).use { jarFile -> + Assert.assertTrue(jarFile.getEntry("TestProcessor.log").size > 0) + Assert.assertTrue(jarFile.getEntry("hello/HELLO.class").size > 0) + Assert.assertTrue(jarFile.getEntry("g/G.class").size > 0) + Assert.assertTrue(jarFile.getEntry("com/example/AClassBuilder.class").size > 0) + } + + extraCheck(result) + } + + @Test + fun testNoProvider() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + File( + project.root, + "test-processor/src/main/resources/META-INF/services/" + + "com.google.devtools.ksp.processing.SymbolProcessorProvider" + ).delete() + gradleRunner.withArguments("build").buildAndFail().let { result -> + Assert.assertTrue(result.output.contains("No providers found in processor classpath.")) + } + } + + @Test + fun testProviderAndRoundLogging() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + gradleRunner.buildAndCheck("--debug", "clean", "build") { result -> + Assert.assertTrue( + result.output.contains( + "i: [ksp] loaded provider(s): [TestProcessorProvider, TestProcessorProvider2]" + ) + ) + Assert.assertTrue(result.output.contains("v: [ksp] round 3 of processing")) + } + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt new file mode 100644 index 00000000..52a62a4d --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/VersionCheckIT.kt @@ -0,0 +1,42 @@ +package com.google.devtools.ksp.test + +import org.gradle.testkit.runner.GradleRunner +import org.junit.Assert +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +@Ignore +class VersionCheckIT { + @Rule + @JvmField + val project: TemporaryTestProject = TemporaryTestProject("playground") + + @Test + fun testVersion() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val result = gradleRunner.withArguments( + "-PkotlinVersion=1.4.20", "clean", "build" + ).buildAndFail() + Assert.assertTrue(result.output.contains("is too new for kotlin")) + } + + @Test + fun testVersionOK() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val result = gradleRunner.withArguments( + "clean", "build" + ).build() + Assert.assertFalse(result.output.contains("is too new for kotlin")) + Assert.assertFalse(result.output.contains("is too old for kotlin")) + } + + @Test + fun testMuteVersionCheck() { + val gradleRunner = GradleRunner.create().withProjectDir(project.root) + val result = gradleRunner.withArguments( + "-PkotlinVersion=1.4.20", "-Pksp.version.check=false", "clean", "build" + ).buildAndFail() + Assert.assertFalse(result.output.contains("is too new for kotlin")) + } +} diff --git a/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt new file mode 100644 index 00000000..cb66ea1b --- /dev/null +++ b/integration-tests/src/test/kotlin/com/google/devtools/ksp/test/fixtures/BuildResultFixture.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Google LLC + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.devtools.ksp.test.fixtures + +import org.gradle.testkit.runner.BuildResult + +private val compileKotlinSrcs = Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)") + +class BuildResultFixture(private val result: BuildResult) { + + /** Get all compiled Kotlin sources in the current build. */ + val compiledKotlinSources by lazy { + compileKotlinSrcs.findAll(result.output) + .asIterable() + .flatMap { + it.groups[1]!!.value.split(", ") + }.toSet() + } +} diff --git a/integration-tests/src/test/resources/buildcache/settings.gradle.kts b/integration-tests/src/test/resources/buildcache/settings.gradle.kts new file mode 100644 index 00000000..24430bc3 --- /dev/null +++ b/integration-tests/src/test/resources/buildcache/settings.gradle.kts @@ -0,0 +1,27 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +buildCache { + val buildCacheDir: String by settings + local { + directory = File(buildCacheDir) + removeUnusedEntriesAfterDays = 30 + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/cmd-options/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/cmd-options/gradle.properties b/integration-tests/src/test/resources/cmd-options/gradle.properties new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/gradle.properties diff --git a/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts new file mode 100644 index 00000000..999cb80a --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/processors/build.gradle.kts @@ -0,0 +1,23 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..c38d2298 --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/processors/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,37 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class TestProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + lateinit var options: Map<String, String> + var rounds = 0 + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + this.logger = logger + this.options = options + this.codeGenerator = codeGenerator + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (options.containsKey("error")) { + throw IllegalStateException("Error on request") + } + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..e6522f19 --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider
\ No newline at end of file diff --git a/integration-tests/src/test/resources/cmd-options/settings.gradle.kts b/integration-tests/src/test/resources/cmd-options/settings.gradle.kts new file mode 100644 index 00000000..79202ef6 --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "cmd-options" + +include(":workload") +include(":processors") diff --git a/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts b/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts new file mode 100644 index 00000000..a79cec86 --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/workload/build.gradle.kts @@ -0,0 +1,19 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + ksp(project(":processors")) +} diff --git a/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt b/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt new file mode 100644 index 00000000..ebf0688c --- /dev/null +++ b/integration-tests/src/test/resources/cmd-options/workload/src/main/kotlin/com/example/A.kt @@ -0,0 +1,4 @@ +package com.example + +fun main() { +} diff --git a/integration-tests/src/test/resources/hmpp/build.gradle.kts b/integration-tests/src/test/resources/hmpp/build.gradle.kts new file mode 100644 index 00000000..8dd65667 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin("multiplatform") apply false +} + +val testRepo: String by project +allprojects { + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} diff --git a/integration-tests/src/test/resources/hmpp/gradle.properties b/integration-tests/src/test/resources/hmpp/gradle.properties new file mode 100644 index 00000000..4a9594ae --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M
\ No newline at end of file diff --git a/integration-tests/src/test/resources/hmpp/settings.gradle.kts b/integration-tests/src/test/resources/hmpp/settings.gradle.kts new file mode 100644 index 00000000..0f0b53a3 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("multiplatform") version kotlinVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "hmpp" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts new file mode 100644 index 00000000..842898f6 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/test-processor/build.gradle.kts @@ -0,0 +1,22 @@ +val kspVersion: String by project + +plugins { + kotlin("multiplatform") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +kotlin { + jvm() + sourceSets { + val jvmMain by getting { + dependencies { + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") + } + kotlin.srcDir("src/main/kotlin") + resources.srcDir("src/main/resources") + } + } +} diff --git a/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt b/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt new file mode 100644 index 00000000..0b1c39a6 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/test-processor/src/main/kotlin/EchoProcessor.kt @@ -0,0 +1,27 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class EchoProcessor(val codeGenerator: CodeGenerator, val logger: KSPLogger) : SymbolProcessor { + var invoked = false + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (invoked) { + return emptyList() + } + invoked = true + + val allInputs = resolver.getAllFiles().map { it.fileName.split(".").first() }.sorted().joinToString("_") + + logger.warn("EchoProcessor: $allInputs") + + codeGenerator.createNewFile(Dependencies(true), "", "($allInputs)").close() + + return emptyList() + } +} + +class EchoProcessorProvider : SymbolProcessorProvider { + override fun create(env: SymbolProcessorEnvironment): SymbolProcessor { + return EchoProcessor(env.codeGenerator, env.logger) + } +} diff --git a/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c0cdf71a --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +EchoProcessorProvider diff --git a/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts b/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts new file mode 100644 index 00000000..a6574fc3 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/build.gradle.kts @@ -0,0 +1,55 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + + js(IR) { + browser() + } + + linuxX64() { + binaries { + executable() + } + } + + sourceSets { + val commonMain by getting + + val jvmJs by sourceSets.creating { + dependsOn(commonMain) + } + + val jvmLinuxX64 by sourceSets.creating { + dependsOn(commonMain) + } + + val jvmOnly by sourceSets.creating { + dependsOn(jvmJs) + dependsOn(jvmLinuxX64) + } + + val linuxX64Main by getting { + dependsOn(jvmLinuxX64) + } + + val jvmMain by getting { + dependsOn(jvmOnly) + } + + val jsMain by getting { + dependsOn(jvmJs) + } + } +} + +dependencies { + add("ksp", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt new file mode 100644 index 00000000..301f3efb --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/commonMain/kotlin/CommonMain.kt @@ -0,0 +1 @@ +class CommonMain diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt new file mode 100644 index 00000000..1192259a --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/jsMain/kotlin/JsMain.kt @@ -0,0 +1 @@ +class JsMain diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt new file mode 100644 index 00000000..b5ad67b7 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmJs/kotlin/JvmJs.kt @@ -0,0 +1 @@ +class JvmJs diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt new file mode 100644 index 00000000..38ab721d --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmLinuxX64/kotlin/JvmLinuxX64.kt @@ -0,0 +1 @@ +class JvmLinuxX64 diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt new file mode 100644 index 00000000..ffae14b7 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmMain/kotlin/JvmMain.kt @@ -0,0 +1 @@ +class JvmMain diff --git a/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt b/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt new file mode 100644 index 00000000..a373d6e5 --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/jvmOnly/kotlin/JvmOnly.kt @@ -0,0 +1 @@ +class JvmOnly diff --git a/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt b/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt new file mode 100644 index 00000000..3058bc8d --- /dev/null +++ b/integration-tests/src/test/resources/hmpp/workload/src/linuxX64Main/kotlin/LinuxX64Main.kt @@ -0,0 +1,4 @@ +class LinuxX64Main + +fun main() { +} diff --git a/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/incremental-classpath/gradle.properties b/integration-tests/src/test/resources/incremental-classpath/gradle.properties new file mode 100644 index 00000000..6b0be573 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/gradle.properties @@ -0,0 +1,3 @@ +ksp.incremental=true +ksp.incremental.log=true +ksp.incremental.intermodule=true
\ No newline at end of file diff --git a/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts new file mode 100644 index 00000000..b89421bb --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l1/build.gradle.kts @@ -0,0 +1,18 @@ +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":l2")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt b/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt new file mode 100644 index 00000000..dfd4184b --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l1/src/main/kotlin/p1/L1.kt @@ -0,0 +1,3 @@ +package p1 + +open class L1 : L2() diff --git a/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts new file mode 100644 index 00000000..d4adefee --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l2/build.gradle.kts @@ -0,0 +1,17 @@ +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt b/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt new file mode 100644 index 00000000..ce4f1ec1 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l2/src/main/kotlin/p1/L2.kt @@ -0,0 +1,3 @@ +package p1 + +open class L2 diff --git a/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts new file mode 100644 index 00000000..d4adefee --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l3/build.gradle.kts @@ -0,0 +1,17 @@ +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt b/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt new file mode 100644 index 00000000..1b79ba13 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l3/src/main/kotlin/p1/L3.kt @@ -0,0 +1,3 @@ +package p1 + +open class L3 diff --git a/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts new file mode 100644 index 00000000..d4adefee --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l4/build.gradle.kts @@ -0,0 +1,17 @@ +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt b/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt new file mode 100644 index 00000000..a8c6aa17 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l4/src/main/kotlin/p1/L4.kt @@ -0,0 +1,3 @@ +package p1 + +open class L4 diff --git a/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts new file mode 100644 index 00000000..d4adefee --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l5/build.gradle.kts @@ -0,0 +1,17 @@ +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt b/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt new file mode 100644 index 00000000..5bfed66b --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/l5/src/main/kotlin/p1/L5.kt @@ -0,0 +1,3 @@ +package p1 + +open class L5 diff --git a/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts new file mode 100644 index 00000000..c4a1f4cd --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/settings.gradle.kts @@ -0,0 +1,24 @@ +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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "incremental-test" + +include(":workload") +include(":validator") +include(":l1") +include(":l2") +include(":l3") +include(":l4") +include(":l5") diff --git a/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/validator/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/incremental-classpath/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt new file mode 100644 index 00000000..a467ade0 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/kotlin/Validator.kt @@ -0,0 +1,79 @@ +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import com.google.devtools.ksp.visitor.KSDefaultVisitor +import java.io.OutputStreamWriter + +class Validator : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + var processed = false + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (processed) { + return emptyList() + } + val validator = object : KSDefaultVisitor<OutputStreamWriter, Unit>() { + override fun defaultHandler(node: KSNode, data: OutputStreamWriter) = Unit + + override fun visitDeclaration(declaration: KSDeclaration, data: OutputStreamWriter) { + data.write(declaration.qualifiedName?.asString() ?: declaration.simpleName.asString()) + declaration.validate() + } + } + + // for each file, create an output from it and everything reachable from it. + val files = resolver.getAllFiles() + files.forEach { file -> + logger.warn("${file.packageName.asString()}/${file.fileName}") + val output = OutputStreamWriter( + codeGenerator.createNewFile( + Dependencies(false, file), + file.packageName.asString(), + file.fileName, + "log" + ) + ) + file.declarations.forEach { + it.accept(validator, output) + } + output.close() + } + + // create an output from k3 + l4 + val k3 = resolver.getClassDeclarationByName("p1.K3")!! + val l4 = resolver.getClassDeclarationByName("p1.L4")!! + codeGenerator.createNewFile(Dependencies(false), "p1", "l4", "log") + codeGenerator.associateWithClasses(listOf(k3, l4), "p1", "l4", "log") + + // create an output from l5 + val l5 = resolver.getClassDeclarationByName("p1.L5")!! + codeGenerator.createNewFile(Dependencies(false), "p1", "l5", "log") + codeGenerator.associateWithClasses(listOf(l5), "p1", "l5", "log") + logger.warn("processing done") + + processed = true + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return Validator().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts new file mode 100644 index 00000000..e8f910b1 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/workload/build.gradle.kts @@ -0,0 +1,26 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":validator")) + implementation(project(":l1")) + implementation(project(":l2")) + implementation(project(":l3")) + implementation(project(":l4")) + implementation(project(":l5")) + testImplementation("junit:junit:4.12") + ksp(project(":validator")) +} diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt new file mode 100644 index 00000000..69c56673 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K1.kt @@ -0,0 +1,5 @@ +package p1 + +open class K1 { + val v = L1() +} diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt new file mode 100644 index 00000000..ea09ec3d --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K2.kt @@ -0,0 +1,5 @@ +package p1 + +open class K2 { + val v = L2() +} diff --git a/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt new file mode 100644 index 00000000..29929705 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-classpath/workload/src/main/kotlin/p1/K3.kt @@ -0,0 +1,5 @@ +package p1 + +open class K3 { + val v = L3() +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/gradle.properties b/integration-tests/src/test/resources/incremental-multi-chain/gradle.properties new file mode 100644 index 00000000..f9325643 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/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/incremental-multi-chain/processors/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt new file mode 100644 index 00000000..d7c25217 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Aggregator.kt @@ -0,0 +1,46 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStreamWriter + +class Aggregator : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + val impls = resolver.getSymbolsWithAnnotation("Impl").map { it as KSDeclaration }.toList() + if (impls.isNotEmpty()) { + val names = impls.map { it.simpleName.asString() }.sorted() + OutputStreamWriter( + codeGenerator.createNewFile( + Dependencies(true), "", "AllImpls" + ) + ).use { + it.write("class AllImpls {\n") + it.write(" override fun toString() = \"$names\"\n") + it.write("}\n") + } + codeGenerator.associate(impls.map { it.containingFile!! }.toList(), "", "AllImpls") + } + return emptyList() + } +} + +class AggregatorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return Aggregator().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt new file mode 100644 index 00000000..fec8d2d2 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/ImplGen.kt @@ -0,0 +1,46 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStreamWriter + +class ImplGen : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + resolver.getSymbolsWithAnnotation("NeedsImpl").forEach { decl -> + decl as KSClassDeclaration + val file = decl.containingFile!! + val baseName = decl.simpleName.asString() + val implName = baseName + "Impl" + OutputStreamWriter( + codeGenerator.createNewFile( + Dependencies(false, file), + "", implName + ) + ).use { + it.write("@Impl class $implName : $baseName\n") + } + } + return emptyList() + } +} + +class ImplGenProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return ImplGen().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt new file mode 100644 index 00000000..5ecf7527 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/kotlin/Validator.kt @@ -0,0 +1,36 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate + +class Validator : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + resolver.getNewFiles().forEach { + logger.warn("validating ${it.fileName}") + it.validate() + } + return emptyList() + } +} + +class ValidatorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return Validator().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..906e3eaf --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/processors/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,3 @@ +AggregatorProvider +ImplGenProvider +ValidatorProvider diff --git a/integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/settings.gradle.kts new file mode 100644 index 00000000..18f47de4 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "incremental-test" + +include(":workload") +include(":processors") diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts new file mode 100644 index 00000000..2f6a0fc5 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/build.gradle.kts @@ -0,0 +1,24 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") + application +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + ksp(project(":processors")) +} + +application { + mainClassName = "MainKt" +} diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt new file mode 100644 index 00000000..ac8854e9 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/K1.kt @@ -0,0 +1,2 @@ +@NeedsImpl +interface K1 diff --git a/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt new file mode 100644 index 00000000..cdf05607 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-multi-chain/workload/src/main/kotlin/Main.kt @@ -0,0 +1,8 @@ +annotation class NeedsImpl +annotation class Impl + +val x: AllImpls = AllImpls() + +fun main() { + println(x.toString()) +} diff --git a/integration-tests/src/test/resources/incremental-removal/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/incremental-removal/gradle.properties b/integration-tests/src/test/resources/incremental-removal/gradle.properties new file mode 100644 index 00000000..f9325643 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/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/incremental-removal/settings.gradle.kts b/integration-tests/src/test/resources/incremental-removal/settings.gradle.kts new file mode 100644 index 00000000..75f651ef --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "incremental-test" + +include(":workload") +include(":validator") diff --git a/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/validator/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/incremental-removal/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt new file mode 100644 index 00000000..635c7d7a --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/validator/src/main/kotlin/Validator.kt @@ -0,0 +1,48 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import java.io.OutputStreamWriter + +class Validator : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + resolver.getSymbolsWithAnnotation("p1.MyAnnotation").singleOrNull()?.let { decl -> + decl as KSClassDeclaration + val file = decl.containingFile!! + OutputStreamWriter( + codeGenerator.createNewFile( + Dependencies(false, file), + "p1", "Foo" + ) + ).use { + it.write("package p1\n\nclass Foo : Bar { override fun s() = \"generated\" }\n") + } + } + resolver.getNewFiles().forEach { + it.validate() + } + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return Validator().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider diff --git a/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts new file mode 100644 index 00000000..caa2c8c2 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/workload/build.gradle.kts @@ -0,0 +1,27 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") + application +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":validator")) + testImplementation("junit:junit:4.12") + ksp(project(":validator")) + kspTest(project(":validator")) +} + +application { + mainClassName = "p1.MainKt" +} diff --git a/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt new file mode 100644 index 00000000..5b31cfdb --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/K1.kt @@ -0,0 +1,4 @@ +package p1 + +@MyAnnotation +class K1 diff --git a/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt new file mode 100644 index 00000000..24b10a60 --- /dev/null +++ b/integration-tests/src/test/resources/incremental-removal/workload/src/main/kotlin/p1/Main.kt @@ -0,0 +1,12 @@ +package p1 + +interface Bar { + fun s(): String +} +val bar: Foo = Foo() + +annotation class MyAnnotation + +fun main() { + println("result: ${bar.s()}") +} diff --git a/integration-tests/src/test/resources/incremental/build.gradle.kts b/integration-tests/src/test/resources/incremental/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/incremental/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/incremental/gradle.properties b/integration-tests/src/test/resources/incremental/gradle.properties new file mode 100644 index 00000000..f9325643 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/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/incremental/settings.gradle.kts b/integration-tests/src/test/resources/incremental/settings.gradle.kts new file mode 100644 index 00000000..75f651ef --- /dev/null +++ b/integration-tests/src/test/resources/incremental/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "incremental-test" + +include(":workload") +include(":validator") diff --git a/integration-tests/src/test/resources/incremental/validator/build.gradle.kts b/integration-tests/src/test/resources/incremental/validator/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/validator/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/incremental/validator/src/main/kotlin/Validator.kt b/integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt new file mode 100644 index 00000000..96952d10 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/validator/src/main/kotlin/Validator.kt @@ -0,0 +1,62 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import com.google.devtools.ksp.visitor.KSDefaultVisitor +import java.io.OutputStreamWriter + +class Validator : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + var processed = false + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (processed) { + return emptyList() + } + val validator = object : KSDefaultVisitor<OutputStreamWriter, Unit>() { + override fun defaultHandler(node: KSNode, data: OutputStreamWriter) = Unit + + override fun visitDeclaration(declaration: KSDeclaration, data: OutputStreamWriter) { + data.write(declaration.qualifiedName?.asString() ?: declaration.simpleName.asString()) + declaration.validate() + } + } + + val files = resolver.getAllFiles() + files.forEach { file -> + logger.warn("${file.packageName.asString()}/${file.fileName}") + val output = OutputStreamWriter( + codeGenerator.createNewFile( + Dependencies(false, file), + file.packageName.asString(), file.fileName, "log" + ) + ) + file.declarations.forEach { + it.accept(validator, output) + } + output.close() + } + processed = true + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return Validator().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/incremental/validator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider diff --git a/integration-tests/src/test/resources/incremental/workload/build.gradle.kts b/integration-tests/src/test/resources/incremental/workload/build.gradle.kts new file mode 100644 index 00000000..00e18e2a --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/build.gradle.kts @@ -0,0 +1,22 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":validator")) + testImplementation("junit:junit:4.12") + ksp(project(":validator")) + kspTest(project(":validator")) +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java new file mode 100644 index 00000000..b2e2e87c --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J1.java @@ -0,0 +1,3 @@ +package p1; + +public class J1 {} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java new file mode 100644 index 00000000..7de0687e --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/J2.java @@ -0,0 +1,4 @@ +package p1; + +public class J2 { +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java new file mode 100644 index 00000000..9866bfee --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2J.java @@ -0,0 +1,11 @@ +package p1; + +import p2.J2; +import p3.*; + +public class TestJ2J { + J1 j1 = null; + J2 j2 = null; + J3 j3 = null; +} + diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java new file mode 100644 index 00000000..b5d055bb --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p1/TestJ2K.java @@ -0,0 +1,10 @@ +package p1; + +import p2.K2; +import p3.*; + +public class TestJ2K { + K1 k1 = null; + K2 k2 = null; + K3 k3 = null; +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java new file mode 100644 index 00000000..1e10b7c4 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p2/J2.java @@ -0,0 +1,4 @@ +package p2; + +public class J2 { +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java new file mode 100644 index 00000000..2e5ed679 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J1.java @@ -0,0 +1,4 @@ +package p3; + +public class J1 { +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java new file mode 100644 index 00000000..fc7d454e --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J2.java @@ -0,0 +1,4 @@ +package p3; + +public class J2 { +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java new file mode 100644 index 00000000..08cfb5ec --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/java/p3/J3.java @@ -0,0 +1,4 @@ +package p3; + +public class J3 { +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt new file mode 100644 index 00000000..eb739a3d --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K1.kt @@ -0,0 +1,3 @@ +package p1 + +open class K1 diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt new file mode 100644 index 00000000..e5134f49 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/K2.kt @@ -0,0 +1,3 @@ +package p1 + +open class K2 diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt new file mode 100644 index 00000000..81cb4d47 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2J.kt @@ -0,0 +1,10 @@ +package p1 + +import p2.J2 +import p3.* + +open class TestK2J() { + val v1: J1 = TODO() + val v2: J2 = TODO() + val v3: J3 = TODO() +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt new file mode 100644 index 00000000..401e340f --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p1/TestK2K.kt @@ -0,0 +1,10 @@ +package p1 + +import p2.K2 +import p3.* + +open class TestK2K() { + val v1: K1 = TODO() + val v2: K2 = TODO() + val v3: K3 = TODO() +} diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt new file mode 100644 index 00000000..55ae6f0a --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p2/K2.kt @@ -0,0 +1,3 @@ +package p2 + +open class K2 diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt new file mode 100644 index 00000000..6f727609 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K1.kt @@ -0,0 +1,3 @@ +package p3 + +open class K1 diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt new file mode 100644 index 00000000..d3aa0f93 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K2.kt @@ -0,0 +1,3 @@ +package p3 + +open class K2 diff --git a/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt new file mode 100644 index 00000000..522ffff4 --- /dev/null +++ b/integration-tests/src/test/resources/incremental/workload/src/main/kotlin/p3/K3.kt @@ -0,0 +1,3 @@ +package p3 + +open class K3 diff --git a/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..7bb6d4d4 --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,41 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStream + +fun OutputStream.appendText(str: String) { + this.write(str.toByteArray()) +} + +class TestProcessor(options: Map<String, String>, val codeGenerator: CodeGenerator) : SymbolProcessor { + val file: OutputStream = codeGenerator.createNewFile(Dependencies(false), "", "TestProcessor", "log") + + init { + file.appendText("TestProcessor: init($options)\n") + + val javaFile = codeGenerator.createNewFile(Dependencies(false), "", "GeneratedFromProvider", "java") + javaFile.appendText("class GeneratedFromProvider {}") + } + + var invoked = false + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (invoked) { + return emptyList() + } + + val fileKt = codeGenerator.createNewFile(Dependencies(false), "", "HelloFromProvider", "java") + + fileKt.appendText("public class HelloFromProvider{\n") + fileKt.appendText(" public int foo() { return 5678; }\n") + fileKt.appendText("}") + + invoked = true + return emptyList() + } + + class Provider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor = TestProcessor(environment.options, environment.codeGenerator) + } +} diff --git a/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..5c51c309 --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/provider-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessor$Provider diff --git a/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts new file mode 100644 index 00000000..73f2260c --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/settings.gradle.kts @@ -0,0 +1,22 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "init-plus-provider" + +include(":workload") +include(":init-processor") +include(":provider-processor") diff --git a/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts b/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts new file mode 100644 index 00000000..da44c4dd --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/workload/build.gradle.kts @@ -0,0 +1,24 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + ksp(project(":provider-processor")) +} + +ksp { + arg("option1", "value1") + arg("option2", "value2") +} diff --git a/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt new file mode 100644 index 00000000..fd782149 --- /dev/null +++ b/integration-tests/src/test/resources/init-plus-provider/workload/src/main/java/com/example/A.kt @@ -0,0 +1,7 @@ +package com.example + +import HelloFromProvider + +fun main() { + HelloFromProvider().foo() +} diff --git a/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..fe418e7b --- /dev/null +++ b/integration-tests/src/test/resources/java-only/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,30 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStreamWriter + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + var rounds = 0 + override fun process(resolver: Resolver): List<KSAnnotated> { + if (++rounds == 1) { + codeGenerator.createNewFile(Dependencies(false), "com.example", "Bar", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("interface Bar\n") + } + } + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java b/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java new file mode 100644 index 00000000..f6a7cf05 --- /dev/null +++ b/integration-tests/src/test/resources/java-only/workload/src/main/java/com/example/Foo.java @@ -0,0 +1,4 @@ +package com.example; + +class Foo implements Bar { +} diff --git a/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts new file mode 100644 index 00000000..5cb08c92 --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "javaNestedClass" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/test-processor/build.gradle.kts new file mode 100644 index 00000000..75c6c7c5 --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt new file mode 100644 index 00000000..80931541 --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/kotlin/ValidateProcessor.kt @@ -0,0 +1,38 @@ +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate + +class ValidateProcessor(env: SymbolProcessorEnvironment) : SymbolProcessor { + var logger: KSPLogger = env.logger + + override fun process(resolver: Resolver): List<KSAnnotated> { + val javaClass = resolver.getClassDeclarationByName("com.example.JavaClass")!! + val nestedClass = javaClass.declarations.filterIsInstance<KSClassDeclaration>() + .single { it.simpleName.asString() == "NestedClass" } + val provideString = nestedClass.declarations.filterIsInstance<KSFunctionDeclaration>() + .single { it.simpleName.asString() == "provideString" } + + if (provideString.returnType!!.resolve().isError) { + logger.error("provideString.returnType: not ok") + } + if (nestedClass.asStarProjectedType().isError == true) { + logger.error("$javaClass.asStarProjectedType(): not ok") + } + if (!nestedClass.validate()) { + logger.error("Failed to validate $nestedClass") + } + if (!javaClass.validate()) { + logger.error("Failed to validate $javaClass") + } + return emptyList() + } +} + +class ValidateProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return ValidateProcessor(env) + } +} diff --git a/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..6af81c2a --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +ValidateProcessorProvider diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts b/integration-tests/src/test/resources/javaNestedClass/workload/build.gradle.kts new file mode 100644 index 00000000..f0ea52b0 --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt new file mode 100644 index 00000000..ebf0688c --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/A.kt @@ -0,0 +1,4 @@ +package com.example + +fun main() { +} diff --git a/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java new file mode 100644 index 00000000..2c0b0e32 --- /dev/null +++ b/integration-tests/src/test/resources/javaNestedClass/workload/src/main/java/com/example/JavaClass.java @@ -0,0 +1,22 @@ +package com.example; + +public class JavaClass { + + public int b2; + + public ENUM e; + + public enum ENUM { + R,G,B + } + + void inject(InjectionTarget t) {} + + class InjectionTarget {} + + static final class NestedClass { + static String provideString() { + return "str"; + } + } +} diff --git a/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts b/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts new file mode 100644 index 00000000..f02ad073 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/annotations/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + } + js(BOTH) { + browser() + nodejs() + } + linuxX64() { + } + androidNativeX64() { + } + androidNativeArm64() { + } + // TODO: Enable after CI's Xcode version catches up. + // iosArm64() + // macosX64() + mingwX64() + sourceSets { + val commonMain by getting + } +} + +tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { + kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler" +} diff --git a/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt b/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt new file mode 100644 index 00000000..b938f1c1 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/annotations/src/commonMain/kotlin/com/example/MyAnnotation.kt @@ -0,0 +1,3 @@ +package com.example + +annotation class MyAnnotation diff --git a/integration-tests/src/test/resources/kmp/build.gradle.kts b/integration-tests/src/test/resources/kmp/build.gradle.kts new file mode 100644 index 00000000..8dd65667 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin("multiplatform") apply false +} + +val testRepo: String by project +allprojects { + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} diff --git a/integration-tests/src/test/resources/kmp/gradle.properties b/integration-tests/src/test/resources/kmp/gradle.properties new file mode 100644 index 00000000..4a9594ae --- /dev/null +++ b/integration-tests/src/test/resources/kmp/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M
\ No newline at end of file diff --git a/integration-tests/src/test/resources/kmp/settings.gradle.kts b/integration-tests/src/test/resources/kmp/settings.gradle.kts new file mode 100644 index 00000000..651619e4 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("multiplatform") version kotlinVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":annotations") +include(":workload") +include(":workload-jvm") +include(":workload-js") +include(":workload-linuxX64") +include(":workload-androidNative") +include(":test-processor") diff --git a/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts new file mode 100644 index 00000000..842898f6 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/test-processor/build.gradle.kts @@ -0,0 +1,22 @@ +val kspVersion: String by project + +plugins { + kotlin("multiplatform") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +kotlin { + jvm() + sourceSets { + val jvmMain by getting { + dependencies { + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") + } + kotlin.srcDir("src/main/kotlin") + resources.srcDir("src/main/resources") + } + } +} diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt new file mode 100644 index 00000000..a702c7bc --- /dev/null +++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ErrorProcessor.kt @@ -0,0 +1,61 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStream + +class ErrorProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + lateinit var file: OutputStream + lateinit var exception: String + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + exception = if (options.containsKey("exception")) { + options["exception"]!! + } else { + "" + } + if (exception == "init") { + throw Exception("Test Exception in init") + } + this.logger = logger + this.codeGenerator = codeGenerator + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (exception == "createTwice") { + codeGenerator.createNewFile(Dependencies.ALL_FILES, "create", "Twice").write("".toByteArray()) + return emptyList() + } + if (exception == "process") { + throw Exception("Test Exception in process") + } + return emptyList() + } + + override fun finish() { + if (exception == "finish") { + throw Exception("Test Exception in finish") + } + } + + override fun onError() { + if (exception == "error") { + throw Exception("Test Exception in error") + } + } +} + +class ErrorProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return ErrorProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..283d50f1 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,74 @@ +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.visitor.KSTopDownVisitor +import java.io.OutputStreamWriter + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger, + val env: SymbolProcessorEnvironment +) : SymbolProcessor { + var invoked = false + + override fun process(resolver: Resolver): List<KSAnnotated> { + val allFiles = resolver.getAllFiles().map { it.fileName } + logger.warn(allFiles.toList().toString()) + if (invoked) { + return emptyList() + } + invoked = true + + logger.warn("language version: ${env.kotlinVersion}") + logger.warn("api version: ${env.apiVersion}") + logger.warn("compiler version: ${env.compilerVersion}") + val platforms = env.platforms.map { it.toString() } + logger.warn("platforms: $platforms") + val list = resolver.getClassDeclarationByName("kotlin.collections.List") + logger.warn("List has superTypes: ${list!!.superTypes.count() > 0}") + + codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("class Foo {\n") + + val visitor = ClassVisitor() + resolver.getAllFiles().forEach { + it.accept(visitor, writer) + } + + writer.write("}\n") + } + } + + allFiles.forEach { + val fn = it.replace(".", "_dot_") + codeGenerator.createNewFile(Dependencies(false), "", fn, "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("// empty\n") + } + } + } + return emptyList() + } +} + +class ClassVisitor : KSTopDownVisitor<OutputStreamWriter, Unit>() { + override fun defaultHandler(node: KSNode, data: OutputStreamWriter) { + } + + override fun visitClassDeclaration( + classDeclaration: KSClassDeclaration, + data: OutputStreamWriter + ) { + super.visitClassDeclaration(classDeclaration, data) + val symbolName = classDeclaration.simpleName.asString().toLowerCase() + data.write(" val $symbolName = true\n") + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create(env: SymbolProcessorEnvironment): SymbolProcessor { + return TestProcessor(env.codeGenerator, env.logger, env) + } +} diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt new file mode 100644 index 00000000..45dbe22b --- /dev/null +++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/kotlin/ValidateProcessor.kt @@ -0,0 +1,29 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate + +class ValidateProcessor(val codeGenerator: CodeGenerator, val logger: KSPLogger) : SymbolProcessor { + var invoked = false + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (invoked) { + return emptyList() + } + invoked = true + + val toValidate = resolver.getSymbolsWithAnnotation("com.example.MyAnnotation") + if (toValidate.firstOrNull() == null || !toValidate.all { it.validate() }) { + logger.error("$toValidate.validate(): not ok") + } + if ((toValidate as? KSClassDeclaration)?.asStarProjectedType()?.isError == true) { + logger.error("$toValidate.asStarProjectedType(): not ok") + } + return emptyList() + } +} + +class ValidateProcessorProvider : SymbolProcessorProvider { + override fun create(env: SymbolProcessorEnvironment): SymbolProcessor { + return ValidateProcessor(env.codeGenerator, env.logger) + } +} diff --git a/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..b688ee02 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,3 @@ +TestProcessorProvider +ValidateProcessorProvider +ErrorProcessorProvider diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts new file mode 100644 index 00000000..ebf66a8e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + androidNativeX64() { + binaries { + executable() + } + } + androidNativeArm64() { + binaries { + executable() + } + } + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":annotations")) + } + } + val androidNativeX64Main by getting + val androidNativeArm64Main by getting + } +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) + add("kspJvm", project(":test-processor")) + add("kspJvmTest", project(":test-processor")) + add("kspAndroidNativeX64", project(":test-processor")) + add("kspAndroidNativeX64Test", project(":test-processor")) + add("kspAndroidNativeArm64", project(":test-processor")) + add("kspAndroidNativeArm64Test", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeArm64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/androidNativeX64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..480d3cb2 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Bar.kt @@ -0,0 +1,5 @@ +package com.example + +class Bar { + val baz = Foo().baz +} diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..83015598 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +class Baz { + val bar = Foo().bar +} diff --git a/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt new file mode 100644 index 00000000..05497afb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-androidNative/src/commonMain/kotlin/com/example/ToBeValidated.kt @@ -0,0 +1,9 @@ +package com.example + +// https://github.com/google/ksp/issues/632 +@MyAnnotation +@ExperimentalMultiplatform +class ToBeValidated { + // https://github.com/google/ksp/issues/574 + val ToBeInferred = listOf("string") +} diff --git a/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts new file mode 100644 index 00000000..d94bf9eb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-js/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + js(BOTH) { + browser() + nodejs() + } + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":annotations")) + } + } + } +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) + add("kspJs", project(":test-processor")) + add("kspJsTest", project(":test-processor")) +} + +tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { + kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler" +} diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..480d3cb2 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Bar.kt @@ -0,0 +1,5 @@ +package com.example + +class Bar { + val baz = Foo().baz +} diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..83015598 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +class Baz { + val bar = Foo().bar +} diff --git a/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt new file mode 100644 index 00000000..05497afb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-js/src/commonMain/kotlin/com/example/ToBeValidated.kt @@ -0,0 +1,9 @@ +package com.example + +// https://github.com/google/ksp/issues/632 +@MyAnnotation +@ExperimentalMultiplatform +class ToBeValidated { + // https://github.com/google/ksp/issues/574 + val ToBeInferred = listOf("string") +} diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts new file mode 100644 index 00000000..f61b2569 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-jvm/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":annotations")) + } + } + } +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) + add("kspJvm", project(":test-processor")) + add("kspJvmTest", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..480d3cb2 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Bar.kt @@ -0,0 +1,5 @@ +package com.example + +class Bar { + val baz = Foo().baz +} diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..83015598 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +class Baz { + val bar = Foo().bar +} diff --git a/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt new file mode 100644 index 00000000..05497afb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-jvm/src/commonMain/kotlin/com/example/ToBeValidated.kt @@ -0,0 +1,9 @@ +package com.example + +// https://github.com/google/ksp/issues/632 +@MyAnnotation +@ExperimentalMultiplatform +class ToBeValidated { + // https://github.com/google/ksp/issues/574 + val ToBeInferred = listOf("string") +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts new file mode 100644 index 00000000..c9ff2f5d --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + linuxX64() { + binaries { + executable() + } + } + // TODO: Enable after CI's Xcode version catches up. + // iosArm64() + // macosX64() + mingwX64() + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":annotations")) + } + } + val linuxX64Main by getting + val linuxX64Test by getting + } +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) + add("kspJvm", project(":test-processor")) + add("kspJvmTest", project(":test-processor")) + add("kspLinuxX64", project(":test-processor")) + add("kspLinuxX64Test", project(":test-processor")) + add("kspMingwX64", project(":test-processor")) + add("kspMingwX64Test", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..480d3cb2 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Bar.kt @@ -0,0 +1,5 @@ +package com.example + +class Bar { + val baz = Foo().baz +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..83015598 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +class Baz { + val bar = Foo().bar +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt new file mode 100644 index 00000000..05497afb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/commonMain/kotlin/com/example/ToBeValidated.kt @@ -0,0 +1,9 @@ +package com.example + +// https://github.com/google/ksp/issues/632 +@MyAnnotation +@ExperimentalMultiplatform +class ToBeValidated { + // https://github.com/google/ksp/issues/574 + val ToBeInferred = listOf("string") +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt new file mode 100644 index 00000000..56063711 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Main/kotlin/ToBeRemoved.kt @@ -0,0 +1 @@ +class ToBeRemoved diff --git a/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt new file mode 100644 index 00000000..836f9f2b --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload-linuxX64/src/linuxX64Test/kotlin/MyTest.kt @@ -0,0 +1,3 @@ +@com.example.MyAnnotation +@ExperimentalMultiplatform +class MyTest diff --git a/integration-tests/src/test/resources/kmp/workload/build.gradle.kts b/integration-tests/src/test/resources/kmp/workload/build.gradle.kts new file mode 100644 index 00000000..f0ba36f1 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/build.gradle.kts @@ -0,0 +1,66 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + js(BOTH) { + browser() + nodejs() + } + linuxX64() { + binaries { + executable() + } + } + androidNativeX64() { + binaries { + executable() + } + } + androidNativeArm64() { + binaries { + executable() + } + } + // TODO: Enable after CI's Xcode version catches up. + // iosArm64() + // macosX64() + mingwX64() + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":annotations")) + } + } + val linuxX64Main by getting + val linuxX64Test by getting + val androidNativeX64Main by getting + val androidNativeArm64Main by getting + } +} + +tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { + kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler" +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) + add("kspJvm", project(":test-processor")) + add("kspJvmTest", project(":test-processor")) + add("kspJs", project(":test-processor")) + add("kspJsTest", project(":test-processor")) + add("kspAndroidNativeX64", project(":test-processor")) + add("kspAndroidNativeX64Test", project(":test-processor")) + add("kspAndroidNativeArm64", project(":test-processor")) + add("kspAndroidNativeArm64Test", project(":test-processor")) + add("kspLinuxX64", project(":test-processor")) + add("kspLinuxX64Test", project(":test-processor")) + add("kspMingwX64", project(":test-processor")) + add("kspMingwX64Test", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/androidNativeArm64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/androidNativeX64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..480d3cb2 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Bar.kt @@ -0,0 +1,5 @@ +package com.example + +class Bar { + val baz = Foo().baz +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..83015598 --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +class Baz { + val bar = Foo().bar +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt new file mode 100644 index 00000000..05497afb --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/commonMain/kotlin/com/example/ToBeValidated.kt @@ -0,0 +1,9 @@ +package com.example + +// https://github.com/google/ksp/issues/632 +@MyAnnotation +@ExperimentalMultiplatform +class ToBeValidated { + // https://github.com/google/ksp/issues/574 + val ToBeInferred = listOf("string") +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt new file mode 100644 index 00000000..bf7bfe3e --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Main/kotlin/Main.kt @@ -0,0 +1,7 @@ +import com.example.Bar +import com.example.Foo + +fun main() { + println(Bar().toString()) + println(Foo().toString()) +} diff --git a/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt new file mode 100644 index 00000000..836f9f2b --- /dev/null +++ b/integration-tests/src/test/resources/kmp/workload/src/linuxX64Test/kotlin/MyTest.kt @@ -0,0 +1,3 @@ +@com.example.MyAnnotation +@ExperimentalMultiplatform +class MyTest diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts new file mode 100644 index 00000000..9d60cbc7 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..7e141927 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,27 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSFunctionDeclaration + +class TestProcessor( + private val codeGenerator: CodeGenerator, + private val options: Map<String, String>, + private val logger: KSPLogger +) : SymbolProcessor { + override fun process(resolver: Resolver): List<KSAnnotated> { + resolver + .getSymbolsWithAnnotation("com.example.ann.MyAnn") + .filterIsInstance<KSFunctionDeclaration>() + .forEach { func -> + val arg = func.annotations.first().arguments.first().value.toString() + if (!arg.startsWith("REPLACE")) + throw IllegalStateException(arg) + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + TestProcessor(environment.codeGenerator, environment.options, environment.logger) +} diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/build.gradle.kts new file mode 100644 index 00000000..f0ea52b0 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java new file mode 100644 index 00000000..a196f754 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/JavaClass.java @@ -0,0 +1,9 @@ +package com.example; + +import com.example.ann.MyAnn; + +public class JavaClass { + @MyAnn(KotlinConsts.ACTION) + public void f() { + } +} diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt new file mode 100644 index 00000000..e5231980 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/KotlinConsts.kt @@ -0,0 +1,8 @@ +package com.example + +class KotlinConsts { + companion object { + const val ACTION = "REPLACE" + const val ACTION2 = "REPLACE" + } +} diff --git a/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt new file mode 100644 index 00000000..6812ee63 --- /dev/null +++ b/integration-tests/src/test/resources/kotlin-consts-in-java/workload/src/main/java/com/example/ann/MyAnn.kt @@ -0,0 +1,3 @@ +package com.example.ann + +annotation class MyAnn(val value: String) diff --git a/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..7892f012 --- /dev/null +++ b/integration-tests/src/test/resources/map-annotation-arguments/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,36 @@ +import com.google.devtools.ksp.getClassDeclarationByName +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + val expected = mapOf( + "unboxedChar" to "Char", + "boxedChar" to "(Char..Char?)", + ) + + override fun process(resolver: Resolver): List<KSAnnotated> { + val j = resolver.getClassDeclarationByName("com.example.AnnotationTest")!! + j.annotations.forEach { annotation -> + annotation.arguments.forEach { + val key = it.name?.asString() + val value = it.value.toString() + if (expected[key] != value) { + logger.error("$key: ${expected[key]} != $value") + } + } + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java new file mode 100644 index 00000000..18696806 --- /dev/null +++ b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/AnnotationTest.java @@ -0,0 +1,8 @@ +package com.example; + +@JavaAnnotation( + unboxedChar = char.class, + boxedChar = Character.class +) +public class AnnotationTest { +} diff --git a/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java new file mode 100644 index 00000000..c7c8368f --- /dev/null +++ b/integration-tests/src/test/resources/map-annotation-arguments/workload/src/main/java/com/example/JavaAnnotation.java @@ -0,0 +1,6 @@ +package com.example; + +public @interface JavaAnnotation { + Class unboxedChar(); + Class boxedChar(); +}
\ No newline at end of file diff --git a/integration-tests/src/test/resources/on-error/build.gradle.kts b/integration-tests/src/test/resources/on-error/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/on-error/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts b/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts new file mode 100644 index 00000000..999cb80a --- /dev/null +++ b/integration-tests/src/test/resources/on-error/on-error-processor/build.gradle.kts @@ -0,0 +1,23 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt new file mode 100644 index 00000000..976019bf --- /dev/null +++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/ErrorProcessor.kt @@ -0,0 +1,70 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStream + +class ErrorProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + lateinit var file: OutputStream + var rounds = 0 + lateinit var exception: String + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + exception = if (options.containsKey("exception")) { + options["exception"]!! + } else { + "" + } + if (exception == "init") { + throw Exception("Test Exception in init") + } + this.logger = logger + this.codeGenerator = codeGenerator + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (exception == "createTwice") { + codeGenerator.createNewFile(Dependencies.ALL_FILES, "create", "Twice").write("".toByteArray()) + return emptyList() + } + if (exception == "process") { + throw Exception("Test Exception in process") + } + rounds++ + if (rounds == 2) { + if (exception == "" || exception == "error") { + logger.error("Error processor: errored at $rounds") + } + } else { + codeGenerator.createNewFile(Dependencies.ALL_FILES, "test", "error", "log") + } + return emptyList() + } + + override fun finish() { + if (exception == "finish") { + throw Exception("Test Exception in finish") + } + } + + override fun onError() { + if (exception == "error") { + throw Exception("Test Exception in error") + } + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return ErrorProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt new file mode 100644 index 00000000..c751c582 --- /dev/null +++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/kotlin/NormalProcessor.kt @@ -0,0 +1,40 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class NormalProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + var rounds = 0 + + override fun onError() { + logger.error("NormalProcessor called error on $rounds") + } + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + this.logger = logger + this.codeGenerator = codeGenerator + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + rounds++ + if (rounds == 1) { + codeGenerator.createNewFile(Dependencies.ALL_FILES, "test", "normal", "log") + } + return emptyList() + } +} + +class TestProcessorProvider2 : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return NormalProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..3a1528c9 --- /dev/null +++ b/integration-tests/src/test/resources/on-error/on-error-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,2 @@ +TestProcessorProvider +TestProcessorProvider2 diff --git a/integration-tests/src/test/resources/on-error/settings.gradle.kts b/integration-tests/src/test/resources/on-error/settings.gradle.kts new file mode 100644 index 00000000..cff61bf9 --- /dev/null +++ b/integration-tests/src/test/resources/on-error/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "on-error" + +include(":workload") +include(":on-error-processor") diff --git a/integration-tests/src/test/resources/on-error/workload/build.gradle.kts b/integration-tests/src/test/resources/on-error/workload/build.gradle.kts new file mode 100644 index 00000000..015259a8 --- /dev/null +++ b/integration-tests/src/test/resources/on-error/workload/build.gradle.kts @@ -0,0 +1,23 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + ksp(project(":on-error-processor")) +} + +tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=MyOptIn" +} diff --git a/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt b/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt new file mode 100644 index 00000000..4752447c --- /dev/null +++ b/integration-tests/src/test/resources/on-error/workload/src/main/kotlin/com/example/A.kt @@ -0,0 +1,11 @@ +package com.example + +@RequiresOptIn +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +annotation class MyOptIn + +@OptIn(MyOptIn::class) +fun main() { + print("hello world") +} diff --git a/integration-tests/src/test/resources/only-resources-file/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/build.gradle.kts new file mode 100644 index 00000000..7a1ba5b1 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin("multiplatform") apply false +} + +val testRepo: String by project +subprojects { + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} diff --git a/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts b/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts new file mode 100644 index 00000000..49845b92 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("multiplatform") version kotlinVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts new file mode 100644 index 00000000..1d1cb300 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/test-processor/build.gradle.kts @@ -0,0 +1,12 @@ +val kspVersion: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +dependencies { + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..6d07416b --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,24 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class TestProcessor(val codeGenerator: CodeGenerator) : SymbolProcessor { + + var invoked = false + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (invoked) { + return emptyList() + } + + codeGenerator.createNewFile(Dependencies(false), "", "HelloSwift", "swift") + + invoked = true + return emptyList() + } + + class Provider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor = TestProcessor(environment.codeGenerator) + } +} diff --git a/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..5c51c309 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessor$Provider diff --git a/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts b/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts new file mode 100644 index 00000000..ed0d40c6 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/workload/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } +} + +dependencies { + add("kspCommonMainMetadata", project(":test-processor")) +} diff --git a/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt b/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt new file mode 100644 index 00000000..f0692550 --- /dev/null +++ b/integration-tests/src/test/resources/only-resources-file/workload/src/commonMain/kotlin/MyStub.kt @@ -0,0 +1 @@ +class MyStub 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 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/output-deps/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} 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 00000000..f9325643 --- /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 00000000..668c5a87 --- /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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +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 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/output-deps/test-processor/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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 00000000..9e8436f9 --- /dev/null +++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,61 @@ +import com.google.devtools.ksp.containingFile +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStreamWriter + +class TestProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + var processed = false + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (processed) { + return emptyList() + } + fun outputForAnno(anno: String) { + val annoFiles = + resolver.getSymbolsWithAnnotation(anno).map { (it as KSDeclaration).containingFile!! }.toList() + 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.getNewFiles().forEach { file -> + logger.warn("${file.packageName.asString()}/${file.fileName}") + val outputBaseFN = file.fileName.replace(".kt", "Generated").replace(".java", "Generated") + codeGenerator.createNewFile(Dependencies(false, file), file.packageName.asString(), outputBaseFN, "kt") + .use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("private val unused = \"unused\"") + } + } + } + processed = true + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return TestProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/output-deps/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider 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 00000000..f6b92cd5 --- /dev/null +++ b/integration-tests/src/test/resources/output-deps/workload/build.gradle.kts @@ -0,0 +1,19 @@ +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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 00000000..e5e1bc0e --- /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 00000000..7de0687e --- /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 00000000..badbcbd8 --- /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 00000000..1c517717 --- /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 00000000..43301819 --- /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 00000000..506ed473 --- /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 diff --git a/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts new file mode 100644 index 00000000..4b1192e1 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/application/build.gradle.kts @@ -0,0 +1,30 @@ +val testRepo: String by project + +plugins { + id("com.android.application") + kotlin("android") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":workload")) +} + +android { + compileSdkVersion(30) + defaultConfig { + applicationId = "org.gradle.kotlin.dsl.samples.androidstudio" + minSdkVersion(30) + targetSdkVersion(30) + versionCode = 1 + versionName = "1.0" + } +} diff --git a/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml new file mode 100644 index 00000000..81a4ba30 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/application/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.myapplication"> +</manifest>
\ No newline at end of file diff --git a/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt b/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt new file mode 100644 index 00000000..4f164e18 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/application/src/main/java/com/example/application/Foo.kt @@ -0,0 +1,3 @@ +package com.example.application + +class Foo diff --git a/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts new file mode 100644 index 00000000..e1f8d720 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/build.gradle.kts @@ -0,0 +1,27 @@ +buildscript { + val testRepo: String by project + + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + google() + } +} + +plugins { + id("com.android.application") apply false + kotlin("android") apply false + id("com.google.devtools.ksp") apply false + id("com.android.library") apply false +} + +allprojects { + val testRepo: String by project + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + google() + } +} diff --git a/integration-tests/src/test/resources/playground-android-multi/gradle.properties b/integration-tests/src/test/resources/playground-android-multi/gradle.properties new file mode 100644 index 00000000..b3c7a033 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M diff --git a/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts new file mode 100644 index 00000000..4c6fe413 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + val agpVersion: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("jvm") version kotlinVersion apply false + kotlin("android") version kotlinVersion apply false + id("com.android.application") version agpVersion apply false + id("com.android.library") version agpVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":application") +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/test-processor/build.gradle.kts new file mode 100644 index 00000000..75c6c7c5 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts new file mode 100644 index 00000000..3897c89d --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/workload/build.gradle.kts @@ -0,0 +1,35 @@ +val testRepo: String by project + +plugins { + // DO NOT CHANGE THE ORDER. + id("com.google.devtools.ksp") + id("com.android.library") + kotlin("android") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} + +android { + compileSdkVersion(30) + defaultConfig { + minSdkVersion(30) + targetSdkVersion(30) + } +} + +ksp { + arg("option1", "value1") + arg("option2", "value2") +} diff --git a/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml new file mode 100644 index 00000000..522bce61 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android-multi/workload/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.mylibrary"> +</manifest>
\ No newline at end of file diff --git a/integration-tests/src/test/resources/playground-android/build.gradle.kts b/integration-tests/src/test/resources/playground-android/build.gradle.kts new file mode 100644 index 00000000..b54fb383 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/build.gradle.kts @@ -0,0 +1,20 @@ +buildscript { + val testRepo: String by project + + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + google() + } +} + +allprojects { + val testRepo: String by project + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + google() + } +} diff --git a/integration-tests/src/test/resources/playground-android/gradle.properties b/integration-tests/src/test/resources/playground-android/gradle.properties new file mode 100644 index 00000000..b3c7a033 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/gradle.properties @@ -0,0 +1 @@ +org.gradle.jvmargs=-Xmx2048M diff --git a/integration-tests/src/test/resources/playground-android/settings.gradle.kts b/integration-tests/src/test/resources/playground-android/settings.gradle.kts new file mode 100644 index 00000000..67a08d91 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + val agpVersion: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("jvm") version kotlinVersion apply false + kotlin("android") version kotlinVersion apply false + id("com.android.application") version agpVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + google() + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-android/test-processor/build.gradle.kts new file mode 100644 index 00000000..75c6c7c5 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts new file mode 100644 index 00000000..b0a0038a --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/workload/build.gradle.kts @@ -0,0 +1,45 @@ +val testRepo: String by project + +plugins { + // DO NOT CHANGE THE ORDER. + id("com.google.devtools.ksp") + id("com.android.application") + kotlin("android") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} + +android { + compileSdkVersion(30) + defaultConfig { + applicationId = "org.gradle.kotlin.dsl.samples.androidstudio" + minSdkVersion(30) + targetSdkVersion(30) + versionCode = 1 + versionName = "1.0" + } + buildTypes { + getByName("release") { + // For regression testing https://github.com/google/ksp/pull/467 + proguardFiles.add(file("proguard-rules.pro")) + isMinifyEnabled = true + } + } +} + +ksp { + arg("option1", "value1") + arg("option2", "value2") +} diff --git a/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro b/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro new file mode 100644 index 00000000..a90bbb3f --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/workload/proguard-rules.pro @@ -0,0 +1 @@ +-keep class com.example.AClass { *; }
\ No newline at end of file diff --git a/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml b/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml new file mode 100644 index 00000000..81a4ba30 --- /dev/null +++ b/integration-tests/src/test/resources/playground-android/workload/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.myapplication"> +</manifest>
\ No newline at end of file diff --git a/integration-tests/src/test/resources/playground-mpp/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/build.gradle.kts new file mode 100644 index 00000000..8dd65667 --- /dev/null +++ b/integration-tests/src/test/resources/playground-mpp/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + kotlin("multiplatform") apply false +} + +val testRepo: String by project +allprojects { + repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} diff --git a/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts b/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts new file mode 100644 index 00000000..49845b92 --- /dev/null +++ b/integration-tests/src/test/resources/playground-mpp/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion apply false + kotlin("multiplatform") version kotlinVersion apply false + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts new file mode 100644 index 00000000..842898f6 --- /dev/null +++ b/integration-tests/src/test/resources/playground-mpp/test-processor/build.gradle.kts @@ -0,0 +1,22 @@ +val kspVersion: String by project + +plugins { + kotlin("multiplatform") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +kotlin { + jvm() + sourceSets { + val jvmMain by getting { + dependencies { + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") + } + kotlin.srcDir("src/main/kotlin") + resources.srcDir("src/main/resources") + } + } +} diff --git a/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts b/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts new file mode 100644 index 00000000..a3179983 --- /dev/null +++ b/integration-tests/src/test/resources/playground-mpp/workload/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("multiplatform") + id("com.google.devtools.ksp") +} + +version = "1.0-SNAPSHOT" + +kotlin { + jvm { + withJava() + } + linuxX64() + mingwX64() + macosX64() + ios() + js(BOTH) { + browser() + nodejs() + } + sourceSets { + val commonMain by getting + val jvmMain by getting { + dependencies { + implementation(project(":test-processor")) + project.dependencies.add("kspJvm", project(":test-processor")) + } + kotlin.srcDir("src/main/java") + } + } +} + +tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { + kotlinOptions.freeCompilerArgs += "-Xuse-deprecated-legacy-compiler" +} + +ksp { + arg("option1", "value1") + arg("option2", "value2") +} diff --git a/integration-tests/src/test/resources/playground/build.gradle.kts b/integration-tests/src/test/resources/playground/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/playground/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/playground/settings.gradle.kts b/integration-tests/src/test/resources/playground/settings.gradle.kts new file mode 100644 index 00000000..9d60cbc7 --- /dev/null +++ b/integration-tests/src/test/resources/playground/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/playground/test-processor/build.gradle.kts b/integration-tests/src/test/resources/playground/test-processor/build.gradle.kts new file mode 100644 index 00000000..75c6c7c5 --- /dev/null +++ b/integration-tests/src/test/resources/playground/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation("com.squareup:javapoet:1.12.1") + implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion") +} + +sourceSets.main { + java.srcDirs("src/main/kotlin") +} diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt new file mode 100644 index 00000000..9434b362 --- /dev/null +++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/Builder.kt @@ -0,0 +1,3 @@ +package com.example.annotation + +annotation class Builder diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt new file mode 100644 index 00000000..efa5ddcf --- /dev/null +++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/BuilderProcessor.kt @@ -0,0 +1,104 @@ +import com.google.devtools.ksp.containingFile +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import java.io.OutputStream + +fun OutputStream.appendText(str: String) { + this.write(str.toByteArray()) +} + +class BuilderProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var logger: KSPLogger + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger, + ) { + this.codeGenerator = codeGenerator + this.logger = logger + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder") + val ret = symbols.filter { !it.validate() } + symbols + .filter { it is KSClassDeclaration && it.validate() } + .forEach { it.accept(BuilderVisitor(), Unit) } + return ret.toList() + } + + inner class BuilderVisitor : KSVisitorVoid() { + override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) { + classDeclaration.primaryConstructor?.accept(this, data) + } + + override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) { + val parent = function.parentDeclaration as KSClassDeclaration + val packageName = parent.containingFile!!.packageName.asString() + val className = "${parent.simpleName.asString()}Builder" + + // For regression testing https://github.com/google/ksp/pull/467 + codeGenerator.createNewFile( + Dependencies(true, function.containingFile!!), + "", + "META-INF/proguard/builder-$className", + "pro" + ).use { proguardFile -> + proguardFile.appendText("-keep class $packageName.$className { *; }") + } + + val file = codeGenerator.createNewFile( + Dependencies(true, function.containingFile!!), packageName, className + ) + file.appendText("package $packageName\n\n") + file.appendText("import hello.HELLO\n\n") + file.appendText("class $className{\n") + function.parameters.forEach { + val name = it.name!!.asString() + val typeName = StringBuilder(it.type.resolve().declaration.qualifiedName?.asString() ?: "<ERROR>") + val typeArgs = it.type.element!!.typeArguments + if (it.type.element!!.typeArguments.toList().isNotEmpty()) { + typeName.append("<") + typeName.append( + typeArgs.map { + val type = it.type?.resolve() + "${it.variance.label} ${type?.declaration?.qualifiedName?.asString() ?: "ERROR"}" + + if (type?.nullability == Nullability.NULLABLE) "?" else "" + }.joinToString(", ") + ) + typeName.append(">") + } + file.appendText(" private var $name: $typeName? = null\n") + file.appendText(" internal fun with${name.capitalize()}($name: $typeName): $className {\n") + file.appendText(" this.$name = $name\n") + file.appendText(" return this\n") + file.appendText(" }\n\n") + } + file.appendText(" internal fun build(): ${parent.qualifiedName!!.asString()} {\n") + file.appendText(" return ${parent.qualifiedName!!.asString()}(") + file.appendText( + function.parameters.map { + "${it.name!!.asString()}!!" + }.joinToString(", ") + ) + file.appendText(")\n") + file.appendText(" }\n") + file.appendText("}\n") + file.close() + } + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment, + ): SymbolProcessor { + return BuilderProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt new file mode 100644 index 00000000..6c6e2354 --- /dev/null +++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/RewriteProcessor.kt @@ -0,0 +1,30 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class RewriteProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + this.codeGenerator = codeGenerator + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + val fileKt = codeGenerator.createNewFile(Dependencies(false), "hello", "HELLO", "java") + return emptyList() + } +} + +class RewriteProcessorProvider : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return RewriteProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + } + } +} diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..4b5a3cb5 --- /dev/null +++ b/integration-tests/src/test/resources/playground/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,283 @@ +import com.google.devtools.ksp.containingFile +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStream + +class TestProcessor : SymbolProcessor { + lateinit var codeGenerator: CodeGenerator + lateinit var file: OutputStream + var invoked = false + + fun emit(s: String, indent: String) { + file.appendText("$indent$s\n") + } + + fun init( + options: Map<String, String>, + kotlinVersion: KotlinVersion, + codeGenerator: CodeGenerator, + logger: KSPLogger + ) { + logger.warn("This is a harmless warning.") + this.codeGenerator = codeGenerator + file = codeGenerator.createNewFile(Dependencies(false), "", "TestProcessor", "log") + emit("TestProcessor: init($options)", "") + + val javaFile = codeGenerator.createNewFile(Dependencies(false), "", "Generated", "java") + javaFile.appendText("class Generated {}") + } + + override fun process(resolver: Resolver): List<KSAnnotated> { + if (invoked) { + return emptyList() + } + val fileKt = codeGenerator.createNewFile(Dependencies(false), "hello", "HELLO", "java") + fileKt.appendText("package hello;\n") + fileKt.appendText("public class HELLO{\n") + fileKt.appendText("public int foo() { return 1234; }\n") + fileKt.appendText("}") + + val files = resolver.getAllFiles() + emit("TestProcessor: process()", "") + val visitor = TestVisitor() + for (file in files) { + emit("TestProcessor: processing ${file.fileName}", "") + file.accept(visitor, "") + } + invoked = true + return emptyList() + } + + inner class TestVisitor : KSVisitor<String, Unit> { + + override fun visitReferenceElement(element: KSReferenceElement, data: String) { + } + + override fun visitModifierListOwner(modifierListOwner: KSModifierListOwner, data: String) { + TODO("Not yet implemented") + } + + override fun visitNode(node: KSNode, data: String) { + TODO("Not yet implemented") + } + + override fun visitPropertyAccessor(accessor: KSPropertyAccessor, data: String) { + TODO("Not yet implemented") + } + + override fun visitDynamicReference(reference: KSDynamicReference, data: String) { + TODO("Not yet implemented") + } + + override fun visitDefNonNullReference(reference: KSDefNonNullReference, data: String) { + TODO("Not yet implemented") + } + + val visited = HashSet<Any>() + + private fun checkVisited(symbol: Any): Boolean { + return if (visited.contains(symbol)) { + true + } else { + visited.add(symbol) + false + } + } + + private fun invokeCommonDeclarationApis(declaration: KSDeclaration, indent: String) { + emit( + "${declaration.modifiers.joinToString(" ")} ${declaration.simpleName.asString()}", indent + ) + declaration.annotations.map { it.accept(this, "$indent ") } + if (declaration.parentDeclaration != null) + emit(" enclosing: ${declaration.parentDeclaration!!.qualifiedName?.asString()}", indent) + declaration.containingFile?.let { emit("${it.packageName.asString()}.${it.fileName}", indent) } + declaration.typeParameters.map { it.accept(this, "$indent ") } + } + + override fun visitFile(file: KSFile, data: String) { +// if (!file.packageName.asString().startsWith("eu.kanade.tachiyomi.data")) { +// return +// } + if (checkVisited(file)) return + file.annotations.forEach { it.accept(this, "$data ") } + emit(file.packageName.asString(), data) + for (declaration in file.declarations) { + declaration.accept(this, data) + } + } + + override fun visitAnnotation(annotation: KSAnnotation, data: String) { + if (checkVisited(annotation)) return + emit("annotation", data) + annotation.annotationType.accept(this, "$data ") + annotation.arguments.forEach { it.accept(this, "$data ") } + } + + override fun visitCallableReference(reference: KSCallableReference, data: String) { + if (checkVisited(reference)) return + emit("element: ", data) + reference.functionParameters.forEach { it.accept(this, "$data ") } + reference.receiverType?.accept(this, "$data receiver") + reference.returnType.accept(this, "$data ") + } + + override fun visitPropertyGetter(getter: KSPropertyGetter, data: String) { + if (checkVisited(getter)) return + emit("propertyGetter: ", data) + getter.annotations.forEach { it.accept(this, "$data ") } + emit(getter.modifiers.joinToString(" "), data) + getter.returnType?.accept(this, "$data ") + } + + override fun visitPropertySetter(setter: KSPropertySetter, data: String) { + if (checkVisited(setter)) return + emit("propertySetter: ", data) + setter.annotations.forEach { it.accept(this, "$data ") } + emit(setter.modifiers.joinToString(" "), data) +// setter.parameter.accept(this, "$data ") + } + + override fun visitTypeArgument(typeArgument: KSTypeArgument, data: String) { + if (checkVisited(typeArgument)) return + typeArgument.annotations.forEach { it.accept(this, "$data ") } + emit( + when (typeArgument.variance) { + Variance.STAR -> "*" + Variance.COVARIANT -> "out" + Variance.CONTRAVARIANT -> "in" + else -> "" + }, + data + ) + typeArgument.type?.accept(this, "$data ") + } + + override fun visitTypeParameter(typeParameter: KSTypeParameter, data: String) { + if (checkVisited(typeParameter)) return + typeParameter.annotations.forEach { it.accept(this, "$data ") } + if (typeParameter.isReified) { + emit("reified ", data) + } + emit( + when (typeParameter.variance) { + Variance.COVARIANT -> "out " + Variance.CONTRAVARIANT -> "in " + else -> "" + } + typeParameter.name.asString(), + data + ) + if (typeParameter.bounds.toList().isNotEmpty()) { + typeParameter.bounds.forEach { it.accept(this, "$data ") } + } + } + + override fun visitValueParameter(valueParameter: KSValueParameter, data: String) { + if (checkVisited(valueParameter)) return + valueParameter.annotations.forEach { it.accept(this, "$data ") } + if (valueParameter.isVararg) { + emit("vararg", "$data ") + } + if (valueParameter.isNoInline) { + emit("noinline", "$data ") + } + if (valueParameter.isCrossInline) { + emit("crossinline ", "$data ") + } + emit(valueParameter.name?.asString() ?: "_", "$data ") + valueParameter.type.accept(this, "$data ") + } + + override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: String) { + if (checkVisited(function)) return + invokeCommonDeclarationApis(function, data) + for (declaration in function.declarations) { + declaration.accept(this, "$data ") + } + function.parameters.forEach { it.accept(this, "$data ") } + function.typeParameters.forEach { it.accept(this, "$data ") } + function.extensionReceiver?.accept(this, "$data extension:") + emit("returnType:", data) + function.returnType?.accept(this, "$data ") + } + + override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: String) { + if (checkVisited(classDeclaration)) return + invokeCommonDeclarationApis(classDeclaration, data) + emit(classDeclaration.classKind.type, data) + for (declaration in classDeclaration.declarations) { + declaration.accept(this, "$data ") + } + classDeclaration.superTypes.forEach { it.accept(this, "$data ") } + classDeclaration.primaryConstructor?.accept(this, "$data ") + } + + override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: String) { + if (checkVisited(property)) return + invokeCommonDeclarationApis(property, data) + property.type.accept(this, "$data ") + property.extensionReceiver?.accept(this, "$data extension:") + property.setter?.accept(this, "$data ") + property.getter?.accept(this, "$data ") + } + + override fun visitTypeReference(typeReference: KSTypeReference, data: String) { + if (checkVisited(typeReference)) return + typeReference.annotations.forEach { it.accept(this, "$data ") } + val type = typeReference.resolve() + type.let { + emit("resolved to: ${it.declaration.qualifiedName?.asString()}", data) + } + // resolved.accept(this, "$data ") + // TODO: KSTypeReferenceJavaImpl hasn't completed yet. + try { + typeReference.element?.accept(this, "$data ") + } catch (e: IllegalStateException) { + emit("TestProcessor: exception: $e", data) + } + } + + override fun visitAnnotated(annotated: KSAnnotated, data: String) { + } + + override fun visitDeclaration(declaration: KSDeclaration, data: String) { + } + + override fun visitDeclarationContainer(declarationContainer: KSDeclarationContainer, data: String) { + } + + override fun visitParenthesizedReference(reference: KSParenthesizedReference, data: String) { + } + + override fun visitClassifierReference(reference: KSClassifierReference, data: String) { + if (checkVisited(reference)) return + if (reference.typeArguments.toList().isNotEmpty()) { + reference.typeArguments.forEach { it.accept(this, "$data ") } + } + } + + override fun visitTypeAlias(typeAlias: KSTypeAlias, data: String) { + } + + override fun visitValueArgument(valueArgument: KSValueArgument, data: String) { + if (checkVisited(valueArgument)) return + val name = valueArgument.name?.asString() ?: "<no name>" + emit("$name: ${valueArgument.value}", data) + valueArgument.annotations.forEach { it.accept(this, "$data ") } + } + } +} + +class TestProcessorProvider2 : SymbolProcessorProvider { + override fun create( + env: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor().apply { + init(env.options, env.kotlinVersion, env.codeGenerator, env.logger) + + env.logger.warn("language version: ${env.kotlinVersion}") + env.logger.warn("api version: ${env.apiVersion}") + env.logger.warn("compiler version: ${env.compilerVersion}") + } + } +} diff --git a/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..3a1528c9 --- /dev/null +++ b/integration-tests/src/test/resources/playground/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,2 @@ +TestProcessorProvider +TestProcessorProvider2 diff --git a/integration-tests/src/test/resources/playground/workload/G.kt b/integration-tests/src/test/resources/playground/workload/G.kt new file mode 100644 index 00000000..f2f80ffe --- /dev/null +++ b/integration-tests/src/test/resources/playground/workload/G.kt @@ -0,0 +1,6 @@ +package g + +import com.example.annotation.Builder + +@Builder +class G diff --git a/integration-tests/src/test/resources/playground/workload/build.gradle.kts b/integration-tests/src/test/resources/playground/workload/build.gradle.kts new file mode 100644 index 00000000..6cb48fad --- /dev/null +++ b/integration-tests/src/test/resources/playground/workload/build.gradle.kts @@ -0,0 +1,36 @@ +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool + +val testRepo: String by project + +plugins { + id("com.google.devtools.ksp") + kotlin("jvm") +} + +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} + +ksp { + arg("option1", "value1") + arg("option2", "value2") +} + +val compileKotlin: AbstractKotlinCompileTool<*> by tasks +tasks.register<Copy>("copyG") { + from("G.kt") + into(File(buildDir, "generatedSources").apply { mkdirs() }) +}.let { + // Magic. `map` creates a provider to propagate task dependency. + compileKotlin.setSource(it.map { it.destinationDir }) +} diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt new file mode 100644 index 00000000..e5fa7f97 --- /dev/null +++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/A.kt @@ -0,0 +1,16 @@ +package com.example + +import hello.HELLO + +fun main() { + val hello = HELLO() + println(hello.foo()) + + val builder = AClassBuilder() + builder + .withA(1) + .withB("foo") + .withC(2.3) + val aClass: AClass = builder.build() + println(aClass.foo()) +} diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt new file mode 100644 index 00000000..180dbe6a --- /dev/null +++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/AClass.kt @@ -0,0 +1,17 @@ +package com.example + +import com.example.annotation.Builder +import hello.HELLO + +@Builder +class AClass(private val a: Int, val b: String, val c: Double, val d: HELLO) { + val p = "$a, $b, $c" + fun foo() = HELLO() + val hello = HELLO() + var hello2: HELLO = HELLO() + get() { return hello2 } + private set + class innerClass<T : HELLO> + + val generic = innerClass<HELLO>() +} diff --git a/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java new file mode 100644 index 00000000..fcfbcbcf --- /dev/null +++ b/integration-tests/src/test/resources/playground/workload/src/main/java/com/example/BClass.java @@ -0,0 +1,14 @@ +package com.example; + +import com.example.annotation.Builder; +import hello.HELLO; +import java.util.List; + +@Builder +public class BClass { + public HELLO hello; + public HELLO helloFun(){ + return null; + } + public List<HELLO> list = null; +}
\ No newline at end of file diff --git a/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..ad548224 --- /dev/null +++ b/integration-tests/src/test/resources/psi-cache/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,40 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + var rounds = 0 + override fun process(resolver: Resolver): List<KSAnnotated> { + rounds++ + + val syms = resolver.getSymbolsWithAnnotation("com.example.Anno").toList() + + syms.forEach { + val v = it.validate() + if (rounds == 2 && v == false) { + logger.error("validation failed: $it") + } + } + + if (rounds == 1) { + codeGenerator.createNewFile(Dependencies(true), "com.example", "Foo1").use { + it.write("package com.example\n\ninterface Foo1\n".toByteArray()) + } + codeGenerator.createNewFile(Dependencies(true), "com.example", "Foo2", "java").use { + it.write("package com.example;\n\npublic interface Foo2{}\n".toByteArray()) + } + return syms.toList() + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt new file mode 100644 index 00000000..e6f5a794 --- /dev/null +++ b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Bar.kt @@ -0,0 +1,6 @@ +package com.example + +annotation class Anno + +@Anno +open class Bar : Foo1, Foo2 diff --git a/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java new file mode 100644 index 00000000..4ccfb0db --- /dev/null +++ b/integration-tests/src/test/resources/psi-cache/workload/src/main/java/com/example/Baz.java @@ -0,0 +1,6 @@ +package com.example; + +@Anno +public class Baz implements Foo1, Foo2 { + +} diff --git a/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..643deb7c --- /dev/null +++ b/integration-tests/src/test/resources/refs-gen/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,48 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import java.io.OutputStreamWriter + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + // FIXME: use getSymbolsWithAnnotation after it is fixed. + var rounds = 0 + override fun process(resolver: Resolver): List<KSAnnotated> { + rounds++ + logger.warn("$rounds: ${resolver.getNewFiles().toList()}") + + if (rounds == 1) { + codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("open class Foo : Goo()\n") + } + } + } + + if (rounds == 2) { + codeGenerator.createNewFile(Dependencies(false), "", "Goo", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("open class Goo : Baz()\n") + } + } + } + + resolver.getNewFiles().forEach { + it.validate() + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..9f126ff9 --- /dev/null +++ b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Bar.kt @@ -0,0 +1,3 @@ +package com.example + +class Bar : Foo() diff --git a/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..58e29ec8 --- /dev/null +++ b/integration-tests/src/test/resources/refs-gen/workload/src/main/kotlin/com/example/Baz.kt @@ -0,0 +1,5 @@ +package com.example + +open class Baz + +val l: List<Int> = TODO() diff --git a/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..934d43df --- /dev/null +++ b/integration-tests/src/test/resources/sealed-subclasses/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,28 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + override fun process(resolver: Resolver): List<KSAnnotated> { + resolver.getNewFiles().forEach { f -> + logger.warn("Processing ${f.fileName}") + f.declarations.forEach { + if (it is KSClassDeclaration) { + val subs = it.getSealedSubclasses().map { it.simpleName.asString() }.toList() + logger.warn("${it.simpleName.asString()} : $subs") + } + } + } + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt new file mode 100644 index 00000000..f4cd105e --- /dev/null +++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl1.kt @@ -0,0 +1,3 @@ +package com.example + +class Impl1 : Sealed() diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt new file mode 100644 index 00000000..aa8f5e86 --- /dev/null +++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Impl2.kt @@ -0,0 +1,3 @@ +package com.example + +class Impl2 : Sealed() diff --git a/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt new file mode 100644 index 00000000..d65d5cc9 --- /dev/null +++ b/integration-tests/src/test/resources/sealed-subclasses/workload/src/main/kotlin/com/example/Sealed.kt @@ -0,0 +1,3 @@ +package com.example + +sealed class Sealed diff --git a/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt b/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt new file mode 100644 index 00000000..f0b0f227 --- /dev/null +++ b/integration-tests/src/test/resources/srcs-gen/test-processor/src/main/kotlin/TestProcessor.kt @@ -0,0 +1,54 @@ +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import java.io.OutputStreamWriter + +class TestProcessor( + val codeGenerator: CodeGenerator, + val logger: KSPLogger +) : SymbolProcessor { + // FIXME: use getSymbolsWithAnnotation after it is fixed. + var rounds = 0 + override fun process(resolver: Resolver): List<KSAnnotated> { + rounds++ + logger.warn("$rounds: ${resolver.getNewFiles().toList().sortedBy { it.fileName }}") + + // Would fail if "Bar.kt" isn't dirty. + val barKt = resolver.getAllFiles().single { it.fileName == "Bar.kt" } + val bazKt = resolver.getAllFiles().single { it.fileName == "Baz.kt" } + + if (rounds == 1) { + codeGenerator.createNewFile(Dependencies(false), "", "Foo", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("open class Foo\n") + } + } + } + + if (rounds == 2) { + val fooKt = resolver.getAllFiles().single { it.fileName == "Foo.kt" } + codeGenerator.createNewFile(Dependencies(false, fooKt, barKt), "", "FooBar", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("open class FooBar\n") + } + } + codeGenerator.createNewFile(Dependencies(false, fooKt, bazKt), "", "FooBaz", "kt").use { output -> + OutputStreamWriter(output).use { writer -> + writer.write("package com.example\n\n") + writer.write("open class FooBaz\n") + } + } + } + + return emptyList() + } +} + +class TestProcessorProvider : SymbolProcessorProvider { + override fun create( + environment: SymbolProcessorEnvironment + ): SymbolProcessor { + return TestProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt new file mode 100644 index 00000000..2eb12746 --- /dev/null +++ b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Bar.kt @@ -0,0 +1,3 @@ +package com.example + +class Bar diff --git a/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt new file mode 100644 index 00000000..f7b34a4f --- /dev/null +++ b/integration-tests/src/test/resources/srcs-gen/workload/src/main/kotlin/com/example/Baz.kt @@ -0,0 +1,3 @@ +package com.example + +class Baz diff --git a/integration-tests/src/test/resources/test-processor/build.gradle.kts b/integration-tests/src/test/resources/test-processor/build.gradle.kts new file mode 100644 index 00000000..c5737a2e --- /dev/null +++ b/integration-tests/src/test/resources/test-processor/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} diff --git a/integration-tests/src/test/resources/test-processor/settings.gradle.kts b/integration-tests/src/test/resources/test-processor/settings.gradle.kts new file mode 100644 index 00000000..9d60cbc7 --- /dev/null +++ b/integration-tests/src/test/resources/test-processor/settings.gradle.kts @@ -0,0 +1,19 @@ +pluginManagement { + val kotlinVersion: String by settings + val kspVersion: String by settings + val testRepo: String by settings + plugins { + id("com.google.devtools.ksp") version kspVersion + kotlin("jvm") version kotlinVersion + } + repositories { + maven(testRepo) + gradlePluginPortal() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") + } +} + +rootProject.name = "playground" + +include(":workload") +include(":test-processor") diff --git a/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts b/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts new file mode 100644 index 00000000..ab249cf0 --- /dev/null +++ b/integration-tests/src/test/resources/test-processor/test-processor/build.gradle.kts @@ -0,0 +1,24 @@ +val kspVersion: String by project +val testRepo: String by project + +plugins { + kotlin("jvm") +} + +group = "com.example" +version = "1.0-SNAPSHOT" + +repositories { + maven(testRepo) + mavenCentral() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +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/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..c91e3e9e --- /dev/null +++ b/integration-tests/src/test/resources/test-processor/test-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +TestProcessorProvider diff --git a/integration-tests/src/test/resources/test-processor/workload/build.gradle.kts b/integration-tests/src/test/resources/test-processor/workload/build.gradle.kts new file mode 100644 index 00000000..f0ea52b0 --- /dev/null +++ b/integration-tests/src/test/resources/test-processor/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() + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/") +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(project(":test-processor")) + ksp(project(":test-processor")) +} |