diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/RuntimePublicAPITest.kt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/RuntimePublicAPITest.kt index 5496f9338af..ea12358811f 100644 --- a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/RuntimePublicAPITest.kt +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/RuntimePublicAPITest.kt @@ -48,7 +48,6 @@ class RuntimePublicAPITest { val publicPackagePrefixes = publicPackages.map { it.replace('.', '/') + '/' } val publicPackageFilter = { className: String -> publicPackagePrefixes.none { className.startsWith(it) } } - println("Reading binary API from $jarFile") val api = JarFile(jarFile).loadApiFromJvmClasses(publicPackageFilter) .filterOutNonPublic(nonPublicPackages) .filterOutAnnotated(nonPublicAnnotations.toSet()) diff --git a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/utils.kt b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/utils.kt index 3cbbef8f5fd..fdc5d2854c5 100644 --- a/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/utils.kt +++ b/libraries/tools/binary-compatibility-validator/src/test/kotlin/org.jetbrains.kotlin.tools.tests/utils.kt @@ -7,7 +7,9 @@ package org.jetbrains.kotlin.tools.tests import kotlinx.validation.api.* import java.io.File +import java.util.LinkedList import kotlin.test.assertEquals +import kotlin.test.assertNotEquals import kotlin.test.fail private val OVERWRITE_EXPECTED_OUTPUT = System.getProperty("overwrite.output")?.toBoolean() ?: false // use -Doverwrite.output=true @@ -27,18 +29,73 @@ private fun assertEqualsToFile(expectedFile: File, actual: CharSequence) { val actualText = actual.trimTrailingWhitespacesAndAddNewlineAtEOF() val expectedText = expectedFile.readText().trimTrailingWhitespacesAndAddNewlineAtEOF() - if (OVERWRITE_EXPECTED_OUTPUT && expectedText != actualText) { - expectedFile.writeText(actualText) - assertEquals(expectedText, actualText, "Actual data differs from file content: ${expectedFile.name}, rewriting") + if (expectedText != actualText) { + if (OVERWRITE_EXPECTED_OUTPUT) { + expectedFile.writeText(actualText) + } + + assertEqualsWithFirstLineDiff( + expectedText, + actualText, + if (OVERWRITE_EXPECTED_OUTPUT) { + "Actual data differs from file content: ${expectedFile.name}, rewriting\n" + } else { + "Actual data differs from file content: ${expectedFile.name}\nTo overwrite the expected API rerun with -Doverwrite.output=true parameter\n" + } + ) + } +} + +fun assertEqualsWithFirstLineDiff(expectedText: String, actualText: String, message: String, diffLinesSurround: Int = 1) { + val actualLinesIterator = actualText.lineSequence().iterator() + val expectedLinesIterator = expectedText.lineSequence().iterator() + + val actualBufferLines = LinkedList() + val expectedBufferLines = LinkedList() + + var diffFound = false + var diffLine = -1 + var line = 0 + + while ((actualLinesIterator.hasNext() || expectedLinesIterator.hasNext()) && (!diffFound || line - diffLine < diffLinesSurround)) { + line++ + + val actualLine: String? = if (actualLinesIterator.hasNext()) actualLinesIterator.next() else null + actualBufferLines.add(actualLine) + + val expectedLine: String? = if (expectedLinesIterator.hasNext()) expectedLinesIterator.next() else null + expectedBufferLines.add(expectedLine) + + if (actualBufferLines.size > diffLinesSurround * 2 + 1) { + actualBufferLines.removeFirst() + expectedBufferLines.removeFirst() + } + + if (!diffFound && actualLine != expectedLine) { + diffFound = true + diffLine = line + } } - assertEquals(expectedText, actualText, "Actual data differs from file content: ${expectedFile.name}\nTo overwrite the expected API rerun with -Doverwrite.output=true parameter\n") + if (diffFound) { + val lineInfo = "↓↓↓ Line $diffLine, $diffLinesSurround lines around the first difference ↓↓↓" + val actualTextAroundDiff = actualBufferLines.filterNotNull().joinToString("\n", prefix = "$lineInfo\n") + val expectedTextAroundDiff = expectedBufferLines.filterNotNull().joinToString("\n", prefix = "$lineInfo\n") + + assertNotEquals(expectedTextAroundDiff, actualTextAroundDiff, "Sanity check - chunks should be different") + + assertEquals( + expectedTextAroundDiff, + actualTextAroundDiff, + message + ) + } } private fun CharSequence.trimTrailingWhitespacesAndAddNewlineAtEOF(): String = - this.lineSequence().map { it.trimEnd() }.joinToString(separator = "\n").let { - if (it.endsWith("\n")) it else it + "\n" - } + this.lineSequence().map { it.trimEnd() }.joinToString(separator = "\n").let { + if (it.endsWith("\n")) it else it + "\n" + } private val UPPER_CASE_CHARS = Regex("[A-Z]+")