[Gradle][MPP] MPPBuildReproducibilityIT: Use custom diff implementation
^KT-52740 Verification Pending
This commit is contained in:
committed by
Space
parent
1e118c06af
commit
c93b1d2aab
+73
-20
@@ -10,11 +10,11 @@ import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.kotlin.gradle.testbase.*
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.condition.OS
|
||||
import java.io.File
|
||||
import kotlin.io.path.appendText
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
|
||||
/* Only supported on platforms that support the 'diff' util */
|
||||
@OsCondition(supportedOn = [OS.LINUX, OS.MAC])
|
||||
@MppGradlePluginTests
|
||||
@DisplayName("Multiplatform Build Reproducibility")
|
||||
class MPPBuildReproducibilityIT : KGPBaseTest() {
|
||||
@@ -38,6 +38,14 @@ class MPPBuildReproducibilityIT : KGPBaseTest() {
|
||||
* - Run the 'diff' tool to ensure that the published artifacts are equal in repo1 & repo2
|
||||
*/
|
||||
private fun TestProject.assertConsecutiveBuildsProduceSameBinaries() {
|
||||
fun File.relativeToProject() = this.relativeTo(projectPath.toFile())
|
||||
|
||||
fun Diff.reportString(): String = when (this) {
|
||||
is Diff.DifferentContent -> "Different File Contents: ${first.relativeToProject()} <-> ${second.relativeToProject()}"
|
||||
is Diff.MissingFile -> "Missing File: ${file.relativeToProject()}"
|
||||
is Diff.TypeMismatch -> "Expected directory, but is file: ${file.relativeToProject()}"
|
||||
}
|
||||
|
||||
buildGradleKts.appendText(
|
||||
"""
|
||||
val repo1 = buildDir.resolve("repo1")
|
||||
@@ -49,22 +57,6 @@ private fun TestProject.assertConsecutiveBuildsProduceSameBinaries() {
|
||||
maven(repo2) { name = "repo2" }
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register<Exec>("checkBuildConsistency") {
|
||||
doFirst {
|
||||
if (!repo1.exists()) {
|
||||
throw IllegalStateException("Missing repo1")
|
||||
}
|
||||
|
||||
if (!repo2.exists()) {
|
||||
throw IllegalStateException("Missing repo2")
|
||||
}
|
||||
}
|
||||
|
||||
commandLine(
|
||||
"diff", repo1.absolutePath, repo2.absolutePath, "-r", "-x", "maven-metadata**"
|
||||
)
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
@@ -72,11 +64,72 @@ private fun TestProject.assertConsecutiveBuildsProduceSameBinaries() {
|
||||
build("publishAllPublicationsToRepo2Repository", "--rerun-tasks") {
|
||||
tasks.forEach { task ->
|
||||
assertTrue(
|
||||
task.outcome in setOf(TaskOutcome.SUCCESS, TaskOutcome.NO_SOURCE),
|
||||
task.outcome in setOf(TaskOutcome.SUCCESS, TaskOutcome.SKIPPED, TaskOutcome.NO_SOURCE),
|
||||
"Expected all tasks to be re-executed. Task ${task.path} was ${task.outcome}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
build("checkBuildConsistency", forceOutput = true)
|
||||
val repo1 = projectPath.resolve("build/repo1").toFile()
|
||||
val repo2 = projectPath.resolve("build/repo2").toFile()
|
||||
|
||||
val diffs = diff(repo1, repo2).filter { diff -> "maven-metadata" !in diff.fileName }
|
||||
if (diffs.isNotEmpty()) {
|
||||
fail(
|
||||
buildString {
|
||||
appendLine("${repo1.relativeToProject()} <-> ${repo2.relativeToProject()} are inconsistent!")
|
||||
diffs.forEach { diff ->
|
||||
appendLine(diff.reportString())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Diff {
|
||||
data class MissingFile(val file: File) : Diff()
|
||||
data class DifferentContent(val first: File, val second: File) : Diff()
|
||||
data class TypeMismatch(val directory: File, val file: File) : Diff()
|
||||
|
||||
val fileName: String
|
||||
get() = when (this) {
|
||||
is DifferentContent -> first.name
|
||||
is MissingFile -> file.name
|
||||
is TypeMismatch -> directory.name
|
||||
}
|
||||
}
|
||||
|
||||
private fun diff(firstRoot: File, secondRoot: File): Set<Diff> {
|
||||
return firstRoot.walkTopDown().flatMap { firstFile ->
|
||||
val secondFile = secondRoot.resolve(firstFile.relativeTo(firstRoot))
|
||||
|
||||
if (!secondFile.exists()) {
|
||||
return@flatMap listOf(Diff.MissingFile(secondFile))
|
||||
}
|
||||
|
||||
/* File is directory */
|
||||
if (firstFile.isDirectory) {
|
||||
if (!secondFile.isDirectory) {
|
||||
return@flatMap listOf(Diff.TypeMismatch(directory = firstFile, file = secondFile))
|
||||
}
|
||||
|
||||
/* Check if all children in the second directory are also present in firstFile */
|
||||
return@flatMap secondFile.listFiles().orEmpty().mapNotNull { secondFileChild ->
|
||||
val firstFileChild = firstRoot.resolve(secondFileChild.relativeTo(secondRoot))
|
||||
if (!firstFileChild.exists()) Diff.MissingFile(firstFileChild)
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
/* File is not a directory */
|
||||
if (firstFile.length() != secondFile.length()) {
|
||||
return@flatMap listOf(Diff.DifferentContent(firstFile, secondFile))
|
||||
}
|
||||
|
||||
if (!firstFile.readBytes().contentEquals(secondFile.readBytes())) {
|
||||
return@flatMap listOf(Diff.DifferentContent(firstFile, secondFile))
|
||||
}
|
||||
|
||||
emptyList()
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user