Testing framework: allow comparing subtrees starting at a given line with expected dump text.
This commit is contained in:
committed by
Dmitry Petrov
parent
c93666f6f1
commit
b80782c295
@@ -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
@@ -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
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
@@ -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'
|
||||
Reference in New Issue
Block a user