Replace kotlin-compile-testing with our own code (#533)

This commit is contained in:
Ben Bader 2023-02-21 15:23:29 -07:00 коммит произвёл GitHub
Родитель 29aab7911c
Коммит dba7318837
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 176 добавлений и 85 удалений

Просмотреть файл

@ -26,8 +26,7 @@ hamcrest = "org.hamcrest:hamcrest:2.2"
kotest-assertions-common = { module = "io.kotest:kotest-common", version.ref = "kotest" }
kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
kotest-assertions-coreJvm = { module = "io.kotest:kotest-assertions-core-jvm", version.ref = "kotest" }
kotest-assertions-compiler = "io.kotest.extensions:kotest-assertions-compiler:1.0.0" # They have forgotten to publish newer binaries
kotlin-compile-testing = "com.github.tschuchortdev:kotlin-compile-testing:1.4.9"
kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
kotlin-test-annotations-common = { module = "org.jetbrains.kotlin:kotlin-test-annotations-common", version.ref = "kotlin" }
kotlin-test-common = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "kotlin" }
@ -35,7 +34,7 @@ kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", versi
[bundles]
kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
testing = ["junit", "hamcrest", "kotest-assertions-core", "kotest-assertions-coreJvm", "kotest-assertions-compiler", "kotlin-compile-testing"]
testing = ["junit", "hamcrest", "kotest-assertions-core", "kotest-assertions-coreJvm"]
[plugins]
dokka = "org.jetbrains.dokka:1.7.20"

Просмотреть файл

@ -37,4 +37,5 @@ dependencies {
testImplementation libs.bundles.kotlin
testImplementation libs.bundles.testing
testImplementation libs.kotlin.compiler
}

Просмотреть файл

