[JVM] Port Stepping and LocalVariable tests to new test infra.
This is in preparation for enabling the tests for FIR which will be easier to do when the tests are on the new infrastructure.
This commit is contained in:
committed by
TeamCityServer
parent
f1c1094393
commit
e9c9d5731e
-305
@@ -1,305 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.debugInformation
|
||||
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.sun.jdi.AbsentInformationException
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.VirtualMachine
|
||||
import com.sun.jdi.event.*
|
||||
import com.sun.jdi.request.EventRequest
|
||||
import com.sun.jdi.request.EventRequest.SUSPEND_ALL
|
||||
import com.sun.jdi.request.StepRequest
|
||||
import com.sun.tools.jdi.SocketAttachingConnector
|
||||
import org.jetbrains.kotlin.backend.common.output.SimpleOutputFileCollection
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAllTo
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.clientserver.TestProcessServer
|
||||
import org.jetbrains.kotlin.test.clientserver.TestProxy
|
||||
import org.jetbrains.kotlin.test.clientserver.getGeneratedClass
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
data class ProcessAndPort(val process: Process, val port: Int)
|
||||
|
||||
abstract class AbstractDebugTest : CodegenTestCase() {
|
||||
|
||||
abstract val virtualMachine: VirtualMachine
|
||||
abstract val proxyPort: Int
|
||||
|
||||
companion object {
|
||||
const val DEBUG_ADDRESS = "127.0.0.1"
|
||||
const val MAIN_CLASS = "org.jetbrains.kotlin.test.clientserver.TestProcessServer"
|
||||
const val TEST_CLASS = "TestKt"
|
||||
const val BOX_METHOD = "box"
|
||||
|
||||
fun setUpVM(virtualMachine: VirtualMachine) {
|
||||
val manager = virtualMachine.eventRequestManager()
|
||||
|
||||
val methodEntryReq = manager.createMethodEntryRequest()
|
||||
methodEntryReq.addClassFilter(TEST_CLASS)
|
||||
methodEntryReq.setSuspendPolicy(EventRequest.SUSPEND_ALL)
|
||||
methodEntryReq.enable()
|
||||
|
||||
val methodExitReq = manager.createMethodExitRequest()
|
||||
methodExitReq.addClassFilter(TEST_CLASS)
|
||||
methodExitReq.setSuspendPolicy(EventRequest.SUSPEND_ALL)
|
||||
methodExitReq.enable()
|
||||
}
|
||||
|
||||
fun getProxyPort(process: Process): Int {
|
||||
val reader = process.inputStream.bufferedReader()
|
||||
reader.readLine()
|
||||
val proxyPort = reader.readLine()
|
||||
.split("port ")
|
||||
.last()
|
||||
.trim()
|
||||
.toInt()
|
||||
reader.close()
|
||||
return proxyPort
|
||||
}
|
||||
|
||||
fun startDebuggeeProcess(): ProcessAndPort {
|
||||
val classpath = listOf(
|
||||
PathUtil.getJarPathForClass(TestProcessServer::class.java),
|
||||
PathUtil.getJarPathForClass(Delegates::class.java) // Add Kotlin runtime JAR
|
||||
)
|
||||
|
||||
val javaExecutablePath = findJavaExecutable().absolutePath
|
||||
val command = arrayOf(
|
||||
if (SystemInfo.isWindows) "\"$javaExecutablePath\"" else javaExecutablePath,
|
||||
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=127.0.0.1:0",
|
||||
"-ea",
|
||||
"-classpath", classpath.joinToString(File.pathSeparator),
|
||||
MAIN_CLASS,
|
||||
TestProcessServer.DEBUG_TEST
|
||||
)
|
||||
|
||||
val process = ProcessBuilder(*command).start()
|
||||
val port = process.inputStream.bufferedReader().readLine()
|
||||
.split("address:")
|
||||
.last()
|
||||
.trim()
|
||||
.toInt()
|
||||
return ProcessAndPort(process, port)
|
||||
}
|
||||
|
||||
fun attachDebugger(port: Int): VirtualMachine {
|
||||
val connector = SocketAttachingConnector()
|
||||
return connector.attach(connector.defaultArguments().toMutableMap().apply {
|
||||
getValue("port").setValue("$port")
|
||||
getValue("hostname").setValue(DEBUG_ADDRESS)
|
||||
})
|
||||
}
|
||||
|
||||
fun findJavaExecutable(): File {
|
||||
val javaBin = File(SystemProperties.getJavaHome(), "bin")
|
||||
return File(javaBin, "java.exe").takeIf { it.exists() }
|
||||
?: File(javaBin, "java").also { assert(it.exists()) }
|
||||
}
|
||||
|
||||
val Location.isInDebugTestInfrastructure: Boolean
|
||||
get() {
|
||||
return this.sourcePath().startsWith("helpers" + File.separatorChar)
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public override fun setUp() {
|
||||
super.setUp()
|
||||
}
|
||||
|
||||
@After
|
||||
public override fun tearDown() {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
internal fun invokeBoxInSeparateProcess(classLoader: URLClassLoader, aClass: Class<*>, port: Int): String {
|
||||
|
||||
val classPath = classLoader.extractUrls().toMutableList()
|
||||
if (classLoader is GeneratedClassLoader) {
|
||||
val outDir = KotlinTestUtils.tmpDirForTest(this)
|
||||
val currentOutput = SimpleOutputFileCollection(classLoader.allGeneratedFiles)
|
||||
currentOutput.writeAllTo(outDir)
|
||||
classPath.add(0, outDir.toURI().toURL())
|
||||
}
|
||||
|
||||
return TestProxy(Integer.valueOf(port), aClass.canonicalName, classPath).runTestNoOutput()
|
||||
}
|
||||
|
||||
internal fun createGeneratedClassLoader(classesDir: File): URLClassLoader {
|
||||
return URLClassLoader(
|
||||
listOf(classesDir.toURI().toURL()).toTypedArray(),
|
||||
ForTestCompileRuntime.runtimeJarClassLoader()
|
||||
)
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(
|
||||
ConfigurationKind.ALL, *listOfNotNull(
|
||||
writeJavaFiles(
|
||||
files
|
||||
)
|
||||
).toTypedArray()
|
||||
)
|
||||
|
||||
loadMultiFiles(files)
|
||||
val classBuilderFactory =
|
||||
OriginCollectingClassBuilderFactory(ClassBuilderMode.FULL)
|
||||
val generationState =
|
||||
GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
|
||||
classFileFactory = generationState.factory
|
||||
|
||||
val tempDirForTest = KtTestUtil.tmpDir("debuggerTest")
|
||||
val classesDir = File(tempDirForTest, "classes")
|
||||
try {
|
||||
classFileFactory.writeAllTo(classesDir)
|
||||
doTest(wholeFile, classesDir)
|
||||
} finally {
|
||||
tempDirForTest.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun doTest(wholeFile: File, classesDir: File) {
|
||||
|
||||
val classLoader = createGeneratedClassLoader(classesDir)
|
||||
val aClass = getGeneratedClass(classLoader, TEST_CLASS)
|
||||
assert(aClass.declaredMethods.any { it.name == BOX_METHOD }) {
|
||||
"Test method $BOX_METHOD not present on test class $TEST_CLASS"
|
||||
}
|
||||
if (virtualMachine.allThreads().any { it.isSuspended }) {
|
||||
virtualMachine.resume()
|
||||
}
|
||||
invokeBoxInSeparateProcess(classLoader, aClass, proxyPort)
|
||||
|
||||
val manager = virtualMachine.eventRequestManager()
|
||||
|
||||
val loggedItems = ArrayList<Any>()
|
||||
var inBoxMethod = false
|
||||
vmLoop@
|
||||
while (true) {
|
||||
val eventSet = virtualMachine.eventQueue().remove(1000) ?: continue
|
||||
for (event in eventSet) {
|
||||
when (event) {
|
||||
is VMDeathEvent, is VMDisconnectEvent -> {
|
||||
break@vmLoop
|
||||
}
|
||||
// We start VM with option 'suspend=n', in case VMStartEvent is still received, discard.
|
||||
is VMStartEvent -> {
|
||||
|
||||
}
|
||||
is MethodEntryEvent -> {
|
||||
if (!inBoxMethod && event.location().method().name() == BOX_METHOD) {
|
||||
if (manager.stepRequests().isEmpty()) {
|
||||
// Create line stepping request to get all normal line steps starting now.
|
||||
val stepReq = manager.createStepRequest(event.thread(), StepRequest.STEP_LINE, StepRequest.STEP_INTO)
|
||||
stepReq.setSuspendPolicy(SUSPEND_ALL)
|
||||
stepReq.addClassExclusionFilter("java.*")
|
||||
stepReq.addClassExclusionFilter("sun.*")
|
||||
stepReq.addClassExclusionFilter("kotlin.*")
|
||||
// Create class prepare request to be able to set breakpoints on class initializer lines.
|
||||
// There are no line stepping events for class initializers, so we depend on breakpoints.
|
||||
val prepareReq = manager.createClassPrepareRequest()
|
||||
prepareReq.setSuspendPolicy(SUSPEND_ALL)
|
||||
prepareReq.addClassExclusionFilter("java.*")
|
||||
prepareReq.addClassExclusionFilter("sun.*")
|
||||
prepareReq.addClassExclusionFilter("kotlin.*")
|
||||
}
|
||||
manager.stepRequests().map { it.enable() }
|
||||
manager.classPrepareRequests().map { it.enable() }
|
||||
inBoxMethod = true
|
||||
storeStep(loggedItems, event)
|
||||
}
|
||||
}
|
||||
is StepEvent -> {
|
||||
// Handle the case where an Exception causing program to exit without MethodExitEvent.
|
||||
if (inBoxMethod && event.location().method().name() == "run") {
|
||||
manager.stepRequests().map { it.disable() }
|
||||
manager.classPrepareRequests().map { it.disable() }
|
||||
manager.breakpointRequests().map { it.disable() }
|
||||
break@vmLoop
|
||||
}
|
||||
if (inBoxMethod) {
|
||||
storeStep(loggedItems, event)
|
||||
}
|
||||
}
|
||||
is MethodExitEvent -> {
|
||||
if (event.location().method().name() == BOX_METHOD) {
|
||||
manager.stepRequests().map { it.disable() }
|
||||
manager.classPrepareRequests().map { it.disable() }
|
||||
manager.breakpointRequests().map { it.disable() }
|
||||
break@vmLoop
|
||||
}
|
||||
}
|
||||
is ClassPrepareEvent -> {
|
||||
if (inBoxMethod) {
|
||||
val initializer = event.referenceType().methods().find { it.isStaticInitializer }
|
||||
try {
|
||||
initializer?.allLineLocations()?.forEach {
|
||||
manager.createBreakpointRequest(it).enable()
|
||||
}
|
||||
} catch (e: AbsentInformationException) {
|
||||
// If there is no line information, do not set breakpoints.
|
||||
}
|
||||
}
|
||||
}
|
||||
is BreakpointEvent -> {
|
||||
if (inBoxMethod) {
|
||||
storeStep(loggedItems, event)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
throw IllegalStateException("event not handled: $event")
|
||||
}
|
||||
}
|
||||
}
|
||||
eventSet.resume()
|
||||
}
|
||||
virtualMachine.resume()
|
||||
checkResult(wholeFile, loggedItems)
|
||||
}
|
||||
|
||||
fun Location.formatAsExpectation(): String {
|
||||
val synthetic = if (method().isSynthetic) " (synthetic)" else ""
|
||||
return "${sourceName()}:${lineNumber()} ${method().name()}$synthetic"
|
||||
}
|
||||
|
||||
/*
|
||||
Compresses runs of the same, linenumber-less location in the log:
|
||||
specifically removes locations without linenumber, that would otherwise
|
||||
print as byte offsets. This avoids overspecifying code generation
|
||||
strategy in debug tests.
|
||||
*/
|
||||
fun <T> compressRunsWithoutLinenumber(loggedItems: List<T>, getLocation: (T) -> Location): List<T> {
|
||||
if (loggedItems.isEmpty()) return listOf()
|
||||
|
||||
val logIterator = loggedItems.iterator()
|
||||
var currentItem = logIterator.next()
|
||||
val result = mutableListOf(currentItem)
|
||||
|
||||
for (logItem in logIterator) {
|
||||
if (getLocation(currentItem).lineNumber() != -1 || getLocation(currentItem).formatAsExpectation() != getLocation(logItem).formatAsExpectation()) {
|
||||
result.add(logItem)
|
||||
currentItem = logItem
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
abstract fun storeStep(loggedItems: ArrayList<Any>, event: Event)
|
||||
|
||||
abstract fun checkResult(wholeFile: File, loggedItems: List<Any>)
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.debugInformation
|
||||
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
|
||||
abstract class AbstractIrLocalVariableTest : AbstractLocalVariableTest() {
|
||||
override val backend: TargetBackend
|
||||
get() = TargetBackend.JVM_IR
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.debugInformation
|
||||
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
|
||||
abstract class AbstractIrSteppingTest : AbstractSteppingTest() {
|
||||
override val backend: TargetBackend
|
||||
get() = TargetBackend.JVM_IR
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
package org.jetbrains.kotlin.codegen.debugInformation
|
||||
|
||||
import com.sun.jdi.*
|
||||
import com.sun.jdi.event.Event
|
||||
import com.sun.jdi.event.LocatableEvent
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.junit.AfterClass
|
||||
import org.junit.BeforeClass
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractLocalVariableTest : AbstractDebugTest() {
|
||||
|
||||
override val virtualMachine: VirtualMachine = Companion.virtualMachine
|
||||
override val proxyPort: Int = Companion.proxyPort
|
||||
|
||||
interface LocalValue
|
||||
|
||||
class LocalPrimitive(val value: String, val valueType: String) : LocalValue {
|
||||
override fun toString(): String {
|
||||
return "$value:$valueType"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalReference(val id: String, val referenceType: String) : LocalValue {
|
||||
override fun toString(): String {
|
||||
return "$referenceType"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalNullValue : LocalValue {
|
||||
override fun toString(): String {
|
||||
return "null"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalVariableRecord(
|
||||
val variable: String,
|
||||
val variableType: String,
|
||||
val value: LocalValue
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "$variable:$variableType=$value"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOCAL_VARIABLES_MARKER = "// LOCAL VARIABLES"
|
||||
const val JVM_LOCAL_VARIABLES_MARKER = "$LOCAL_VARIABLES_MARKER JVM"
|
||||
const val JVM_IR_LOCAL_VARIABLES_MARKER = "$LOCAL_VARIABLES_MARKER JVM_IR"
|
||||
|
||||
const val LOCAL_VARIABLES = "// LOCAL VARIABLES"
|
||||
var proxyPort = 0
|
||||
lateinit var process: Process
|
||||
lateinit var virtualMachine: VirtualMachine
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setUpTest() {
|
||||
val (process, port) = startDebuggeeProcess()
|
||||
this.process = process
|
||||
virtualMachine = attachDebugger(port)
|
||||
setUpVM(virtualMachine)
|
||||
|
||||
proxyPort = getProxyPort(process)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun tearDownTest() {
|
||||
process.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
data class LVTStep(
|
||||
val location : Location,
|
||||
val visibleVars: Collection<LocalVariableRecord>
|
||||
)
|
||||
|
||||
override fun storeStep(loggedItems: ArrayList<Any>, event: Event) {
|
||||
val locatableEvent = event as LocatableEvent
|
||||
waitUntil { locatableEvent.thread().isSuspended }
|
||||
val location = locatableEvent.location()
|
||||
if (location.isInDebugTestInfrastructure) return
|
||||
if (location.method().isSynthetic) return
|
||||
|
||||
val frame = locatableEvent.thread().frame(0)
|
||||
val visibleVars = try {
|
||||
frame.visibleVariables().map { variable -> toRecord(frame, variable) }
|
||||
} catch (e: AbsentInformationException) {
|
||||
// LVT Completely absent - not distinguished from an empty table
|
||||
listOf()
|
||||
}
|
||||
loggedItems.add(LVTStep(location, visibleVars))
|
||||
}
|
||||
|
||||
override fun checkResult(wholeFile: File, loggedItems: List<Any>) {
|
||||
val lines = wholeFile.readLines()
|
||||
|
||||
val expectedLocalVariables = mutableListOf<String>()
|
||||
val lineIterator = lines.iterator()
|
||||
for (line in lineIterator) {
|
||||
if (line.startsWith(LOCAL_VARIABLES_MARKER)) break
|
||||
}
|
||||
|
||||
var currentBackend = TargetBackend.ANY
|
||||
for (line in lineIterator) {
|
||||
if (line.trim() == "") continue
|
||||
if (line.startsWith(LOCAL_VARIABLES_MARKER)) {
|
||||
currentBackend = when (line) {
|
||||
LOCAL_VARIABLES_MARKER -> TargetBackend.ANY
|
||||
JVM_LOCAL_VARIABLES_MARKER -> TargetBackend.JVM
|
||||
JVM_IR_LOCAL_VARIABLES_MARKER -> TargetBackend.JVM_IR
|
||||
else -> error("Expected JVM backend: $line")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (currentBackend == TargetBackend.ANY || currentBackend == backend) {
|
||||
expectedLocalVariables.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val compressedLog = compressRunsWithoutLinenumber(loggedItems as List<LVTStep>, LVTStep::location)
|
||||
|
||||
val actualLocalVariables = compressedLog.joinToString("\n") {
|
||||
"// ${it.location.formatAsExpectation()}: ${it.visibleVars.joinToString(", ")}".trim()
|
||||
}
|
||||
|
||||
TestCase.assertEquals(expectedLocalVariables.joinToString("\n"), actualLocalVariables)
|
||||
}
|
||||
|
||||
private fun toRecord(frame: StackFrame, variable: LocalVariable): LocalVariableRecord {
|
||||
val value = frame.getValue(variable)
|
||||
val valueRecord = if (value == null) {
|
||||
LocalNullValue()
|
||||
} else if (value is ObjectReference && value.referenceType().name() != "java.lang.String") {
|
||||
LocalReference(value.uniqueID().toString(), value.referenceType().name())
|
||||
} else {
|
||||
LocalPrimitive(value.toString(), value.type().name())
|
||||
}
|
||||
return LocalVariableRecord(variable.name(), variable.typeName(), valueRecord)
|
||||
}
|
||||
|
||||
private fun waitUntil(condition: () -> Boolean) {
|
||||
while (!condition()) {
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.debugInformation
|
||||
|
||||
import com.sun.jdi.VirtualMachine
|
||||
import com.sun.jdi.event.Event
|
||||
import com.sun.jdi.event.LocatableEvent
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils.assertEqualsToFile
|
||||
import org.jetbrains.kotlin.test.TargetBackend
|
||||
import org.junit.AfterClass
|
||||
import org.junit.BeforeClass
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractSteppingTest : AbstractDebugTest() {
|
||||
|
||||
override val virtualMachine: VirtualMachine = Companion.virtualMachine
|
||||
override val proxyPort: Int = Companion.proxyPort
|
||||
|
||||
companion object {
|
||||
const val LINENUMBERS_MARKER = "// LINENUMBERS"
|
||||
const val FORCE_STEP_INTO_MARKER = "// FORCE_STEP_INTO"
|
||||
const val JVM_LINENUMBER_MARKER = "$LINENUMBERS_MARKER JVM"
|
||||
const val JVM_IR_LINENUMBER_MARKER = "$LINENUMBERS_MARKER JVM_IR"
|
||||
var proxyPort = 0
|
||||
lateinit var process: Process
|
||||
lateinit var virtualMachine: VirtualMachine
|
||||
|
||||
@BeforeClass
|
||||
@JvmStatic
|
||||
fun setUpTest() {
|
||||
val (process, port) = startDebuggeeProcess()
|
||||
this.process = process
|
||||
virtualMachine = attachDebugger(port)
|
||||
setUpVM(virtualMachine)
|
||||
|
||||
proxyPort = getProxyPort(process)
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@JvmStatic
|
||||
fun tearDownTest() {
|
||||
process.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
override fun storeStep(loggedItems: ArrayList<Any>, event: Event) {
|
||||
assert(event is LocatableEvent)
|
||||
loggedItems.add(event)
|
||||
}
|
||||
|
||||
override fun checkResult(wholeFile: File, loggedItems: List<Any>) {
|
||||
val actual = mutableListOf<String>()
|
||||
val lines = wholeFile.readLines()
|
||||
val forceStepInto = lines.any { it.startsWith(FORCE_STEP_INTO_MARKER) }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val actualLineNumbers = compressRunsWithoutLinenumber(loggedItems as List<LocatableEvent>, LocatableEvent::location)
|
||||
.filter {
|
||||
val location = it.location()
|
||||
// Ignore:
|
||||
// - synthetic code with no line number information
|
||||
// unless force step into behavior is requested.
|
||||
// - helper code from the test infrastructure
|
||||
(forceStepInto || !location.method().isSynthetic) && !location.isInDebugTestInfrastructure
|
||||
}
|
||||
.map { "// ${it.location().formatAsExpectation()}" }
|
||||
val actualLineNumbersIterator = actualLineNumbers.iterator()
|
||||
|
||||
val lineIterator = lines.iterator()
|
||||
for (line in lineIterator) {
|
||||
actual.add(line)
|
||||
if (line.startsWith(LINENUMBERS_MARKER) || line.startsWith(FORCE_STEP_INTO_MARKER)) break
|
||||
}
|
||||
|
||||
var currentBackend = TargetBackend.ANY
|
||||
for (line in lineIterator) {
|
||||
if (line.startsWith(LINENUMBERS_MARKER)) {
|
||||
actual.add(line)
|
||||
currentBackend = when (line) {
|
||||
LINENUMBERS_MARKER -> TargetBackend.ANY
|
||||
JVM_LINENUMBER_MARKER -> TargetBackend.JVM
|
||||
JVM_IR_LINENUMBER_MARKER -> TargetBackend.JVM_IR
|
||||
else -> error("Expected JVM backend: $line")
|
||||
}
|
||||
continue
|
||||
}
|
||||
if (currentBackend == TargetBackend.ANY || currentBackend == backend) {
|
||||
if (actualLineNumbersIterator.hasNext()) {
|
||||
actual.add(actualLineNumbersIterator.next())
|
||||
}
|
||||
} else {
|
||||
actual.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
actualLineNumbersIterator.forEach { actual.add(it) }
|
||||
|
||||
assertEqualsToFile(wholeFile, actual.joinToString("\n"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user