Testing framework: allow comparing subtrees starting at a given line with expected dump text.

This commit is contained in:
Dmitry Petrov
2016-08-11 11:40:07 +03:00
committed by Dmitry Petrov
parent c93666f6f1
commit b80782c295
6 changed files with 72 additions and 24 deletions
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.text.StringUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.dumpTreesFromLineNumber
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.utils.rethrow
import java.io.File
@@ -52,6 +53,11 @@ abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
actual.append(actualCount).append(" ").append(expectation.needle).append("\n")
}
for (irTreeFileLabel in expectations.irTreeFileLabels) {
val actualTrees = irFile.dumpTreesFromLineNumber(irTreeFileLabel.lineNumber)
KotlinTestUtils.assertEqualsToFile(irTreeFileLabel.expectedTextFile, actualTrees)
}
try {
TestCase.assertEquals(irFileDump, expected.toString(), actual.toString())
}
@@ -61,40 +67,52 @@ abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
}
}
protected class Expectations(val irExpectedTextFile: File?, val regexps: List<RegexpInText>)
internal class Expectations(val irExpectedTextFile: File?, val regexps: List<RegexpInText>, val irTreeFileLabels: List<IrTreeFileLabel>)
protected class RegexpInText(val numberOfOccurrences: Int, val needle: String) {
internal class RegexpInText(val numberOfOccurrences: Int, val needle: String) {
constructor(countStr: String, needle: String) : this(Integer.valueOf(countStr), needle)
}
internal class IrTreeFileLabel(val expectedTextFile: File, val lineNumber: Int)
companion object {
private val EXPECTED_OCCURRENCES_PATTERN = Regex("""^\s*//\s*(\d+)\s*(.*)$""")
private val EXPECT_TXT_PATTERN = Regex("""^\s*//\s*IR_FILE_TXT\s+(.*)$""")
private val IR_FILE_TXT_PATTERN = Regex("""^\s*//\s*IR_FILE_TXT\s+(.*)$""")
private val IR_TREES_TXT_PATTERN = Regex("""// \s*<<<\s+(.*)$""")
private fun parseExpectations(dir: File, testFile: TestFile): Expectations {
internal fun parseExpectations(dir: File, testFile: TestFile): Expectations {
var expectedTextFileName: String? = null
val regexps = ArrayList<RegexpInText>()
for (line in testFile.content.split("\n")) {
val treeFiles = ArrayList<IrTreeFileLabel>()
for ((lineNumber, line) in testFile.content.split("\n").withIndex()) {
EXPECTED_OCCURRENCES_PATTERN.matchEntire(line)?.let { matchResult ->
regexps.add(RegexpInText(matchResult.groupValues[1], matchResult.groupValues[2]))
regexps.add(RegexpInText(matchResult.groupValues[1], matchResult.groupValues[2].trim()))
}
EXPECT_TXT_PATTERN.matchEntire(line)?.let { matchResult ->
expectedTextFileName = matchResult.groupValues[1]
?: IR_FILE_TXT_PATTERN.matchEntire(line)?.let { matchResult ->
expectedTextFileName = matchResult.groupValues[1].trim()
}
?: IR_TREES_TXT_PATTERN.find(line)?.let { matchResult ->
val fileName = matchResult.groupValues[1].trim()
val file = createExpectedTextFile(testFile, dir, fileName)
treeFiles.add(IrTreeFileLabel(file, lineNumber))
}
}
val expectedTextFile: File? = expectedTextFileName?.let {
val textFile = File(dir, expectedTextFileName)
if (!textFile.exists()) {
TestCase.assertTrue("Can't create an IR expected text containingFile: ${textFile.absolutePath}", textFile.createNewFile())
PrintWriter(FileWriter(textFile)).use {
it.println("$expectedTextFileName: new IR expected text containingFile for ${testFile.name}")
}
}
textFile
}
val expectedTextFile: File? = expectedTextFileName?.let { createExpectedTextFile(testFile, dir, it) }
return Expectations(expectedTextFile, regexps)
return Expectations(expectedTextFile, regexps, treeFiles)
}
internal fun createExpectedTextFile(testFile: TestFile, dir: File, fileName: String): File {
val textFile = File(dir, fileName)
if (!textFile.exists()) {
TestCase.assertTrue("Can't create an IR expected text containingFile: ${textFile.absolutePath}", textFile.createNewFile())
PrintWriter(FileWriter(textFile)).use {
it.println("$fileName: new IR expected text containingFile for ${testFile.name}")
}
}
return textFile
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
interface IrFile : IrCompoundDeclaration {
val name: String
val fileEntry: SourceLocationManager.FileEntry
val module: IrModule
override val descriptor: PackageFragmentDescriptor
@@ -32,7 +33,7 @@ interface IrFile : IrCompoundDeclaration {
}
class IrFileImpl(
val fileEntry: SourceLocationManager.FileEntry,
override val fileEntry: SourceLocationManager.FileEntry,
override val name: String,
override val descriptor: PackageFragmentDescriptor
) : IrCompoundDeclarationBase(0, fileEntry.maxOffset, IrDeclarationOriginKind.DEFINED), IrFile {
@@ -17,17 +17,25 @@
package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.SourceLocationManager
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCallExpression
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.utils.Printer
fun IrDeclaration.dump(): String {
fun IrElement.dump(): String {
val sb = StringBuilder()
accept(DumpIrTreeVisitor(sb), "")
return sb.toString()
}
fun IrFile.dumpTreesFromLineNumber(lineNumber: Int): String {
val sb = StringBuilder()
accept(DumpTreeFromSourceLineVisitor(fileEntry, lineNumber, sb), null)
return sb.toString()
}
class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
val printer = Printer(out, " ")
val elementRenderer = RenderIrElementVisitor()
@@ -67,3 +75,20 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
private fun String.withLabel(label: String) =
if (label.isEmpty()) this else "$label: $this"
}
class DumpTreeFromSourceLineVisitor(
val fileEntry: SourceLocationManager.FileEntry,
val lineNumber: Int,
out: Appendable
): IrElementVisitor<Unit, Nothing?> {
val dumper = DumpIrTreeVisitor(out)
override fun visitElement(element: IrElement, data: Nothing?) {
if (fileEntry.getLineNumber(element.startOffset) == lineNumber) {
element.accept(dumper, "")
return
}
element.acceptChildren(this, data)
}
}
+2 -2
View File
@@ -20,10 +20,10 @@ IrFile /calls.kt
IrFunction public fun kotlin.Int.ext1(): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
DUMMY KtThisExpression
$RECEIVER of: <root>
IrFunction public fun kotlin.Int.ext2(/*0*/ x: kotlin.Int): kotlin.Int
IrExpressionBody
RETURN type=kotlin.Int
CALL foo
x: DUMMY KtThisExpression
x: $RECEIVER of: <root>
y: VAR x
+1 -1
View File
@@ -1,5 +1,5 @@
fun testFun(): String { return "OK" }
val testSimpleVal = 1
val testSimpleVal = 1 // <<< smoke.testSimpleVal.txt
val testValWithGetter: Int get() = 42
var testSimpleVar = 2
var testVarWithAccessors: Int
+4
View File
@@ -0,0 +1,4 @@
IrProperty public val testSimpleVal: kotlin.Int = 1 getter=null setter=null
IrExpressionBody
RETURN type=kotlin.Int
LITERAL Int type=kotlin.Int value='1'