Add log parser tests

KT-8487
This commit is contained in:
Alexey Tsvetkov
2016-02-09 14:18:13 +03:00
parent 4f2f0f0a60
commit 7856bd5d0a
9 changed files with 299 additions and 33 deletions
@@ -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()
@@ -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()
}
@@ -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
}
}
@@ -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
------------------------------------------
@@ -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:
@@ -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'
@@ -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'
@@ -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
------------------------------------------
@@ -0,0 +1,6 @@
Step #1
Compiled java files:
src/JavaClass.java
Compiled kotlin files:
src/usage.kt
Compile errors: