Skip to content

Commit 70aa6a7

Browse files
fzhinkinSpace Team
authored andcommitted
[ABI Validation] Stop supporting case insensitive file names
Filename case mismatch is no longer tolerated on case-sensitive filesystems. Fixes Kotlin/binary-compatibility-validator#231 Pull request Kotlin/binary-compatibility-validator#237 Moved from Kotlin/binary-compatibility-validator@4e91d92
1 parent 1ca2cb9 commit 70aa6a7

File tree

4 files changed

+53
-37
lines changed

4 files changed

+53
-37
lines changed

libraries/tools/abi-validation/api/binary-compatibility-validator.api

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public class kotlinx/validation/KotlinApiBuildTask : kotlinx/validation/BuildTas
7878
public class kotlinx/validation/KotlinApiCompareTask : org/gradle/api/DefaultTask {
7979
public field generatedApiFile Ljava/io/File;
8080
public field projectApiFile Ljava/io/File;
81-
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
81+
public fun <init> ()V
8282
public final fun getGeneratedApiFile ()Ljava/io/File;
8383
public final fun getProjectApiFile ()Ljava/io/File;
8484
public final fun setGeneratedApiFile (Ljava/io/File;)V

libraries/tools/abi-validation/src/functionalTest/kotlin/kotlinx/validation/test/DefaultConfigTests.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ package kotlinx.validation.test
77

88
import kotlinx.validation.api.*
99
import org.assertj.core.api.*
10+
import org.junit.Assume
1011
import org.junit.Test
12+
import java.io.File
13+
import java.nio.file.Files
1114
import kotlin.test.*
1215

1316
internal class DefaultConfigTests : BaseKotlinGradleTest() {
@@ -90,7 +93,9 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() {
9093
}
9194

9295
@Test
93-
fun `apiCheck should succeed when public classes match api file ignoring case`() {
96+
fun `apiCheck should fail when public classes match api file ignoring case`() {
97+
Assume.assumeTrue(underlyingFsIsCaseSensitive())
98+
9499
val runner = test {
95100
buildGradleKts {
96101
resolve("/examples/gradle/base/withPlugin.gradle.kts")
@@ -107,8 +112,8 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() {
107112
}
108113
}
109114

110-
runner.build().apply {
111-
assertTaskSuccess(":apiCheck")
115+
runner.buildAndFail().apply {
116+
assertTaskFailure(":apiCheck")
112117
}
113118
}
114119

@@ -237,4 +242,16 @@ internal class DefaultConfigTests : BaseKotlinGradleTest() {
237242
assertTaskSuccess(":apiCheck")
238243
}
239244
}
245+
246+
247+
private fun underlyingFsIsCaseSensitive(): Boolean {
248+
val f = Files.createTempFile("UPPER", "UPPER").toFile()
249+
f.deleteOnExit()
250+
try {
251+
val lower = File(f.absolutePath.lowercase())
252+
return !lower.exists()
253+
} finally {
254+
f.delete()
255+
}
256+
}
240257
}

libraries/tools/abi-validation/src/functionalTest/kotlin/kotlinx/validation/test/MultipleJvmTargetsTest.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,34 @@ internal class MultipleJvmTargetsTest : BaseKotlinGradleTest() {
134134
}
135135
}
136136

137+
// Scenario from #233: if there were two targets (and two dumps, correspondingly),
138+
// removal of one of the targets should trigger validation failure.
139+
@Test
140+
fun testValidationAfterTargetRemoval() {
141+
val runner = test {
142+
buildGradleKts {
143+
resolve("/examples/gradle/base/withPlugin.gradle.kts")
144+
}
145+
kotlin("AnotherBuildConfig.kt") {
146+
resolve("/examples/classes/AnotherBuildConfig.kt")
147+
}
148+
// let's pretend, there were multiple targets before
149+
for (tgtName in listOf("jvm", "anotherJvm")) {
150+
dir("$API_DIR/$tgtName/") {
151+
file("${rootProjectDir.name.lowercase()}.api") {
152+
resolve("/examples/classes/AnotherBuildConfig.dump")
153+
}
154+
}
155+
}
156+
runner {
157+
arguments.add(":apiCheck")
158+
}
159+
}
160+
runner.buildAndFail().apply {
161+
assertTaskFailure(":apiCheck")
162+
}
163+
}
164+
137165
private val jvmApiDump: File get() = rootProjectDir.resolve("$API_DIR/jvm/testproject.api")
138166
private val anotherApiDump: File get() = rootProjectDir.resolve("$API_DIR/anotherJvm/testproject.api")
139167

libraries/tools/abi-validation/src/main/kotlin/KotlinApiCompareTask.kt

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,11 @@ package kotlinx.validation
88
import com.github.difflib.DiffUtils
99
import com.github.difflib.UnifiedDiffUtils
1010
import java.io.*
11-
import java.util.TreeMap
1211
import javax.inject.Inject
1312
import org.gradle.api.*
14-
import org.gradle.api.file.*
15-
import org.gradle.api.model.ObjectFactory
1613
import org.gradle.api.tasks.*
1714

18-
public open class KotlinApiCompareTask @Inject constructor(private val objects: ObjectFactory): DefaultTask() {
15+
public open class KotlinApiCompareTask @Inject constructor(): DefaultTask() {
1916

2017
@InputFiles
2118
@PathSensitive(PathSensitivity.RELATIVE)
@@ -41,43 +38,17 @@ public open class KotlinApiCompareTask @Inject constructor(private val objects:
4138
}
4239
val subject = projectName
4340

44-
/*
45-
* We use case-insensitive comparison to workaround issues with case-insensitive OSes
46-
* and Gradle behaving slightly different on different platforms.
47-
* We neither know original sensitivity of existing .api files, not
48-
* build ones, because projectName that is part of the path can have any sensitvity.
49-
* To workaround that, we replace paths we are looking for the same paths that
50-
* actually exist on FS.
51-
*/
52-
fun caseInsensitiveMap() = TreeMap<String, RelativePath> { rp, rp2 ->
53-
rp.compareTo(rp2, true)
54-
}
55-
56-
val apiBuildDirFiles = caseInsensitiveMap()
57-
val expectedApiFiles = caseInsensitiveMap()
58-
59-
objects.fileTree().from(buildApiDir).visit { file ->
60-
apiBuildDirFiles[file.name] = file.relativePath
61-
}
62-
objects.fileTree().from(projectApiDir).visit { file ->
63-
expectedApiFiles[file.name] = file.relativePath
64-
}
65-
66-
if (!expectedApiFiles.containsKey(projectApiFile.name)) {
41+
if (!projectApiFile.exists()) {
6742
error("File ${projectApiFile.name} is missing from ${projectApiDir.relativeDirPath()}, please run " +
6843
":$subject:apiDump task to generate one")
6944
}
70-
if (!apiBuildDirFiles.containsKey(generatedApiFile.name)) {
45+
if (!generatedApiFile.exists()) {
7146
error("File ${generatedApiFile.name} is missing from dump results.")
7247
}
7348

7449
// Normalize case-sensitivity
75-
val expectedApiDeclaration = expectedApiFiles.getValue(projectApiFile.name)
76-
val actualApiDeclaration = apiBuildDirFiles.getValue(generatedApiFile.name)
7750
val diffSet = mutableSetOf<String>()
78-
val expectedFile = expectedApiDeclaration.getFile(projectApiDir)
79-
val actualFile = actualApiDeclaration.getFile(buildApiDir)
80-
val diff = compareFiles(expectedFile, actualFile)
51+
val diff = compareFiles(projectApiFile, generatedApiFile)
8152
if (diff != null) diffSet.add(diff)
8253
if (diffSet.isNotEmpty()) {
8354
val diffText = diffSet.joinToString("\n\n")

0 commit comments

Comments
 (0)