Skip to content

Commit

Permalink
Align JVM- and KLib-validation behavior for empty projects
Browse files Browse the repository at this point in the history
Also, reworked error reporting for the compare task.
  • Loading branch information
fzhinkin committed Jun 6, 2024
1 parent b220a9c commit 8b0f562
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() {
}
}

val projectName = rootProjectDir.name
runner.buildAndFail().apply {
assertTrue { output.contains("Please ensure that ':apiDump' was executed") }
Assertions.assertThat(output).contains(
"Expected file with API declarations 'api/$projectName.api' does not exist."
).contains(
"Please ensure that ':apiDump' was executed in order to get an API dump to compare the build against"
)
assertTaskFailure(":apiCheck")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.gradle.testkit.runner.BuildResult
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.utils.addToStdlib.butIf
import org.junit.Assert
import org.junit.Assume
import org.junit.Test
import java.io.File
Expand Down Expand Up @@ -49,27 +50,32 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() {
resolve("/examples/gradle/base/withNativePlugin.gradle.kts")
}
}

private fun BaseKotlinScope.additionalBuildConfig(config: String) {
buildGradleKts {
resolve(config)
}
}

private fun BaseKotlinScope.addToSrcSet(pathTestFile: String, sourceSet: String = "commonMain") {
val fileName = Paths.get(pathTestFile).fileName.toString()
kotlin(fileName, sourceSet) {
resolve(pathTestFile)
}
}

private fun BaseKotlinScope.runApiCheck() {
runner {
arguments.add(":apiCheck")
}
}

private fun BaseKotlinScope.runApiDump() {
runner {
arguments.add(":apiDump")
}
}