@ -41,8 +41,11 @@ import io.kotest.matchers.string.shouldMatch
import io.kotest.matchers.string.shouldNotContain
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.io.TempDir
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import java.io.File
@Execution(ExecutionMode.CONCURRENT)
class KotlinCodeGeneratorTest {
@TempDir
lateinit var tempDir: File
@ -87,8 +90,7 @@ class KotlinCodeGeneratorTest {
val files = KotlinCodeGenerator(FieldNamingPolicy.JAVA).generate(schema)
// files.shouldCompile()
files.forEach { println("$it") }
files.shouldCompile()
}
@Test
@ -131,7 +133,7 @@ class KotlinCodeGeneratorTest {
val gen = KotlinCodeGenerator().filePerType()
val specs = gen.generate(schema)
// specs.shouldCompile()
specs.shouldCompile()
specs.single().name shouldBe "Constants" // ".kt" suffix is appended when the file is written out
}
@ -144,12 +146,10 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.shouldCompile()
val struct = specs.single().members.single() as TypeSpec
println(specs.single())
struct.name shouldBe "Empty"
struct.modifiers.any { it == KModifier.DATA } shouldBe false
struct.funSpecs.any { it.name == "toString" } shouldBe true
@ -168,7 +168,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.shouldCompile()
val struct = specs.single().members.single() as TypeSpec
@ -191,7 +191,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.shouldCompile()
val struct = specs.single().members.single() as TypeSpec
@ -213,7 +213,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.shouldCompile()
val struct = specs.single().members.single() as TypeSpec
@ -236,7 +236,7 @@ class KotlinCodeGeneratorTest {
val schema = load(thrift)
val specs = KotlinCodeGenerator(FieldNamingPolicy.JAVA).generate(schema)
// specs.shouldCompile()
specs.shouldCompile()
val xception = specs.single().members.single() as TypeSpec
xception.propertySpecs.single().name shouldBe "message_"
}
@ -253,7 +253,7 @@ class KotlinCodeGeneratorTest {
val specs = generate(thrift) {
withDataClassBuilders()
}
// specs.shouldCompile()
specs.shouldCompile()
}
@Test
@ -272,8 +272,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.forEach { println(it) }
specs.shouldCompile()
}
@Test
@ -294,8 +293,7 @@ class KotlinCodeGeneratorTest {
generateServer()
withDataClassBuilders()
}
// specs.shouldCompile()
specs.forEach { println(it) }
specs.shouldCompile()
}
@Test
@ -311,8 +309,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val specs = generate(thrift)
// specs.shouldCompile()
specs.forEach { println(it) }
specs.shouldCompile()
}
@Test
@ -327,7 +324,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val file = generate(thrift).single()
// file.shouldCompile()
file.shouldCompile()
val svc = file.members.first { it is TypeSpec && it.name == "Foo" } as TypeSpec
val method = svc.funSpecs.single()
method.name shouldBe "doIt"
@ -428,7 +425,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { coroutineServiceClients() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
|public interface Svc {
@ -481,9 +478,9 @@ class KotlinCodeGeneratorTest {
emitJvmName()
filePerNamespace()
emitFileComment(false)
}
.single()
// file.shouldCompile()
}.single()
file.shouldCompile()
file.toString() shouldBe """
|@file:JvmName("ThriftTypes")
@ -510,9 +507,9 @@ class KotlinCodeGeneratorTest {
emitJvmName()
filePerType()
emitFileComment(false)
}
.single()
// file.shouldCompile()
}.single()
file.shouldCompile()
file.toString() shouldBe """
|@file:JvmName("Constants")
@ -541,7 +538,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { coroutineServiceClients() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
|sealed class Union : Struct {
@ -562,7 +559,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { coroutineServiceClients() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
|
@ -607,7 +604,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| public class Builder : StructBuilder<Union> {
@ -651,7 +648,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift)
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldNot contain("""
| class Builder
@ -672,7 +669,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) //{ shouldImplementStruct() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldNot contain("""
| : Struct
@ -697,7 +694,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| public override fun read(protocol: Protocol) = read(protocol, Builder())
@ -766,7 +763,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift)
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| public override fun read(protocol: Protocol): Union {
@ -834,7 +831,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| private class UnionAdapter : Adapter<Union, Builder> {
@ -855,7 +852,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift)
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| private class UnionAdapter : Adapter<Union> {
@ -872,7 +869,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { coroutineServiceClients() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
|class Union() : Struct {
@ -895,7 +892,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
|public sealed class UnionStruct : Struct {
@ -927,7 +924,7 @@ class KotlinCodeGeneratorTest {
""".trimIndent()
val file = generate(thrift)
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| @JvmField
@ -947,7 +944,7 @@ class KotlinCodeGeneratorTest {
""".trimMargin()
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() should contain("""
| public override fun build(): Bonk = Bonk(message = this.message, type = this.type)
@ -981,7 +978,7 @@ class KotlinCodeGeneratorTest {
}"""
val file = generate(thrift) { withDataClassBuilders() }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
}
@ -1028,7 +1025,7 @@ class KotlinCodeGeneratorTest {
withDataClassBuilders()
failOnUnknownEnumValues(false)
}
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
}
@ -1072,7 +1069,7 @@ class KotlinCodeGeneratorTest {
}"""
val file = generate(thrift) { failOnUnknownEnumValues(false) }
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
}
@ -1097,7 +1094,7 @@ class KotlinCodeGeneratorTest {
withDataClassBuilders()
builderRequiredConstructor()
}
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
}
@ -1122,7 +1119,7 @@ class KotlinCodeGeneratorTest {
withDataClassBuilders()
builderRequiredConstructor()
}
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
}
@ -1149,7 +1146,7 @@ class KotlinCodeGeneratorTest {
withDataClassBuilders()
builderRequiredConstructor()
}
// file.shouldCompile()
file.shouldCompile()
file.single().toString() shouldContain expected
file.single().toString() shouldNotContain notExpected
}
@ -1337,7 +1334,7 @@ class KotlinCodeGeneratorTest {
val file = generate(thrift).single()
file.toString() shouldContain expected
// file.shouldCompile()
file.shouldCompile()
}
@Test
@ -1398,7 +1395,7 @@ class KotlinCodeGeneratorTest {
val file = generate(thrift) { withDataClassBuilders() }.single()
file.toString() shouldContain expected
// file.shouldCompile()
file.shouldCompile()
}
@Test
@ -1411,7 +1408,7 @@ class KotlinCodeGeneratorTest {
val file = generate(thrift) { emitFileComment(true) }
.single()
// file.shouldCompile()
file.shouldCompile()
val lines = file.toString().split("\n")
lines[0] shouldBe "// Automatically generated by the Thrifty compiler; do not edit!"
@ -1456,8 +1453,6 @@ class KotlinCodeGeneratorTest {
val file = generate(thrift)
println(file)
file.toString() shouldContain """
|public val THE_FOO: Foo = Foo(
| text = "FOO",
@ -1490,7 +1485,7 @@ class KotlinCodeGeneratorTest {
positionOfA shouldBeLessThan positionOfD
// files.shouldCompile()
files.shouldCompile()
}
private fun generate(thrift: String, config: (KotlinCodeGenerator.() -> KotlinCodeGenerator)? = null): List<FileSpec> {

Просмотреть файл

@ -21,51 +21,147 @@
package com.microsoft.thrifty.kgen
import com.squareup.kotlinpoet.FileSpec
import com.tschuchort.compiletesting.KotlinCompilation
import com.tschuchort.compiletesting.KotlinCompilation.ExitCode
import com.tschuchort.compiletesting.KotlinCompilation.Result
import com.tschuchort.compiletesting.SourceFile
import io.kotest.matchers.Matcher
import io.kotest.matchers.MatcherResult
import io.kotest.matchers.should
import java.io.ByteArrayOutputStream
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services
import java.nio.file.Files
import kotlin.streams.toList
import java.nio.file.Path
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.absolutePathString
import kotlin.io.path.deleteRecursively
fun List<FileSpec>.shouldCompile() {
this should compiles
this should compile()
}
fun FileSpec.shouldCompile() {
listOf(this) should compiles
listOf(this) should compile()
}
private fun compileCodeSnippet(codeSnippet: List<FileSpec>): Result {
val kotlinCompilation = KotlinCompilation()
.apply {
inheritClassPath = true
verbose = false
messageOutputStream = ByteArrayOutputStream()
}
codeSnippet.forEach { it.writeTo(kotlinCompilation.workingDir) }
kotlinCompilation.sources = Files.find(
kotlinCompilation.workingDir.toPath(),
20,
{ _, basicFileAttributes -> basicFileAttributes.isRegularFile })
.map { SourceFile.fromPath(it.toFile()) }.toList()
return kotlinCompilation.compile()
fun compile(): ShouldCompileMatcher {
return ShouldCompileMatcher()
}
private val compiles = object : Matcher<List<FileSpec>> {
open class ShouldCompileMatcher : Matcher<List<FileSpec>> {
private val collector = LogEverythingMessageCollector()
private var debugLoggingEnabled = false
fun withDebugLogging(): ShouldCompileMatcher {
debugLoggingEnabled = true
return this
}
override fun test(value: List<FileSpec>): MatcherResult {
val compilationResult = compileCodeSnippet(value)
return MatcherResult(
compilationResult.exitCode == ExitCode.OK,
{
"Expected code to compile, but it failed to compile with error: \n${compilationResult.messages}"
},
{ "Expected code to fail to compile, but it compiled" }
)
return withTempDir { dir ->
val code = compileKotlin(dir, value)
MatcherResult(
code == ExitCode.OK,
{ formatCompilerErrors(collector) },
{ "compilation should have failed but did not" }
)
}
}
private fun formatCompilerErrors(collector: LogEverythingMessageCollector): String {
return buildString {
append("compilation failed:")
for (message in collector.messages.filter { isSeverityPrintable(it.severity) }) {
append("\n\t")
append(message)
}
append("\n")
}
}
private fun isSeverityPrintable(sev: CompilerMessageSeverity): Boolean {
return debugLoggingEnabled || sev.isError
}
// 'deleteRecursively' is far more convenient than any other option, but is
// unfortunately still "experimental".
@OptIn(ExperimentalPathApi::class)
private inline fun <T> withTempDir(fn: (Path) -> T): T {
val tempDir = Files.createTempDirectory("kotlin-compile")
try {
return fn(tempDir)
} finally {
tempDir.deleteRecursively()
}
}
private fun compileKotlin(rootDir: Path, files: List<FileSpec>): ExitCode {
val sources = Files.createDirectory(rootDir.resolve("src"))
val output = Files.createDirectory(rootDir.resolve("bin"))
files.forEach { it.writeTo(sources) }
val args = K2JVMCompilerArguments().apply {
destination = output.absolutePathString()
freeArgs += sources.absolutePathString()
classpath = System.getProperty("java.class.path")
// This tells the compiler not to try to find kotlin-stdlib and kotlin-reflect
// on our local machine; it's not guaranteed that any given machine will have
// a predictable kotlin home. Moreover, *we already have these on the classpath*
// and so they'll be available anyway.
noStdlib = true
noReflect = true
// we have a Need for Speed
useK2 = true
noOptimize = true
useFastJarFileSystem = true
useIR = true
}
return K2JVMCompiler().exec(collector, Services.EMPTY, args)
}
}
data class Message(
val severity: CompilerMessageSeverity,
val text: String,
val location: CompilerMessageSourceLocation?
) {
override fun toString() = buildString {
append(severity.presentableName[0])
append(": ")
append(text)
if (location != null) {
append("(")
append(location.path)
append(":")
append(location.line)
append(")")
}
}
}
private class LogEverythingMessageCollector : MessageCollector {
private val messageArrayList = arrayListOf<Message>()
val messages: List<Message>
get() = messageArrayList
override fun clear() {
messageArrayList.clear()
}
override fun hasErrors(): Boolean {
return messageArrayList.isNotEmpty()
}
override fun report(
severity: CompilerMessageSeverity,
message: String,
location: CompilerMessageSourceLocation?
) {
messageArrayList += Message(severity, message, location)
}
}