Compare lookups after comparing build logs
This way tests are more informative when compilation goes wrong.
This commit is contained in:
@@ -583,7 +583,15 @@ public class KotlinTestUtils {
|
|||||||
assertEqualsToFile(expectedFile, actual, s -> s);
|
assertEqualsToFile(expectedFile, actual, s -> s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual) {
|
||||||
|
assertEqualsToFile(message, expectedFile, actual, s -> s);
|
||||||
|
}
|
||||||
|
|
||||||
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
public static void assertEqualsToFile(@NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||||
|
assertEqualsToFile("Actual data differs from file content", expectedFile, actual, sanitizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertEqualsToFile(@NotNull String message, @NotNull File expectedFile, @NotNull String actual, @NotNull Function1<String, String> sanitizer) {
|
||||||
try {
|
try {
|
||||||
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
String actualText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(actual.trim()));
|
||||||
|
|
||||||
@@ -596,7 +604,7 @@ public class KotlinTestUtils {
|
|||||||
String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
String expectedText = JetTestUtilsKt.trimTrailingWhitespacesAndAddNewlineAtEOF(StringUtil.convertLineSeparators(expected.trim()));
|
||||||
|
|
||||||
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
if (!Comparing.equal(sanitizer.invoke(expectedText), sanitizer.invoke(actualText))) {
|
||||||
throw new FileComparisonFailure("Actual data differs from file content: " + expectedFile.getName(),
|
throw new FileComparisonFailure(message + ": " + expectedFile.getName(),
|
||||||
expected, actual, expectedFile.getAbsolutePath());
|
expected, actual, expectedFile.getAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+61
-68
@@ -149,13 +149,6 @@ abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
|
|||||||
// ignore KDoc like comments which starts with `/**`, example: /** text */
|
// ignore KDoc like comments which starts with `/**`, example: /** text */
|
||||||
private val COMMENT_WITH_LOOKUP_INFO = "/\\*[^*]+\\*/".toRegex()
|
private val COMMENT_WITH_LOOKUP_INFO = "/\\*[^*]+\\*/".toRegex()
|
||||||
|
|
||||||
private fun removeCommentsCommentsWithLookupInfo(files: Iterable<File>) {
|
|
||||||
files.forEach {
|
|
||||||
val content = it.readText()
|
|
||||||
it.writeText(content.replace(COMMENT_WITH_LOOKUP_INFO, ""))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected lateinit var srcDir: File
|
protected lateinit var srcDir: File
|
||||||
protected lateinit var outDir: File
|
protected lateinit var outDir: File
|
||||||
private var isICEnabledBackup: Boolean = false
|
private var isICEnabledBackup: Boolean = false
|
||||||
@@ -185,11 +178,11 @@ abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
|
|||||||
|
|
||||||
sb.appendln("Compiling files:")
|
sb.appendln("Compiling files:")
|
||||||
for (compiledFile in compiledFiles.sortedBy { it.canonicalPath }) {
|
for (compiledFile in compiledFiles.sortedBy { it.canonicalPath }) {
|
||||||
val lookupCount = lookupsCount[compiledFile]
|
val lookupsFromFile = lookups[compiledFile]
|
||||||
val lookupStatus = when {
|
val lookupStatus = when {
|
||||||
lookupCount == 0 -> "(no lookups)"
|
lookupsFromFile == null -> "(unknown)"
|
||||||
lookupCount != null && lookupCount > 0 -> ""
|
lookupsFromFile.isEmpty() -> "(no lookups)"
|
||||||
else -> "(unknown)"
|
else -> ""
|
||||||
}
|
}
|
||||||
sb.indentln("${compiledFile.toRelativeString(workingDir)}$lookupStatus")
|
sb.indentln("${compiledFile.toRelativeString(workingDir)}$lookupStatus")
|
||||||
}
|
}
|
||||||
@@ -207,30 +200,50 @@ abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
|
|||||||
val steps = getModificationsToPerform(testDir, moduleNames = null, allowNoFilesWithSuffixInTestData = true, touchPolicy = TouchPolicy.CHECKSUM)
|
val steps = getModificationsToPerform(testDir, moduleNames = null, allowNoFilesWithSuffixInTestData = true, touchPolicy = TouchPolicy.CHECKSUM)
|
||||||
.filter { it.isNotEmpty() }
|
.filter { it.isNotEmpty() }
|
||||||
|
|
||||||
makeAndCheckLookups(dirtyFiles, workToOriginalFileMap, incrementalData).logOutput("INITIAL BUILD")
|
val filesToLookups = arrayListOf<Map<File, List<LookupInfo>>>()
|
||||||
|
fun CompilerOutput.originalFilesToLookups() =
|
||||||
|
compiledFiles.associateBy({ workToOriginalFileMap[it]!! }, { lookups[it] ?: emptyList() })
|
||||||
|
|
||||||
|
make(dirtyFiles, incrementalData).apply {
|
||||||
|
logOutput("INITIAL BUILD")
|
||||||
|
filesToLookups.add(originalFilesToLookups())
|
||||||
|
|
||||||
|
}
|
||||||
for ((i, modifications) in steps.withIndex()) {
|
for ((i, modifications) in steps.withIndex()) {
|
||||||
dirtyFiles = modifications.mapNotNullTo(HashSet()) { it.perform(workingDir, workToOriginalFileMap) }
|
dirtyFiles = modifications.mapNotNullTo(HashSet()) { it.perform(workingDir, workToOriginalFileMap) }
|
||||||
makeAndCheckLookups(dirtyFiles, workToOriginalFileMap, incrementalData).logOutput("STEP ${i + 1}")
|
make(dirtyFiles, incrementalData).apply {
|
||||||
|
logOutput("STEP ${i + 1}")
|
||||||
|
filesToLookups.add(originalFilesToLookups())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val expectedBuildLog = File(testDir, "build.log")
|
val expectedBuildLog = File(testDir, "build.log")
|
||||||
UsefulTestCase.assertSameLinesWithFile(expectedBuildLog.canonicalPath, sb.toString())
|
UsefulTestCase.assertSameLinesWithFile(expectedBuildLog.canonicalPath, sb.toString())
|
||||||
|
|
||||||
|
assertEquals(steps.size + 1, filesToLookups.size)
|
||||||
|
for ((i, lookupsAtStepI) in filesToLookups.withIndex()) {
|
||||||
|
val step = if (i == 0) "INITIAL BUILD" else "STEP $i"
|
||||||
|
for ((file, lookups) in lookupsAtStepI) {
|
||||||
|
checkLookupsInFile(step, file, lookups)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CompilerOutput(
|
private class CompilerOutput(
|
||||||
val exitCode: String,
|
val exitCode: String,
|
||||||
val errors: List<String>,
|
val errors: List<String>,
|
||||||
val compiledFiles: Iterable<File>,
|
val compiledFiles: Iterable<File>,
|
||||||
val lookupsCount: Map<File, Int>
|
val lookups: Map<File, List<LookupInfo>>
|
||||||
)
|
)
|
||||||
private class IncrementalData(val sourceToOutput: MutableMap<File, MutableSet<File>> = hashMapOf())
|
private class IncrementalData(val sourceToOutput: MutableMap<File, MutableSet<File>> = hashMapOf())
|
||||||
|
|
||||||
private fun makeAndCheckLookups(
|
private fun make(
|
||||||
filesToCompile: Iterable<File>,
|
filesToCompile: Iterable<File>,
|
||||||
workingToOriginalFileMap: Map<File, File>,
|
|
||||||
incrementalData: IncrementalData
|
incrementalData: IncrementalData
|
||||||
): CompilerOutput {
|
): CompilerOutput {
|
||||||
removeCommentsCommentsWithLookupInfo(filesToCompile)
|
filesToCompile.forEach {
|
||||||
|
it.writeText(it.readText().replace(COMMENT_WITH_LOOKUP_INFO, ""))
|
||||||
|
}
|
||||||
|
|
||||||
for (dirtyFile in filesToCompile) {
|
for (dirtyFile in filesToCompile) {
|
||||||
incrementalData.sourceToOutput.remove(dirtyFile)?.forEach {
|
incrementalData.sourceToOutput.remove(dirtyFile)?.forEach {
|
||||||
@@ -259,74 +272,54 @@ abstract class AbstractLookupTrackerTest : TestWithWorkingDir() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val lookupsCount = checkLookups(filesToCompile, lookupTracker, workingToOriginalFileMap)
|
val lookups = lookupTracker.lookups.groupBy { File(it.filePath) }
|
||||||
return CompilerOutput(exitCode.toString(), messageCollector.errors, filesToCompile, lookupsCount)
|
val lookupsFromCompiledFiles = filesToCompile.associate { it to (lookups[it] ?: emptyList()) }
|
||||||
|
return CompilerOutput(exitCode.toString(), messageCollector.errors, filesToCompile, lookupsFromCompiledFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun Services.Builder.registerAdditionalServices() {}
|
protected open fun Services.Builder.registerAdditionalServices() {}
|
||||||
|
|
||||||
private fun checkLookups(
|
private fun checkLookupsInFile(step: String, expectedFile: File, lookupsFromFile: List<LookupInfo>) {
|
||||||
compiledFiles: Iterable<File>,
|
val text = expectedFile.readText().replace(COMMENT_WITH_LOOKUP_INFO, "")
|
||||||
lookupTracker: TestLookupTracker,
|
val lines = text.lines().toMutableList()
|
||||||
workingToOriginalFileMap: Map<File, File>
|
|
||||||
): Map<File, Int> {
|
|
||||||
fun checkLookupsInFile(expectedFile: File, actualFile: File, lookupsFromFile: List<LookupInfo>) {
|
|
||||||
val text = actualFile.readText()
|
|
||||||
|
|
||||||
val matchResult = COMMENT_WITH_LOOKUP_INFO.find(text)
|
for ((line, lookupsFromLine) in lookupsFromFile.groupBy { it.position.line }) {
|
||||||
if (matchResult != null) {
|
val columnToLookups = lookupsFromLine.groupBy { it.position.column }.toList().sortedBy { it.first }
|
||||||
throw AssertionError("File $actualFile contains multiline comment in range ${matchResult.range}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val lines = text.lines().toMutableList()
|
val lineContent = lines[line - 1]
|
||||||
|
val parts = ArrayList<CharSequence>(columnToLookups.size * 2)
|
||||||
|
|
||||||
for ((line, lookupsFromLine) in lookupsFromFile.groupBy { it.position.line }) {
|
var start = 0
|
||||||
val columnToLookups = lookupsFromLine.groupBy { it.position.column }.toList().sortedBy { it.first }
|
|
||||||
|
|
||||||
val lineContent = lines[line - 1]
|
for ((column, lookupsFromColumn) in columnToLookups) {
|
||||||
val parts = ArrayList<CharSequence>(columnToLookups.size * 2)
|
val end = column - 1
|
||||||
|
parts.add(lineContent.subSequence(start, end))
|
||||||
|
|
||||||
var start = 0
|
val lookups = lookupsFromColumn.distinct().joinToString(separator = " ", prefix = "/*", postfix = "*/") {
|
||||||
|
val rest = lineContent.substring(end)
|
||||||
|
|
||||||
for ((column, lookupsFromColumn) in columnToLookups) {
|
val name =
|
||||||
val end = column - 1
|
when {
|
||||||
parts.add(lineContent.subSequence(start, end))
|
rest.startsWith(it.name) || // same name
|
||||||
|
rest.startsWith("$" + it.name) || // backing field
|
||||||
|
DECLARATION_STARTS_WITH.any { rest.startsWith(it) } // it's declaration
|
||||||
|
-> ""
|
||||||
|
else -> "(" + it.name + ")"
|
||||||
|
}
|
||||||
|
|
||||||
val lookups = lookupsFromColumn.distinct().joinToString(separator = " ", prefix = "/*", postfix = "*/") {
|
it.scopeKind.toString()[0].toLowerCase().toString() + ":" + it.scopeFqName.let { if (it.isNotEmpty()) it else "<root>" } + name
|
||||||
val rest = lineContent.substring(end)
|
|
||||||
|
|
||||||
val name =
|
|
||||||
when {
|
|
||||||
rest.startsWith(it.name) || // same name
|
|
||||||
rest.startsWith("$" + it.name) || // backing field
|
|
||||||
DECLARATION_STARTS_WITH.any { rest.startsWith(it) } // it's declaration
|
|
||||||
-> ""
|
|
||||||
else -> "(" + it.name + ")"
|
|
||||||
}
|
|
||||||
|
|
||||||
it.scopeKind.toString()[0].toLowerCase().toString() + ":" + it.scopeFqName.let { if (it.isNotEmpty()) it else "<root>" } + name
|
|
||||||
}
|
|
||||||
|
|
||||||
parts.add(lookups)
|
|
||||||
|
|
||||||
start = end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lines[line - 1] = parts.joinToString("") + lineContent.subSequence(start, lineContent.length)
|
parts.add(lookups)
|
||||||
|
|
||||||
|
start = end
|
||||||
}
|
}
|
||||||
|
|
||||||
val actual = lines.joinToString("\n")
|
lines[line - 1] = parts.joinToString("") + lineContent.subSequence(start, lineContent.length)
|
||||||
|
|
||||||
KotlinTestUtils.assertEqualsToFile(expectedFile, actual)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileToLookups = lookupTracker.lookups.groupBy { File(it.filePath) }
|
val actual = lines.joinToString("\n")
|
||||||
return compiledFiles.associate { actualFile ->
|
KotlinTestUtils.assertEqualsToFile("Lookups do not match after $step", expectedFile, actual)
|
||||||
val expectedFile = workingToOriginalFileMap[actualFile]!!
|
|
||||||
val lookupsInFile = fileToLookups[actualFile]
|
|
||||||
lookupsInFile?.let { checkLookupsInFile(expectedFile, actualFile, it) }
|
|
||||||
actualFile to (lookupsInFile?.size ?: 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user