private fun assertApiCheckPassed(buildResult: BuildResult) {
buildResult.assertTaskSuccess(":apiCheck")
}
Expand Down Expand Up @@ -581,8 +587,10 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() {
checkKlibDump(runner.build(), "/examples/classes/AnotherBuildConfig.klib.dump")

// Update the source file by adding a declaration
val updatedSourceFile = File(this::class.java.getResource(
"/examples/classes/AnotherBuildConfigModified.kt")!!.toURI()
val updatedSourceFile = File(
this::class.java.getResource(
"/examples/classes/AnotherBuildConfigModified.kt"
)!!.toURI()
)
val existingSource = runner.projectDir.resolve(
"src/commonMain/kotlin/AnotherBuildConfig.kt"
Expand All @@ -602,8 +610,10 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() {
checkKlibDump(runner.build(), "/examples/classes/AnotherBuildConfig.klib.dump")

// Update the source file by adding a declaration
val updatedSourceFile = File(this::class.java.getResource(
"/examples/classes/AnotherBuildConfigLinuxArm64.kt")!!.toURI()
val updatedSourceFile = File(
this::class.java.getResource(
"/examples/classes/AnotherBuildConfigLinuxArm64.kt"
)!!.toURI()
)
val existingSource = runner.projectDir.resolve(
"src/linuxArm64Main/kotlin/AnotherBuildConfigLinuxArm64.kt"
Expand All @@ -627,8 +637,10 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() {
assertApiCheckPassed(runner.build())

// Update the source file by adding a declaration
val updatedSourceFile = File(this::class.java.getResource(
"/examples/classes/AnotherBuildConfigModified.kt")!!.toURI()
val updatedSourceFile = File(
this::class.java.getResource(
"/examples/classes/AnotherBuildConfigModified.kt"
)!!.toURI()
)
val existingSource = runner.projectDir.resolve(
"src/commonMain/kotlin/AnotherBuildConfig.kt"
Expand Down Expand Up @@ -701,13 +713,18 @@ internal class KlibVerificationTests : BaseKotlinGradleTest() {
}

@Test
fun `apiCheck should not fail for empty project`() {
fun `apiCheck should fail for empty project`() {
val runner = test {
baseProjectSetting()
addToSrcSet("/examples/classes/AnotherBuildConfig.kt", sourceSet = "commonTest")
runApiCheck()
}
runner.build()
runner.buildAndFail().apply {
assertTaskFailure(":klibApiExtractForValidation")
Assertions.assertThat(output).contains(
"File with project's API declarations 'api/testproject.klib.api' does not exist."
)
}
}

@Test
Expand Down
47 changes: 20 additions & 27 deletions src/main/kotlin/KotlinApiCompareTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.*

@CacheableTask
public open class KotlinApiCompareTask @Inject constructor(): DefaultTask() {
public open class KotlinApiCompareTask @Inject constructor() : DefaultTask() {

@get:InputFiles
@get:SkipWhenEmpty
@get:InputFiles // don't fail the task if file does not exist, instead print custom error message from verify()
@get:PathSensitive(PathSensitivity.RELATIVE)
public val projectApiFile: RegularFileProperty = project.objects.fileProperty()

@get:InputFiles
@get:SkipWhenEmpty
@get:InputFiles // don't fail the task if file does not exist, instead print custom error message from verify()
@get:PathSensitive(PathSensitivity.RELATIVE)
public val generatedApiFile: RegularFileProperty = project.objects.fileProperty()

Expand All @@ -35,51 +33,46 @@ public open class KotlinApiCompareTask @Inject constructor(): DefaultTask() {
val projectApiFile = projectApiFile.get().asFile
val generatedApiFile = generatedApiFile.get().asFile

val projectApiDir = projectApiFile.parentFile
if (!projectApiDir.exists()) {
error("Expected folder with API declarations '$projectApiDir' does not exist.\n" +
"Please ensure that ':apiDump' was executed in order to get API dump to compare the build against")
}
val buildApiDir = generatedApiFile.parentFile
if (!buildApiDir.exists()) {
error("Expected folder with generate API declarations '$buildApiDir' does not exist.")
}
val subject = projectName

if (!projectApiFile.exists()) {
error("File ${projectApiFile.name} is missing from ${projectApiDir.relativeDirPath()}, please run " +
":$subject:apiDump task to generate one")
error(
"Expected file with API declarations '${projectApiFile.relativeTo(rootDir)}' does not exist.\n" +
"Please ensure that ':apiDump' was executed in order to get " +
"an API dump to compare the build against"
)
}
if (!generatedApiFile.exists()) {
error("File ${generatedApiFile.name} is missing from dump results.")
error(
"Expected file with generated API declarations '${generatedApiFile.relativeTo(rootDir)}'" +
" does not exist."
)
}

// Normalize case-sensitivity
val diffSet = mutableSetOf<String>()
val diff = compareFiles(projectApiFile, generatedApiFile)
if (diff != null) diffSet.add(diff)
if (diffSet.isNotEmpty()) {
val diffText = diffSet.joinToString("\n\n")
error("API check failed for project $subject.\n$diffText\n\n You can run :$subject:apiDump task to overwrite API declarations")
val subject = projectName
error(
"API check failed for project $subject.\n$diffText\n\n" +
"You can run :$subject:apiDump task to overwrite API declarations"
)
}
}

private fun File.relativeDirPath(): String {
return toRelativeString(rootDir) + File.separator
}

private fun compareFiles(checkFile: File, builtFile: File): String? {
val checkText = checkFile.readText()
val builtText = builtFile.readText()

// We don't compare full text because newlines on Windows & Linux/macOS are different
// We don't compare a full text because newlines on Windows & Linux/macOS are different
val checkLines = checkText.lines()
val builtLines = builtText.lines()
if (checkLines == builtLines)
return null

val patch = DiffUtils.diff(checkLines, builtLines)
val diff = UnifiedDiffUtils.generateUnifiedDiff(checkFile.toString(), builtFile.toString(), checkLines, patch, 3)
val diff =
UnifiedDiffUtils.generateUnifiedDiff(checkFile.toString(), builtFile.toString(), checkLines, patch, 3)
return diff.joinToString("\n")
}
}
11 changes: 8 additions & 3 deletions src/main/kotlin/KotlinKlibExtractAbiTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public abstract class KotlinKlibExtractAbiTask : DefaultTask() {
/**
* Merged KLib dump that should be filtered by this task.
*/
@get:InputFiles
@get:InputFiles // don't fail the task if file does not exist, instead print custom error message from generate()
@get:PathSensitive(PathSensitivity.RELATIVE)
public abstract val inputAbiFile: RegularFileProperty

Expand All @@ -47,13 +47,18 @@ public abstract class KotlinKlibExtractAbiTask : DefaultTask() {
@get:OutputFile
public abstract val outputAbiFile: RegularFileProperty

private val rootDir = project.rootDir

@OptIn(ExperimentalBCVApi::class)
@TaskAction
internal fun generate() {
val inputFile = inputAbiFile.asFile.get()
if (!inputFile.exists()) return
if (!inputFile.exists()) {
error("File with project's API declarations '${inputFile.relativeTo(rootDir)}' does not exist.\n" +
"Please ensure that ':apiDump' was executed in order to get API dump to compare the build against")
}
if (inputFile.length() == 0L) {
error("Project ABI file $inputAbiFile is empty.")
error("Project ABI file ${inputFile.relativeTo(rootDir)} is empty.")
}
val dump = KlibDump.from(inputFile)
val enabledTargets = requiredTargets.get().map(KlibTarget::targetName).toSet()
Expand Down

0 comments on commit 8b0f562

Please sign in to comment.