Add log parser tests
KT-8487
This commit is contained in:
+3
-33
@@ -2,13 +2,14 @@ package org.jetbrains.kotlin.gradle
|
||||
|
||||
import com.google.common.io.Files
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.jetbrains.kotlin.gradle.incremental.BuildStep
|
||||
import org.jetbrains.kotlin.gradle.incremental.parseTestBuildLog
|
||||
import org.junit.Assume
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
|
||||
abstract class BaseIncrementalGradleIT : BaseGradleIT() {
|
||||
|
||||
open inner class IncrementalTestProject(name: String, wrapperVersion: String = "2.4", minLogLevel: LogLevel = LogLevel.DEBUG) : Project(name, wrapperVersion, minLogLevel) {
|
||||
@@ -69,37 +70,6 @@ abstract class BaseIncrementalGradleIT : BaseGradleIT() {
|
||||
modificationStage = actualStage + 1
|
||||
}
|
||||
|
||||
class StageResults(val stage: Int, val compiledKotlinFiles: HashSet<String> = hashSetOf(), val compiledJavaFiles: HashSet<String> = hashSetOf(), var compileSucceeded: Boolean = true)
|
||||
|
||||
fun parseTestBuildLog(file: File): List<StageResults> {
|
||||
class StagedLines(val stage: Int, val line: String)
|
||||
|
||||
return file.readLines()
|
||||
.map { if (it.startsWith("========== Step")) "" else it }
|
||||
.fold(arrayListOf<StagedLines>()) { slines, line ->
|
||||
val (curStage, prevWasBlank) = slines.lastOrNull()?.let{ Pair(it.stage, it.line.isBlank()) } ?: Pair(0, false)
|
||||
slines.add(StagedLines(curStage + if (line.isBlank() && prevWasBlank) 1 else 0, line))
|
||||
slines
|
||||
}
|
||||
.fold(arrayListOf<StageResults>()) { res, sline ->
|
||||
// for lazy creation of the node
|
||||
fun curStageResults(): StageResults {
|
||||
if (res.isEmpty() || sline.stage > res.last().stage) {
|
||||
res.add(StageResults(sline.stage))
|
||||
}
|
||||
return res.last()
|
||||
}
|
||||
|
||||
when {
|
||||
sline.line.endsWith(".java", ignoreCase = true) -> curStageResults().compiledJavaFiles.add(sline.line)
|
||||
sline.line.endsWith(".kt", ignoreCase = true) -> curStageResults().compiledKotlinFiles.add(sline.line)
|
||||
sline.line.equals("COMPILATION FAILED", ignoreCase = true) -> curStageResults().compileSucceeded = false
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun IncrementalTestProject.performAndAssertBuildStages(options: BuildOptions = defaultBuildOptions(), weakTesting: Boolean = false) {
|
||||
|
||||
val checkKnown = testIsKnownJpsTestProject(resourcesRoot)
|
||||
@@ -133,7 +103,7 @@ abstract class BaseIncrementalGradleIT : BaseGradleIT() {
|
||||
}
|
||||
}
|
||||
|
||||
fun IncrementalTestProject.buildAndAssertStageResults(expected: StageResults, options: BuildOptions = defaultBuildOptions(), weakTesting: Boolean = false) {
|
||||
fun IncrementalTestProject.buildAndAssertStageResults(expected: BuildStep, options: BuildOptions = defaultBuildOptions(), weakTesting: Boolean = false) {
|
||||
build("build", options = options) {
|
||||
if (expected.compileSucceeded) {
|
||||
assertSuccessful()
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.jetbrains.kotlin.gradle.incremental.parseTestBuildLog
|
||||
import org.jetbrains.kotlin.utils.Printer
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
import java.io.File
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
class BuildLogParserParametrizedIT : BaseGradleIT() {
|
||||
|
||||
@Parameterized.Parameter
|
||||
@JvmField
|
||||
var testDirName: String = ""
|
||||
|
||||
@Test
|
||||
fun testParser() {
|
||||
val testDir = File(TEST_ROOT, testDirName)
|
||||
val logFile = File(testDir, LOG_FILE_NAME)
|
||||
assert(logFile.isFile) { "Log file: $logFile does not exist" }
|
||||
|
||||
val parsedStages = parseTestBuildLog(logFile)
|
||||
val sb = StringBuilder()
|
||||
val p = Printer(sb)
|
||||
|
||||
for ((i, stage) in parsedStages.withIndex()) {
|
||||
if (i > 0) {
|
||||
p.println()
|
||||
}
|
||||
|
||||
p.println("Step #${i + 1}")
|
||||
|
||||
p.println("Compiled java files:")
|
||||
p.printlnSorted(stage.compiledJavaFiles)
|
||||
|
||||
p.println("Compiled kotlin files:")
|
||||
p.printlnSorted(stage.compiledKotlinFiles)
|
||||
|
||||
p.println("Compile errors:")
|
||||
p.printlnSorted(stage.compileErrors)
|
||||
}
|
||||
|
||||
val actual = sb.toString()
|
||||
val expectedFile = File(testDir, EXPECTED_PARSED_LOG_FILE_NAME)
|
||||
|
||||
if (!expectedFile.isFile) {
|
||||
expectedFile.createNewFile()
|
||||
expectedFile.writeText(actual)
|
||||
|
||||
throw AssertionError("Expected file log did not exist, created: $expectedFile")
|
||||
}
|
||||
|
||||
val expectedContent = expectedFile.readText()
|
||||
Assert.assertEquals("Parsed content was unexpected: ", actual, expectedContent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TEST_ROOT = File(resourcesRootFile, "buildLogsParserData")
|
||||
private val LOG_FILE_NAME = "build.log"
|
||||
private val EXPECTED_PARSED_LOG_FILE_NAME = "expected.txt"
|
||||
|
||||
@Suppress("unused")
|
||||
@Parameterized.Parameters(name = "{index}: {0}")
|
||||
@JvmStatic
|
||||
fun data(): List<Array<String>> {
|
||||
val directories = TEST_ROOT.listFiles().filter { it.isDirectory }
|
||||
return directories.map { arrayOf(it.name) }.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Comparable<T>> Printer.printlnSorted(elements: Iterable<T>) {
|
||||
pushIndent()
|
||||
elements.sorted().forEach { this.println(it) }
|
||||
popIndent()
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package org.jetbrains.kotlin.gradle.incremental
|
||||
|
||||
import java.io.File
|
||||
|
||||
class BuildStep(
|
||||
val compiledKotlinFiles: MutableSet<String> = hashSetOf(),
|
||||
val compiledJavaFiles: MutableSet<String> = hashSetOf(),
|
||||
val compileErrors: MutableList<String> = arrayListOf()
|
||||
) {
|
||||
val compileSucceeded: Boolean
|
||||
get() = compileErrors.isEmpty()
|
||||
}
|
||||
|
||||
fun parseTestBuildLog(file: File): List<BuildStep> {
|
||||
fun splitSteps(lines: List<String>): List<List<String>> {
|
||||
val stepsLines = mutableListOf<MutableList<String>>()
|
||||
|
||||
for (line in lines) {
|
||||
when {
|
||||
line.matches("=+ Step #\\d+ =+".toRegex()) -> {
|
||||
stepsLines.add(mutableListOf())
|
||||
}
|
||||
else -> {
|
||||
stepsLines.lastOrNull()?.add(line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stepsLines
|
||||
}
|
||||
|
||||
fun BuildStep.parseStepCompiledFiles(stepLines: List<String>) {
|
||||
var readFiles = false
|
||||
|
||||
for (line in stepLines) {
|
||||
if (line.startsWith("Compiling files:")) {
|
||||
readFiles = true
|
||||
continue
|
||||
}
|
||||
|
||||
if (readFiles && line.startsWith("End of files")) {
|
||||
readFiles = false
|
||||
continue
|
||||
}
|
||||
|
||||
if (readFiles) {
|
||||
val path = line.trim()
|
||||
|
||||
if (path.endsWith(".kt")) {
|
||||
compiledKotlinFiles.add(path)
|
||||
}
|
||||
else if (path.endsWith(".java")) {
|
||||
compiledJavaFiles.add(path)
|
||||
}
|
||||
else {
|
||||
throw IllegalStateException("Expected .kt or .java file, got: $path")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BuildStep.parseErrors(stepLines: List<String>) {
|
||||
val startIndex = stepLines.indexOfLast { it.startsWith("COMPILATION FAILED") }
|
||||
|
||||
if (startIndex > 0) {
|
||||
compileErrors.addAll(stepLines.subList(startIndex + 1, stepLines.size))
|
||||
}
|
||||
}
|
||||
|
||||
val stepsLines = splitSteps(file.readLines())
|
||||
|
||||
|
||||
return stepsLines.map { stepLines ->
|
||||
val buildStep = BuildStep()
|
||||
buildStep.parseStepCompiledFiles(stepLines)
|
||||
buildStep.parseErrors(stepLines)
|
||||
buildStep
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
================ Step #1 =================
|
||||
|
||||
Cleaning output files:
|
||||
out/production/module/META-INF/module.kotlin_module
|
||||
out/production/module/inline/InlineGetKt.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/inlineGet.kt
|
||||
End of files
|
||||
Marked as dirty by Kotlin:
|
||||
src/UsageVal.kt
|
||||
src/UsageVar.kt
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Cleaning output files:
|
||||
out/production/module/usage/UsageVal.class
|
||||
out/production/module/usage/UsageVar.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/UsageVal.kt
|
||||
src/UsageVar.kt
|
||||
End of files
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Exit code: NOTHING_DONE
|
||||
------------------------------------------
|
||||
|
||||
================ Step #2 =================
|
||||
|
||||
Cleaning output files:
|
||||
out/production/module/META-INF/module.kotlin_module
|
||||
out/production/module/inline/InlineSetKt.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/inlineSet.kt
|
||||
End of files
|
||||
Marked as dirty by Kotlin:
|
||||
src/UsageVar.kt
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Cleaning output files:
|
||||
out/production/module/usage/UsageVar.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/UsageVar.kt
|
||||
End of files
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Exit code: NOTHING_DONE
|
||||
------------------------------------------
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
Step #1
|
||||
Compiled java files:
|
||||
Compiled kotlin files:
|
||||
src/UsageVal.kt
|
||||
src/UsageVar.kt
|
||||
src/inlineGet.kt
|
||||
Compile errors:
|
||||
|
||||
Step #2
|
||||
Compiled java files:
|
||||
Compiled kotlin files:
|
||||
src/UsageVar.kt
|
||||
src/inlineSet.kt
|
||||
Compile errors:
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
================ Step #1 =================
|
||||
|
||||
Cleaning output files:
|
||||
out/production/module1/META-INF/module1.kotlin_module
|
||||
out/production/module1/a/A.class
|
||||
out/production/module1/a/ClassAnnotation.class
|
||||
out/production/module1/a/FileAnnotation.class
|
||||
out/production/module1/a/Module1_aKt.class
|
||||
End of files
|
||||
Compiling files:
|
||||
module1/src/module1_a.kt
|
||||
End of files
|
||||
Marked as dirty by Kotlin:
|
||||
module2/src/module2_b.kt
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Exit code: NOTHING_DONE
|
||||
------------------------------------------
|
||||
Cleaning output files:
|
||||
out/production/module2/META-INF/module2.kotlin_module
|
||||
out/production/module2/b/B.class
|
||||
out/production/module2/b/Module2_bKt.class
|
||||
End of files
|
||||
Compiling files:
|
||||
module2/src/module2_b.kt
|
||||
End of files
|
||||
Exit code: ABORT
|
||||
------------------------------------------
|
||||
COMPILATION FAILED
|
||||
Cannot access 'FileAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'A': it is 'internal' in 'a'
|
||||
Cannot access 'FileAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'ClassAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'ClassAnnotation': it is 'internal' in 'a'
|
||||
Function effective visibility 'public' should be the same or less permissive than its parameter type effective visibility 'internal'
|
||||
Cannot access 'A': it is 'internal' in 'a'
|
||||
Cannot access 'a': it is 'internal' in 'a'
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
Step #1
|
||||
Compiled java files:
|
||||
Compiled kotlin files:
|
||||
module1/src/module1_a.kt
|
||||
module2/src/module2_b.kt
|
||||
Compile errors:
|
||||
Cannot access 'A': it is 'internal' in 'a'
|
||||
Cannot access 'A': it is 'internal' in 'a'
|
||||
Cannot access 'ClassAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'ClassAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'FileAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'FileAnnotation': it is 'internal' in 'a'
|
||||
Cannot access 'a': it is 'internal' in 'a'
|
||||
Function effective visibility 'public' should be the same or less permissive than its parameter type effective visibility 'internal'
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
================ Step #1 =================
|
||||
|
||||
Cleaning output files:
|
||||
out/production/module/JavaClass.class
|
||||
out/production/module/META-INF/module.kotlin_module
|
||||
out/production/module/UsageKt.class
|
||||
End of files
|
||||
Compiling files:
|
||||
src/usage.kt
|
||||
End of files
|
||||
Exit code: ADDITIONAL_PASS_REQUIRED
|
||||
------------------------------------------
|
||||
Compiling files:
|
||||
src/JavaClass.java
|
||||
End of files
|
||||
Exit code: NOTHING_DONE
|
||||
------------------------------------------
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
Step #1
|
||||
Compiled java files:
|
||||
src/JavaClass.java
|
||||
Compiled kotlin files:
|
||||
src/usage.kt
|
||||
Compile errors:
|
||||
Reference in New Issue
Block a user