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 junit.framework.TestCase
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.util.dump
|
import org.jetbrains.kotlin.ir.util.dump
|
||||||
|
import org.jetbrains.kotlin.ir.util.dumpTreesFromLineNumber
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.utils.rethrow
|
import org.jetbrains.kotlin.utils.rethrow
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -52,6 +53,11 @@ abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
|
|||||||
actual.append(actualCount).append(" ").append(expectation.needle).append("\n")
|
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 {
|
try {
|
||||||
TestCase.assertEquals(irFileDump, expected.toString(), actual.toString())
|
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)
|
constructor(countStr: String, needle: String) : this(Integer.valueOf(countStr), needle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class IrTreeFileLabel(val expectedTextFile: File, val lineNumber: Int)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val EXPECTED_OCCURRENCES_PATTERN = Regex("""^\s*//\s*(\d+)\s*(.*)$""")
|
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
|
var expectedTextFileName: String? = null
|
||||||
val regexps = ArrayList<RegexpInText>()
|
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 ->
|
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 ->
|
?: IR_FILE_TXT_PATTERN.matchEntire(line)?.let { matchResult ->
|
||||||
expectedTextFileName = matchResult.groupValues[1]
|
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 expectedTextFile: File? = expectedTextFileName?.let { createExpectedTextFile(testFile, dir, it) }
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
interface IrFile : IrCompoundDeclaration {
|
||||||
val name: String
|
val name: String
|
||||||
|
val fileEntry: SourceLocationManager.FileEntry
|
||||||
val module: IrModule
|
val module: IrModule
|
||||||
override val descriptor: PackageFragmentDescriptor
|
override val descriptor: PackageFragmentDescriptor
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ interface IrFile : IrCompoundDeclaration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IrFileImpl(
|
class IrFileImpl(
|
||||||
val fileEntry: SourceLocationManager.FileEntry,
|
override val fileEntry: SourceLocationManager.FileEntry,
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val descriptor: PackageFragmentDescriptor
|
override val descriptor: PackageFragmentDescriptor
|
||||||
) : IrCompoundDeclarationBase(0, fileEntry.maxOffset, IrDeclarationOriginKind.DEFINED), IrFile {
|
) : IrCompoundDeclarationBase(0, fileEntry.maxOffset, IrDeclarationOriginKind.DEFINED), IrFile {
|
||||||
|
|||||||
@@ -17,17 +17,25 @@
|
|||||||
package org.jetbrains.kotlin.ir.util
|
package org.jetbrains.kotlin.ir.util
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
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.IrDeclaration
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCallExpression
|
import org.jetbrains.kotlin.ir.expressions.IrCallExpression
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
import org.jetbrains.kotlin.utils.Printer
|
import org.jetbrains.kotlin.utils.Printer
|
||||||
|
|
||||||
fun IrDeclaration.dump(): String {
|
fun IrElement.dump(): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
accept(DumpIrTreeVisitor(sb), "")
|
accept(DumpIrTreeVisitor(sb), "")
|
||||||
return sb.toString()
|
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> {
|
class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
|
||||||
val printer = Printer(out, " ")
|
val printer = Printer(out, " ")
|
||||||
val elementRenderer = RenderIrElementVisitor()
|
val elementRenderer = RenderIrElementVisitor()
|
||||||
@@ -67,3 +75,20 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
|
|||||||
private fun String.withLabel(label: String) =
|
private fun String.withLabel(label: String) =
|
||||||
if (label.isEmpty()) this else "$label: $this"
|
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
|
IrFunction public fun kotlin.Int.ext1(): kotlin.Int
|
||||||
IrExpressionBody
|
IrExpressionBody
|
||||||
RETURN type=kotlin.Int
|
RETURN type=kotlin.Int
|
||||||
DUMMY KtThisExpression
|
$RECEIVER of: <root>
|
||||||
IrFunction public fun kotlin.Int.ext2(/*0*/ x: kotlin.Int): kotlin.Int
|
IrFunction public fun kotlin.Int.ext2(/*0*/ x: kotlin.Int): kotlin.Int
|
||||||
IrExpressionBody
|
IrExpressionBody
|
||||||
RETURN type=kotlin.Int
|
RETURN type=kotlin.Int
|
||||||
CALL foo
|
CALL foo
|
||||||
x: DUMMY KtThisExpression
|
x: $RECEIVER of: <root>
|
||||||
y: VAR x
|
y: VAR x
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fun testFun(): String { return "OK" }
|
fun testFun(): String { return "OK" }
|
||||||
val testSimpleVal = 1
|
val testSimpleVal = 1 // <<< smoke.testSimpleVal.txt
|
||||||
val testValWithGetter: Int get() = 42
|
val testValWithGetter: Int get() = 42
|
||||||
var testSimpleVar = 2
|
var testSimpleVar = 2
|
||||||
var testVarWithAccessors: Int
|
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