Skip to content

Commit

Permalink
Relax API Versioning test (#63)
Browse files Browse the repository at this point in the history
* Make ApiVersioning more flexible for version changing

* simplify work with updating gold files
  • Loading branch information
Mr3zee authored May 17, 2024
1 parent 6a706ae commit 6904f27
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 41 deletions.
28 changes: 22 additions & 6 deletions runtime/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
*/

import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest

/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/
import org.jetbrains.kotlin.utils.fileUtils.withReplacedExtensionOrNull
import java.nio.file.Files

plugins {
alias(libs.plugins.conventions.kmp)
Expand Down Expand Up @@ -67,10 +65,28 @@ tasks.withType<KotlinJvmTest> {
environment("LIBRARY_VERSION", libs.versions.rpc.core.get())
}

val resourcesPath = projectDir.resolve("src/jvmTest/resources")
val tmpExt = "tmp"
val goldExt = "gold"

tasks.named("clean") {
doLast {
projectDir.resolve("src/jvmTest/resources").walk().forEach {
if (it.isFile && it.extension == "tmp") {
resourcesPath.walk().forEach {
if (it.isFile && it.extension == tmpExt) {
it.delete()
}
}
}
}

tasks.create("moveToGold") {
doLast {
resourcesPath.walk().forEach {
if (it.isFile && it.extension == tmpExt) {
val gold = it.withReplacedExtensionOrNull(tmpExt, goldExt)?.toPath()
?: error("Expected file with replaced '.$tmpExt' extension to '.$goldExt' extension: $it")

Files.write(gold, it.readBytes())
it.delete()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

package kotlinx.rpc.test.api

import kotlinx.rpc.test.api.ApiVersioningTest.Companion.CLASS_DUMPS_DIR
import kotlinx.rpc.test.api.ApiVersioningTest.Companion.CURRENT_CLASS_DUMPS_DIR
import kotlinx.rpc.test.api.ApiVersioningTest.Companion.LATEST_CLASS_DUMPS_DIR
import kotlinx.rpc.test.api.util.StringGoldContent
import kotlinx.rpc.test.api.util.checkGold
import kotlin.reflect.KClass
Expand All @@ -26,7 +27,8 @@ class ApiTestContext {
sampled.add(clazz)

val log = checkGold(
fileDir = CLASS_DUMPS_DIR,
latestDir = LATEST_CLASS_DUMPS_DIR,
currentDir = CURRENT_CLASS_DUMPS_DIR,
filename = clazz.simpleName!!,
content = StringGoldContent(currentContent),
parseGoldFile = { StringGoldContent(it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import org.jetbrains.krpc.test.api.util.SamplingData
import org.junit.Test
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isDirectory
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.name
import kotlin.test.Ignore
import kotlin.test.assertEquals
import kotlin.test.fail
Expand Down Expand Up @@ -158,13 +161,34 @@ class ApiVersioningTest {
val LIBRARY_VERSION_DIR = System.getenv("LIBRARY_VERSION")?.versionToDirName()
?: error("Expected LIBRARY_VERSION env variable")

val CLASS_DUMPS_DIR: Path = Path("src/jvmTest/resources/class_dumps/$LIBRARY_VERSION_DIR")
val CURRENT_CLASS_DUMPS_DIR: Path = Path("src/jvmTest/resources/class_dumps/")
.resolve(LIBRARY_VERSION_DIR)

val LATEST_CLASS_DUMPS_DIR: Path = Path("src/jvmTest/resources/class_dumps/")
.latestVersionOrCurrent()

val WIRE_DUMPS_DIR: Path = Path("src/jvmTest/resources/wire_dumps/")
val INDEXED_ENUM_DUMPS_DIR: Path = Path("src/jvmTest/resources/indexed_enum_dumps/")

private fun String.versionToDirName(): String {
return replace('.', '_').replace('-', '_')
}

fun Path.latestVersionOrCurrent(): Path {
return listDirectoryEntries()
.filter { it.isDirectory() }
.sortedWith { a, b ->
val aBeta = a.name.contains("beta")
val bBeta = b.name.contains("beta")
when {
aBeta && bBeta -> a.compareTo(b)
aBeta -> -1
bBeta -> 1
else -> a.compareTo(b)
}
}.lastOrNull()
?: resolve(LIBRARY_VERSION_DIR)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ fun <E> testEnum(
)
}

val log = checkGold(INDEXED_ENUM_DUMPS_DIR, name, EnumGoldContent(values.toSet()), fromText) ?: return
val log = checkGold(
latestDir = INDEXED_ENUM_DUMPS_DIR,
currentDir = INDEXED_ENUM_DUMPS_DIR,
filename = name,
content = EnumGoldContent(values.toSet()),
parseGoldFile = fromText,
) ?: return

fail(log)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import kotlinx.rpc.test.KRPCTestClient
import kotlinx.rpc.test.KRPCTestServer
import kotlinx.rpc.test.KRPCTestServiceBackend
import kotlinx.rpc.test.LocalTransport
import kotlinx.rpc.test.api.ApiVersioningTest.Companion.latestVersionOrCurrent
import kotlinx.rpc.test.api.util.GoldComparable
import kotlinx.rpc.test.api.util.GoldComparisonResult
import kotlinx.rpc.test.api.util.GoldUtils
Expand Down Expand Up @@ -73,7 +74,8 @@ class WireSamplingTestScope(private val sampleName: String, scope: TestScope) :
}

val log = checkGold(
fileDir = CURRENT_WIRE_DUMPS_DIR,
latestDir = LATEST_WIRE_DUMPS_DIR,
currentDir = CURRENT_WIRE_DUMPS_DIR,
filename = "${sampleName}_${format.name.lowercase()}",
content = WireContent(finishedToolkit.logs, format.commentBinaryOutput),
parseGoldFile = { WireContent.fromText(it) },
Expand Down Expand Up @@ -183,6 +185,9 @@ class WireSamplingTestScope(private val sampleName: String, scope: TestScope) :
companion object {
private val CURRENT_WIRE_DUMPS_DIR = ApiVersioningTest.WIRE_DUMPS_DIR
.resolve(ApiVersioningTest.LIBRARY_VERSION_DIR)

private val LATEST_WIRE_DUMPS_DIR = ApiVersioningTest.WIRE_DUMPS_DIR
.latestVersionOrCurrent()
}
}

Expand Down
36 changes: 6 additions & 30 deletions runtime/src/jvmTest/kotlin/kotlinx/rpc/test/api/util/GoldChecks.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import kotlinx.rpc.test.api.util.GoldUtils.GOLD_EXTENSION
import kotlinx.rpc.test.api.util.GoldUtils.TMP_EXTENSION
import java.io.File
import java.nio.file.Path
import java.util.*

interface GoldComparable<T : GoldComparable<T>> {
fun compare(other: T): GoldComparisonResult
Expand Down Expand Up @@ -44,18 +43,19 @@ class StringGoldContent(private val value: String) : GoldComparable<StringGoldCo
* Returns log if tmp file was created, null otherwise
*/
fun <T : GoldComparable<T>> checkGold(
fileDir: Path,
latestDir: Path,
currentDir: Path,
filename: String,
content: T,
parseGoldFile: (String) -> T,
): String? {
val (gold, goldPath) = loadGold(fileDir, filename, parseGoldFile) ?: (null to null)
val (gold, goldPath) = loadGold(latestDir, filename, parseGoldFile) ?: (null to null)

val comparison = gold?.let { content.compare(gold) }
?: GoldComparisonResult.Failure("No previous Gold file")

if (comparison is GoldComparisonResult.Failure) {
return writeTmp(fileDir, filename, content.dump(), goldPath, comparison.message)
return writeTmp(currentDir, filename, content.dump(), goldPath, comparison.message)
}

return null
Expand All @@ -81,7 +81,7 @@ private fun writeTmp(
reason: String?,
): String {
if (isCI) {
return "Attempting to write temp files on CI"
return "Attempting to write temp files on CI: $fileDir/$filename, reason: $reason"
}

val file = fileDir.resolve("$filename.$TMP_EXTENSION").toFile()
Expand All @@ -100,7 +100,7 @@ private fun writeTmp(
Please, review and commit:
${goldFileLine(goldPath)}
Temp file: file://${file.absolutePath}
Move to Gold: file://${createMoveToGoldScript(file.absolutePath)}
Run 'moveToGold' Gradle task to update gold files.
""".trimIndent()
}

Expand All @@ -112,30 +112,6 @@ private fun goldFileLine(goldPath: String?): String {
}
}

private fun createMoveToGoldScript(tempFile: String): String {
val goldFile = tempFile.replace(".$TMP_EXTENSION", ".$GOLD_EXTENSION")
return createTempScriptFile {
appendText("#!/bin/sh")
appendText(GoldUtils.NewLine)
val command = "${if (isUnix) "mv -f" else "move /y"} \"$tempFile\" \"$goldFile\""
appendText(command)
}
}

private fun createTempScriptFile(write: File.() -> Unit): String {
val realExtension = if (isUnix) "sh" else "bat"
val tmpExtension = "tmp" // cannot create temp file with .sh extension (too short)

val tempFile = kotlin.io.path.createTempFile(tmpExtension).toFile().apply(write)

val realFile = File(tempFile.absolutePath.replace(".$tmpExtension", ".$realExtension"))
tempFile.renameTo(realFile)
realFile.setExecutable(true)

return realFile.absolutePath
}

private val isUnix = !System.getProperty("os.name").lowercase(Locale.getDefault()).contains("win")
private val isCI = System.getenv("TEAMCITY_VERSION") != null

object GoldUtils {
Expand Down

0 comments on commit 6904f27

Please sign in to comment.