Refactor debugger tests
1. Move tests to their own module 2. Avoid sharing the 'tinyApp' project between tests 3. Clean up option directive handling
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompileOnly(intellijDep())
|
||||
|
||||
testCompile(project(":idea:jvm-debugger:jvm-debugger-core"))
|
||||
testCompile(project(":idea:jvm-debugger:jvm-debugger-evaluation"))
|
||||
testCompile(project(":idea:jvm-debugger:jvm-debugger-sequence"))
|
||||
testCompile(project(":compiler:backend"))
|
||||
testCompile(files("${System.getProperty("java.home")}/../lib/tools.jar"))
|
||||
testCompile(project(":kotlin-test:kotlin-test-junit"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
testCompile(projectTests(":idea:idea-test-framework")) { isTransitive = false }
|
||||
testCompile(commonDep("junit:junit"))
|
||||
|
||||
testCompile(intellijPluginDep("stream-debugger"))
|
||||
|
||||
Platform[191].orLower {
|
||||
testCompileOnly(intellijDep()) { includeJars("java-api", "java-impl") }
|
||||
}
|
||||
|
||||
Platform[192].orHigher {
|
||||
testCompileOnly(intellijPluginDep("java")) { includeJars("java-api", "java-impl") }
|
||||
testRuntime(intellijPluginDep("java"))
|
||||
}
|
||||
|
||||
testRuntime(project(":nj2k:nj2k-services")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-jvm"))
|
||||
testRuntime(project(":idea:idea-native")) { isTransitive = false }
|
||||
testRuntime(project(":idea:idea-gradle-native")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-native:kotlin-native-library-reader")) { isTransitive = false }
|
||||
testRuntime(project(":kotlin-native:kotlin-native-utils")) { isTransitive = false }
|
||||
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
testRuntime(project(":sam-with-receiver-ide-plugin"))
|
||||
testRuntime(project(":allopen-ide-plugin"))
|
||||
testRuntime(project(":noarg-ide-plugin"))
|
||||
testRuntime(project(":kotlin-scripting-idea"))
|
||||
testRuntime(project(":kotlinx-serialization-ide-plugin"))
|
||||
|
||||
testRuntime(intellijDep())
|
||||
testRuntime(intellijRuntimeAnnotations())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { none() }
|
||||
"test" { projectDefault() }
|
||||
}
|
||||
|
||||
projectTest(parallel = true) {
|
||||
dependsOn(":dist")
|
||||
workingDir = rootDir
|
||||
}
|
||||
|
||||
testsJar()
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.engine.AsyncStackTraceProvider
|
||||
import com.intellij.debugger.engine.JavaValue
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinCoroutinesAsyncStackTraceProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.getSafe
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import java.lang.reflect.Modifier
|
||||
|
||||
abstract class AbstractAsyncStackTraceTest : KotlinDescriptorTestCaseWithStepping() {
|
||||
private companion object {
|
||||
const val MARGIN = " "
|
||||
val ASYNC_STACKTRACE_EP_NAME = AsyncStackTraceProvider.EP.name
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val asyncStackTraceProvider = getAsyncStackTraceProvider()
|
||||
if (asyncStackTraceProvider == null) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
doOnBreakpoint {
|
||||
val frameProxy = this.frameProxy
|
||||
if (frameProxy != null) {
|
||||
try {
|
||||
val stackTrace = asyncStackTraceProvider.getAsyncStackTraceSafe(frameProxy, this)
|
||||
if (stackTrace != null && stackTrace.isNotEmpty()) {
|
||||
print(renderAsyncStackTrace(stackTrace), ProcessOutputTypes.SYSTEM)
|
||||
} else {
|
||||
println("No async stack trace available", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
val stackTrace = e.stackTraceAsString()
|
||||
System.err.println("Exception occurred on calculating async stack traces: $stackTrace")
|
||||
throw e
|
||||
}
|
||||
} else {
|
||||
println("FrameProxy is 'null', can't calculate async stack trace", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
|
||||
resume(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAsyncStackTraceProvider(): KotlinCoroutinesAsyncStackTraceProvider? {
|
||||
val area = Extensions.getArea(null)
|
||||
if (!area.hasExtensionPoint(ASYNC_STACKTRACE_EP_NAME)) {
|
||||
System.err.println("$ASYNC_STACKTRACE_EP_NAME extension point is not found (probably old IDE version)")
|
||||
return null
|
||||
}
|
||||
|
||||
val extensionPoint = area.getExtensionPoint<Any>(ASYNC_STACKTRACE_EP_NAME)
|
||||
val provider = extensionPoint.extensions.firstIsInstanceOrNull<KotlinCoroutinesAsyncStackTraceProvider>()
|
||||
|
||||
if (provider == null) {
|
||||
System.err.println("Kotlin coroutine async stack trace provider is not found")
|
||||
}
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
private fun Throwable.stackTraceAsString(): String {
|
||||
val writer = StringWriter()
|
||||
printStackTrace(PrintWriter(writer))
|
||||
return writer.toString()
|
||||
}
|
||||
|
||||
private fun renderAsyncStackTrace(trace: List<StackFrameItem>) = buildString {
|
||||
appendln("Async stack trace:")
|
||||
for (item in trace) {
|
||||
append(MARGIN).appendln(item.toString())
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val variablesField = item.javaClass.declaredFields
|
||||
.first { !Modifier.isStatic(it.modifiers) && it.type == List::class.java }
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val variables = variablesField.getSafe(item) as? List<JavaValue>
|
||||
|
||||
if (variables != null) {
|
||||
for (variable in variables) {
|
||||
val descriptor = variable.descriptor
|
||||
val name = descriptor.calcValueName()
|
||||
val value = descriptor.calcValue(evaluationContext)
|
||||
|
||||
append(MARGIN).append(MARGIN).append(name).append(" = ").appendln(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.core.util.getLineCount
|
||||
import org.jetbrains.kotlin.idea.core.util.getLineEndOffset
|
||||
import org.jetbrains.kotlin.idea.core.util.getLineStartOffset
|
||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.*
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.allKotlinFiles
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import java.io.File
|
||||
|
||||
abstract class AbstractBreakpointApplicabilityTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
private companion object {
|
||||
private const val COMMENT = "///"
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String {
|
||||
return PluginTestCaseBase.getTestDataPathBase() + "/debugger/breakpointApplicability/"
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
myFixture.testDataPath = PluginTestCaseBase.getTestDataPathBase()
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
myFixture.configureByFile(getPath(path))
|
||||
|
||||
val file = File(path)
|
||||
val ktFile = project.allKotlinFiles().single()
|
||||
|
||||
val actualContents = checkBreakpoints(ktFile, BreakpointChecker())
|
||||
KotlinTestUtils.assertEqualsToFile(file, actualContents)
|
||||
}
|
||||
|
||||
private fun checkBreakpoints(file: KtFile, checker: BreakpointChecker): String {
|
||||
val lineCount = file.getLineCount()
|
||||
return (0..lineCount).joinToString("\n") { line -> checkLine(file, line, checker) }
|
||||
}
|
||||
|
||||
private fun checkLine(file: KtFile, line: Int, checker: BreakpointChecker): String {
|
||||
val lineText = file.getLine(line)
|
||||
val expectedBreakpointTypes = lineText.substringAfterLast(COMMENT).trim().split(",").map { it.trim() }.toSortedSet()
|
||||
val actualBreakpointTypes = checker.check(file, line).map { it.prefix }.distinct().toSortedSet()
|
||||
|
||||
return if (expectedBreakpointTypes != actualBreakpointTypes) {
|
||||
val lineWithoutComments = lineText.substringBeforeLast(COMMENT).trimEnd()
|
||||
if (actualBreakpointTypes.isNotEmpty()) {
|
||||
"$lineWithoutComments $COMMENT " + actualBreakpointTypes.joinToString()
|
||||
} else {
|
||||
lineWithoutComments
|
||||
}
|
||||
} else {
|
||||
lineText
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiFile.getLine(line: Int): String {
|
||||
val start = getLineStartOffset(line, skipWhitespace = false) ?: error("Cannot find start for line $line")
|
||||
val end = getLineEndOffset(line) ?: error("Cannot find end for line $line")
|
||||
if (start >= end) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return text.substring(start, end)
|
||||
}
|
||||
|
||||
private fun getPath(path: String): String {
|
||||
return path.substringAfter(PluginTestCaseBase.TEST_DATA_PROJECT_RELATIVE.drop(1), path)
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.sun.jdi.ThreadReference
|
||||
import org.jetbrains.kotlin.codegen.ClassFileFactory
|
||||
import org.jetbrains.kotlin.codegen.OriginCollectingClassBuilderFactory
|
||||
import org.jetbrains.kotlin.codegen.getClassFiles
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.idea.debugger.FileRankingCalculator
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.junit.Assert
|
||||
|
||||
abstract class AbstractFileRankingTest : LowLevelDebuggerTestBase() {
|
||||
override fun doTest(
|
||||
options: Set<String>,
|
||||
mainThread: ThreadReference,
|
||||
factory: OriginCollectingClassBuilderFactory,
|
||||
classFileFactory: ClassFileFactory,
|
||||
state: GenerationState
|
||||
) {
|
||||
val allKtFiles = classFileFactory.inputFiles.distinct()
|
||||
fun getKtFiles(name: String) = allKtFiles.filter { it.name == name }
|
||||
|
||||
val doNotCheckClassFqName = "DO_NOT_CHECK_CLASS_FQNAME" in options
|
||||
val strictMode = "DISABLE_STRICT_MODE" !in options
|
||||
|
||||
val expectedRanks: Map<Pair<KtFile, Int>, Int> = allKtFiles.asSequence().flatMap { ktFile ->
|
||||
ktFile.text.lines()
|
||||
.asSequence()
|
||||
.withIndex()
|
||||
.map {
|
||||
val matchResult = "^.*// (R: (-?\\d+)( L: (\\d+))?)\\s*$".toRegex().matchEntire(it.value) ?: return@map null
|
||||
|
||||
val rank = matchResult.groupValues[2].toInt()
|
||||
val line = matchResult.groupValues.getOrNull(4)?.takeIf { !it.isEmpty() }?.toInt()
|
||||
|
||||
if (line != null && line != it.index + 1) {
|
||||
throw IllegalArgumentException("Bad line in directive at ${ktFile.name}:${it.index + 1}\n${it.value}")
|
||||
}
|
||||
|
||||
(ktFile to it.index + 1) to rank
|
||||
}
|
||||
.filterNotNull()
|
||||
}.toMap()
|
||||
|
||||
val calculator = object : FileRankingCalculator(checkClassFqName = !doNotCheckClassFqName) {
|
||||
override fun analyze(element: KtElement) = state.bindingContext
|
||||
}
|
||||
|
||||
val problems = mutableListOf<String>()
|
||||
|
||||
val classNameToKtFile = factory.origins.asSequence()
|
||||
.filter { it.key is ClassNode }
|
||||
.map {
|
||||
val ktFile = (it.value.element?.containingFile as? KtFile) ?: return@map null
|
||||
val name = (it.key as ClassNode).name.replace('/', '.')
|
||||
|
||||
name to ktFile
|
||||
}
|
||||
.filterNotNull()
|
||||
.toMap()
|
||||
|
||||
val skipClasses = skipLoadingClasses(options)
|
||||
for (outputFile in classFileFactory.getClassFiles()) {
|
||||
val className = outputFile.internalName.replace('/', '.')
|
||||
if (className in skipClasses) {
|
||||
continue
|
||||
}
|
||||
|
||||
val expectedFile = classNameToKtFile[className] ?: throw IllegalStateException("Can't find source for $className")
|
||||
|
||||
val jdiClass = mainThread.virtualMachine().classesByName(className).singleOrNull()
|
||||
?: error("Class '$className' was not found in the debuggee process class loader")
|
||||
|
||||
val locations = jdiClass.allLineLocations()
|
||||
assert(locations.isNotEmpty()) { "There are no locations for class $className" }
|
||||
|
||||
val allFilesWithSameName = getKtFiles(expectedFile.name)
|
||||
|
||||
for (location in locations) {
|
||||
if (location.method().isBridge || location.method().isSynthetic) continue
|
||||
|
||||
val fileWithRankings: Map<KtFile, Int> = calculator.rankFiles(allFilesWithSameName, location)
|
||||
|
||||
for ((ktFile, rank) in fileWithRankings) {
|
||||
val expectedRank = expectedRanks[ktFile to (location.lineNumber())]
|
||||
if (expectedRank != null) {
|
||||
Assert.assertEquals("Invalid expected rank at $location", expectedRank, rank)
|
||||
}
|
||||
}
|
||||
|
||||
val fileWithMaxScore = fileWithRankings.maxBy { it.value }!!
|
||||
val actualFile = fileWithMaxScore.key
|
||||
|
||||
if (strictMode) {
|
||||
require(fileWithMaxScore.value >= 0) { "Max score is negative at $location" }
|
||||
|
||||
// Allow only one element with max ranking
|
||||
require(fileWithRankings.filter { it.value == fileWithMaxScore.value }.count() == 1) {
|
||||
"Score is the same for several files at $location"
|
||||
}
|
||||
}
|
||||
|
||||
if (actualFile != expectedFile) {
|
||||
problems += "Location ${location.sourceName()}:${location.lineNumber() - 1} is associated with a wrong KtFile:\n" +
|
||||
" - expected: ${expectedFile.virtualFilePath}\n" +
|
||||
" - actual: ${actualFile.virtualFilePath}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (problems.isNotEmpty()) {
|
||||
throw AssertionError(buildString {
|
||||
appendln("There were association errors:").appendln()
|
||||
problems.joinTo(this, "\n\n")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun skipLoadingClasses(options: Set<String>): Set<String> {
|
||||
val skipClasses = options.mapTo(mutableSetOf()) { it.substringAfter("DO_NOT_LOAD:", "").trim() }
|
||||
skipClasses.remove("")
|
||||
return skipClasses
|
||||
}
|
||||
}
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.engine.ContextUtil
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
|
||||
import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl
|
||||
import com.intellij.debugger.engine.events.SuspendContextCommandImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl.createDebuggerContext
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import com.intellij.xdebugger.impl.ui.tree.ValueMarkup
|
||||
import com.sun.jdi.ObjectReference
|
||||
import org.jetbrains.eval4j.ObjectValue
|
||||
import org.jetbrains.eval4j.Value
|
||||
import org.jetbrains.eval4j.jdi.asValue
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase.TestFile
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.FramePrinter
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.FramePrinterDelegate
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstruction
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findLinesWithPrefixesRemoved
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes
|
||||
import java.io.File
|
||||
import javax.swing.tree.TreeNode
|
||||
|
||||
private data class CodeFragment(val text: String, val result: String, val kind: CodeFragmentKind)
|
||||
|
||||
private data class DebugLabel(val name: String, val localName: String)
|
||||
|
||||
private class EvaluationTestData(
|
||||
val instructions: List<SteppingInstruction>,
|
||||
val fragments: List<CodeFragment>,
|
||||
val debugLabels: List<DebugLabel>
|
||||
)
|
||||
|
||||
abstract class AbstractKotlinEvaluateExpressionTest : KotlinDescriptorTestCaseWithStepping(), FramePrinterDelegate {
|
||||
private companion object {
|
||||
private val ID_PART_REGEX = "id=[0-9]*".toRegex()
|
||||
}
|
||||
|
||||
override val debuggerContext: DebuggerContextImpl
|
||||
get() = super.debuggerContext
|
||||
|
||||
private var isMultipleBreakpointsTest = false
|
||||
|
||||
private var framePrinter: FramePrinter? = null
|
||||
|
||||
fun doSingleBreakpointTest(path: String) {
|
||||
isMultipleBreakpointsTest = false
|
||||
doTest(path)
|
||||
}
|
||||
|
||||
fun doMultipleBreakpointsTest(path: String) {
|
||||
isMultipleBreakpointsTest = true
|
||||
doTest(path)
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val wholeFile = files.wholeFile
|
||||
|
||||
val instructions = SteppingInstruction.parse(wholeFile)
|
||||
val expressions = loadExpressions(wholeFile)
|
||||
val blocks = loadCodeBlocks(files.originalFile)
|
||||
val debugLabels = loadDebugLabels(wholeFile)
|
||||
|
||||
val data = EvaluationTestData(instructions, expressions + blocks, debugLabels)
|
||||
|
||||
framePrinter = FramePrinter(myDebuggerSession, this, preferences, testRootDisposable)
|
||||
|
||||
if (isMultipleBreakpointsTest) {
|
||||
performMultipleBreakpointTest(data)
|
||||
} else {
|
||||
performSingleBreakpointTest(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
framePrinter?.close()
|
||||
framePrinter = null
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
private fun performSingleBreakpointTest(data: EvaluationTestData) {
|
||||
process(data.instructions)
|
||||
|
||||
doOnBreakpoint {
|
||||
createDebugLabels(data.debugLabels)
|
||||
|
||||
val exceptions = linkedMapOf<String, Throwable>()
|
||||
|
||||
for ((expression, expected, kind) in data.fragments) {
|
||||
mayThrow(exceptions, expression) {
|
||||
evaluate(this, expression, kind, expected)
|
||||
}
|
||||
}
|
||||
|
||||
val completion = { resume(this) }
|
||||
framePrinter?.printFrame(completion) ?: completion()
|
||||
|
||||
checkExceptions(exceptions)
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun performMultipleBreakpointTest(data: EvaluationTestData) {
|
||||
val exceptions = linkedMapOf<String, Throwable>()
|
||||
|
||||
for ((expression, expected) in data.fragments) {
|
||||
mayThrow(exceptions, expression) {
|
||||
doOnBreakpoint {
|
||||
try {
|
||||
evaluate(this, expression, CodeFragmentKind.EXPRESSION, expected)
|
||||
} finally {
|
||||
val completion = { resume(this) }
|
||||
framePrinter?.printFrame(completion) ?: completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkExceptions(exceptions)
|
||||
finish()
|
||||
}
|
||||
|
||||
override fun evaluate(suspendContext: SuspendContextImpl, textWithImports: TextWithImportsImpl) {
|
||||
evaluate(suspendContext, textWithImports, null)
|
||||
}
|
||||
|
||||
private fun evaluate(suspendContext: SuspendContextImpl, text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String?) {
|
||||
val textWithImports = TextWithImportsImpl(codeFragmentKind, text, "", KotlinFileType.INSTANCE)
|
||||
return evaluate(suspendContext, textWithImports, expectedResult)
|
||||
}
|
||||
|
||||
private fun evaluate(suspendContext: SuspendContextImpl, item: TextWithImportsImpl, expectedResult: String?) {
|
||||
val evaluationContext = this.evaluationContext
|
||||
val sourcePosition = ContextUtil.getSourcePosition(suspendContext)
|
||||
|
||||
// Default test debuggerContext doesn't provide a valid stackFrame so we have to create one more for evaluation purposes.
|
||||
val frameProxy = suspendContext.frameProxy
|
||||
val threadProxy = frameProxy?.threadProxy()
|
||||
val debuggerContext = createDebuggerContext(myDebuggerSession, suspendContext, threadProxy, frameProxy)
|
||||
debuggerContext.initCaches()
|
||||
|
||||
val contextElement = ContextUtil.getContextElement(debuggerContext)!!
|
||||
|
||||
assert(KotlinCodeFragmentFactory().isContextAccepted(contextElement)) {
|
||||
val text = runReadAction { contextElement.text }
|
||||
"KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. " +
|
||||
"ContextElement = $text"
|
||||
}
|
||||
|
||||
contextElement.putCopyableUserData(KotlinCodeFragmentFactory.DEBUG_CONTEXT_FOR_TESTS, debuggerContext)
|
||||
|
||||
suspendContext.runActionInSuspendCommand {
|
||||
try {
|
||||
val evaluator = runReadAction {
|
||||
EvaluatorBuilderImpl.build(
|
||||
item,
|
||||
contextElement,
|
||||
sourcePosition,
|
||||
this@AbstractKotlinEvaluateExpressionTest.project
|
||||
)
|
||||
}
|
||||
?: throw AssertionError("Cannot create an Evaluator for Evaluate Expression")
|
||||
|
||||
val value = evaluator.evaluate(evaluationContext)
|
||||
val actualResult = value.asValue().asString()
|
||||
if (expectedResult != null) {
|
||||
assertEquals(
|
||||
"Evaluate expression returns wrong result for ${item.text}:\n" +
|
||||
"expected = $expectedResult\n" +
|
||||
"actual = $actualResult\n",
|
||||
expectedResult, actualResult
|
||||
)
|
||||
}
|
||||
} catch (e: EvaluateException) {
|
||||
val expectedMessage = e.message?.replaceFirst(
|
||||
ID_PART_REGEX,
|
||||
"id=ID"
|
||||
)
|
||||
assertEquals(
|
||||
"Evaluate expression throws wrong exception for ${item.text}:\n" +
|
||||
"expected = $expectedResult\n" +
|
||||
"actual = $expectedMessage\n",
|
||||
expectedResult,
|
||||
expectedMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun logDescriptor(descriptor: NodeDescriptorImpl, text: String) {
|
||||
super.logDescriptor(descriptor, text)
|
||||
}
|
||||
|
||||
override fun expandAll(tree: Tree, runnable: () -> Unit, filter: (TreeNode) -> Boolean, suspendContext: SuspendContextImpl) {
|
||||
super.expandAll(tree, runnable, HashSet(), filter, suspendContext)
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.runActionInSuspendCommand(action: SuspendContextImpl.() -> Unit) {
|
||||
if (myInProgress) {
|
||||
action()
|
||||
} else {
|
||||
val command = object : SuspendContextCommandImpl(this) {
|
||||
override fun contextAction(suspendContext: SuspendContextImpl) {
|
||||
action(suspendContext)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to execute the action inside a command if we aren't already inside it.
|
||||
debuggerSession.process.managerThread?.invoke(command) ?: command.contextAction(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mayThrow(collector: MutableMap<String, Throwable>, expression: String, f: () -> Unit) {
|
||||
try {
|
||||
f()
|
||||
} catch (e: Throwable) {
|
||||
collector[expression] = e
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkExceptions(exceptions: MutableMap<String, Throwable>) {
|
||||
if (exceptions.isNotEmpty()) {
|
||||
for (exc in exceptions.values) {
|
||||
exc.printStackTrace()
|
||||
}
|
||||
|
||||
val expressionsText = exceptions.entries.joinToString("\n") { (k, v) -> "expression: $k, exception: ${v.message}" }
|
||||
|
||||
@Suppress("ConvertToStringTemplate")
|
||||
throw AssertionError("Test failed:\n" + expressionsText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Value.asString(): String {
|
||||
if (this is ObjectValue && this.value is ObjectReference) {
|
||||
return this.toString().replaceFirst(ID_PART_REGEX, "id=ID")
|
||||
}
|
||||
return this.toString()
|
||||
}
|
||||
|
||||
private fun loadExpressions(testFile: TestFile): List<CodeFragment> {
|
||||
val directives = findLinesWithPrefixesRemoved(testFile.content, "// EXPRESSION: ")
|
||||
val expected = findLinesWithPrefixesRemoved(testFile.content, "// RESULT: ")
|
||||
assert(directives.size == expected.size) { "Sizes of test directives are different" }
|
||||
return directives.zip(expected).map { (text, result) -> CodeFragment(text, result, CodeFragmentKind.EXPRESSION) }
|
||||
}
|
||||
|
||||
private fun loadCodeBlocks(wholeFile: File): List<CodeFragment> {
|
||||
val regexp = (Regex.escape(wholeFile.name) + ".fragment\\d*").toRegex()
|
||||
val fragmentFiles = wholeFile.parentFile.listFiles { _, name -> regexp.matches(name) } ?: emptyArray()
|
||||
|
||||
val codeFragments = mutableListOf<CodeFragment>()
|
||||
|
||||
for (fragmentFile in fragmentFiles) {
|
||||
val contents = FileUtil.loadFile(fragmentFile, true)
|
||||
val value = findStringWithPrefixes(contents, "// RESULT: ") ?: error("'RESULT' directive is missing in $fragmentFile")
|
||||
codeFragments += CodeFragment(contents, value, CodeFragmentKind.CODE_BLOCK)
|
||||
}
|
||||
|
||||
return codeFragments
|
||||
}
|
||||
|
||||
private fun loadDebugLabels(testFile: TestFile): List<DebugLabel> {
|
||||
return findLinesWithPrefixesRemoved(testFile.content, "// DEBUG_LABEL: ")
|
||||
.map { text ->
|
||||
val labelParts = text.split("=")
|
||||
assert(labelParts.size == 2) { "Wrong format for DEBUG_LABEL directive: // DEBUG_LABEL: {localVariableName} = {labelText}" }
|
||||
|
||||
val localName = labelParts[0].trim()
|
||||
val name = labelParts[1].trim()
|
||||
DebugLabel(name, localName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDebugLabels(labels: List<DebugLabel>) {
|
||||
if (labels.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val markupMap = NodeDescriptorImpl.getMarkupMap(debugProcess) ?: return
|
||||
|
||||
for ((name, localName) in labels) {
|
||||
val localVariable = evaluationContext.frameProxy!!.visibleVariableByName(localName)
|
||||
assert(localVariable != null) { "Cannot find localVariable for label: name = $localName" }
|
||||
|
||||
val localVariableValue = evaluationContext.frameProxy!!.getValue(localVariable) as? ObjectReference
|
||||
assert(localVariableValue != null) { "Local variable $localName should be an ObjectReference" }
|
||||
|
||||
markupMap[localVariableValue] = ValueMarkup(name, null, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstruction
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstructionKind
|
||||
|
||||
abstract class AbstractKotlinSteppingTest : KotlinDescriptorTestCaseWithStepping() {
|
||||
private enum class Category(val instruction: SteppingInstructionKind?) {
|
||||
StepInto(SteppingInstructionKind.StepInto),
|
||||
StepOut(SteppingInstructionKind.StepOut),
|
||||
StepOver(SteppingInstructionKind.StepOver),
|
||||
ForceStepOver(SteppingInstructionKind.ForceStepOver),
|
||||
SmartStepInto(SteppingInstructionKind.SmartStepInto),
|
||||
Custom(null)
|
||||
}
|
||||
|
||||
private var category: Category? = null
|
||||
|
||||
protected fun doStepIntoTest(path: String) = doTest(path, Category.StepInto)
|
||||
protected fun doStepOutTest(path: String) = doTest(path, Category.StepOut)
|
||||
protected fun doStepOverTest(path: String) = doTest(path, Category.StepOver)
|
||||
protected fun doStepOverForceTest(path: String) = doTest(path, Category.ForceStepOver)
|
||||
protected fun doSmartStepIntoTest(path: String) = doTest(path, Category.SmartStepInto)
|
||||
protected fun doCustomTest(path: String) = doTest(path, Category.Custom)
|
||||
|
||||
override fun tearDown() {
|
||||
category = null
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
private fun doTest(path: String, category: Category) {
|
||||
this.category = category
|
||||
super.doTest(path)
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
val category = this.category ?: error("Category is not specified")
|
||||
val specificKind = category.instruction
|
||||
|
||||
if (specificKind != null) {
|
||||
val instruction = SteppingInstruction.parseSingle(files.wholeFile, specificKind)
|
||||
?: SteppingInstruction(specificKind, 1)
|
||||
|
||||
process(listOf(instruction))
|
||||
} else {
|
||||
val instructions = SteppingInstruction.parse(files.wholeFile)
|
||||
process(instructions)
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.debugger.NoDataException;
|
||||
import com.intellij.debugger.PositionManager;
|
||||
import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.engine.DebugProcess;
|
||||
import com.intellij.debugger.engine.DebugProcessEvents;
|
||||
import com.intellij.debugger.engine.DebugProcessImpl;
|
||||
import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.sun.jdi.Location;
|
||||
import com.sun.jdi.ReferenceType;
|
||||
import kotlin.collections.CollectionsKt;
|
||||
import kotlin.io.FilesKt;
|
||||
import kotlin.sequences.SequencesKt;
|
||||
import kotlin.text.StringsKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection;
|
||||
import org.jetbrains.kotlin.checkers.CompilerTestLanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactories;
|
||||
import org.jetbrains.kotlin.codegen.GenerationUtils;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.config.*;
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinPositionManager;
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinPositionManagerFactory;
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches;
|
||||
import org.jetbrains.kotlin.idea.debugger.test.mock.MockLocation;
|
||||
import org.jetbrains.kotlin.idea.debugger.test.mock.MockVirtualMachine;
|
||||
import org.jetbrains.kotlin.idea.debugger.test.mock.SmartMockReferenceTypeContext;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCaseKt;
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor;
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartProvider;
|
||||
import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TestJdkKind;
|
||||
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.jetbrains.kotlin.idea.debugger.test.DebuggerTestUtils.DEBUGGER_TESTDATA_PATH_BASE;
|
||||
import static org.jetbrains.kotlin.idea.debugger.test.DebuggerTestUtils.DEBUGGER_TESTDATA_PATH_RELATIVE;
|
||||
|
||||
public abstract class AbstractPositionManagerTest extends KotlinLightCodeInsightFixtureTestCase {
|
||||
// Breakpoint is given as a line comment on a specific line, containing the regexp to match the name of the class where that line
|
||||
// can be found. This pattern matches against these line comments and saves the class name in the first group
|
||||
private static final Pattern BREAKPOINT_PATTERN = Pattern.compile("^.*//\\s*(.+)\\s*$");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return DEBUGGER_TESTDATA_PATH_BASE + "/positionManager";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
myFixture.setTestDataPath(DEBUGGER_TESTDATA_PATH_BASE);
|
||||
}
|
||||
|
||||
private DebugProcessImpl debugProcess;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static KotlinPositionManager createPositionManager(
|
||||
@NotNull DebugProcess process,
|
||||
@NotNull List<KtFile> files,
|
||||
@NotNull GenerationState state
|
||||
) {
|
||||
KotlinPositionManager positionManager = (KotlinPositionManager) new KotlinPositionManagerFactory().createPositionManager(process);
|
||||
assertNotNull(positionManager);
|
||||
|
||||
for (KtFile file : files) {
|
||||
KotlinDebuggerCaches.Companion.addTypeMapper(file, state.getTypeMapper());
|
||||
}
|
||||
|
||||
return positionManager;
|
||||
}
|
||||
|
||||
protected void doTest(@NotNull String fileName) {
|
||||
String path = getPath(fileName);
|
||||
|
||||
if (fileName.endsWith(".kt")) {
|
||||
myFixture.configureByFile(path);
|
||||
} else {
|
||||
SequencesKt.forEach(FilesKt.walkTopDown(new File(path)), file -> {
|
||||
String fileName1 = file.getName();
|
||||
String path1 = getPath(fileName1);
|
||||
myFixture.configureByFile(path1);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
performTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getPath(@NotNull String fileName) {
|
||||
return StringsKt.substringAfter(fileName, DEBUGGER_TESTDATA_PATH_RELATIVE, fileName);
|
||||
}
|
||||
|
||||
private void performTest() {
|
||||
Project project = getProject();
|
||||
List<KtFile> files = new ArrayList<>(KotlinLightCodeInsightFixtureTestCaseKt.allKotlinFiles(project));
|
||||
if (files.isEmpty()) return;
|
||||
|
||||
List<Breakpoint> breakpoints = Lists.newArrayList();
|
||||
for (KtFile file : files) {
|
||||
breakpoints.addAll(extractBreakpointsInfo(file, file.getText()));
|
||||
}
|
||||
|
||||
CompilerConfiguration configuration = KotlinTestUtils.newConfiguration(ConfigurationKind.JDK_ONLY, TestJdkKind.MOCK_JDK);
|
||||
// TODO: delete this once IDEVirtualFileFinder supports loading .kotlin_builtins files
|
||||
CommonConfigurationKeysKt.setLanguageVersionSettings(configuration, new CompilerTestLanguageVersionSettings(
|
||||
Collections.emptyMap(),
|
||||
ApiVersion.LATEST_STABLE,
|
||||
LanguageVersion.LATEST_STABLE,
|
||||
Collections.singletonMap(JvmAnalysisFlags.getSuppressMissingBuiltinsError(), true)
|
||||
));
|
||||
|
||||
GenerationState state =
|
||||
GenerationUtils.compileFiles(files, configuration, ClassBuilderFactories.TEST, scope -> PackagePartProvider.Empty.INSTANCE);
|
||||
|
||||
Map<String, ReferenceType> referencesByName = getReferenceMap(state.getFactory());
|
||||
|
||||
debugProcess = createDebugProcess(referencesByName);
|
||||
|
||||
PositionManager positionManager = createPositionManager(debugProcess, files, state);
|
||||
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
try {
|
||||
for (Breakpoint breakpoint : breakpoints) {
|
||||
assertBreakpointIsHandledCorrectly(breakpoint, positionManager);
|
||||
}
|
||||
}
|
||||
catch (NoDataException e) {
|
||||
throw ExceptionUtilsKt.rethrow(e);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() {
|
||||
if (debugProcess != null) {
|
||||
debugProcess.stop(true);
|
||||
debugProcess.dispose();
|
||||
debugProcess = null;
|
||||
}
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
private static Collection<Breakpoint> extractBreakpointsInfo(KtFile file, String fileContent) {
|
||||
Collection<Breakpoint> breakpoints = Lists.newArrayList();
|
||||
String[] lines = StringUtil.convertLineSeparators(fileContent).split("\n");
|
||||
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
Matcher matcher = BREAKPOINT_PATTERN.matcher(lines[i]);
|
||||
if (matcher.matches()) {
|
||||
breakpoints.add(new Breakpoint(file, i, matcher.group(1)));
|
||||
}
|
||||
}
|
||||
|
||||
return breakpoints;
|
||||
}
|
||||
|
||||
private static Map<String, ReferenceType> getReferenceMap(OutputFileCollection outputFiles) {
|
||||
return new SmartMockReferenceTypeContext(outputFiles).getReferenceTypesByName();
|
||||
}
|
||||
|
||||
private DebugProcessEvents createDebugProcess(Map<String, ReferenceType> referencesByName) {
|
||||
return new DebugProcessEvents(getProject()) {
|
||||
private VirtualMachineProxyImpl virtualMachineProxy;
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public VirtualMachineProxyImpl getVirtualMachineProxy() {
|
||||
if (virtualMachineProxy == null) {
|
||||
virtualMachineProxy = new MockVirtualMachineProxy(this, referencesByName);
|
||||
}
|
||||
return virtualMachineProxy;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public GlobalSearchScope getSearchScope() {
|
||||
return GlobalSearchScope.allScope(getProject());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void assertBreakpointIsHandledCorrectly(Breakpoint breakpoint, PositionManager positionManager) throws NoDataException {
|
||||
SourcePosition position = SourcePosition.createFromLine(breakpoint.file, breakpoint.lineNumber);
|
||||
List<ReferenceType> classes = positionManager.getAllClasses(position);
|
||||
assertNotNull(classes);
|
||||
assertFalse("Classes not found for line " + (breakpoint.lineNumber + 1) + ", expected " + breakpoint.classNameRegexp,
|
||||
classes.isEmpty());
|
||||
|
||||
if (classes.stream().noneMatch(clazz -> clazz.name().matches(breakpoint.classNameRegexp))) {
|
||||
throw new AssertionError("Breakpoint class '" + breakpoint.classNameRegexp +
|
||||
"' from line " + (breakpoint.lineNumber + 1) + " was not found in the PositionManager classes names: " +
|
||||
classes.stream().map(ReferenceType::name).collect(Collectors.joining(",")));
|
||||
}
|
||||
|
||||
ReferenceType typeWithFqName = classes.get(0);
|
||||
Location location = new MockLocation(typeWithFqName, breakpoint.file.getName(), breakpoint.lineNumber + 1);
|
||||
|
||||
SourcePosition actualPosition = positionManager.getSourcePosition(location);
|
||||
assertNotNull(actualPosition);
|
||||
assertEquals(position.getFile(), actualPosition.getFile());
|
||||
assertEquals(position.getLine(), actualPosition.getLine());
|
||||
}
|
||||
|
||||
private static class Breakpoint {
|
||||
private final KtFile file;
|
||||
private final int lineNumber; // 0-based
|
||||
private final String classNameRegexp;
|
||||
|
||||
private Breakpoint(KtFile file, int lineNumber, String classNameRegexp) {
|
||||
this.file = file;
|
||||
this.lineNumber = lineNumber;
|
||||
this.classNameRegexp = classNameRegexp;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockVirtualMachineProxy extends VirtualMachineProxyImpl {
|
||||
private final Map<String, ReferenceType> referencesByName;
|
||||
|
||||
private MockVirtualMachineProxy(DebugProcessEvents debugProcess, Map<String, ReferenceType> referencesByName) {
|
||||
super(debugProcess, new MockVirtualMachine());
|
||||
this.referencesByName = referencesByName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReferenceType> allClasses() {
|
||||
return new ArrayList<>(referencesByName.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReferenceType> classesByName(@NotNull String name) {
|
||||
return CollectionsKt.listOfNotNull(referencesByName.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.idea.test.invalidateLibraryCache
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.junit.Assert
|
||||
|
||||
abstract class AbstractSelectExpressionForDebuggerTest : LightCodeInsightFixtureTestCase() {
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
invalidateLibraryCache(project)
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
doTest(path, true)
|
||||
}
|
||||
|
||||
fun doTestWoMethodCalls(path: String) {
|
||||
doTest(path, false)
|
||||
}
|
||||
|
||||
fun doTest(path: String, allowMethodCalls: Boolean) {
|
||||
myFixture.configureByFile(path)
|
||||
|
||||
val elementAt = myFixture.file?.findElementAt(myFixture.caretOffset)!!
|
||||
val selectedExpression = KotlinEditorTextProvider.findExpressionInner(elementAt, allowMethodCalls)
|
||||
|
||||
val expected = InTextDirectivesUtils.findStringWithPrefixes(myFixture.file?.text!!, "// EXPECTED: ")
|
||||
|
||||
val actualResult = if (selectedExpression != null) {
|
||||
KotlinEditorTextProvider.getElementInfo(selectedExpression) { it.text }
|
||||
} else {
|
||||
"null"
|
||||
}
|
||||
|
||||
Assert.assertEquals("Another expression should be selected", expected, actualResult)
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): KotlinLightProjectDescriptor = KotlinLightProjectDescriptor.INSTANCE
|
||||
|
||||
override fun getTestDataPath() = PluginTestCaseBase.getTestDataPathBase() + "/debugger/selectExpression"
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture
|
||||
import org.jetbrains.kotlin.idea.core.util.CodeInsightUtils
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.KotlinSmartStepIntoHandler
|
||||
import org.jetbrains.kotlin.idea.debugger.test.mock.MockSourcePosition
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
|
||||
abstract class AbstractSmartStepIntoTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
private val fixture: JavaCodeInsightTestFixture
|
||||
get() = myFixture
|
||||
|
||||
protected fun doTest(path: String) {
|
||||
fixture.configureByFile(path)
|
||||
|
||||
val offset = fixture.caretOffset
|
||||
val line = fixture.getDocument(fixture.file!!)!!.getLineNumber(offset)
|
||||
|
||||
val lineStart = CodeInsightUtils.getStartLineOffset(file, line)!!
|
||||
val elementAtOffset = file.findElementAt(lineStart)
|
||||
|
||||
val position = MockSourcePosition(
|
||||
myFile = fixture.file,
|
||||
myLine = line,
|
||||
myOffset = offset,
|
||||
myEditor = fixture.editor,
|
||||
myElementAt = elementAtOffset
|
||||
)
|
||||
|
||||
val actual = KotlinSmartStepIntoHandler().findSmartStepTargets(position).map { it.presentation }
|
||||
|
||||
val expected = InTextDirectivesUtils.findListWithPrefixes(fixture.file?.text!!.replace("\\,", "+++"), "// EXISTS: ")
|
||||
.map { it.replace("+++", ",") }
|
||||
|
||||
for (actualTargetName in actual) {
|
||||
assert(actualTargetName in expected) {
|
||||
"Unexpected step into target was found: $actualTargetName\n${renderTableWithResults(expected, actual)}" +
|
||||
"\n // EXISTS: ${actual.joinToString()}"
|
||||
}
|
||||
}
|
||||
|
||||
for (expectedTargetName in expected) {
|
||||
assert(expectedTargetName in actual) {
|
||||
"Missed step into target: $expectedTargetName\n${renderTableWithResults(expected, actual)}" +
|
||||
"\n // EXISTS: ${actual.joinToString()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderTableWithResults(expected: List<String>, actual: List<String>): String {
|
||||
val sb = StringBuilder()
|
||||
|
||||
val maxExtStrSize = (expected.maxBy { it.length }?.length ?: 0) + 5
|
||||
val longerList = (if (expected.size < actual.size) actual else expected).sorted()
|
||||
val shorterList = (if (expected.size < actual.size) expected else actual).sorted()
|
||||
for ((i, element) in longerList.withIndex()) {
|
||||
sb.append(element)
|
||||
sb.append(" ".repeat(maxExtStrSize - element.length))
|
||||
if (i < shorterList.size) sb.append(shorterList[i])
|
||||
sb.append("\n")
|
||||
}
|
||||
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
override fun getTestDataPath(): String {
|
||||
return "$DEBUGGER_TESTDATA_PATH_BASE/smartStepInto"
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor() = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/asyncStackTrace")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class AsyncStackTraceTestGenerated extends AbstractAsyncStackTraceTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAsyncStackTrace() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/asyncStackTrace"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("asyncFunctions.kt")
|
||||
public void testAsyncFunctions() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/asyncStackTrace/asyncFunctions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("asyncLambdas.kt")
|
||||
public void testAsyncLambdas() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/asyncStackTrace/asyncLambdas.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("asyncSimple.kt")
|
||||
public void testAsyncSimple() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/asyncStackTrace/asyncSimple.kt");
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class BreakpointApplicabilityTestGenerated extends AbstractBreakpointApplicabilityTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInBreakpointApplicability() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("constructors.kt")
|
||||
public void testConstructors() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/constructors.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functions.kt")
|
||||
public void testFunctions() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/functions.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineOnly.kt")
|
||||
public void testInlineOnly() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/inlineOnly.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("locals.kt")
|
||||
public void testLocals() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/locals.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("properties.kt")
|
||||
public void testProperties() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/properties.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/breakpointApplicability/simple.kt");
|
||||
}
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil.doWriteAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.newvfs.ArchiveFileSystem
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
|
||||
import org.jetbrains.kotlin.cli.common.output.writeAllTo
|
||||
import org.jetbrains.kotlin.cli.jvm.compiler.findMainClass
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase.TestFile
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.config.JVMConfigurationKeys
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.patchDexTests
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.test.MockLibraryUtil
|
||||
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
|
||||
import java.io.File
|
||||
|
||||
class DebuggerTestCompilerFacility(files: List<TestFile>, private val jvmTarget: JvmTarget, private val applyDexPatch: Boolean) {
|
||||
private val kotlinStdlibPath = ForTestCompileRuntime.runtimeJarForTests().absolutePath
|
||||
|
||||
private val mainFiles: TestFilesByLanguage
|
||||
private val libraryFiles: TestFilesByLanguage
|
||||
|
||||
init {
|
||||
val splitFiles = splitByTarget(files)
|
||||
mainFiles = splitByLanguage(splitFiles.main)
|
||||
libraryFiles = splitByLanguage(splitFiles.library)
|
||||
}
|
||||
|
||||
fun compileExternalLibrary(name: String, srcDir: File, classesDir: File) {
|
||||
val libSrcPath = File(DEBUGGER_TESTDATA_PATH_BASE, "lib/$name")
|
||||
if (!libSrcPath.exists()) {
|
||||
error("Library $name does not exist")
|
||||
}
|
||||
|
||||
val testFiles = libSrcPath.walk().filter { it.isFile }.toList().map {
|
||||
val path = it.toRelativeString(libSrcPath)
|
||||
TestFile(path, FileUtil.loadFile(it, true))
|
||||
}
|
||||
|
||||
val libraryFiles = splitByLanguage(testFiles)
|
||||
compileLibrary(libraryFiles, srcDir, classesDir)
|
||||
}
|
||||
|
||||
fun compileLibrary(srcDir: File, classesDir: File) {
|
||||
compileLibrary(this.libraryFiles, srcDir, classesDir)
|
||||
|
||||
srcDir.refreshAndToVirtualFile()?.let { KtUsefulTestCase.refreshRecursively(it) }
|
||||
classesDir.refreshAndToVirtualFile()?.let { KtUsefulTestCase.refreshRecursively(it) }
|
||||
}
|
||||
|
||||
private fun compileLibrary(libraryFiles: TestFilesByLanguage, srcDir: File, classesDir: File) = with(libraryFiles) {
|
||||
resources.copy(classesDir)
|
||||
(kotlin + java).copy(srcDir)
|
||||
|
||||
if (kotlin.isNotEmpty()) {
|
||||
MockLibraryUtil.compileKotlin(
|
||||
srcDir.absolutePath,
|
||||
classesDir,
|
||||
listOf("-jvm-target", jvmTarget.description),
|
||||
kotlinStdlibPath
|
||||
)
|
||||
}
|
||||
|
||||
if (java.isNotEmpty()) {
|
||||
CodegenTestUtil.compileJava(
|
||||
java.map { File(srcDir, it.name).absolutePath },
|
||||
listOf(kotlinStdlibPath, classesDir.absolutePath),
|
||||
listOf("-g"),
|
||||
classesDir
|
||||
)
|
||||
}
|
||||
|
||||
if (applyDexPatch) {
|
||||
patchDexTests(classesDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the qualified name of the main test class.
|
||||
fun compileTestSources(module: Module, srcDir: File, classesDir: File, libClassesDir: File): String = with(mainFiles) {
|
||||
resources.copy(srcDir)
|
||||
resources.copy(classesDir) // sic!
|
||||
(kotlin + java).copy(srcDir)
|
||||
|
||||
val ktFiles = mutableListOf<KtFile>()
|
||||
|
||||
doWriteAction {
|
||||
for (file in kotlin + java) {
|
||||
val ioFile = File(srcDir, file.name)
|
||||
val virtualFile = ioFile.refreshAndToVirtualFile() ?: error("Cannot find a VirtualFile instance for file $file")
|
||||
val psiFile = PsiManager.getInstance(module.project).findFile(virtualFile) ?: continue
|
||||
|
||||
if (psiFile is KtFile) {
|
||||
ktFiles += psiFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ktFiles.isEmpty()) {
|
||||
error("No Kotlin files found")
|
||||
}
|
||||
|
||||
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(classesDir)
|
||||
LocalFileSystem.getInstance().refreshAndFindFileByIoFile(libClassesDir)
|
||||
|
||||
lateinit var mainClassName: String
|
||||
|
||||
doWriteAction {
|
||||
mainClassName = compileKotlinFilesInIde(module, ktFiles, classesDir)
|
||||
}
|
||||
|
||||
if (java.isNotEmpty()) {
|
||||
CodegenTestUtil.compileJava(
|
||||
java.map { File(srcDir, it.name).absolutePath },
|
||||
getClasspath(module) + listOf(classesDir.absolutePath),
|
||||
listOf("-g"),
|
||||
classesDir
|
||||
)
|
||||
}
|
||||
|
||||
if (applyDexPatch) {
|
||||
patchDexTests(classesDir)
|
||||
}
|
||||
|
||||
return mainClassName
|
||||
}
|
||||
|
||||
private fun compileKotlinFilesInIde(module: Module, files: List<KtFile>, classesDir: File): String {
|
||||
val project = module.project
|
||||
val resolutionFacade = KotlinCacheService.getInstance(project).getResolutionFacade(files)
|
||||
|
||||
val analysisResult = resolutionFacade.analyzeWithAllCompilerChecks(files)
|
||||
analysisResult.throwIfError()
|
||||
|
||||
val moduleDescriptor = resolutionFacade.moduleDescriptor
|
||||
val bindingContext = analysisResult.bindingContext
|
||||
|
||||
val configuration = CompilerConfiguration()
|
||||
configuration.put(JVMConfigurationKeys.JVM_TARGET, jvmTarget)
|
||||
|
||||
val state = GenerationState.Builder(project, ClassBuilderFactories.BINARIES, moduleDescriptor, bindingContext, files, configuration)
|
||||
.generateDeclaredClassFilter(GenerationState.GenerateClassFilter.GENERATE_ALL)
|
||||
.codegenFactory(DefaultCodegenFactory)
|
||||
.build()
|
||||
|
||||
KotlinCodegenFacade.compileCorrectFiles(state, CompilationErrorHandler.THROW_EXCEPTION)
|
||||
|
||||
val extraDiagnostics = state.collectedExtraJvmDiagnostics
|
||||
if (!extraDiagnostics.isEmpty()) {
|
||||
val compoundMessage = extraDiagnostics.joinToString("\n") { DefaultErrorMessages.render(it) }
|
||||
error("One or more errors occurred during code generation: \n$compoundMessage")
|
||||
}
|
||||
|
||||
state.factory.writeAllTo(classesDir)
|
||||
|
||||
return findMainClass(state, files)?.asString() ?: error("Cannot find main class name")
|
||||
}
|
||||
|
||||
private fun getClasspath(module: Module): List<String> {
|
||||
val moduleRootManager = ModuleRootManager.getInstance(module)
|
||||
val classpath = moduleRootManager.orderEntries.filterIsInstance<LibraryOrderEntry>()
|
||||
.flatMap { it.library?.rootProvider?.getFiles(OrderRootType.CLASSES)?.asList().orEmpty() }
|
||||
|
||||
val paths = mutableListOf<String>()
|
||||
for (entry in classpath) {
|
||||
val fileSystem = entry.fileSystem
|
||||
if (fileSystem is ArchiveFileSystem) {
|
||||
val localFile = fileSystem.getLocalByEntry(entry) ?: continue
|
||||
paths += localFile.path
|
||||
} else if (fileSystem is LocalFileSystem) {
|
||||
paths += entry.path
|
||||
}
|
||||
}
|
||||
|
||||
return paths
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.refreshAndToVirtualFile(): VirtualFile? = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(this)
|
||||
|
||||
private fun List<TestFile>.copy(destination: File) {
|
||||
for (file in this) {
|
||||
val target = File(destination, file.name)
|
||||
target.parentFile.mkdirs()
|
||||
target.writeText(file.content)
|
||||
}
|
||||
}
|
||||
|
||||
class TestFilesByTarget(val main: List<TestFile>, val library: List<TestFile>)
|
||||
|
||||
class TestFilesByLanguage(val kotlin: List<TestFile>, val java: List<TestFile>, val resources: List<TestFile>)
|
||||
|
||||
private fun splitByTarget(files: List<TestFile>): TestFilesByTarget {
|
||||
val main = mutableListOf<TestFile>()
|
||||
val lib = mutableListOf<TestFile>()
|
||||
|
||||
for (file in files) {
|
||||
val container = if (file.name.startsWith("lib/") || file.name.startsWith("customLib/")) lib else main
|
||||
container += file
|
||||
}
|
||||
|
||||
return TestFilesByTarget(main = main, library = lib)
|
||||
}
|
||||
|
||||
private fun splitByLanguage(files: List<TestFile>): TestFilesByLanguage {
|
||||
val kotlin = mutableListOf<TestFile>()
|
||||
val java = mutableListOf<TestFile>()
|
||||
val resources = mutableListOf<TestFile>()
|
||||
|
||||
for (file in files) {
|
||||
@Suppress("MoveVariableDeclarationIntoWhen")
|
||||
val extension = file.name.substringAfterLast(".", missingDelimiterValue = "")
|
||||
|
||||
val container = when (extension) {
|
||||
"kt", "kts" -> kotlin
|
||||
"java" -> java
|
||||
else -> resources
|
||||
}
|
||||
|
||||
container += file
|
||||
}
|
||||
|
||||
return TestFilesByLanguage(kotlin = kotlin, java = java, resources = resources)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:JvmName("DebuggerTestUtils")
|
||||
package org.jetbrains.kotlin.idea.debugger.test
|
||||
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
|
||||
const val DEBUGGER_TESTDATA_PATH_RELATIVE = "idea/jvm-debugger/jvm-debugger-test/testData"
|
||||
|
||||
@JvmField
|
||||
val DEBUGGER_TESTDATA_PATH_BASE = KotlinTestUtils.getHomeDirectory() + "/" + DEBUGGER_TESTDATA_PATH_RELATIVE
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class FileRankingTestGenerated extends AbstractFileRankingTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInFileRanking() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousClasses.kt")
|
||||
public void testAnonymousClasses() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/anonymousClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("differentFlags.kt")
|
||||
public void testDifferentFlags() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/differentFlags.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("init.kt")
|
||||
public void testInit() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/init.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdas.kt")
|
||||
public void testLambdas() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/lambdas.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multilinePrimaryConstructor.kt")
|
||||
public void testMultilinePrimaryConstructor() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/multilinePrimaryConstructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multilinePrimaryConstructorWithBody.kt")
|
||||
public void testMultilinePrimaryConstructorWithBody() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/multilinePrimaryConstructorWithBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parametersWithUnloadedClass.kt")
|
||||
public void testParametersWithUnloadedClass() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/parametersWithUnloadedClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyDelegates.kt")
|
||||
public void testPropertyDelegates() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/propertyDelegates.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sameClassName.kt")
|
||||
public void testSameClassName() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/sameClassName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("sameClassNameDifferentMethodNames.kt")
|
||||
public void testSameClassNameDifferentMethodNames() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/sameClassNameDifferentMethodNames.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevel.kt")
|
||||
public void testTopLevel() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/fileRanking/topLevel.kt");
|
||||
}
|
||||
}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.impl.DescriptorTestCase
|
||||
import com.intellij.debugger.impl.OutputChecker
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.roots.LibraryOrderEntry
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.roots.ui.configuration.libraryEditor.NewLibraryEditor
|
||||
import com.intellij.openapi.util.ThrowableComputable
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testFramework.EdtTestUtil
|
||||
import com.intellij.xdebugger.XDebugSession
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase.TestFile
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.config.JvmTarget
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.*
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.BreakpointCreator
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.KotlinOutputChecker
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.LogPropagator
|
||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.TestMetadata
|
||||
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||
import org.junit.ComparisonFailure
|
||||
import java.io.File
|
||||
|
||||
internal const val KOTLIN_LIBRARY_NAME = "KotlinLibrary"
|
||||
internal const val TEST_LIBRARY_NAME = "TestLibrary"
|
||||
|
||||
class TestFiles(val originalFile: File, val wholeFile: TestFile, files: List<TestFile>) : List<TestFile> by files
|
||||
|
||||
abstract class KotlinDescriptorTestCase : DescriptorTestCase() {
|
||||
private lateinit var testAppDirectory: File
|
||||
private lateinit var sourcesOutputDirectory: File
|
||||
|
||||
private lateinit var librarySrcDirectory: File
|
||||
private lateinit var libraryOutputDirectory: File
|
||||
|
||||
private lateinit var mainClassName: String
|
||||
|
||||
override fun getTestAppPath(): String = testAppDirectory.absolutePath
|
||||
override fun getTestProjectJdk() = PluginTestCaseBase.fullJdk()
|
||||
|
||||
private fun systemLogger(message: String) = println(message, ProcessOutputTypes.SYSTEM)
|
||||
|
||||
private var breakpointCreator: BreakpointCreator? = null
|
||||
private var logPropagator: LogPropagator? = null
|
||||
|
||||
private var oldValues: OldValuesStorage? = null
|
||||
|
||||
override fun runBare() {
|
||||
testAppDirectory = KotlinTestUtils.tmpDir("debuggerTestSources")
|
||||
sourcesOutputDirectory = File(testAppDirectory, "src").apply { mkdirs() }
|
||||
|
||||
librarySrcDirectory = File(testAppDirectory, "libSrc").apply { mkdirs() }
|
||||
libraryOutputDirectory = File(testAppDirectory, "lib").apply { mkdirs() }
|
||||
|
||||
super.runBare()
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
|
||||
KotlinDebuggerCaches.LOG_COMPILATIONS = true
|
||||
logPropagator = LogPropagator(::systemLogger).apply { attach() }
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
KotlinDebuggerCaches.LOG_COMPILATIONS = false
|
||||
|
||||
oldValues?.revertValues()
|
||||
oldValues = null
|
||||
|
||||
detachLibraries()
|
||||
|
||||
logPropagator?.detach()
|
||||
logPropagator = null
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
fun doTest(path: String) {
|
||||
val wholeFile = File(path)
|
||||
val wholeFileContents = FileUtil.loadFile(wholeFile, true)
|
||||
|
||||
val testFiles = createTestFiles(wholeFile, wholeFileContents)
|
||||
val preferences = DebuggerPreferences(myProject, wholeFileContents)
|
||||
|
||||
oldValues = SettingsMutators.mutate(preferences)
|
||||
|
||||
val rawJvmTarget = preferences[DebuggerPreferenceKeys.JVM_TARGET]
|
||||
val jvmTarget = JvmTarget.fromString(rawJvmTarget) ?: error("Invalid JVM target value: $rawJvmTarget")
|
||||
val applyDexPatch = preferences[DebuggerPreferenceKeys.EMULATE_DEX]
|
||||
|
||||
val compilerFacility = DebuggerTestCompilerFacility(testFiles, jvmTarget, applyDexPatch)
|
||||
|
||||
for (library in preferences[DebuggerPreferenceKeys.ATTACH_LIBRARY]) {
|
||||
compilerFacility.compileExternalLibrary(library, librarySrcDirectory, libraryOutputDirectory)
|
||||
}
|
||||
|
||||
compilerFacility.compileLibrary(librarySrcDirectory, libraryOutputDirectory)
|
||||
mainClassName = compilerFacility.compileTestSources(myModule, sourcesOutputDirectory, File(appOutputPath), libraryOutputDirectory)
|
||||
|
||||
breakpointCreator = BreakpointCreator(
|
||||
project,
|
||||
::systemLogger,
|
||||
preferences
|
||||
).apply { createAdditionalBreakpoints(wholeFileContents) }
|
||||
|
||||
createLocalProcess(mainClassName)
|
||||
doMultiFileTest(testFiles, preferences)
|
||||
}
|
||||
|
||||
private fun createTestFiles(wholeFile: File, wholeFileContents: String): TestFiles {
|
||||
val testFiles = KotlinTestUtils.createTestFiles<Void, TestFile>(
|
||||
wholeFile.name,
|
||||
wholeFileContents,
|
||||
object : KotlinTestUtils.TestFileFactoryNoModules<TestFile>() {
|
||||
override fun create(fileName: String, text: String, directives: Map<String, String>): TestFile {
|
||||
return TestFile(fileName, text)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
val wholeTestFile = TestFile(wholeFile.name, wholeFileContents)
|
||||
return TestFiles(wholeFile, wholeTestFile, testFiles)
|
||||
}
|
||||
|
||||
abstract fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences)
|
||||
|
||||
override fun initOutputChecker(): OutputChecker {
|
||||
return KotlinOutputChecker(getTestDirectoryPath(), testAppPath, appOutputPath)
|
||||
}
|
||||
|
||||
override fun setUpModule() {
|
||||
super.setUpModule()
|
||||
attachLibraries()
|
||||
}
|
||||
|
||||
override fun setUpProject() {
|
||||
super.setUpProject()
|
||||
File(appOutputPath).mkdirs()
|
||||
}
|
||||
|
||||
override fun createBreakpoints(file: PsiFile?) {
|
||||
if (file != null) {
|
||||
val breakpointCreator = this.breakpointCreator ?: error(BreakpointCreator::class.java.simpleName + " should be set")
|
||||
breakpointCreator.createBreakpoints(file)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createJavaParameters(mainClass: String?): JavaParameters {
|
||||
return super.createJavaParameters(mainClass).apply {
|
||||
ModuleRootManager.getInstance(myModule).orderEntries.asSequence().filterIsInstance<LibraryOrderEntry>()
|
||||
classPath.add(ForTestCompileRuntime.runtimeJarForTests())
|
||||
classPath.add(libraryOutputDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachLibraries() {
|
||||
runWriteAction {
|
||||
val kotlinStdlibJar = ForTestCompileRuntime.runtimeJarForTests()
|
||||
val kotlinStdlibSourcesJar = ForTestCompileRuntime.runtimeSourcesJarForTests()
|
||||
|
||||
val model = ModuleRootManager.getInstance(myModule).modifiableModel
|
||||
attachLibrary(model, KOTLIN_LIBRARY_NAME, kotlinStdlibJar, kotlinStdlibSourcesJar)
|
||||
attachLibrary(model, TEST_LIBRARY_NAME, libraryOutputDirectory, librarySrcDirectory)
|
||||
model.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun detachLibraries() {
|
||||
EdtTestUtil.runInEdtAndGet(ThrowableComputable {
|
||||
ConfigLibraryUtil.removeLibrary(module, KOTLIN_LIBRARY_NAME)
|
||||
ConfigLibraryUtil.removeLibrary(module, TEST_LIBRARY_NAME)
|
||||
})
|
||||
}
|
||||
|
||||
private fun attachLibrary(model: ModifiableRootModel, libraryName: String, classes: File, sources: File) {
|
||||
val customLibEditor = NewLibraryEditor().apply {
|
||||
name = libraryName
|
||||
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(classes), OrderRootType.CLASSES)
|
||||
addRoot(VfsUtil.getUrlForLibraryRoot(sources), OrderRootType.SOURCES)
|
||||
}
|
||||
|
||||
ConfigLibraryUtil.addLibrary(customLibEditor, model, null)
|
||||
}
|
||||
|
||||
override fun checkTestOutput() {
|
||||
if (KotlinTestUtils.isAllFilesPresentTest(getTestName(false))) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
super.checkTestOutput()
|
||||
} catch (e: ComparisonFailure) {
|
||||
KotlinTestUtils.assertEqualsToFile(File(getTestDirectoryPath(), getTestName(true) + ".out"), e.actual)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getData(dataId: String): Any? {
|
||||
if (XDebugSession.DATA_KEY.`is`(dataId)) {
|
||||
return myDebuggerSession?.xDebugSession
|
||||
}
|
||||
|
||||
return super.getData(dataId)
|
||||
}
|
||||
|
||||
private fun getTestDirectoryPath(): String = javaClass.getAnnotation(TestMetadata::class.java).value
|
||||
}
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.debugger.actions.MethodSmartStepTarget
|
||||
import com.intellij.debugger.engine.*
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||
import com.intellij.debugger.impl.JvmSteppingCommandProvider
|
||||
import com.intellij.debugger.impl.PositionUtil
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.sun.jdi.request.StepRequest
|
||||
import org.jetbrains.kotlin.idea.debugger.stepping.*
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstruction
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.SteppingInstructionKind
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.renderSourcePosition
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
abstract class KotlinDescriptorTestCaseWithStepping : KotlinDescriptorTestCase() {
|
||||
private val dp: DebugProcessImpl
|
||||
get() = debugProcess ?: throw AssertionError("createLocalProcess() should be called before getDebugProcess()")
|
||||
|
||||
@Volatile
|
||||
private var myEvaluationContext: EvaluationContextImpl? = null
|
||||
val evaluationContext get() = myEvaluationContext!!
|
||||
|
||||
@Volatile
|
||||
private var myDebuggerContext: DebuggerContextImpl? = null
|
||||
protected open val debuggerContext get() = myDebuggerContext!!
|
||||
|
||||
@Volatile
|
||||
private var myCommandProvider: KotlinSteppingCommandProvider? = null
|
||||
private val commandProvider get() = myCommandProvider!!
|
||||
|
||||
private fun initContexts(suspendContext: SuspendContextImpl) {
|
||||
myEvaluationContext = createEvaluationContext(suspendContext)
|
||||
myDebuggerContext = createDebuggerContext(suspendContext)
|
||||
myCommandProvider = JvmSteppingCommandProvider.EP_NAME.extensions.firstIsInstance<KotlinSteppingCommandProvider>()
|
||||
}
|
||||
|
||||
internal fun process(instructions: List<SteppingInstruction>) {
|
||||
instructions.forEach(this::process)
|
||||
}
|
||||
|
||||
internal fun doOnBreakpoint(action: SuspendContextImpl.() -> Unit) {
|
||||
super.onBreakpoint {
|
||||
try {
|
||||
initContexts(it)
|
||||
it.printContext()
|
||||
it.action()
|
||||
} catch (e: AssertionError) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
resume(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun finish() {
|
||||
doOnBreakpoint {
|
||||
resume(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.doStepInto(ignoreFilters: Boolean, smartStepFilter: MethodFilter?) {
|
||||
val stepIntoCommand =
|
||||
runReadAction { commandProvider.getStepIntoCommand(this, ignoreFilters, smartStepFilter, StepRequest.STEP_LINE) }
|
||||
?: dp.createStepIntoCommand(this, ignoreFilters, smartStepFilter)
|
||||
|
||||
dp.managerThread.schedule(stepIntoCommand)
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.doStepOut() {
|
||||
val stepOutCommand = runReadAction { commandProvider.getStepOutCommand(this, debuggerContext) }
|
||||
?: dp.createStepOutCommand(this)
|
||||
|
||||
dp.managerThread.schedule(stepOutCommand)
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.doStepOver(ignoreBreakpoints: Boolean = false) {
|
||||
val stepOverCommand = runReadAction { commandProvider.getStepOverCommand(this, ignoreBreakpoints, debuggerContext) }
|
||||
?: dp.createStepOverCommand(this, ignoreBreakpoints)
|
||||
|
||||
dp.managerThread.schedule(stepOverCommand)
|
||||
}
|
||||
|
||||
private fun process(instruction: SteppingInstruction) {
|
||||
fun loop(count: Int, block: SuspendContextImpl.() -> Unit) {
|
||||
repeat(count) {
|
||||
doOnBreakpoint(block)
|
||||
}
|
||||
}
|
||||
|
||||
when (instruction.kind) {
|
||||
SteppingInstructionKind.StepInto -> loop(instruction.arg) { doStepInto(false, null) }
|
||||
SteppingInstructionKind.StepOut -> loop(instruction.arg) { doStepOut() }
|
||||
SteppingInstructionKind.StepOver -> loop(instruction.arg) { doStepOver() }
|
||||
SteppingInstructionKind.ForceStepOver -> loop(instruction.arg) { doStepOver(ignoreBreakpoints = true) }
|
||||
SteppingInstructionKind.SmartStepInto -> loop(instruction.arg) { doSmartStepInto() }
|
||||
SteppingInstructionKind.SmartStepIntoByIndex -> doOnBreakpoint { doSmartStepInto(instruction.arg) }
|
||||
SteppingInstructionKind.Resume -> loop(instruction.arg) { resume(this) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int = 0) {
|
||||
this.doSmartStepInto(chooseFromList, false)
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.printContext() {
|
||||
runReadAction {
|
||||
if (this.frameProxy == null) {
|
||||
return@runReadAction println("Context thread is null", ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
|
||||
val sourcePosition = PositionUtil.getSourcePosition(this)
|
||||
println(renderSourcePosition(sourcePosition), ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SuspendContextImpl.doSmartStepInto(chooseFromList: Int, ignoreFilters: Boolean) {
|
||||
val filters = createSmartStepIntoFilters()
|
||||
if (chooseFromList == 0) {
|
||||
filters.forEach {
|
||||
dp.managerThread!!.schedule(dp.createStepIntoCommand(this, ignoreFilters, it))
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
dp.managerThread!!.schedule(dp.createStepIntoCommand(this, ignoreFilters, filters[chooseFromList - 1]))
|
||||
} catch (e: IndexOutOfBoundsException) {
|
||||
val elementText = runReadAction { debuggerContext.sourcePosition.elementAt.getElementTextWithContext() }
|
||||
throw AssertionError("Couldn't find smart step into command at: \n$elementText", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSmartStepIntoFilters() = runReadAction {
|
||||
val position = debuggerContext.sourcePosition
|
||||
|
||||
val stepTargets = KotlinSmartStepIntoHandler().findSmartStepTargets(position)
|
||||
stepTargets.mapNotNull { stepTarget ->
|
||||
when (stepTarget) {
|
||||
is KotlinLambdaSmartStepTarget ->
|
||||
KotlinLambdaMethodFilter(
|
||||
stepTarget.getLambda(), stepTarget.getCallingExpressionLines()!!, stepTarget.isInline, stepTarget.isSuspend
|
||||
)
|
||||
is KotlinMethodSmartStepTarget ->
|
||||
KotlinBasicStepMethodFilter(
|
||||
stepTarget.declaration?.createSmartPointer(),
|
||||
stepTarget.isInvoke,
|
||||
stepTarget.targetMethodName,
|
||||
stepTarget.getCallingExpressionLines()!!
|
||||
)
|
||||
is MethodSmartStepTarget -> BasicStepMethodFilter(stepTarget.method, stepTarget.getCallingExpressionLines())
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1262
File diff suppressed because it is too large
Load Diff
+1260
File diff suppressed because it is too large
Load Diff
+191
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.idea.debugger.test
|
||||
|
||||
import com.intellij.util.PathUtil
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.sun.jdi.ThreadReference
|
||||
import com.sun.jdi.VirtualMachine
|
||||
import com.sun.tools.jdi.SocketAttachingConnector
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||
import org.jetbrains.kotlin.test.ConfigurationKind
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.Socket
|
||||
import java.nio.file.Files
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
abstract class LowLevelDebuggerTestBase : CodegenTestCase() {
|
||||
private companion object {
|
||||
private const val DEBUG_ADDRESS = "127.0.0.1"
|
||||
private const val DEBUG_PORT = 5115
|
||||
}
|
||||
|
||||
override fun doMultiFileTest(wholeFile: File, files: List<TestFile>) {
|
||||
createEnvironmentWithMockJdkAndIdeaAnnotations(ConfigurationKind.ALL, *listOfNotNull(writeJavaFiles(files)).toTypedArray())
|
||||
|
||||
val options = wholeFile.readLines()
|
||||
.asSequence()
|
||||
.filter { it.matches("^// ?[\\w_]+(:.*)?$".toRegex()) }
|
||||
.map { it.drop(2).trim() }
|
||||
.filter { !it.startsWith("FILE:") }
|
||||
.toSet()
|
||||
|
||||
val skipLoadingClasses = skipLoadingClasses(options)
|
||||
|
||||
loadMultiFiles(files)
|
||||
val classBuilderFactory = OriginCollectingClassBuilderFactory(ClassBuilderMode.FULL)
|
||||
val generationState = GenerationUtils.compileFiles(myFiles.psiFiles, myEnvironment, classBuilderFactory)
|
||||
classFileFactory = generationState.factory
|
||||
|
||||
val tempDirForTest = Files.createTempDirectory("debuggerTest").toFile()
|
||||
|
||||
try {
|
||||
val classesDir = File(tempDirForTest, "classes").apply {
|
||||
writeMainClass(this)
|
||||
for (classFile in classFileFactory.getClassFiles()) {
|
||||
File(this, classFile.relativePath).mkdirAndWriteBytes(classFile.asByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
val process = startDebuggeeProcess(classesDir, skipLoadingClasses)
|
||||
waitUntil { isPortOpen() }
|
||||
|
||||
val virtualMachine = attachDebugger()
|
||||
|
||||
try {
|
||||
val mainThread = virtualMachine.allThreads().single { it.name() == "main" }
|
||||
waitUntil { areCompiledClassesLoaded(mainThread, classFileFactory, skipLoadingClasses) }
|
||||
doTest(options, mainThread, classBuilderFactory, classFileFactory, generationState)
|
||||
} finally {
|
||||
virtualMachine.exit(0)
|
||||
process.destroy()
|
||||
}
|
||||
} finally {
|
||||
tempDirForTest.deleteRecursively()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun doTest(
|
||||
options: Set<String>,
|
||||
mainThread: ThreadReference,
|
||||
factory: OriginCollectingClassBuilderFactory,
|
||||
classFileFactory: ClassFileFactory,
|
||||
state: GenerationState
|
||||
)
|
||||
|
||||
private fun isPortOpen(): Boolean {
|
||||
return try {
|
||||
Socket(DEBUG_ADDRESS, DEBUG_PORT).close()
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun areCompiledClassesLoaded(
|
||||
mainThread: ThreadReference,
|
||||
classFileFactory: ClassFileFactory,
|
||||
skipLoadingClasses: Set<String>
|
||||
): Boolean {
|
||||
|
||||
for (outputFile in classFileFactory.getClassFiles()) {
|
||||
val fqName = outputFile.internalName.replace('/', '.')
|
||||
if (fqName in skipLoadingClasses) {
|
||||
continue
|
||||
}
|
||||
|
||||
mainThread.virtualMachine().classesByName(fqName).firstOrNull() ?: return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
protected open fun skipLoadingClasses(options: Set<String>): Set<String> {
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
private fun startDebuggeeProcess(classesDir: File, skipLoadingClasses: Set<String>): Process {
|
||||
val classesToLoad = this.classFileFactory.getClassFiles()
|
||||
.map { it.qualifiedName }
|
||||
.filter { it !in skipLoadingClasses }
|
||||
.joinToString(",")
|
||||
|
||||
val classpath = listOf(
|
||||
classesDir.absolutePath,
|
||||
PathUtil.getJarPathForClass(Delegates::class.java) // Add Kotlin runtime JAR
|
||||
)
|
||||
|
||||
val command = arrayOf(
|
||||
findJavaExecutable().absolutePath,
|
||||
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$DEBUG_PORT",
|
||||
"-ea",
|
||||
"-classpath", classpath.joinToString(File.pathSeparator),
|
||||
"-D${DebuggerMain.CLASSES_TO_LOAD}=$classesToLoad",
|
||||
DebuggerMain::class.java.name
|
||||
)
|
||||
|
||||
return ProcessBuilder(*command).inheritIO().start()
|
||||
}
|
||||
|
||||
private fun attachDebugger(): VirtualMachine {
|
||||
val connector = SocketAttachingConnector()
|
||||
return connector.attach(connector.defaultArguments().toMutableMap().apply {
|
||||
getValue("port").setValue("$DEBUG_PORT")
|
||||
getValue("hostname").setValue(DEBUG_ADDRESS)
|
||||
})
|
||||
}
|
||||
|
||||
private fun findJavaExecutable(): File {
|
||||
val javaBin = File(SystemProperties.getJavaHome(), "bin")
|
||||
return File(javaBin, "java.exe").takeIf { it.exists() }
|
||||
?: File(javaBin, "java").also { assert(it.exists()) }
|
||||
}
|
||||
|
||||
private fun writeMainClass(classesDir: File) {
|
||||
val mainClassResourceName = DebuggerMain::class.java.name.replace('.', '/') + ".class"
|
||||
val resource = javaClass.classLoader.getResource(mainClassResourceName)
|
||||
?: error("Resource not found: $mainClassResourceName")
|
||||
|
||||
val mainClassBytes = resource.readBytes()
|
||||
File(classesDir, mainClassResourceName).mkdirAndWriteBytes(mainClassBytes)
|
||||
}
|
||||
|
||||
internal val OutputFile.internalName
|
||||
get() = relativePath.substringBeforeLast(".class")
|
||||
|
||||
internal val OutputFile.qualifiedName
|
||||
get() = internalName.replace('/', '.')
|
||||
}
|
||||
|
||||
private fun File.mkdirAndWriteBytes(array: ByteArray) {
|
||||
parentFile.mkdirs()
|
||||
writeBytes(array)
|
||||
}
|
||||
|
||||
private fun waitUntil(condition: () -> Boolean) {
|
||||
while (!condition()) {
|
||||
Thread.sleep(30)
|
||||
}
|
||||
}
|
||||
|
||||
private object DebuggerMain {
|
||||
const val CLASSES_TO_LOAD = "classes.to.load"
|
||||
|
||||
@JvmField
|
||||
val lock = Any()
|
||||
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
System.getProperty(CLASSES_TO_LOAD).split(',').forEach { Class.forName(it) }
|
||||
synchronized(lock) {
|
||||
// Wait until debugger is attached
|
||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
||||
(lock as Object).wait()
|
||||
}
|
||||
}
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class PositionManagerTestGenerated extends AbstractPositionManagerTest {
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/positionManager")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class SingleFile extends AbstractPositionManagerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSingleFile() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/positionManager"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, false);
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousFunction.kt")
|
||||
public void testAnonymousFunction() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/anonymousFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("anonymousNamedFunction.kt")
|
||||
public void testAnonymousNamedFunction() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/anonymousNamedFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("class.kt")
|
||||
public void testClass() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/class.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classObject.kt")
|
||||
public void testClassObject() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/classObject.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("enum.kt")
|
||||
public void testEnum() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/enum.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFunction.kt")
|
||||
public void testExtensionFunction() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/extensionFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteral.kt")
|
||||
public void testFunctionLiteral() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/functionLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteralInVal.kt")
|
||||
public void testFunctionLiteralInVal() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/functionLiteralInVal.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerClass.kt")
|
||||
public void testInnerClass() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/innerClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("JvmNameAnnotation.kt")
|
||||
public void testJvmNameAnnotation() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/JvmNameAnnotation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localFunction.kt")
|
||||
public void testLocalFunction() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/localFunction.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectDeclaration.kt")
|
||||
public void testObjectDeclaration() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/objectDeclaration.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectExpression.kt")
|
||||
public void testObjectExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/objectExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("package.kt")
|
||||
public void testPackage() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/package.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyAccessor.kt")
|
||||
public void testPropertyAccessor() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/propertyAccessor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyInitializer.kt")
|
||||
public void testPropertyInitializer() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/propertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("topLevelPropertyInitializer.kt")
|
||||
public void testTopLevelPropertyInitializer() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/topLevelPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("trait.kt")
|
||||
public void testTrait() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/trait.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("twoClasses.kt")
|
||||
public void testTwoClasses() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/twoClasses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("_DefaultPackage.kt")
|
||||
public void test_DefaultPackage() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/_DefaultPackage.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/positionManager")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class MultiFile extends AbstractPositionManagerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMultiFile() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/positionManager"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, false);
|
||||
}
|
||||
|
||||
@TestMetadata("multiFilePackage")
|
||||
public void testMultiFilePackage() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/multiFilePackage/");
|
||||
}
|
||||
|
||||
@TestMetadata("multiFileSameName")
|
||||
public void testMultiFileSameName() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/positionManager/multiFileSameName/");
|
||||
}
|
||||
}
|
||||
}
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class SelectExpressionForDebuggerTestGenerated extends AbstractSelectExpressionForDebuggerTest {
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class SelectExpression extends AbstractSelectExpressionForDebuggerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSelectExpression() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, false);
|
||||
}
|
||||
|
||||
@TestMetadata("annotation.kt")
|
||||
public void testAnnotation() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/annotation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayExpression.kt")
|
||||
public void testArrayExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/arrayExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression.kt")
|
||||
public void testBinaryExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/binaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call.kt")
|
||||
public void testCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/call.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companionObjectCall.kt")
|
||||
public void testCompanionObjectCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/companionObjectCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("companionObjectCall2.kt")
|
||||
public void testCompanionObjectCall2() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/companionObjectCall2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("expressionInPropertyInitializer.kt")
|
||||
public void testExpressionInPropertyInitializer() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/expressionInPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFun.kt")
|
||||
public void testExtensionFun() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/extensionFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("firstCallInChain.kt")
|
||||
public void testFirstCallInChain() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/firstCallInChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("fullyQualified.kt")
|
||||
public void testFullyQualified() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/fullyQualified.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funArgument.kt")
|
||||
public void testFunArgument() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/funArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteral.kt")
|
||||
public void testFunctionLiteral() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/functionLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("getConvention.kt")
|
||||
public void testGetConvention() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/getConvention.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("imports.kt")
|
||||
public void testImports() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/imports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infixCall.kt")
|
||||
public void testInfixCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/infixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infixCallArgument.kt")
|
||||
public void testInfixCallArgument() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/infixCallArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isExpression.kt")
|
||||
public void testIsExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/isExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("javaStaticMehtodCall.kt")
|
||||
public void testJavaStaticMehtodCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/javaStaticMehtodCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("keyword.kt")
|
||||
public void testKeyword() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/keyword.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("modifier.kt")
|
||||
public void testModifier() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/modifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nameArgument.kt")
|
||||
public void testNameArgument() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/nameArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("objectMethodCall.kt")
|
||||
public void testObjectMethodCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/objectMethodCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("package.kt")
|
||||
public void testPackage() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/package.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("param.kt")
|
||||
public void testParam() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/param.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyCall.kt")
|
||||
public void testPropertyCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/propertyCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyDeclaration.kt")
|
||||
public void testPropertyDeclaration() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/propertyDeclaration.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionProperty.kt")
|
||||
public void testQualifiedExpressionProperty() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/qualifiedExpressionProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionReceiver.kt")
|
||||
public void testQualifiedExpressionReceiver() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/qualifiedExpressionReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionSelector.kt")
|
||||
public void testQualifiedExpressionSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/qualifiedExpressionSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("super.kt")
|
||||
public void testSuper() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/super.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superSelector.kt")
|
||||
public void testSuperSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/superSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("this.kt")
|
||||
public void testThis() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/this.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisSelector.kt")
|
||||
public void testThisSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/thisSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisWithLabel.kt")
|
||||
public void testThisWithLabel() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/thisWithLabel.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unaryExpression.kt")
|
||||
public void testUnaryExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/unaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("userType.kt")
|
||||
public void testUserType() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/userType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("userTypeGeneric.kt")
|
||||
public void testUserTypeGeneric() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/userTypeGeneric.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("userTypeQualified.kt")
|
||||
public void testUserTypeQualified() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/userTypeQualified.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class DisallowMethodCalls extends AbstractSelectExpressionForDebuggerTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestWoMethodCalls, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDisallowMethodCalls() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("binaryExpression.kt")
|
||||
public void testBinaryExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/binaryExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("call.kt")
|
||||
public void testCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/call.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("expressionInPropertyInitializer.kt")
|
||||
public void testExpressionInPropertyInitializer() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/expressionInPropertyInitializer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionFun.kt")
|
||||
public void testExtensionFun() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/extensionFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funArgument.kt")
|
||||
public void testFunArgument() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/funArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteral.kt")
|
||||
public void testFunctionLiteral() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/functionLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("getConvention.kt")
|
||||
public void testGetConvention() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/getConvention.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infixCall.kt")
|
||||
public void testInfixCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/infixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infixCallArgument.kt")
|
||||
public void testInfixCallArgument() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/infixCallArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("isExpression.kt")
|
||||
public void testIsExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/isExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyCall.kt")
|
||||
public void testPropertyCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/propertyCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionProperty.kt")
|
||||
public void testQualifiedExpressionProperty() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/qualifiedExpressionProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionReceiver.kt")
|
||||
public void testQualifiedExpressionReceiver() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/qualifiedExpressionReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("qualifiedExpressionSelector.kt")
|
||||
public void testQualifiedExpressionSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/qualifiedExpressionSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("super.kt")
|
||||
public void testSuper() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/super.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("superSelector.kt")
|
||||
public void testSuperSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/superSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("this.kt")
|
||||
public void testThis() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/this.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisSelector.kt")
|
||||
public void testThisSelector() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/thisSelector.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("thisWithLabel.kt")
|
||||
public void testThisWithLabel() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/thisWithLabel.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unaryExpression.kt")
|
||||
public void testUnaryExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/selectExpression/disallowMethodCalls/unaryExpression.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.idea.debugger.test;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class SmartStepIntoTestGenerated extends AbstractSmartStepIntoTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSmartStepInto() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("annotation.kt")
|
||||
public void testAnnotation() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/annotation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("arrayAccess.kt")
|
||||
public void testArrayAccess() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/arrayAccess.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("callChain.kt")
|
||||
public void testCallChain() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/callChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("constructor.kt")
|
||||
public void testConstructor() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/constructor.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("conventionMethod.kt")
|
||||
public void testConventionMethod() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/conventionMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedPropertyGetter.kt")
|
||||
public void testDelegatedPropertyGetter() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/delegatedPropertyGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("doWhile.kt")
|
||||
public void testDoWhile() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/doWhile.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dotQualified.kt")
|
||||
public void testDotQualified() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/dotQualified.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dotQualifiedInParam.kt")
|
||||
public void testDotQualifiedInParam() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/dotQualifiedInParam.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("empty.kt")
|
||||
public void testEmpty() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/empty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("for.kt")
|
||||
public void testFor() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/for.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funLiteral.kt")
|
||||
public void testFunLiteral() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/funLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("funWithExpressionBody.kt")
|
||||
public void testFunWithExpressionBody() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/funWithExpressionBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("if.kt")
|
||||
public void testIf() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/if.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("infixCall.kt")
|
||||
public void testInfixCall() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/infixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlinedFunLiteral.kt")
|
||||
public void testInlinedFunLiteral() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/inlinedFunLiteral.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlinedFunctionalExpression.kt")
|
||||
public void testInlinedFunctionalExpression() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/inlinedFunctionalExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invoke.kt")
|
||||
public void testInvoke() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/invoke.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("libraryFun.kt")
|
||||
public void testLibraryFun() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/libraryFun.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multiline.kt")
|
||||
public void testMultiline() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/multiline.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multilineCallChain.kt")
|
||||
public void testMultilineCallChain() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/multilineCallChain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("object.kt")
|
||||
public void testObject() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/object.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("param.kt")
|
||||
public void testParam() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/param.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parantesized.kt")
|
||||
public void testParantesized() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/parantesized.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyGetter.kt")
|
||||
public void testPropertyGetter() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/propertyGetter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("renderer.kt")
|
||||
public void testRenderer() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/renderer.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/simple.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringTemplate.kt")
|
||||
public void testStringTemplate() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/stringTemplate.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("unary.kt")
|
||||
public void testUnary() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/unary.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("when.kt")
|
||||
public void testWhen() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/when.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("while.kt")
|
||||
public void testWhile() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/smartStepInto/while.kt");
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.mock
|
||||
|
||||
import com.sun.jdi.*
|
||||
|
||||
class MockLocation(private val declaringType: ReferenceType, private val sourceName: String, private val lineNumber: Int) : Location {
|
||||
override fun declaringType() = declaringType
|
||||
override fun sourceName() = sourceName
|
||||
override fun lineNumber() = lineNumber
|
||||
override fun method() = MockMethod()
|
||||
override fun codeIndex() = throw UnsupportedOperationException()
|
||||
override fun sourceName(s: String) = throw UnsupportedOperationException()
|
||||
override fun sourcePath() = throw AbsentInformationException()
|
||||
override fun sourcePath(s: String) = throw AbsentInformationException()
|
||||
override fun lineNumber(s: String) = throw UnsupportedOperationException()
|
||||
override fun compareTo(other: Location) = throw UnsupportedOperationException()
|
||||
override fun virtualMachine() = throw UnsupportedOperationException()
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.mock
|
||||
|
||||
import com.sun.jdi.Location
|
||||
import com.sun.jdi.Method
|
||||
|
||||
class MockMethod : Method {
|
||||
override fun name() = ""
|
||||
override fun allLineLocations() = emptyList<Location>()
|
||||
|
||||
override fun isSynthetic() = throw UnsupportedOperationException()
|
||||
override fun isFinal() = throw UnsupportedOperationException()
|
||||
override fun isStatic() = throw UnsupportedOperationException()
|
||||
override fun declaringType() = throw UnsupportedOperationException()
|
||||
override fun signature() = throw UnsupportedOperationException()
|
||||
override fun genericSignature() = throw UnsupportedOperationException()
|
||||
override fun variables() = throw UnsupportedOperationException()
|
||||
override fun variablesByName(name: String?) = throw UnsupportedOperationException()
|
||||
override fun bytecodes() = throw UnsupportedOperationException()
|
||||
override fun isBridge() = throw UnsupportedOperationException()
|
||||
override fun isObsolete() = throw UnsupportedOperationException()
|
||||
override fun isSynchronized() = throw UnsupportedOperationException()
|
||||
override fun allLineLocations(stratum: String?, sourceName: String?) = throw UnsupportedOperationException()
|
||||
override fun isNative() = throw UnsupportedOperationException()
|
||||
override fun locationOfCodeIndex(codeIndex: Long) = throw UnsupportedOperationException()
|
||||
override fun arguments() = throw UnsupportedOperationException()
|
||||
override fun isAbstract() = throw UnsupportedOperationException()
|
||||
override fun isVarArgs() = throw UnsupportedOperationException()
|
||||
override fun returnTypeName() = throw UnsupportedOperationException()
|
||||
override fun argumentTypes( ) = throw UnsupportedOperationException()
|
||||
override fun isConstructor() = throw UnsupportedOperationException()
|
||||
override fun locationsOfLine(lineNumber: Int) = throw UnsupportedOperationException()
|
||||
override fun locationsOfLine(stratum: String?, sourceName: String?, lineNumber: Int) = throw UnsupportedOperationException()
|
||||
override fun argumentTypeNames() = throw UnsupportedOperationException()
|
||||
override fun returnType() = throw UnsupportedOperationException()
|
||||
override fun isStaticInitializer() = throw UnsupportedOperationException()
|
||||
override fun location() = throw UnsupportedOperationException()
|
||||
override fun compareTo(other: Method?) = throw UnsupportedOperationException()
|
||||
override fun isPackagePrivate() = throw UnsupportedOperationException()
|
||||
override fun isPrivate() = throw UnsupportedOperationException()
|
||||
override fun isProtected() = throw UnsupportedOperationException()
|
||||
override fun isPublic() = throw UnsupportedOperationException()
|
||||
override fun modifiers() = throw UnsupportedOperationException()
|
||||
override fun virtualMachine() = throw UnsupportedOperationException()
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.mock
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
class MockSourcePosition(
|
||||
private val myFile: PsiFile? = null,
|
||||
private val myElementAt: PsiElement? = null,
|
||||
private val myLine: Int? = null,
|
||||
private val myOffset: Int? = null,
|
||||
private val myEditor: Editor? = null
|
||||
) : SourcePosition() {
|
||||
override fun getFile(): PsiFile {
|
||||
return myFile ?: throw UnsupportedOperationException("Parameter file isn't set for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun getElementAt(): PsiElement {
|
||||
return myElementAt ?: throw UnsupportedOperationException("Parameter elementAt isn't set for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun getLine(): Int {
|
||||
return myLine ?: throw UnsupportedOperationException("Parameter line isn't set for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun getOffset(): Int {
|
||||
return myOffset ?: throw UnsupportedOperationException("Parameter offset isn't set for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun openEditor(requestFocus: Boolean): Editor {
|
||||
return myEditor ?: throw UnsupportedOperationException("Parameter editor isn't set for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun navigate(requestFocus: Boolean) {
|
||||
throw UnsupportedOperationException("navigate() isn't supported for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun canNavigate(): Boolean {
|
||||
throw UnsupportedOperationException("canNavigate() isn't supported for MockSourcePosition")
|
||||
}
|
||||
|
||||
override fun canNavigateToSource(): Boolean {
|
||||
throw UnsupportedOperationException("canNavigateToSource() isn't supported for MockSourcePosition")
|
||||
}
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.mock;
|
||||
|
||||
import com.sun.jdi.*;
|
||||
import com.sun.jdi.event.EventQueue;
|
||||
import com.sun.jdi.request.EventRequestManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MockVirtualMachine implements VirtualMachine {
|
||||
@Override
|
||||
public List<ThreadGroupReference> topLevelThreadGroups() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String version() {
|
||||
return "1.6";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<ReferenceType> classesByName(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReferenceType> allClasses() {
|
||||
// Can't throw since this method is invoked during VirtualMachineProxy initialization
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void redefineClasses(Map<? extends ReferenceType, byte[]> map) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThreadReference> allThreads() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventQueue eventQueue() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventRequestManager eventRequestManager() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BooleanValue mirrorOf(boolean b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteValue mirrorOf(byte b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharValue mirrorOf(char c) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortValue mirrorOf(short i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntegerValue mirrorOf(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongValue mirrorOf(long l) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatValue mirrorOf(float v) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleValue mirrorOf(double v) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringReference mirrorOf(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoidValue mirrorOfVoid() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Process process() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultStratum(String s) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultStratum() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "JVM mock";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebugTraceMode(int i) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWatchFieldModification() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canWatchFieldAccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetBytecodes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetSyntheticAttribute() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetOwnedMonitorInfo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetCurrentContendedMonitor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetMonitorInfo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseInstanceFilters() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRedefineClasses() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddMethod() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUnrestrictedlyRedefineClasses() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPopFrames() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetSourceDebugExtension() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRequestVMDeathEvent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetMethodReturnValues() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetInstanceInfo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUseSourceNameFilters() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canForceEarlyReturn() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeModified() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRequestMonitorEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetMonitorFrameInfo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetClassFileVersion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canGetConstantPool() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] instanceCounts(List<? extends ReferenceType> types) {
|
||||
return new long[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualMachine virtualMachine() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.mock
|
||||
|
||||
import com.sun.jdi.*
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFile
|
||||
import org.jetbrains.kotlin.backend.common.output.OutputFileCollection
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LineNumberNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
|
||||
class SmartMockReferenceTypeContext(outputFiles: List<OutputFile>) {
|
||||
constructor(outputFiles: OutputFileCollection) : this(outputFiles.asList())
|
||||
|
||||
val virtualMachine = MockVirtualMachine()
|
||||
|
||||
val classes = outputFiles
|
||||
.filter { it.relativePath.endsWith(".class") }
|
||||
.map { it.readClass() }
|
||||
|
||||
private val referenceTypes: List<ReferenceType> by lazy {
|
||||
classes.map { SmartMockReferenceType(it, this) }
|
||||
}
|
||||
|
||||
val referenceTypesByName by lazy {
|
||||
referenceTypes.map { Pair(it.name(), it) }.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
private fun OutputFile.readClass(): ClassNode {
|
||||
val classNode = ClassNode()
|
||||
ClassReader(asByteArray()).accept(classNode, ClassReader.EXPAND_FRAMES)
|
||||
return classNode
|
||||
}
|
||||
|
||||
class SmartMockReferenceType(val classNode: ClassNode, private val context: SmartMockReferenceTypeContext) : ReferenceType {
|
||||
override fun instances(maxInstances: Long) = emptyList<ObjectReference>()
|
||||
override fun isPublic() = (classNode.access and Opcodes.ACC_PUBLIC) != 0
|
||||
override fun classLoader() = null
|
||||
override fun sourceName(): String? = classNode.sourceFile
|
||||
override fun defaultStratum() = "Java"
|
||||
override fun isStatic() = (classNode.access and Opcodes.ACC_STATIC) != 0
|
||||
override fun modifiers() = classNode.access
|
||||
override fun isProtected() = (classNode.access and Opcodes.ACC_PROTECTED) != 0
|
||||
override fun isFinal() = (classNode.access and Opcodes.ACC_FINAL) != 0
|
||||
override fun allLineLocations() = methodsCached.flatMap { it.allLineLocations() }
|
||||
override fun genericSignature(): String? = classNode.signature
|
||||
override fun isAbstract() = (classNode.access and Opcodes.ACC_ABSTRACT) != 0
|
||||
override fun isPrepared() = true
|
||||
override fun name() = classNode.name.replace('/', '.')
|
||||
override fun isInitialized() = true
|
||||
override fun sourcePaths(stratum: String) = listOf(classNode.sourceFile)
|
||||
override fun failedToInitialize() = false
|
||||
override fun virtualMachine() = context.virtualMachine
|
||||
override fun isPrivate() = (classNode.access and Opcodes.ACC_PRIVATE) != 0
|
||||
override fun signature(): String? = classNode.signature
|
||||
override fun sourceNames(stratum: String) = listOf(classNode.sourceFile)
|
||||
override fun availableStrata() = emptyList<String>()
|
||||
|
||||
private val methodsCached by lazy { classNode.methods.map { MockMethod(it, this) } }
|
||||
override fun methods() = methodsCached
|
||||
|
||||
override fun nestedTypes(): List<ReferenceType> {
|
||||
val fromInnerClasses = classNode.innerClasses
|
||||
.filter { it.outerName == classNode.name }
|
||||
.mapNotNull { context.classes.find { c -> it.name == c.name } }
|
||||
|
||||
val fromOuterClasses = context.classes.filter { it.outerClass == classNode.name }
|
||||
|
||||
return (fromInnerClasses + fromOuterClasses).distinctBy { it.name }.map { SmartMockReferenceType(it, context) }
|
||||
}
|
||||
|
||||
override fun isPackagePrivate(): Boolean {
|
||||
return ((classNode.access and Opcodes.ACC_PUBLIC) == 0
|
||||
&& (classNode.access and Opcodes.ACC_PROTECTED) == 0
|
||||
&& (classNode.access and Opcodes.ACC_PRIVATE) == 0)
|
||||
}
|
||||
|
||||
override fun isVerified() = true
|
||||
|
||||
override fun fields() = TODO()
|
||||
override fun allFields() = TODO()
|
||||
override fun fieldByName(fieldName: String) = TODO()
|
||||
override fun getValue(p0: Field?) = TODO()
|
||||
override fun visibleFields() = TODO()
|
||||
override fun allLineLocations(stratum: String, sourceName: String) = TODO()
|
||||
override fun majorVersion() = TODO()
|
||||
override fun constantPoolCount() = TODO()
|
||||
override fun constantPool() = TODO()
|
||||
override fun compareTo(other: ReferenceType?) = TODO()
|
||||
override fun sourceDebugExtension() = TODO()
|
||||
override fun visibleMethods() = TODO()
|
||||
override fun locationsOfLine(lineNumber: Int) = TODO()
|
||||
override fun locationsOfLine(stratum: String, sourceName: String, lineNumber: Int) = TODO()
|
||||
override fun getValues(p0: MutableList<out Field>?) = TODO()
|
||||
override fun minorVersion() = TODO()
|
||||
override fun classObject() = TODO()
|
||||
override fun methodsByName(p0: String?) = TODO()
|
||||
override fun methodsByName(p0: String?, p1: String?) = TODO()
|
||||
override fun allMethods() = TODO()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other?.javaClass != javaClass) return false
|
||||
|
||||
other as SmartMockReferenceType
|
||||
|
||||
return classNode.name == other.classNode.name
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return classNode.name.hashCode()
|
||||
}
|
||||
|
||||
class MockMethod(private val methodNode: MethodNode, val containingClass: SmartMockReferenceType) : Method {
|
||||
override fun virtualMachine() = containingClass.context.virtualMachine
|
||||
|
||||
override fun modifiers() = methodNode.access
|
||||
override fun isStaticInitializer() = methodNode.name == "<clinit>"
|
||||
override fun isPublic() = (methodNode.access and Opcodes.ACC_PUBLIC) != 0
|
||||
override fun isNative() = (methodNode.access and Opcodes.ACC_NATIVE) != 0
|
||||
override fun isStatic() = (methodNode.access and Opcodes.ACC_STATIC) != 0
|
||||
override fun isBridge() = (methodNode.access and Opcodes.ACC_BRIDGE) != 0
|
||||
override fun isProtected() = (methodNode.access and Opcodes.ACC_PROTECTED) != 0
|
||||
override fun isFinal() = (methodNode.access and Opcodes.ACC_FINAL) != 0
|
||||
override fun isAbstract() = (methodNode.access and Opcodes.ACC_ABSTRACT) != 0
|
||||
override fun isSynthetic() = (methodNode.access and Opcodes.ACC_SYNTHETIC) != 0
|
||||
override fun isConstructor() = methodNode.name == "<init>"
|
||||
override fun isPrivate() = (methodNode.access and Opcodes.ACC_PRIVATE) != 0
|
||||
|
||||
override fun isPackagePrivate(): Boolean {
|
||||
return ((methodNode.access and Opcodes.ACC_PUBLIC) == 0
|
||||
&& (methodNode.access and Opcodes.ACC_PROTECTED) == 0
|
||||
&& (methodNode.access and Opcodes.ACC_PRIVATE) == 0)
|
||||
}
|
||||
|
||||
override fun declaringType() = containingClass
|
||||
override fun name(): String? = methodNode.name
|
||||
override fun signature(): String? = methodNode.signature
|
||||
|
||||
override fun location(): Location? {
|
||||
val instructionList = methodNode.instructions ?: return null
|
||||
var current = instructionList.first
|
||||
while (current != null) {
|
||||
if (current is LineNumberNode) {
|
||||
return MockLocation(this, current.line)
|
||||
}
|
||||
current = current.next
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun allLineLocations(): List<Location> {
|
||||
val instructionList = methodNode.instructions ?: return emptyList()
|
||||
var current = instructionList.first
|
||||
val locations = mutableListOf<Location>()
|
||||
while (current != null) {
|
||||
if (current is LineNumberNode) {
|
||||
locations += MockLocation(this, current.line)
|
||||
}
|
||||
current = current.next
|
||||
}
|
||||
return locations
|
||||
}
|
||||
|
||||
override fun argumentTypeNames() = TODO()
|
||||
override fun arguments() = TODO()
|
||||
override fun allLineLocations(p0: String?, p1: String?) = TODO()
|
||||
override fun genericSignature() = TODO()
|
||||
override fun returnType() = TODO()
|
||||
override fun compareTo(other: Method?) = TODO()
|
||||
override fun isObsolete() = false
|
||||
override fun variablesByName(p0: String?) = TODO()
|
||||
override fun argumentTypes() = TODO()
|
||||
override fun locationOfCodeIndex(p0: Long) = TODO()
|
||||
override fun bytecodes() = TODO()
|
||||
override fun returnTypeName() = TODO()
|
||||
override fun locationsOfLine(p0: Int) = TODO()
|
||||
override fun locationsOfLine(p0: String?, p1: String?, p2: Int) = TODO()
|
||||
override fun variables() = TODO()
|
||||
override fun isVarArgs() = TODO()
|
||||
override fun isSynchronized() = TODO()
|
||||
}
|
||||
|
||||
private class MockLocation(val method: MockMethod, val line: Int) : Location {
|
||||
override fun virtualMachine() = method.containingClass.context.virtualMachine
|
||||
override fun sourceName() = method.containingClass.sourceName()
|
||||
override fun lineNumber() = line
|
||||
override fun sourcePath() = sourceName()
|
||||
override fun declaringType() = method.containingClass
|
||||
override fun method() = method
|
||||
|
||||
override fun sourceName(stratum: String) = TODO()
|
||||
override fun codeIndex() = TODO()
|
||||
override fun lineNumber(stratum: String) = TODO()
|
||||
override fun sourcePath(stratum: String) = TODO()
|
||||
|
||||
override fun compareTo(other: Location?) = TODO()
|
||||
}
|
||||
}
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("ClassName")
|
||||
|
||||
package org.jetbrains.kotlin.idea.debugger.test.preference
|
||||
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.jvm.javaType
|
||||
|
||||
class DebuggerPreferenceKey<T : Any>(val name: String, val type: Class<*>, val defaultValue: T)
|
||||
|
||||
private inline fun <reified T : Any> debuggerPreferenceKey(defaultValue: T): ReadOnlyProperty<Any, DebuggerPreferenceKey<T>> {
|
||||
val clazz = T::class.java
|
||||
|
||||
return object : ReadOnlyProperty<Any, DebuggerPreferenceKey<T>> {
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>) = DebuggerPreferenceKey(property.name, clazz, defaultValue)
|
||||
}
|
||||
}
|
||||
|
||||
internal object DebuggerPreferenceKeys {
|
||||
val SKIP_SYNTHETIC_METHODS by debuggerPreferenceKey(true)
|
||||
val SKIP_CONSTRUCTORS: DebuggerPreferenceKey<Boolean> by debuggerPreferenceKey(false)
|
||||
val SKIP_CLASSLOADERS by debuggerPreferenceKey(true)
|
||||
val TRACING_FILTERS_ENABLED by debuggerPreferenceKey(true)
|
||||
val SKIP_GETTERS by debuggerPreferenceKey(false)
|
||||
|
||||
val DISABLE_KOTLIN_INTERNAL_CLASSES by debuggerPreferenceKey(false)
|
||||
val RENDER_DELEGATED_PROPERTIES by debuggerPreferenceKey(false)
|
||||
val IS_FILTER_FOR_STDLIB_ALREADY_ADDED by debuggerPreferenceKey(false)
|
||||
|
||||
val FORCE_RANKING by debuggerPreferenceKey(false)
|
||||
val EMULATE_DEX by debuggerPreferenceKey(false)
|
||||
|
||||
val PRINT_FRAME by debuggerPreferenceKey(false)
|
||||
val SHOW_KOTLIN_VARIABLES by debuggerPreferenceKey(false)
|
||||
val DESCRIPTOR_VIEW_OPTIONS by debuggerPreferenceKey("FULL")
|
||||
|
||||
val ATTACH_LIBRARY by debuggerPreferenceKey(emptyList<String>())
|
||||
|
||||
val SKIP by debuggerPreferenceKey(emptyList<String>())
|
||||
val WATCH_FIELD_ACCESS by debuggerPreferenceKey(true)
|
||||
val WATCH_FIELD_MODIFICATION by debuggerPreferenceKey(true)
|
||||
val WATCH_FIELD_INITIALISATION by debuggerPreferenceKey(false)
|
||||
|
||||
val JVM_TARGET by debuggerPreferenceKey("1.8")
|
||||
|
||||
val values: List<DebuggerPreferenceKey<*>> by lazy {
|
||||
DebuggerPreferenceKeys::class.declaredMemberProperties
|
||||
.filter { (it.returnType.javaType as? ParameterizedType)?.rawType == DebuggerPreferenceKey::class.java }
|
||||
.map { it.get(DebuggerPreferenceKeys) as DebuggerPreferenceKey<*> }
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.preference
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
|
||||
class DebuggerPreferences(val project: Project, fileContents: String) {
|
||||
private val values: Map<String, Any?>
|
||||
|
||||
init {
|
||||
val values = HashMap<String, Any?>()
|
||||
for (key in DebuggerPreferenceKeys.values) {
|
||||
val list = findValues(fileContents, key.name).takeIf { it.isNotEmpty() } ?: continue
|
||||
|
||||
fun errorValue(): Nothing = error("Error value for key ${key.name}")
|
||||
|
||||
val convertedValue: Any = when (key.type) {
|
||||
java.lang.Boolean::class.java -> list.singleOrNull()?.toBoolean() ?: errorValue()
|
||||
String::class.java -> list.singleOrNull() ?: errorValue()
|
||||
java.lang.Integer::class.java -> list.singleOrNull()?.toIntOrNull() ?: errorValue()
|
||||
List::class.java -> list
|
||||
else -> error("Cannot find a converter for type ${key.type}")
|
||||
}
|
||||
values[key.name] = convertedValue
|
||||
}
|
||||
this.values = values
|
||||
}
|
||||
|
||||
private fun findValues(fileContents: String, key: String): List<String> {
|
||||
val list: List<String> = InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContents, "// $key: ")
|
||||
if (list.isNotEmpty()) {
|
||||
return list
|
||||
}
|
||||
|
||||
if (InTextDirectivesUtils.findLinesWithPrefixesRemoved(fileContents, true, false, "// $key").isNotEmpty()) {
|
||||
return listOf("true")
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
operator fun <T : Any> get(key: DebuggerPreferenceKey<T>): T {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return values[key.name] as T? ?: key.defaultValue
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.preference
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
internal abstract class SettingsMutator<T : Any>(val key: DebuggerPreferenceKey<T>) {
|
||||
abstract fun setValue(value: T, project: Project): T
|
||||
|
||||
open fun revertValue(value: T, project: Project) {
|
||||
setValue(value, project)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <T : Any> SettingsMutator<T>.setValue(preferences: DebuggerPreferences): OldValueStorage<T> {
|
||||
val project = preferences.project
|
||||
return OldValueStorage(this, project, setValue(preferences[key], project))
|
||||
}
|
||||
|
||||
internal class OldValueStorage<T : Any>(
|
||||
private val mutator: SettingsMutator<T>,
|
||||
private val project: Project,
|
||||
private val oldValue: T
|
||||
) {
|
||||
fun revertValue() = mutator.revertValue(oldValue, project)
|
||||
}
|
||||
|
||||
internal class OldValuesStorage(private val oldValues: List<OldValueStorage<*>>) {
|
||||
fun revertValues() = oldValues.forEach { it.revertValue() }
|
||||
}
|
||||
|
||||
internal fun List<SettingsMutator<*>>.mutate(preferences: DebuggerPreferences): OldValuesStorage {
|
||||
return OldValuesStorage(map { it.setValue(preferences) })
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.preference
|
||||
|
||||
import com.intellij.debugger.settings.DebuggerSettings
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.idea.compiler.configuration.Kotlin2JvmCompilerArgumentsHolder
|
||||
import org.jetbrains.kotlin.idea.debugger.DebuggerUtils
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinDebuggerSettings
|
||||
import org.jetbrains.kotlin.idea.debugger.ToggleKotlinVariablesState
|
||||
import org.jetbrains.kotlin.idea.debugger.emulateDexDebugInTests
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.DISABLE_KOTLIN_INTERNAL_CLASSES
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.IS_FILTER_FOR_STDLIB_ALREADY_ADDED
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.RENDER_DELEGATED_PROPERTIES
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.SKIP_CONSTRUCTORS
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.SKIP_CLASSLOADERS
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.SKIP_GETTERS
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.SKIP_SYNTHETIC_METHODS
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.TRACING_FILTERS_ENABLED
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys.EMULATE_DEX
|
||||
import kotlin.reflect.KMutableProperty1
|
||||
|
||||
internal val SettingsMutators: List<SettingsMutator<*>> = listOf(
|
||||
DebuggerSettingsMutator(SKIP_SYNTHETIC_METHODS, DebuggerSettings::SKIP_SYNTHETIC_METHODS),
|
||||
DebuggerSettingsMutator(SKIP_CONSTRUCTORS, DebuggerSettings::SKIP_CONSTRUCTORS),
|
||||
DebuggerSettingsMutator(SKIP_CLASSLOADERS, DebuggerSettings::SKIP_CLASSLOADERS),
|
||||
DebuggerSettingsMutator(TRACING_FILTERS_ENABLED, DebuggerSettings::TRACING_FILTERS_ENABLED),
|
||||
DebuggerSettingsMutator(SKIP_GETTERS, DebuggerSettings::SKIP_GETTERS),
|
||||
KotlinSettingsMutator(DISABLE_KOTLIN_INTERNAL_CLASSES, KotlinDebuggerSettings::DEBUG_DISABLE_KOTLIN_INTERNAL_CLASSES),
|
||||
KotlinSettingsMutator(RENDER_DELEGATED_PROPERTIES, KotlinDebuggerSettings::DEBUG_RENDER_DELEGATED_PROPERTIES),
|
||||
KotlinSettingsMutator(IS_FILTER_FOR_STDLIB_ALREADY_ADDED, KotlinDebuggerSettings::DEBUG_IS_FILTER_FOR_STDLIB_ALREADY_ADDED),
|
||||
DexSettingsMutator(EMULATE_DEX),
|
||||
KotlinVariablesModeSettingsMutator,
|
||||
JvmTargetSettingsMutator,
|
||||
ForceRankingSettingsMutator
|
||||
)
|
||||
|
||||
private class DexSettingsMutator(key: DebuggerPreferenceKey<Boolean>): SettingsMutator<Boolean>(key) {
|
||||
override fun setValue(value: Boolean, project: Project): Boolean {
|
||||
val oldValue = emulateDexDebugInTests
|
||||
emulateDexDebugInTests = value
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
|
||||
private class DebuggerSettingsMutator<T : Any>(
|
||||
key: DebuggerPreferenceKey<T>,
|
||||
private val prop: KMutableProperty1<DebuggerSettings, T>
|
||||
) : SettingsMutator<T>(key) {
|
||||
override fun setValue(value: T, project: Project): T {
|
||||
val debuggerSettings = DebuggerSettings.getInstance()
|
||||
val oldValue = prop.get(debuggerSettings)
|
||||
prop.set(debuggerSettings, value)
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinSettingsMutator<T : Any>(
|
||||
key: DebuggerPreferenceKey<T>,
|
||||
private val prop: KMutableProperty1<KotlinDebuggerSettings, T>
|
||||
) : SettingsMutator<T>(key) {
|
||||
override fun setValue(value: T, project: Project): T {
|
||||
val debuggerSettings = KotlinDebuggerSettings.getInstance()
|
||||
val oldValue = prop.get(debuggerSettings)
|
||||
prop.set(debuggerSettings, value)
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
|
||||
private object KotlinVariablesModeSettingsMutator : SettingsMutator<Boolean>(DebuggerPreferenceKeys.SHOW_KOTLIN_VARIABLES) {
|
||||
override fun setValue(value: Boolean, project: Project): Boolean {
|
||||
val service = ToggleKotlinVariablesState.getService()
|
||||
val oldValue = service.kotlinVariableView
|
||||
service.kotlinVariableView = value
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
|
||||
private object JvmTargetSettingsMutator : SettingsMutator<String>(DebuggerPreferenceKeys.JVM_TARGET) {
|
||||
override fun setValue(value: String, project: Project): String {
|
||||
var oldValue: String? = null
|
||||
Kotlin2JvmCompilerArgumentsHolder.getInstance(project).update {
|
||||
oldValue = jvmTarget
|
||||
jvmTarget = value.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
return oldValue ?: ""
|
||||
}
|
||||
}
|
||||
|
||||
private object ForceRankingSettingsMutator : SettingsMutator<Boolean>(DebuggerPreferenceKeys.FORCE_RANKING) {
|
||||
override fun setValue(value: Boolean, project: Project): Boolean {
|
||||
val oldValue = DebuggerUtils.forceRanking
|
||||
DebuggerUtils.forceRanking = value
|
||||
return oldValue
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence
|
||||
|
||||
import com.intellij.debugger.streams.test.StreamChainBuilderTestCase
|
||||
import com.intellij.debugger.streams.wrapper.StreamChain
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable
|
||||
import com.intellij.testFramework.LightPlatformTestCase
|
||||
import com.intellij.testFramework.PsiTestUtil
|
||||
import junit.framework.TestCase
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.idea.caches.project.LibraryModificationTracker
|
||||
import org.jetbrains.kotlin.idea.debugger.test.DEBUGGER_TESTDATA_PATH_BASE
|
||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||
|
||||
abstract class KotlinPsiChainBuilderTestCase(private val relativePath: String) : StreamChainBuilderTestCase() {
|
||||
override fun getTestDataPath(): String = "$DEBUGGER_TESTDATA_PATH_BASE/sequence/psi/$relativeTestPath"
|
||||
|
||||
override fun getFileExtension(): String = ".kt"
|
||||
abstract val kotlinChainBuilder: StreamChainBuilder
|
||||
override fun getChainBuilder(): StreamChainBuilder = kotlinChainBuilder
|
||||
private val stdLibName = "kotlin-stdlib"
|
||||
|
||||
protected abstract fun doTest()
|
||||
|
||||
final override fun getRelativeTestPath(): String = relativePath
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
ApplicationManager.getApplication().runWriteAction {
|
||||
@Suppress("UnstableApiUsage")
|
||||
if (ProjectLibraryTable.getInstance(LightPlatformTestCase.getProject()).getLibraryByName(stdLibName) == null) {
|
||||
val stdLibPath = ForTestCompileRuntime.runtimeJarForTests()
|
||||
PsiTestUtil.addLibrary(
|
||||
testRootDisposable,
|
||||
LightPlatformTestCase.getModule(),
|
||||
stdLibName,
|
||||
stdLibPath.parent,
|
||||
stdLibPath.name
|
||||
)
|
||||
}
|
||||
}
|
||||
LibraryModificationTracker.getInstance(LightPlatformTestCase.getProject()).incModificationCount()
|
||||
}
|
||||
|
||||
|
||||
override fun getProjectJDK(): Sdk {
|
||||
return PluginTestCaseBase.mockJdk9()
|
||||
}
|
||||
|
||||
abstract class Positive(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
|
||||
override fun doTest() {
|
||||
val chains = buildChains()
|
||||
checkChains(chains)
|
||||
}
|
||||
|
||||
private fun checkChains(chains: MutableList<StreamChain>) {
|
||||
TestCase.assertFalse(chains.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Negative(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
|
||||
override fun doTest() {
|
||||
val elementAtCaret = configureAndGetElementAtCaret()
|
||||
TestCase.assertFalse(chainBuilder.isChainExists(elementAtCaret))
|
||||
TestCase.assertTrue(chainBuilder.build(elementAtCaret).isEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.dsl
|
||||
|
||||
import com.intellij.debugger.streams.test.DslTestCase
|
||||
import com.intellij.debugger.streams.trace.dsl.impl.DslImpl
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinCollectionsPeekCallFactory
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinStatementFactory
|
||||
import org.jetbrains.kotlin.idea.debugger.test.DEBUGGER_TESTDATA_PATH_RELATIVE
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class KotlinDslTest : DslTestCase(DslImpl(KotlinStatementFactory(KotlinCollectionsPeekCallFactory()))) {
|
||||
override fun getTestDataPath(): String {
|
||||
return "$DEBUGGER_TESTDATA_PATH_RELATIVE/sequence/dsl"
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec
|
||||
|
||||
import com.intellij.debugger.streams.lib.LibrarySupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.collections.KotlinCollectionSupportProvider
|
||||
|
||||
@Suppress("unused")
|
||||
abstract class AbstractCollectionTraceTestCase : KotlinTraceTestCase() {
|
||||
override val librarySupportProvider: LibrarySupportProvider = KotlinCollectionSupportProvider()
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec
|
||||
|
||||
import com.intellij.debugger.streams.lib.LibrarySupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.java.JavaStandardLibrarySupportProvider
|
||||
|
||||
@Suppress("unused")
|
||||
abstract class AbstractJavaStreamTraceTestCase : KotlinTraceTestCase() {
|
||||
override val librarySupportProvider: LibrarySupportProvider = JavaStandardLibrarySupportProvider()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec
|
||||
|
||||
import com.intellij.debugger.streams.lib.LibrarySupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.sequence.KotlinSequenceSupportProvider
|
||||
|
||||
abstract class AbstractSequenceTraceTestCase : KotlinTraceTestCase() {
|
||||
override val librarySupportProvider: LibrarySupportProvider = KotlinSequenceSupportProvider()
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec
|
||||
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.impl.OutputChecker
|
||||
import com.intellij.debugger.streams.lib.LibrarySupportProvider
|
||||
import com.intellij.debugger.streams.psi.DebuggerPositionResolver
|
||||
import com.intellij.debugger.streams.psi.impl.DebuggerPositionResolverImpl
|
||||
import com.intellij.debugger.streams.trace.*
|
||||
import com.intellij.debugger.streams.trace.impl.TraceResultInterpreterImpl
|
||||
import com.intellij.debugger.streams.wrapper.StreamChain
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.xdebugger.XDebugSessionListener
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||
import org.jetbrains.kotlin.idea.debugger.test.KotlinDescriptorTestCaseWithStepping
|
||||
import org.jetbrains.kotlin.idea.debugger.test.TestFiles
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
abstract class KotlinTraceTestCase : KotlinDescriptorTestCaseWithStepping() {
|
||||
private companion object {
|
||||
val DEFAULT_CHAIN_SELECTOR = ChainSelector.byIndex(0)
|
||||
}
|
||||
|
||||
private lateinit var traceChecker: StreamTraceChecker
|
||||
|
||||
override fun initOutputChecker(): OutputChecker {
|
||||
traceChecker = StreamTraceChecker(this)
|
||||
return super.initOutputChecker()
|
||||
}
|
||||
|
||||
abstract val librarySupportProvider: LibrarySupportProvider
|
||||
|
||||
override fun doMultiFileTest(files: TestFiles, preferences: DebuggerPreferences) {
|
||||
// Sequence expressions are verbose. Disable expression logging for sequence debugger
|
||||
KotlinDebuggerCaches.LOG_COMPILATIONS = false
|
||||
|
||||
val session = debuggerSession.xDebugSession ?: kotlin.test.fail("XDebugSession is null")
|
||||
assertNotNull(session)
|
||||
|
||||
val completed = AtomicBoolean(false)
|
||||
val positionResolver = getPositionResolver()
|
||||
val chainBuilder = getChainBuilder()
|
||||
val resultInterpreter = getResultInterpreter()
|
||||
val expressionBuilder = getExpressionBuilder()
|
||||
|
||||
val chainSelector = DEFAULT_CHAIN_SELECTOR
|
||||
|
||||
session.addSessionListener(object : XDebugSessionListener {
|
||||
override fun sessionPaused() {
|
||||
if (completed.getAndSet(true)) {
|
||||
resume()
|
||||
return
|
||||
}
|
||||
try {
|
||||
sessionPausedImpl()
|
||||
} catch (t: Throwable) {
|
||||
println("Exception caught: $t, ${t.message}", ProcessOutputTypes.SYSTEM)
|
||||
t.printStackTrace()
|
||||
|
||||
resume()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun sessionPausedImpl() {
|
||||
printContext(debugProcess.debuggerContext)
|
||||
val chain = ApplicationManager.getApplication().runReadAction(
|
||||
Computable<StreamChain> {
|
||||
val elementAtBreakpoint = positionResolver.getNearestElementToBreakpoint(session)
|
||||
val chains = if (elementAtBreakpoint == null) null else chainBuilder.build(elementAtBreakpoint)
|
||||
if (chains == null || chains.isEmpty()) null else chainSelector.select(chains)
|
||||
})
|
||||
|
||||
if (chain == null) {
|
||||
complete(null, null, null, FailureReason.CHAIN_CONSTRUCTION)
|
||||
return
|
||||
}
|
||||
|
||||
EvaluateExpressionTracer(session, expressionBuilder, resultInterpreter).trace(chain, object : TracingCallback {
|
||||
override fun evaluated(result: TracingResult, context: EvaluationContextImpl) {
|
||||
complete(chain, result, null, null)
|
||||
}
|
||||
|
||||
override fun evaluationFailed(traceExpression: String, message: String) {
|
||||
complete(chain, null, message, FailureReason.EVALUATION)
|
||||
}
|
||||
|
||||
override fun compilationFailed(traceExpression: String, message: String) {
|
||||
complete(chain, null, message, FailureReason.COMPILATION)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun complete(chain: StreamChain?, result: TracingResult?, error: String?, errorReason: FailureReason?) {
|
||||
try {
|
||||
if (error != null) {
|
||||
assertNotNull(errorReason)
|
||||
assertNotNull(chain)
|
||||
throw AssertionError(error)
|
||||
} else {
|
||||
assertNull(errorReason)
|
||||
handleSuccess(chain, result)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
println("Exception caught: " + t + ", " + t.message, ProcessOutputTypes.SYSTEM)
|
||||
} finally {
|
||||
resume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resume() {
|
||||
ApplicationManager.getApplication().invokeLater { session.resume() }
|
||||
}
|
||||
}, testRootDisposable)
|
||||
}
|
||||
|
||||
private fun getPositionResolver(): DebuggerPositionResolver {
|
||||
return DebuggerPositionResolverImpl()
|
||||
}
|
||||
|
||||
protected fun handleSuccess(chain: StreamChain?, result: TracingResult?) {
|
||||
kotlin.test.assertNotNull(chain)
|
||||
kotlin.test.assertNotNull(result)
|
||||
|
||||
println(chain.text, ProcessOutputTypes.SYSTEM)
|
||||
|
||||
val trace = result.trace
|
||||
traceChecker.checkChain(trace)
|
||||
|
||||
val resolvedTrace = result.resolve(librarySupportProvider.librarySupport.resolverFactory)
|
||||
traceChecker.checkResolvedChain(resolvedTrace)
|
||||
}
|
||||
|
||||
private fun getResultInterpreter(): TraceResultInterpreter {
|
||||
return TraceResultInterpreterImpl(librarySupportProvider.librarySupport.interpreterFactory)
|
||||
}
|
||||
|
||||
private fun getChainBuilder(): StreamChainBuilder {
|
||||
return librarySupportProvider.chainBuilder
|
||||
}
|
||||
|
||||
private fun getExpressionBuilder(): TraceExpressionBuilder {
|
||||
return librarySupportProvider.getExpressionBuilder(project)
|
||||
}
|
||||
|
||||
protected enum class FailureReason {
|
||||
COMPILATION, EVALUATION, CHAIN_CONSTRUCTION
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
protected interface ChainSelector {
|
||||
fun select(chains: List<StreamChain>): StreamChain
|
||||
|
||||
companion object {
|
||||
fun byIndex(index: Int): ChainSelector {
|
||||
return object : ChainSelector {
|
||||
override fun select(chains: List<StreamChain>): StreamChain = chains[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+357
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec;
|
||||
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||
import org.jetbrains.kotlin.test.TargetBackend;
|
||||
import org.jetbrains.kotlin.test.TestMetadata;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public class SequenceTraceTestCaseGenerated extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSequence() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true, "terminal");
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Append extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAppend() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("PlusArray.kt")
|
||||
public void testPlusArray() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append/PlusArray.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("PlusElement.kt")
|
||||
public void testPlusElement() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append/PlusElement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("PlusSequence.kt")
|
||||
public void testPlusSequence() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append/PlusSequence.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("PlusSingle.kt")
|
||||
public void testPlusSingle() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/append/PlusSingle.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Distinct extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInDistinct() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Distinct.kt")
|
||||
public void testDistinct() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/Distinct.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctBy.kt")
|
||||
public void testDistinctBy() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctBy.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctByBigPrimitives.kt")
|
||||
public void testDistinctByBigPrimitives() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctByBigPrimitives.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctByNullableElement.kt")
|
||||
public void testDistinctByNullableElement() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctByNullableElement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctByNullableKey.kt")
|
||||
public void testDistinctByNullableKey() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctByNullableKey.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctByNullableKeyAndElement.kt")
|
||||
public void testDistinctByNullableKeyAndElement() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctByNullableKeyAndElement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DistinctObjects.kt")
|
||||
public void testDistinctObjects() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/distinct/DistinctObjects.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Filter extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInFilter() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Drop.kt")
|
||||
public void testDrop() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/Drop.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("DropWhile.kt")
|
||||
public void testDropWhile() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/DropWhile.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Filter.kt")
|
||||
public void testFilter() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/Filter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FilterIndexed.kt")
|
||||
public void testFilterIndexed() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/FilterIndexed.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FilterIsInstance.kt")
|
||||
public void testFilterIsInstance() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/FilterIsInstance.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FilterNot.kt")
|
||||
public void testFilterNot() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/FilterNot.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Minus.kt")
|
||||
public void testMinus() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/Minus.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("MinusElement.kt")
|
||||
public void testMinusElement() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/MinusElement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Take.kt")
|
||||
public void testTake() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/Take.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("TakeWhile.kt")
|
||||
public void testTakeWhile() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/filter/TakeWhile.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/flatMap")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class FlatMap extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInFlatMap() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/flatMap"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("FlatMap.kt")
|
||||
public void testFlatMap() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/flatMap/FlatMap.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Flatten.kt")
|
||||
public void testFlatten() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/flatMap/Flatten.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Map extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMap() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Map.kt")
|
||||
public void testMap() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map/Map.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("MapIndexed.kt")
|
||||
public void testMapIndexed() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map/MapIndexed.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("MapNotNull.kt")
|
||||
public void testMapNotNull() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map/MapNotNull.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("WithIndex.kt")
|
||||
public void testWithIndex() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/map/WithIndex.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Misc extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMisc() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("AsSequence.kt")
|
||||
public void testAsSequence() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/AsSequence.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Chunked.kt")
|
||||
public void testChunked() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/Chunked.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ChunkedWithTransform.kt")
|
||||
public void testChunkedWithTransform() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ChunkedWithTransform.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ConstrainOnce.kt")
|
||||
public void testConstrainOnce() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ConstrainOnce.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("OnEach.kt")
|
||||
public void testOnEach() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/OnEach.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("RequireNoNulls.kt")
|
||||
public void testRequireNoNulls() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/RequireNoNulls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("Windowed.kt")
|
||||
public void testWindowed() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/Windowed.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("WindowedWithBigStep.kt")
|
||||
public void testWindowedWithBigStep() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/WindowedWithBigStep.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("WindowedWithPartial.kt")
|
||||
public void testWindowedWithPartial() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/WindowedWithPartial.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("WindowedWithStep.kt")
|
||||
public void testWindowedWithStep() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/WindowedWithStep.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ZipWithGreater.kt")
|
||||
public void testZipWithGreater() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ZipWithGreater.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ZipWithLesser.kt")
|
||||
public void testZipWithLesser() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ZipWithLesser.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ZipWithNextMany.kt")
|
||||
public void testZipWithNextMany() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ZipWithNextMany.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ZipWithNextSingle.kt")
|
||||
public void testZipWithNextSingle() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ZipWithNextSingle.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("ZipWithSame.kt")
|
||||
public void testZipWithSame() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/misc/ZipWithSame.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Sort extends AbstractSequenceTraceTestCase {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInSort() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("Sorted.kt")
|
||||
public void testSorted() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort/Sorted.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SortedBy.kt")
|
||||
public void testSortedBy() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort/SortedBy.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SortedByDescending.kt")
|
||||
public void testSortedByDescending() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort/SortedByDescending.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SortedDescending.kt")
|
||||
public void testSortedDescending() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort/SortedDescending.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("SortedWith.kt")
|
||||
public void testSortedWith() throws Exception {
|
||||
runTest("idea/jvm-debugger/jvm-debugger-test/testData/sequence/streams/sequence/sort/SortedWith.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.exec
|
||||
|
||||
import com.intellij.debugger.streams.resolve.ResolvedStreamCall
|
||||
import com.intellij.debugger.streams.resolve.ResolvedStreamChain
|
||||
import com.intellij.debugger.streams.trace.*
|
||||
import com.intellij.execution.ExecutionTestCase
|
||||
import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.testFramework.UsefulTestCase
|
||||
import junit.framework.TestCase
|
||||
import java.util.*
|
||||
|
||||
class StreamTraceChecker(private val testCase: ExecutionTestCase) {
|
||||
fun checkChain(trace: List<TraceInfo>) {
|
||||
for (info in trace) {
|
||||
val name = info.call.name
|
||||
println(name)
|
||||
|
||||
print(" before: ")
|
||||
val before = info.valuesOrderBefore
|
||||
println(traceToString(before.values))
|
||||
|
||||
print(" after: ")
|
||||
val after = info.valuesOrderAfter
|
||||
println(traceToString(after.values))
|
||||
}
|
||||
}
|
||||
|
||||
fun checkResolvedChain(result: ResolvedTracingResult) {
|
||||
val resolvedChain = result.resolvedChain
|
||||
|
||||
checkChain(resolvedChain)
|
||||
checkTracesIsCorrectInBothDirections(resolvedChain)
|
||||
|
||||
val terminator = resolvedChain.terminator
|
||||
resolvedChain.intermediateCalls.forEach { x -> printBeforeAndAfterValues(x.stateBefore, x.stateAfter) }
|
||||
printBeforeAndAfterValues(terminator.stateBefore, terminator.stateAfter)
|
||||
}
|
||||
|
||||
private fun printBeforeAndAfterValues(before: NextAwareState?, after: PrevAwareState?) {
|
||||
TestCase.assertFalse(before == null && after == null)
|
||||
val call = before?.nextCall ?: after!!.prevCall
|
||||
TestCase.assertNotNull(call)
|
||||
println("mappings for " + call!!.name)
|
||||
println(" direct:")
|
||||
if (before != null) {
|
||||
printMapping(before.trace, { before.getNextValues(it) }, Direction.FORWARD)
|
||||
} else {
|
||||
println(" no")
|
||||
}
|
||||
|
||||
println(" reverse:")
|
||||
if (after != null) {
|
||||
printMapping(after.trace, { after.getPrevValues(it) }, Direction.BACKWARD)
|
||||
} else {
|
||||
println(" not found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun printMapping(
|
||||
values: List<TraceElement>,
|
||||
mapper: (TraceElement) -> List<TraceElement>,
|
||||
direction: Direction
|
||||
) {
|
||||
if (values.isEmpty()) {
|
||||
println(" empty")
|
||||
}
|
||||
for (element in values) {
|
||||
val mappedValues = mapper(element)
|
||||
val mapped = traceToString(mappedValues)
|
||||
val line = when (direction) {
|
||||
Direction.FORWARD -> element.time.toString() + " -> " + mapped
|
||||
else -> mapped + " <- " + element.time
|
||||
}
|
||||
println(" $line")
|
||||
}
|
||||
}
|
||||
|
||||
private enum class Direction {
|
||||
FORWARD, BACKWARD
|
||||
}
|
||||
|
||||
private fun checkChain(chain: ResolvedStreamChain) {
|
||||
val intermediates = chain.intermediateCalls
|
||||
val terminator = chain.terminator
|
||||
if (intermediates.isEmpty()) {
|
||||
TestCase.assertFalse(terminator.stateBefore is PrevAwareState)
|
||||
}
|
||||
|
||||
checkIntermediates(chain.intermediateCalls)
|
||||
|
||||
TestCase.assertEquals(terminator.call.name, terminator.stateBefore.nextCall.name)
|
||||
val after = terminator.stateAfter
|
||||
if (after != null) {
|
||||
val terminatorCall = after.prevCall
|
||||
TestCase.assertNotNull(terminatorCall)
|
||||
TestCase.assertEquals(terminator.call.name, terminatorCall!!.name)
|
||||
}
|
||||
|
||||
if (intermediates.isNotEmpty()) {
|
||||
val lastIntermediate = intermediates[intermediates.size - 1]
|
||||
val stateAfterIntermediates = lastIntermediate.stateAfter
|
||||
UsefulTestCase.assertInstanceOf(stateAfterIntermediates, NextAwareState::class.java)
|
||||
TestCase.assertEquals(terminator.call.name, (stateAfterIntermediates as NextAwareState).nextCall.name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIntermediates(intermediates: List<ResolvedStreamCall.Intermediate>) {
|
||||
for (i in 0 until intermediates.size - 1) {
|
||||
val prev = intermediates[i]
|
||||
val next = intermediates[i + 1]
|
||||
TestCase.assertSame(prev.stateAfter, next.stateBefore)
|
||||
val prevCall = prev.stateAfter.prevCall
|
||||
TestCase.assertNotNull(prevCall)
|
||||
TestCase.assertEquals(prev.call.name, prevCall!!.name)
|
||||
TestCase.assertEquals(next.call.name, next.stateBefore.nextCall.name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTracesIsCorrectInBothDirections(resolvedChain: ResolvedStreamChain) {
|
||||
for (intermediate in resolvedChain.intermediateCalls) {
|
||||
checkNeighborTraces(intermediate.stateBefore, intermediate.stateAfter)
|
||||
}
|
||||
|
||||
val terminator = resolvedChain.terminator
|
||||
val after = terminator.stateAfter
|
||||
if (after != null) {
|
||||
checkNeighborTraces(terminator.stateBefore, after)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkNeighborTraces(left: NextAwareState, right: PrevAwareState) {
|
||||
val leftValues = HashSet(left.trace)
|
||||
val rightValues = HashSet(right.trace)
|
||||
|
||||
checkThatMappingsIsCorrect(
|
||||
leftValues,
|
||||
rightValues,
|
||||
{ left.getNextValues(it) },
|
||||
{ right.getPrevValues(it) })
|
||||
checkThatMappingsIsCorrect(
|
||||
rightValues,
|
||||
leftValues,
|
||||
{ right.getPrevValues(it) },
|
||||
{ left.getNextValues(it) })
|
||||
}
|
||||
|
||||
private fun checkThatMappingsIsCorrect(
|
||||
prev: Set<TraceElement>,
|
||||
next: Set<TraceElement>,
|
||||
toNext: (TraceElement) -> List<TraceElement>,
|
||||
toPrev: (TraceElement) -> List<TraceElement>
|
||||
) {
|
||||
for (leftElement in prev) {
|
||||
val mapToRight = toNext.invoke(leftElement)
|
||||
for (rightElement in mapToRight) {
|
||||
TestCase.assertTrue(next.contains(rightElement))
|
||||
TestCase.assertTrue(toPrev.invoke(rightElement).contains(leftElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun traceToString(trace: Collection<TraceElement>): String {
|
||||
if (trace.isEmpty()) {
|
||||
return "nothing"
|
||||
}
|
||||
|
||||
return trace.map { it.time }.sorted().joinToString(",")
|
||||
}
|
||||
|
||||
private fun println(msg: String) = testCase.println(msg, ProcessOutputTypes.SYSTEM)
|
||||
private fun print(msg: String) = testCase.print(msg, ProcessOutputTypes.SYSTEM)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi
|
||||
|
||||
import com.intellij.debugger.streams.trace.impl.handler.type.GenericType
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.KotlinPsiChainBuilderTestCase
|
||||
|
||||
abstract class TypedChainTestCase(relativePath: String) : KotlinPsiChainBuilderTestCase(relativePath) {
|
||||
protected fun doTest(producerAfterType: GenericType, vararg intermediateAfterTypes: GenericType) {
|
||||
val elementAtCaret = configureAndGetElementAtCaret()
|
||||
assertNotNull(elementAtCaret)
|
||||
val chains = chainBuilder.build(elementAtCaret)
|
||||
assertFalse(chains.isEmpty())
|
||||
val chain = chains[0]
|
||||
val intermediateCalls = chain.intermediateCalls
|
||||
assertEquals(intermediateAfterTypes.size, intermediateCalls.size)
|
||||
assertEquals(producerAfterType, chain.qualifierExpression.typeAfter)
|
||||
|
||||
if (intermediateAfterTypes.isNotEmpty()) {
|
||||
assertEquals(producerAfterType, intermediateCalls[0].typeBefore)
|
||||
for (i in 0 until intermediateAfterTypes.size - 1) {
|
||||
assertEquals(intermediateAfterTypes[i], intermediateCalls[i].typeAfter)
|
||||
assertEquals(intermediateAfterTypes[i], intermediateCalls[i + 1].typeBefore)
|
||||
}
|
||||
|
||||
val lastAfterType = intermediateAfterTypes[intermediateAfterTypes.size - 1]
|
||||
assertEquals(lastAfterType, chain.terminationCall.typeBefore)
|
||||
val lastCall = intermediateCalls[intermediateCalls.size - 1]
|
||||
assertEquals(lastAfterType, lastCall.typeAfter)
|
||||
} else {
|
||||
assertEquals(producerAfterType, chain.terminationCall.typeBefore)
|
||||
}
|
||||
}
|
||||
|
||||
override fun doTest() {
|
||||
fail("Use doTest(producerAfterType, vararg intermediateAfterTypes)")
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.collection
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.KotlinPsiChainBuilderTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.collections.KotlinCollectionSupportProvider
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class PositiveCollectionBuildTest : KotlinPsiChainBuilderTestCase.Positive("collection/positive") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = KotlinCollectionSupportProvider().chainBuilder
|
||||
|
||||
fun testIntermediateIsLastCall() = doTest()
|
||||
fun testOnlyFilterCall() = doTest()
|
||||
fun testTerminationCallUsed() = doTest()
|
||||
fun testOnlyTerminationCallUsed() = doTest()
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.collection
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.collections.KotlinCollectionSupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.psi.TypedChainTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class TypedCollectionChainTest : TypedChainTestCase("collection/positive/types") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = KotlinCollectionSupportProvider().chainBuilder
|
||||
|
||||
fun testAny() = doTest(KotlinSequenceTypes.ANY)
|
||||
fun testNullableAny() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testBoolean() = doTest(KotlinSequenceTypes.BOOLEAN)
|
||||
fun testNullableBoolean() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testByte() = doTest(KotlinSequenceTypes.BYTE)
|
||||
fun testNullableByte() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testShort() = doTest(KotlinSequenceTypes.SHORT)
|
||||
fun testNullableShort() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testInt() = doTest(KotlinSequenceTypes.INT)
|
||||
fun testNullableInt() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testLong() = doTest(KotlinSequenceTypes.LONG)
|
||||
fun testNullableLong() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testFloat() = doTest(KotlinSequenceTypes.FLOAT)
|
||||
fun testNullableFloat() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testDouble() = doTest(KotlinSequenceTypes.DOUBLE)
|
||||
fun testNullableDouble() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testChar() = doTest(KotlinSequenceTypes.CHAR)
|
||||
fun testNullableChar() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testNullableAnyToPrimitive() = doTest(KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.BOOLEAN)
|
||||
fun testPrimitiveToNullableAny() = doTest(KotlinSequenceTypes.INT, KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testAnyToPrimitive() = doTest(KotlinSequenceTypes.ANY, KotlinSequenceTypes.INT)
|
||||
fun testPrimitiveToAny() = doTest(KotlinSequenceTypes.INT, KotlinSequenceTypes.ANY)
|
||||
|
||||
fun testNullableToNotNull() = doTest(KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.INT)
|
||||
fun testNotNullToNullable() = doTest(KotlinSequenceTypes.DOUBLE, KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testFewTransitions1() = doTest(KotlinSequenceTypes.BYTE, KotlinSequenceTypes.ANY, KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.INT)
|
||||
fun testFewTransitions2() = doTest(KotlinSequenceTypes.CHAR, KotlinSequenceTypes.BOOLEAN, KotlinSequenceTypes.DOUBLE, KotlinSequenceTypes.ANY)
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.java
|
||||
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class AmbiguousChainsTest : PositiveJavaStreamTest("ambiguous") {
|
||||
fun testSimpleExpression() = doTest(2)
|
||||
|
||||
fun testNestedExpression() = doTest(3)
|
||||
fun testSimpleFunctionParameter() = doTest(2)
|
||||
fun testNestedFunctionParameters() = doTest(3)
|
||||
fun testNestedFunctionParametersReversed() = doTest(3)
|
||||
|
||||
fun testStreamProducerParameter() = doTest(2)
|
||||
fun testStreamIntermediateCallParameter() = doTest(2)
|
||||
fun testStreamTerminatorParameter() = doTest(2)
|
||||
fun testStreamAllPositions() = doTest(4)
|
||||
|
||||
fun testNestedStreamProducerParameter() = doTest(3)
|
||||
fun testNestedStreamIntermediateCallParameter() = doTest(3)
|
||||
fun testNestedStreamTerminatorCallParameter() = doTest(3)
|
||||
|
||||
fun testNestedCallInLambda() = doTest(2)
|
||||
fun testNestedCallInAnonymous() = doTest(2)
|
||||
|
||||
fun testLinkedChain() = doTest(3)
|
||||
|
||||
private fun doTest(chainsCount: Int) {
|
||||
val chains = buildChains()
|
||||
assertEquals(chainsCount, chains.size)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.java
|
||||
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class LocationPositiveChainTest : PositiveJavaStreamTest("location") {
|
||||
fun testAnonymousBody() = doTest()
|
||||
|
||||
fun testAssignExpression() = doTest()
|
||||
|
||||
fun testFirstParameterOfFunction() = doTest()
|
||||
fun testLambdaBody() = doTest()
|
||||
|
||||
fun testParameterInAssignExpression() = doTest()
|
||||
fun testParameterInReturnExpression() = doTest()
|
||||
|
||||
fun testReturnExpression() = doTest()
|
||||
|
||||
fun testSecondParameterOfFunction() = doTest()
|
||||
|
||||
fun testSingleExpression() = doTest()
|
||||
|
||||
fun testBeforeStatement() = doTest()
|
||||
|
||||
fun testBetweenChainCallsBeforeDot() = doTest()
|
||||
fun testBetweenChainCallsAfterDot() = doTest()
|
||||
|
||||
fun testInEmptyParameterList() = doTest()
|
||||
|
||||
fun testBetweenParametersBeforeComma() = doTest()
|
||||
fun testBetweenParametersAfterComma() = doTest()
|
||||
|
||||
fun testInAnyLambda() = doTest()
|
||||
fun testInAnyAnonymous() = doTest()
|
||||
fun testInString() = doTest()
|
||||
fun testInVariableName() = doTest()
|
||||
fun testInMethodReference() = doTest()
|
||||
|
||||
fun testAsMethodExpression() = doTest()
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.java
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.KotlinPsiChainBuilderTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.java.JavaStandardLibrarySupportProvider
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class NegativeJavaStreamTest : KotlinPsiChainBuilderTestCase.Negative("streams/negative") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = JavaStandardLibrarySupportProvider().chainBuilder
|
||||
|
||||
fun testFakeStream() = doTest()
|
||||
|
||||
fun testWithoutTerminalOperation() = doTest()
|
||||
|
||||
fun testNoBreakpoint() = doTest()
|
||||
|
||||
fun testBreakpointOnMethod() = doTest()
|
||||
fun testBreakpointOnIfCondition() = doTest()
|
||||
fun testBreakpointOnNewScope() = doTest()
|
||||
fun testBreakpointOnElseBranch() = doTest()
|
||||
|
||||
fun testInLambda() = doTest()
|
||||
fun testInLambdaWithBody() = doTest()
|
||||
fun testInAnonymous() = doTest()
|
||||
|
||||
fun testAfterStatement() = doTest()
|
||||
|
||||
fun testInPreviousStatement() = doTest()
|
||||
fun testInNextStatement() = doTest()
|
||||
|
||||
fun testIdea173415() = doTest()
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.java
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.KotlinPsiChainBuilderTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.java.JavaStandardLibrarySupportProvider
|
||||
|
||||
abstract class PositiveJavaStreamTest(subDirectory: String) : KotlinPsiChainBuilderTestCase.Positive("streams/positive/$subDirectory") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = JavaStandardLibrarySupportProvider().chainBuilder
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.java
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.java.JavaStandardLibrarySupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.psi.TypedChainTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.DOUBLE
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.INT
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.LONG
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes.NULLABLE_ANY
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class TypedJavaChainTest : TypedChainTestCase("streams/positive/types") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = JavaStandardLibrarySupportProvider().chainBuilder
|
||||
|
||||
fun testOneCall() = doTest(NULLABLE_ANY)
|
||||
fun testMapToSame() = doTest(NULLABLE_ANY, NULLABLE_ANY)
|
||||
fun testPrimitiveOneCall() = doTest(INT)
|
||||
fun testPrimitiveMapToSame() = doTest(LONG, LONG)
|
||||
|
||||
fun testMapToPrimitive() = doTest(NULLABLE_ANY, DOUBLE)
|
||||
fun testMapToObj() = doTest(DOUBLE, NULLABLE_ANY)
|
||||
fun testMapPrimitiveToPrimitive() = doTest(LONG, INT)
|
||||
|
||||
fun testFewTransitions() = doTest(NULLABLE_ANY, INT, NULLABLE_ANY, LONG, DOUBLE)
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.sequence.psi.sequence
|
||||
|
||||
import com.intellij.debugger.streams.wrapper.StreamChainBuilder
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.lib.sequence.KotlinSequenceSupportProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.test.sequence.psi.TypedChainTestCase
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.trace.dsl.KotlinSequenceTypes
|
||||
import org.jetbrains.kotlin.test.JUnit3WithIdeaConfigurationRunner
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(JUnit3WithIdeaConfigurationRunner::class)
|
||||
class TypedSequenceChain : TypedChainTestCase("sequence/positive/types") {
|
||||
override val kotlinChainBuilder: StreamChainBuilder = KotlinSequenceSupportProvider().chainBuilder
|
||||
|
||||
fun testAny() = doTest(KotlinSequenceTypes.ANY)
|
||||
fun testNullableAny() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testBoolean() = doTest(KotlinSequenceTypes.BOOLEAN)
|
||||
fun testNullableBoolean() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testByte() = doTest(KotlinSequenceTypes.BYTE)
|
||||
fun testNullableByte() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testShort() = doTest(KotlinSequenceTypes.SHORT)
|
||||
fun testNullableShort() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testInt() = doTest(KotlinSequenceTypes.INT)
|
||||
fun testNullableInt() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testLong() = doTest(KotlinSequenceTypes.LONG)
|
||||
fun testNullableLong() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testFloat() = doTest(KotlinSequenceTypes.FLOAT)
|
||||
fun testNullableFloat() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testDouble() = doTest(KotlinSequenceTypes.DOUBLE)
|
||||
fun testNullableDouble() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testChar() = doTest(KotlinSequenceTypes.CHAR)
|
||||
fun testNullableChar() = doTest(KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testNullableAnyToPrimitive() = doTest(KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.BOOLEAN)
|
||||
fun testPrimitiveToNullableAny() = doTest(KotlinSequenceTypes.INT, KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testAnyToPrimitive() = doTest(KotlinSequenceTypes.ANY, KotlinSequenceTypes.INT)
|
||||
fun testPrimitiveToAny() = doTest(KotlinSequenceTypes.INT, KotlinSequenceTypes.ANY)
|
||||
|
||||
fun testNullableToNotNull() = doTest(KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.INT)
|
||||
fun testNotNullToNullable() = doTest(KotlinSequenceTypes.DOUBLE, KotlinSequenceTypes.NULLABLE_ANY)
|
||||
|
||||
fun testFewTransitions1() =
|
||||
doTest(KotlinSequenceTypes.BYTE, KotlinSequenceTypes.ANY, KotlinSequenceTypes.NULLABLE_ANY, KotlinSequenceTypes.INT)
|
||||
|
||||
fun testFewTransitions2() =
|
||||
doTest(KotlinSequenceTypes.CHAR, KotlinSequenceTypes.BOOLEAN, KotlinSequenceTypes.DOUBLE, KotlinSequenceTypes.ANY)
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils.findLinesWithPrefixesRemoved
|
||||
import com.intellij.debugger.DebuggerInvocationUtil
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import org.jetbrains.kotlin.test.testFramework.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint
|
||||
import com.intellij.debugger.ui.breakpoints.BreakpointManager
|
||||
import com.intellij.debugger.ui.breakpoints.LineBreakpoint
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.xdebugger.XDebuggerUtil
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpointManager
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpointProperties
|
||||
import com.intellij.xdebugger.breakpoints.XBreakpointType
|
||||
import com.intellij.xdebugger.breakpoints.XLineBreakpointType
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaBreakpointProperties
|
||||
import org.jetbrains.java.debugger.breakpoints.properties.JavaLineBreakpointProperties
|
||||
import org.jetbrains.kotlin.idea.debugger.breakpoints.*
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
internal class BreakpointCreator(
|
||||
private val project: Project,
|
||||
private val logger: (String) -> Unit,
|
||||
private val preferences: DebuggerPreferences
|
||||
) {
|
||||
fun createBreakpoints(file: PsiFile) {
|
||||
val document = runReadAction { PsiDocumentManager.getInstance(project).getDocument(file) } ?: return
|
||||
val breakpointManager = XDebuggerManager.getInstance(project).breakpointManager
|
||||
val kotlinFieldBreakpointType = findBreakpointType(KotlinFieldBreakpointType::class.java)
|
||||
val virtualFile = file.virtualFile
|
||||
|
||||
val runnable = {
|
||||
var offset = -1
|
||||
while (true) {
|
||||
val fileText = document.text
|
||||
offset = fileText.indexOf("point!", offset + 1)
|
||||
if (offset == -1) break
|
||||
|
||||
val commentLine = document.getLineNumber(offset)
|
||||
val lineIndex = commentLine + 1
|
||||
|
||||
val comment = fileText
|
||||
.substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine))
|
||||
.trim()
|
||||
|
||||
when {
|
||||
@Suppress("SpellCheckingInspection") comment.startsWith("//FieldWatchpoint!") -> {
|
||||
val javaBreakpoint = createBreakpointOfType(
|
||||
breakpointManager,
|
||||
kotlinFieldBreakpointType,
|
||||
lineIndex,
|
||||
virtualFile
|
||||
)
|
||||
|
||||
(javaBreakpoint as? KotlinFieldBreakpoint)?.apply {
|
||||
@Suppress("SpellCheckingInspection")
|
||||
val fieldName = comment.substringAfter("//FieldWatchpoint! (").substringBefore(")")
|
||||
|
||||
setFieldName(fieldName)
|
||||
setWatchAccess(preferences[DebuggerPreferenceKeys.WATCH_FIELD_ACCESS])
|
||||
setWatchModification(preferences[DebuggerPreferenceKeys.WATCH_FIELD_MODIFICATION])
|
||||
setWatchInitialization(preferences[DebuggerPreferenceKeys.WATCH_FIELD_INITIALISATION])
|
||||
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
logger("KotlinFieldBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1}")
|
||||
}
|
||||
}
|
||||
comment.startsWith("//Breakpoint!") -> {
|
||||
val ordinal = getPropertyFromComment(comment, "lambdaOrdinal")?.toInt()
|
||||
val condition = getPropertyFromComment(comment, "condition")
|
||||
createLineBreakpoint(breakpointManager, file, lineIndex, ordinal, condition)
|
||||
}
|
||||
comment.startsWith("//FunctionBreakpoint!") -> {
|
||||
createFunctionBreakpoint(breakpointManager, file, lineIndex)
|
||||
}
|
||||
else -> throw AssertionError("Cannot create breakpoint at line ${lineIndex + 1}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
DebuggerInvocationUtil.invokeAndWait(project, runnable, ModalityState.defaultModalityState())
|
||||
} else {
|
||||
runnable.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun createAdditionalBreakpoints(fileContents: String) {
|
||||
val breakpoints = findLinesWithPrefixesRemoved(fileContents, "// ADDITIONAL_BREAKPOINT: ")
|
||||
for (breakpoint in breakpoints) {
|
||||
val position = breakpoint.split(".kt:")
|
||||
assert(position.size == 2) { "Couldn't parse position from test directive: directive = $breakpoint" }
|
||||
|
||||
var lineMarker = position[1]
|
||||
var ordinal: Int? = null
|
||||
|
||||
if (lineMarker.contains(":(") && lineMarker.endsWith(")")) {
|
||||
val lineMarkerAndOrdinal = lineMarker.split(":(")
|
||||
lineMarker = lineMarkerAndOrdinal[0]
|
||||
ordinal = lineMarkerAndOrdinal[1].substringBefore(")").toInt()
|
||||
}
|
||||
|
||||
createBreakpoint(position[0], lineMarker, ordinal)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPropertyFromComment(comment: String, propertyName: String): String? {
|
||||
if (comment.contains("$propertyName = ")) {
|
||||
val result = comment.substringAfter("$propertyName = ")
|
||||
if (result.contains(", ")) {
|
||||
return result.substringBefore(", ")
|
||||
}
|
||||
if (result.contains(")")) {
|
||||
return result.substringBefore(")")
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createBreakpoint(fileName: String, lineMarker: String, ordinal: Int?) {
|
||||
val sourceFiles = runReadAction {
|
||||
FilenameIndex.getAllFilesByExt(project, "kt")
|
||||
.filter { it.name.contains(fileName) && it.contentsToByteArray().toString(Charsets.UTF_8).contains(lineMarker) }
|
||||
}
|
||||
|
||||
assert(sourceFiles.size == 1) { "One source file should be found: name = $fileName, sourceFiles = $sourceFiles" }
|
||||
|
||||
val runnable = Runnable {
|
||||
val psiSourceFile = PsiManager.getInstance(project).findFile(sourceFiles.first())!!
|
||||
|
||||
val breakpointManager = XDebuggerManager.getInstance(project).breakpointManager
|
||||
val document = PsiDocumentManager.getInstance(project).getDocument(psiSourceFile)!!
|
||||
|
||||
val index = psiSourceFile.text!!.indexOf(lineMarker)
|
||||
val lineNumber = document.getLineNumber(index) + 1 // lineMarker is for previous line
|
||||
|
||||
createLineBreakpoint(breakpointManager, psiSourceFile, lineNumber, ordinal, null)
|
||||
}
|
||||
|
||||
DebuggerInvocationUtil.invokeAndWait(project, runnable, ModalityState.defaultModalityState())
|
||||
}
|
||||
|
||||
private fun createFunctionBreakpoint(breakpointManager: XBreakpointManager, file: PsiFile, lineIndex: Int) {
|
||||
val breakpointType = findBreakpointType(KotlinFunctionBreakpointType::class.java)
|
||||
val breakpoint = createBreakpointOfType(breakpointManager, breakpointType, lineIndex, file.virtualFile)
|
||||
if (breakpoint is KotlinFunctionBreakpoint) {
|
||||
logger("FunctionBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLineBreakpoint(
|
||||
breakpointManager: XBreakpointManager,
|
||||
file: PsiFile,
|
||||
lineIndex: Int,
|
||||
lambdaOrdinal: Int?,
|
||||
condition: String?
|
||||
) {
|
||||
val kotlinLineBreakpointType = findBreakpointType(KotlinLineBreakpointType::class.java)
|
||||
val javaBreakpoint = createBreakpointOfType(
|
||||
breakpointManager,
|
||||
kotlinLineBreakpointType,
|
||||
lineIndex,
|
||||
file.virtualFile
|
||||
)
|
||||
|
||||
if (javaBreakpoint is LineBreakpoint<*>) {
|
||||
val properties = javaBreakpoint.xBreakpoint.properties as? JavaLineBreakpointProperties ?: return
|
||||
var suffix = ""
|
||||
if (lambdaOrdinal != null) {
|
||||
if (lambdaOrdinal != -1) {
|
||||
properties.lambdaOrdinal = lambdaOrdinal - 1
|
||||
} else {
|
||||
properties.lambdaOrdinal = lambdaOrdinal
|
||||
}
|
||||
suffix += " lambdaOrdinal = $lambdaOrdinal"
|
||||
}
|
||||
if (condition != null) {
|
||||
javaBreakpoint.setCondition(TextWithImportsImpl(CodeFragmentKind.EXPRESSION, condition))
|
||||
suffix += " condition = $condition"
|
||||
}
|
||||
|
||||
BreakpointManager.addBreakpoint(javaBreakpoint)
|
||||
logger("LineBreakpoint created at ${file.virtualFile.name}:${lineIndex + 1}$suffix")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBreakpointOfType(
|
||||
breakpointManager: XBreakpointManager,
|
||||
breakpointType: XLineBreakpointType<XBreakpointProperties<*>>,
|
||||
lineIndex: Int,
|
||||
virtualFile: VirtualFile
|
||||
): Breakpoint<out JavaBreakpointProperties<*>>? {
|
||||
if (!breakpointType.canPutAt(virtualFile, lineIndex, project)) return null
|
||||
val xBreakpoint = runWriteAction {
|
||||
breakpointManager.addLineBreakpoint(
|
||||
breakpointType,
|
||||
virtualFile.url,
|
||||
lineIndex,
|
||||
breakpointType.createBreakpointProperties(virtualFile, lineIndex)
|
||||
)
|
||||
}
|
||||
return BreakpointManager.getJavaBreakpoint(xBreakpoint)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private inline fun <reified T : XBreakpointType<*, *>> findBreakpointType(javaClass: Class<T>): XLineBreakpointType<XBreakpointProperties<*>> {
|
||||
return XDebuggerUtil.getInstance().findBreakpointType(javaClass) as XLineBreakpointType<XBreakpointProperties<*>>
|
||||
}
|
||||
}
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import com.intellij.debugger.SourcePosition
|
||||
import com.intellij.debugger.engine.*
|
||||
import com.intellij.debugger.engine.evaluation.CodeFragmentKind
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImports
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
|
||||
import com.intellij.debugger.impl.DebuggerSession
|
||||
import com.intellij.debugger.settings.NodeRendererSettings
|
||||
import com.intellij.debugger.ui.impl.watch.*
|
||||
import com.intellij.debugger.ui.tree.*
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.roots.JdkOrderEntry
|
||||
import com.intellij.openapi.roots.libraries.LibraryUtil
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.psi.PsiExpression
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl
|
||||
import com.intellij.xdebugger.impl.frame.XDebugViewSessionListener
|
||||
import com.intellij.xdebugger.impl.frame.XVariablesView
|
||||
import com.intellij.xdebugger.impl.frame.XWatchesViewImpl
|
||||
import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants
|
||||
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree
|
||||
import com.intellij.xdebugger.impl.ui.tree.nodes.*
|
||||
import org.jetbrains.kotlin.idea.debugger.KotlinFrameExtraVariablesProvider
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinCodeFragmentFactory
|
||||
import org.jetbrains.kotlin.idea.debugger.invokeInManagerThread
|
||||
import org.jetbrains.kotlin.idea.debugger.test.KOTLIN_LIBRARY_NAME
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferenceKeys
|
||||
import org.jetbrains.kotlin.idea.debugger.test.preference.DebuggerPreferences
|
||||
import org.jetbrains.kotlin.idea.debugger.test.util.PrinterConfig.DescriptorViewOptions
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import java.io.Closeable
|
||||
import javax.swing.tree.TreeNode
|
||||
|
||||
class FramePrinter(
|
||||
debuggerSession: DebuggerSession,
|
||||
private val delegate: FramePrinterDelegate,
|
||||
private val preferences: DebuggerPreferences,
|
||||
private val testRootDisposable: Disposable
|
||||
) : Closeable {
|
||||
private companion object {
|
||||
fun getClassRenderer() = NodeRendererSettings.getInstance()!!.classRenderer!!
|
||||
}
|
||||
|
||||
private lateinit var variablesView: XVariablesView
|
||||
private lateinit var watchesView: XWatchesViewImpl
|
||||
|
||||
private val oldShowFqTypeNames: Boolean
|
||||
|
||||
init {
|
||||
ApplicationManager.getApplication().invokeAndWait(
|
||||
{
|
||||
variablesView = createVariablesView(debuggerSession)
|
||||
watchesView = createWatchesView(debuggerSession)
|
||||
}, ModalityState.any()
|
||||
)
|
||||
|
||||
getClassRenderer().let { renderer ->
|
||||
oldShowFqTypeNames = renderer.SHOW_FQ_TYPE_NAMES
|
||||
renderer.SHOW_FQ_TYPE_NAMES = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
getClassRenderer().SHOW_FQ_TYPE_NAMES = oldShowFqTypeNames
|
||||
}
|
||||
|
||||
fun printFrame(completion: () -> Unit) {
|
||||
if (preferences[DebuggerPreferenceKeys.PRINT_FRAME]) {
|
||||
doPrintFrame(completion)
|
||||
} else {
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
private fun doPrintFrame(completion: () -> Unit) {
|
||||
val tree = variablesView.tree
|
||||
|
||||
val config = getPrinterConfig()
|
||||
|
||||
fun processor() {
|
||||
Printer(delegate, config).printTree(tree)
|
||||
|
||||
for (extra in getExtraVars()) {
|
||||
watchesView.addWatchExpression(XExpressionImpl.fromText(extra.text), -1, false)
|
||||
}
|
||||
|
||||
Printer(delegate, config).printTree(watchesView.tree)
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
// TODO why this is needed? Otherwise some tests are never ended
|
||||
val filter: (TreeNode) -> Boolean = { it !is XValueNodeImpl || it.name != "cause" }
|
||||
|
||||
delegate.expandAll(tree, ::processor, filter, delegate.evaluationContext.suspendContext)
|
||||
}
|
||||
|
||||
private fun getPrinterConfig(): PrinterConfig {
|
||||
val skipInPrintFrame = preferences[DebuggerPreferenceKeys.SKIP].flatMap { it.split(',') }.map { it.trim() }
|
||||
val viewOptions = DescriptorViewOptions.valueOf(preferences[DebuggerPreferenceKeys.DESCRIPTOR_VIEW_OPTIONS])
|
||||
return PrinterConfig(skipInPrintFrame, viewOptions)
|
||||
}
|
||||
|
||||
private fun createWatchesView(debuggerSession: DebuggerSession): XWatchesViewImpl {
|
||||
val session = debuggerSession.xDebugSession as XDebugSessionImpl
|
||||
val watchesView = XWatchesViewImpl(session, false)
|
||||
Disposer.register(testRootDisposable, watchesView)
|
||||
XDebugViewSessionListener.attach(watchesView, session)
|
||||
return watchesView
|
||||
}
|
||||
|
||||
private fun createVariablesView(debuggerSession: DebuggerSession): XVariablesView {
|
||||
val session = debuggerSession.xDebugSession as XDebugSessionImpl
|
||||
val variablesView = XVariablesView(session)
|
||||
Disposer.register(testRootDisposable, variablesView)
|
||||
XDebugViewSessionListener.attach(variablesView, session)
|
||||
return variablesView
|
||||
}
|
||||
|
||||
private fun getExtraVars(): Set<TextWithImports> {
|
||||
return KotlinFrameExtraVariablesProvider()
|
||||
.collectVariables(delegate.debuggerContext.sourcePosition, delegate.evaluationContext, hashSetOf())
|
||||
}
|
||||
}
|
||||
|
||||
private class PrinterConfig(
|
||||
val variablesToSkipInPrintFrame: List<String> = emptyList(),
|
||||
val viewOptions: DescriptorViewOptions = DescriptorViewOptions.FULL
|
||||
) {
|
||||
enum class DescriptorViewOptions {
|
||||
FULL, NAME_EXPRESSION, NAME_EXPRESSION_RESULT
|
||||
}
|
||||
|
||||
fun shouldRenderSourcesPosition(): Boolean {
|
||||
return when (viewOptions) {
|
||||
DescriptorViewOptions.FULL -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldRenderExpression(): Boolean {
|
||||
return when {
|
||||
viewOptions.toString().contains("EXPRESSION") -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun renderLabel(node: TreeNode, descriptor: NodeDescriptorImpl): String {
|
||||
return when {
|
||||
descriptor is WatchItemDescriptor -> descriptor.calcValueName()
|
||||
viewOptions.toString().contains("NAME") -> (node as? XValueNodeImpl)?.name ?: descriptor.name ?: descriptor.label
|
||||
else -> descriptor.label
|
||||
}
|
||||
}
|
||||
|
||||
fun shouldComputeResultOfCreateExpression(): Boolean {
|
||||
return viewOptions == DescriptorViewOptions.NAME_EXPRESSION_RESULT
|
||||
}
|
||||
}
|
||||
|
||||
private class Printer(private val delegate: FramePrinterDelegate, private val config: PrinterConfig) {
|
||||
fun printTree(tree: XDebuggerTree) {
|
||||
val root = tree.treeModel.root as TreeNode
|
||||
printNode(root, 0)
|
||||
}
|
||||
|
||||
private fun printNode(node: TreeNode, indent: Int) {
|
||||
val project = delegate.debuggerContext.project
|
||||
|
||||
val descriptor = when (node) {
|
||||
is DebuggerTreeNodeImpl -> node.descriptor
|
||||
is XValueNodeImpl -> (node.valueContainer as? JavaValue)?.descriptor ?: MessageDescriptor(node.text.toString())
|
||||
is XStackFrameNode -> (node.valueContainer as? JavaStackFrame)?.descriptor
|
||||
is XValueGroupNodeImpl -> (node.valueContainer as? JavaStaticGroup)?.descriptor
|
||||
is WatchesRootNode -> null
|
||||
is WatchNodeImpl -> WatchItemDescriptor(project, TextWithImportsImpl(CodeFragmentKind.EXPRESSION, node.expression.expression))
|
||||
is MessageTreeNode -> MessageDescriptor(node.text.toString())
|
||||
else -> MessageDescriptor(node.toString())
|
||||
}
|
||||
|
||||
if (descriptor != null && printDescriptor(node, descriptor, indent)) {
|
||||
return
|
||||
}
|
||||
|
||||
printChildren(node, indent + 2)
|
||||
}
|
||||
|
||||
fun printDescriptor(node: TreeNode, descriptor: NodeDescriptorImpl, indent: Int): Boolean {
|
||||
if (descriptor is DefaultNodeDescriptor || config.variablesToSkipInPrintFrame.contains(descriptor.name)) {
|
||||
return true
|
||||
}
|
||||
|
||||
val label = calculateLabel(node, descriptor) ?: return true
|
||||
|
||||
val project = delegate.debuggerContext.project
|
||||
val debugProcess = delegate.debuggerContext.debugProcess ?: error("Debugger process is not launched")
|
||||
|
||||
val text = buildString {
|
||||
append(" ".repeat(indent + 1))
|
||||
append(getPrefix(descriptor))
|
||||
append(label)
|
||||
if (config.shouldRenderSourcesPosition() && hasSourcePosition(descriptor)) {
|
||||
val sp = debugProcess.invokeInManagerThread {
|
||||
SourcePositionProvider.getSourcePosition(descriptor, project, delegate.debuggerContext)
|
||||
}
|
||||
append(" (sp = ${render(sp)})")
|
||||
}
|
||||
|
||||
if (config.shouldRenderExpression() && descriptor is ValueDescriptorImpl) {
|
||||
val expression = debugProcess.invokeInManagerThread {
|
||||
descriptor.getTreeEvaluation((node as XValueNodeImpl).valueContainer as JavaValue, it) as? PsiExpression
|
||||
}
|
||||
|
||||
if (expression != null) {
|
||||
val text = TextWithImportsImpl(expression)
|
||||
val imports = expression.getUserData(DebuggerTreeNodeExpression.ADDITIONAL_IMPORTS_KEY)?.joinToString { it } ?: ""
|
||||
|
||||
val codeFragment = KotlinCodeFragmentFactory().createPresentationCodeFragment(
|
||||
TextWithImportsImpl(text.kind, text.text, text.imports + imports, text.fileType),
|
||||
delegate.debuggerContext.sourcePosition.elementAt, project
|
||||
)
|
||||
val codeFragmentText = codeFragment.text
|
||||
|
||||
if (config.shouldComputeResultOfCreateExpression()) {
|
||||
debugProcess.invokeInManagerThread {
|
||||
val suspendContext = it.suspendContext ?: error(SuspendContext::class.java.simpleName + " is not set")
|
||||
val fragment = TextWithImportsImpl(text.kind, codeFragmentText, codeFragment.importsToString(), text.fileType)
|
||||
delegate.evaluate(suspendContext, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
append(" (expression = $codeFragmentText)")
|
||||
}
|
||||
}
|
||||
append("\n")
|
||||
}
|
||||
|
||||
delegate.logDescriptor(descriptor, text)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun calculateLabel(node: TreeNode, descriptor: NodeDescriptorImpl): String? {
|
||||
var label = config.renderLabel(node, descriptor)
|
||||
|
||||
// TODO: update presentation before calc label
|
||||
if (label == NodeDescriptorImpl.UNKNOWN_VALUE_MESSAGE && descriptor is StaticDescriptor) {
|
||||
label = "static = " + NodeRendererSettings.getInstance().classRenderer.renderTypeName(descriptor.type.name())
|
||||
}
|
||||
|
||||
if (label.endsWith(XDebuggerUIConstants.COLLECTING_DATA_MESSAGE)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return label
|
||||
}
|
||||
|
||||
private fun getPrefix(descriptor: NodeDescriptorImpl): String {
|
||||
val prefix = when (descriptor) {
|
||||
is StackFrameDescriptor -> "frame"
|
||||
is WatchItemDescriptor -> "extra"
|
||||
is LocalVariableDescriptor -> "local"
|
||||
is StaticDescriptor -> "static"
|
||||
is ThisDescriptorImpl -> "this"
|
||||
is FieldDescriptor -> "field"
|
||||
is ArrayElementDescriptor -> "element"
|
||||
is MessageDescriptor -> ""
|
||||
else -> "unknown"
|
||||
}
|
||||
return prefix + " ".repeat("unknown ".length - prefix.length) + if (descriptor is MessageDescriptor) " - " else " = "
|
||||
}
|
||||
|
||||
private fun hasSourcePosition(descriptor: NodeDescriptorImpl): Boolean {
|
||||
return when (descriptor) {
|
||||
is LocalVariableDescriptor,
|
||||
is FieldDescriptor -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun printChildren(node: TreeNode, indent: Int) {
|
||||
val e = node.children()
|
||||
while (e.hasMoreElements()) {
|
||||
printNode(e.nextElement() as TreeNode, indent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun render(sp: SourcePosition?): String {
|
||||
return renderSourcePosition(sp).replace(":", ", ")
|
||||
}
|
||||
}
|
||||
|
||||
fun renderSourcePosition(sourcePosition: SourcePosition?): String {
|
||||
if (sourcePosition == null) {
|
||||
return "null"
|
||||
}
|
||||
|
||||
val virtualFile = sourcePosition.file.originalFile.virtualFile ?: sourcePosition.file.viewProvider.virtualFile
|
||||
|
||||
val libraryEntry = LibraryUtil.findLibraryEntry(virtualFile, sourcePosition.file.project)
|
||||
if (libraryEntry != null && (libraryEntry is JdkOrderEntry || libraryEntry.presentableName == KOTLIN_LIBRARY_NAME)) {
|
||||
val suffix = if (sourcePosition.isInCompiledFile()) "COMPILED" else "EXT"
|
||||
return FileUtil.getNameWithoutExtension(virtualFile.name) + ".!$suffix!"
|
||||
}
|
||||
|
||||
return virtualFile.name + ":" + (sourcePosition.line + 1)
|
||||
}
|
||||
|
||||
private fun SourcePosition.isInCompiledFile(): Boolean {
|
||||
val ktFile = file as? KtFile ?: return false
|
||||
return ktFile.isCompiled
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import com.intellij.debugger.engine.SuspendContextImpl
|
||||
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
|
||||
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl
|
||||
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
|
||||
import com.intellij.ui.treeStructure.Tree
|
||||
import javax.swing.tree.TreeNode
|
||||
|
||||
interface FramePrinterDelegate {
|
||||
val debuggerContext: DebuggerContextImpl
|
||||
val evaluationContext: EvaluationContextImpl
|
||||
|
||||
fun evaluate(suspendContext: SuspendContextImpl, textWithImports: TextWithImportsImpl)
|
||||
|
||||
fun expandAll(tree: Tree, runnable: () -> Unit, filter: (TreeNode) -> Boolean, suspendContext: SuspendContextImpl)
|
||||
fun logDescriptor(descriptor: NodeDescriptorImpl, text: String)
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import com.intellij.debugger.impl.OutputChecker
|
||||
import com.intellij.idea.IdeaLogger
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.text.StringUtilRt
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import org.junit.Assert
|
||||
import java.io.File
|
||||
import kotlin.math.min
|
||||
|
||||
internal class KotlinOutputChecker(
|
||||
private val testDir: String,
|
||||
appPath: String,
|
||||
outputPath: String
|
||||
) : OutputChecker(appPath, outputPath) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private val LOG = Logger.getInstance(KotlinOutputChecker::class.java)
|
||||
|
||||
private const val CONNECT_PREFIX = "Connected to the target VM"
|
||||
private const val DISCONNECT_PREFIX = "Disconnected from the target VM"
|
||||
private const val RUN_JAVA = "Run Java"
|
||||
|
||||
//ERROR: JDWP Unable to get JNI 1.2 environment, jvm->GetEnv() return code = -2
|
||||
private val JDI_BUG_OUTPUT_PATTERN_1 =
|
||||
Regex("ERROR:\\s+JDWP\\s+Unable\\s+to\\s+get\\s+JNI\\s+1\\.2\\s+environment,\\s+jvm->GetEnv\\(\\)\\s+return\\s+code\\s+=\\s+-2")
|
||||
|
||||
//JDWP exit error AGENT_ERROR_NO_JNI_ENV(183): [../../../src/share/back/util.c:820]
|
||||
private val JDI_BUG_OUTPUT_PATTERN_2 = Regex("JDWP\\s+exit\\s+error\\s+AGENT_ERROR_NO_JNI_ENV.*]")
|
||||
}
|
||||
|
||||
private lateinit var myTestName: String
|
||||
|
||||
override fun init(testName: String) {
|
||||
super.init(testName)
|
||||
this.myTestName = Character.toLowerCase(testName[0]) + testName.substring(1)
|
||||
}
|
||||
|
||||
// Copied from the base OutputChecker.checkValid(). Need to intercept call to base preprocessBuffer() method
|
||||
override fun checkValid(jdk: Sdk, sortClassPath: Boolean) {
|
||||
if (IdeaLogger.ourErrorsOccurred != null) {
|
||||
throw IdeaLogger.ourErrorsOccurred
|
||||
}
|
||||
|
||||
val actual = preprocessBuffer(buildOutputString())
|
||||
|
||||
val outDir = File(testDir)
|
||||
var outFile = File(outDir, "$myTestName.out")
|
||||
if (!outFile.exists()) {
|
||||
if (SystemInfo.isWindows) {
|
||||
val winOut = File(outDir, "$myTestName.win.out")
|
||||
if (winOut.exists()) {
|
||||
outFile = winOut
|
||||
}
|
||||
} else if (SystemInfo.isUnix) {
|
||||
val unixOut = File(outDir, "$myTestName.unx.out")
|
||||
if (unixOut.exists()) {
|
||||
outFile = unixOut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!outFile.exists()) {
|
||||
FileUtil.writeToFile(outFile, actual)
|
||||
LOG.error("Test file created ${outFile.path}\n**************** Don't forget to put it into VCS! *******************")
|
||||
} else {
|
||||
val originalText = FileUtilRt.loadFile(outFile, CharsetToolkit.UTF8)
|
||||
val expected = StringUtilRt.convertLineSeparators(originalText)
|
||||
if (expected != actual) {
|
||||
println("expected:")
|
||||
println(originalText)
|
||||
println("actual:")
|
||||
println(actual)
|
||||
|
||||
val len = min(expected.length, actual.length)
|
||||
if (expected.length != actual.length) {
|
||||
println("Text sizes differ: expected " + expected.length + " but actual: " + actual.length)
|
||||
}
|
||||
if (expected.length > len) {
|
||||
println("Rest from expected text is: \"" + expected.substring(len) + "\"")
|
||||
} else if (actual.length > len) {
|
||||
println("Rest from actual text is: \"" + actual.substring(len) + "\"")
|
||||
}
|
||||
|
||||
Assert.assertEquals(originalText, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun preprocessBuffer(buffer: String): String {
|
||||
val lines = buffer.lines().toMutableList()
|
||||
|
||||
val connectedIndex = lines.indexOfFirst { it.startsWith(CONNECT_PREFIX) }
|
||||
lines[connectedIndex] = CONNECT_PREFIX
|
||||
|
||||
val runCommandIndex = connectedIndex - 1
|
||||
lines[runCommandIndex] = RUN_JAVA
|
||||
|
||||
val disconnectedIndex = lines.indexOfFirst { it.startsWith(DISCONNECT_PREFIX) }
|
||||
lines[disconnectedIndex] = DISCONNECT_PREFIX
|
||||
|
||||
return lines.filter { !(it.matches(JDI_BUG_OUTPUT_PATTERN_1) || it.matches(
|
||||
JDI_BUG_OUTPUT_PATTERN_2
|
||||
)) }.joinToString("\n")
|
||||
}
|
||||
|
||||
private fun buildOutputString(): String {
|
||||
// Call base method with reflection
|
||||
val m = OutputChecker::class.java.getDeclaredMethod("buildOutputString")!!
|
||||
val isAccessible = m.isAccessible
|
||||
|
||||
try {
|
||||
m.isAccessible = true
|
||||
return m.invoke(this) as String
|
||||
} finally {
|
||||
m.isAccessible = isAccessible
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import org.apache.log4j.AppenderSkeleton
|
||||
import org.apache.log4j.Level
|
||||
import org.apache.log4j.Logger
|
||||
import org.apache.log4j.spi.LoggingEvent
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
|
||||
|
||||
internal class LogPropagator(val systemLogger: (String) -> Unit) {
|
||||
private var oldLogLevel: Level? = null
|
||||
private val logger = Logger.getLogger(KotlinDebuggerCaches::class.java)
|
||||
private var appender: AppenderSkeleton? = null
|
||||
|
||||
fun attach() {
|
||||
oldLogLevel = logger.level
|
||||
logger.level = Level.DEBUG
|
||||
|
||||
appender = object : AppenderSkeleton() {
|
||||
override fun append(event: LoggingEvent?) {
|
||||
val message = event?.renderedMessage
|
||||
if (message != null) {
|
||||
systemLogger(message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {}
|
||||
override fun requiresLayout() = false
|
||||
}
|
||||
|
||||
logger.addAppender(appender)
|
||||
}
|
||||
|
||||
fun detach() {
|
||||
logger.removeAppender(appender)
|
||||
appender = null
|
||||
|
||||
logger.level = oldLogLevel
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import org.jetbrains.kotlin.codegen.CodegenTestCase
|
||||
|
||||
enum class SteppingInstructionKind(val directiveName: String) {
|
||||
StepInto("STEP_INTO"),
|
||||
StepOut("STEP_OUT"),
|
||||
StepOver("STEP_OVER"),
|
||||
ForceStepOver("STEP_OVER_FORCE"),
|
||||
SmartStepInto("SMART_STEP_INTO"),
|
||||
SmartStepIntoByIndex("SMART_STEP_INTO_BY_INDEX"),
|
||||
Resume("RESUME")
|
||||
}
|
||||
|
||||
class SteppingInstruction(val kind: SteppingInstructionKind, val arg: Int) {
|
||||
companion object {
|
||||
fun parse(file: CodegenTestCase.TestFile): List<SteppingInstruction> {
|
||||
return parse(file, Companion::parseLine)
|
||||
}
|
||||
|
||||
fun parseSingle(file: CodegenTestCase.TestFile, kind: SteppingInstructionKind): SteppingInstruction? {
|
||||
val instructions = parse(file) { line -> parseKind(line, kind) }
|
||||
if (instructions.size > 1) {
|
||||
error("Several instructions found for kind $kind")
|
||||
}
|
||||
|
||||
return instructions.singleOrNull()
|
||||
}
|
||||
|
||||
private fun parse(file: CodegenTestCase.TestFile, processor: (String) -> SteppingInstruction?): List<SteppingInstruction> {
|
||||
return file.content.lineSequence()
|
||||
.map { it.trimStart() }
|
||||
.filter { it.startsWith("// ") }
|
||||
.mapNotNullTo(mutableListOf(), processor)
|
||||
}
|
||||
|
||||
private fun parseLine(line: String): SteppingInstruction? {
|
||||
for (kind in SteppingInstructionKind.values()) {
|
||||
parseKind(line, kind)?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun parseKind(line: String, kind: SteppingInstructionKind): SteppingInstruction? {
|
||||
val prefix = "// " + kind.directiveName + ": "
|
||||
if (line.startsWith(prefix)) {
|
||||
val rawValue = line.drop(prefix.length).trim()
|
||||
val n = rawValue.toIntOrNull() ?: error("Int expected, got $rawValue")
|
||||
return SteppingInstruction(kind, n)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.idea.debugger.test.util
|
||||
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
fun patchDexTests(classesDir: File) {
|
||||
classesDir.walk()
|
||||
.filter { it.isFile && it.nameWithoutExtension.endsWith("Dex") && it.extension == "class" }
|
||||
.forEach(::applyDexLikePatch)
|
||||
}
|
||||
|
||||
private fun applyDexLikePatch(file: File) {
|
||||
val reader = ClassReader(file.readBytes())
|
||||
val writer = ClassWriter(ClassWriter.COMPUTE_FRAMES)
|
||||
|
||||
val visitor = writer
|
||||
.withRemoveSourceDebugExtensionVisitor()
|
||||
.withRemoveSameLinesInLineTableVisitor()
|
||||
|
||||
reader.accept(visitor, 0)
|
||||
file.writeBytes(writer.toByteArray())
|
||||
}
|
||||
|
||||
private fun ClassVisitor.withRemoveSourceDebugExtensionVisitor(): ClassVisitor {
|
||||
return object : ClassVisitor(Opcodes.API_VERSION, this) {
|
||||
override fun visitSource(source: String?, debug: String?) {
|
||||
super.visitSource(source, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassVisitor.withRemoveSameLinesInLineTableVisitor(): ClassVisitor {
|
||||
return object : ClassVisitor(Opcodes.API_VERSION, this) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
desc: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val methodVisitor = super.visitMethod(access, name, desc, signature, exceptions) ?: return null
|
||||
|
||||
return object : MethodVisitor(Opcodes.API_VERSION, methodVisitor) {
|
||||
val labels = HashSet<String>()
|
||||
|
||||
override fun visitLineNumber(line: Int, start: Label?) {
|
||||
val added = labels.add(start.toString())
|
||||
|
||||
if (added) {
|
||||
super.visitLineNumber(line, start)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+44
@@ -1,3 +1,4 @@
|
||||
// FILE: text.kt
|
||||
package isInsideInlineLambda
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -73,3 +74,46 @@ class A {
|
||||
// ADDITIONAL_BREAKPOINT: isInsideInlineLambdaInLibrary.kt:Breakpoint5:(1)
|
||||
// EXPRESSION: it + 15
|
||||
// RESULT: 20: I
|
||||
|
||||
// FILE: isInsideInlineLambdaInLibrary.kt
|
||||
package isInsideInlineLambdaInLibrary
|
||||
|
||||
public fun test() {
|
||||
val a = A()
|
||||
//Breakpoint1
|
||||
a.foo(1) { 1 }
|
||||
|
||||
// inside other lambda
|
||||
a.foo(100) {
|
||||
//Breakpoint2
|
||||
a.foo(2) { 1 }
|
||||
1
|
||||
}
|
||||
|
||||
// inside variable declaration
|
||||
//Breakpoint3
|
||||
val x = a.foo(3) { 1 }
|
||||
|
||||
// inside object declaration
|
||||
val y = object {
|
||||
fun foo() {
|
||||
//Breakpoint4
|
||||
a.foo(4) { 1 }
|
||||
}
|
||||
}
|
||||
y.foo()
|
||||
|
||||
// inside local function
|
||||
fun local() {
|
||||
//Breakpoint5
|
||||
a.foo(5) { 1 }
|
||||
}
|
||||
local()
|
||||
}
|
||||
|
||||
class A {
|
||||
inline fun foo(i: Int, f: (i: Int) -> Int): A {
|
||||
f(i)
|
||||
return this
|
||||
}
|
||||
}
|
||||
idea/jvm-debugger/jvm-debugger-test/testData/evaluation/multipleBreakpoints/isInsideInlineLambda.out
Vendored
+20
-20
@@ -1,34 +1,34 @@
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:6 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:11 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:17 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:23 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:31 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:9 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:17 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:25 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:33 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambda.kt:43 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:7 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:12 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:18 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:24 lambdaOrdinal = 1
|
||||
LineBreakpoint created at isInsideInlineLambdaInLibrary.kt:32 lambdaOrdinal = 1
|
||||
LineBreakpoint created at text.kt:10 lambdaOrdinal = 1
|
||||
LineBreakpoint created at text.kt:18 lambdaOrdinal = 1
|
||||
LineBreakpoint created at text.kt:26 lambdaOrdinal = 1
|
||||
LineBreakpoint created at text.kt:34 lambdaOrdinal = 1
|
||||
LineBreakpoint created at text.kt:44 lambdaOrdinal = 1
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
isInsideInlineLambda.kt:9
|
||||
text.kt:10
|
||||
Compile bytecode for it + 1
|
||||
isInsideInlineLambda.kt:17
|
||||
text.kt:18
|
||||
Compile bytecode for it + 2
|
||||
isInsideInlineLambda.kt:25
|
||||
text.kt:26
|
||||
Compile bytecode for it + 3
|
||||
isInsideInlineLambda.kt:33
|
||||
text.kt:34
|
||||
Compile bytecode for it + 4
|
||||
isInsideInlineLambda.kt:43
|
||||
text.kt:44
|
||||
Compile bytecode for it + 5
|
||||
isInsideInlineLambdaInLibrary.kt:6
|
||||
isInsideInlineLambdaInLibrary.kt:7
|
||||
Compile bytecode for it + 11
|
||||
isInsideInlineLambdaInLibrary.kt:11
|
||||
isInsideInlineLambdaInLibrary.kt:12
|
||||
Compile bytecode for it + 12
|
||||
isInsideInlineLambdaInLibrary.kt:17
|
||||
isInsideInlineLambdaInLibrary.kt:18
|
||||
Compile bytecode for it + 13
|
||||
isInsideInlineLambdaInLibrary.kt:23
|
||||
isInsideInlineLambdaInLibrary.kt:24
|
||||
Compile bytecode for it + 14
|
||||
isInsideInlineLambdaInLibrary.kt:31
|
||||
isInsideInlineLambdaInLibrary.kt:32
|
||||
Compile bytecode for it + 15
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+85
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: customLibClassName.kt
|
||||
package customLibClassName
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -26,4 +27,87 @@ fun main(args: Array<String>) {
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: simpleLibFile.kt:public fun foo() {
|
||||
// EXPRESSION: 1 + 5
|
||||
// RESULT: 6: I
|
||||
// RESULT: 6: I
|
||||
|
||||
// FILE: lib/oneFunSameClassName/1/a1.kt
|
||||
@file:JvmName("SameNameOneFunSameFileName")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.oneFunSameClassName
|
||||
|
||||
public fun oneFunSameFileNameFun(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/oneFunSameClassName/2/a2.kt
|
||||
@file:JvmName("SameNameOneFunSameFileName")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.oneFunSameClassName
|
||||
|
||||
public fun oneFunSameFileNameFun2(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/twoFunDifferentSignature/1/a1.kt
|
||||
@file:JvmName("SameNameTwoFunDifferentSignature")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.twoFunDifferentSignature
|
||||
|
||||
public fun twoFunDifferentSignatureFun(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/twoFunDifferentSignature/2/a2.kt
|
||||
@file:JvmName("SameNameTwoFunDifferentSignature")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.twoFunDifferentSignature
|
||||
|
||||
public fun twoFunDifferentSignatureFun(i: Int): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/breakpointOnLocalProperty/1/a1.kt
|
||||
@file:JvmName("SameNameBreakpointOnLocalProperty")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.breakpointOnLocalProperty
|
||||
|
||||
public fun breakpointOnLocalPropertyFun(): Int {
|
||||
val a = 1
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/breakpointOnLocalProperty/2/a2.kt
|
||||
@file:JvmName("SameNameBreakpointOnLocalProperty")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.breakpointOnLocalProperty
|
||||
|
||||
public fun breakpointOnLocalPropertyFun2(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/property/1/a1.kt
|
||||
@file:JvmName("SameNameProperty")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.property
|
||||
|
||||
public val foo: Int =
|
||||
1
|
||||
|
||||
// FILE: lib/property/2/a2.kt
|
||||
@file:JvmName("SameNameProperty")
|
||||
@file:JvmMultifileClass
|
||||
package customLib.property
|
||||
|
||||
public fun someFun(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// FILE: lib/simpleLibFile/simpleLibFile.kt
|
||||
package customLib.simpleLibFile
|
||||
|
||||
public fun foo() {
|
||||
1 + 1
|
||||
}
|
||||
|
||||
class B {
|
||||
public var prop: Int = 1
|
||||
}
|
||||
+10
-10
@@ -1,19 +1,19 @@
|
||||
LineBreakpoint created at a1.kt:6
|
||||
LineBreakpoint created at a1.kt:6
|
||||
LineBreakpoint created at a1.kt:6
|
||||
LineBreakpoint created at a1.kt:6
|
||||
LineBreakpoint created at simpleLibFile.kt:4
|
||||
LineBreakpoint created at a1.kt:7
|
||||
LineBreakpoint created at a1.kt:7
|
||||
LineBreakpoint created at a1.kt:7
|
||||
LineBreakpoint created at a1.kt:7
|
||||
LineBreakpoint created at simpleLibFile.kt:5
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
a1.kt:6
|
||||
a1.kt:7
|
||||
Compile bytecode for 1 + 1
|
||||
a1.kt:6
|
||||
a1.kt:7
|
||||
Compile bytecode for 1 + 2
|
||||
a1.kt:6
|
||||
a1.kt:7
|
||||
Compile bytecode for 1 + 3
|
||||
a1.kt:6
|
||||
a1.kt:7
|
||||
Compile bytecode for 1 + 4
|
||||
simpleLibFile.kt:4
|
||||
simpleLibFile.kt:5
|
||||
Compile bytecode for 1 + 5
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+10
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: test.kt
|
||||
package localFunInLibrary
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -6,4 +7,12 @@ fun main(args: Array<String>) {
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: localFunCustomLib.kt:localFunInLibraryCustomLibProperty
|
||||
// EXPRESSION: localFun()
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: localFunCustomLib.kt
|
||||
package customLib.localFunInLibraryCustomLib
|
||||
|
||||
public fun localFunInLibraryCustomLibMainFun() {
|
||||
fun localFun() = 1
|
||||
val localFunInLibraryCustomLibProperty = 1
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at localFunCustomLib.kt:6
|
||||
LineBreakpoint created at localFunCustomLib.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
localFunCustomLib.kt:6
|
||||
localFunCustomLib.kt:7
|
||||
Compile bytecode for localFun()
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
Vendored
+17
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: breakpointInInlineFun.kt
|
||||
package breakpointInInlineFun
|
||||
|
||||
import customLib.inlineFunInLibrary.*
|
||||
@@ -9,4 +10,19 @@ fun main(args: Array<String>) {
|
||||
// RESUME: 2
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt:public inline fun inlineFun
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt: Breakpoint 2
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunInLibrary.kt: Breakpoint 2
|
||||
|
||||
// FILE: customLib/inlineFunInLibrary/inlineFunInLibrary.kt
|
||||
package customLib.inlineFunInLibrary
|
||||
|
||||
public inline fun inlineFun(f: () -> Unit) {
|
||||
1 + 1
|
||||
inlineFunInner {
|
||||
1 + 1
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun inlineFunInner(f: () -> Unit) {
|
||||
// Breakpoint 2
|
||||
1 + 1
|
||||
}
|
||||
Vendored
+4
-4
@@ -1,9 +1,9 @@
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:4
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:12
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:5
|
||||
LineBreakpoint created at inlineFunInLibrary.kt:13
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
inlineFunInLibrary.kt:4
|
||||
inlineFunInLibrary.kt:12
|
||||
inlineFunInLibrary.kt:5
|
||||
inlineFunInLibrary.kt:13
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
Vendored
+5
@@ -1,3 +1,4 @@
|
||||
// FILE: classFromAnotherPackage.kt
|
||||
package classFromAnotherPackage
|
||||
|
||||
import forTests.MyJavaClass
|
||||
@@ -12,3 +13,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
// EXPRESSION: forTests.MyJavaClass()
|
||||
// RESULT: instance of forTests.MyJavaClass(id=ID): LforTests/MyJavaClass;
|
||||
|
||||
// FILE: forTests/MyJavaClass.java
|
||||
package forTests;
|
||||
public class MyJavaClass {}
|
||||
idea/jvm-debugger/jvm-debugger-test/testData/evaluation/singleBreakpoint/classFromAnotherPackage.out
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at classFromAnotherPackage.kt:7
|
||||
LineBreakpoint created at classFromAnotherPackage.kt:8
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
classFromAnotherPackage.kt:7
|
||||
classFromAnotherPackage.kt:8
|
||||
Compile bytecode for MyJavaClass()
|
||||
Compile bytecode for forTests.MyJavaClass()
|
||||
Disconnected from the target VM
|
||||
|
||||
+26
@@ -1,3 +1,4 @@
|
||||
// FILE: createExpressionWithArray.kt
|
||||
package createExpressionWithArray
|
||||
|
||||
import forTests.MyJavaClass
|
||||
@@ -13,3 +14,28 @@ fun main(args: Array<String>) {
|
||||
|
||||
// PRINT_FRAME
|
||||
// DESCRIPTOR_VIEW_OPTIONS: NAME_EXPRESSION_RESULT
|
||||
|
||||
// FILE: forTests/MyJavaClass.java
|
||||
package forTests;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
public class MyJavaClass {
|
||||
public static class BaseClass {
|
||||
public final int i2 = 1;
|
||||
}
|
||||
|
||||
public BaseClass getBaseClassValue() {
|
||||
return new BaseClass();
|
||||
}
|
||||
public BaseClass getInnerClassValue() {
|
||||
return new InnerClass();
|
||||
}
|
||||
|
||||
public static class InnerClass extends BaseClass {
|
||||
public final int i = 1;
|
||||
}
|
||||
|
||||
public MyJavaClass() {}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at createExpressionWithArray.kt:11
|
||||
LineBreakpoint created at createExpressionWithArray.kt:12
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
createExpressionWithArray.kt:11
|
||||
createExpressionWithArray.kt:12
|
||||
Compile bytecode for args
|
||||
Compile bytecode for baseArray
|
||||
Compile bytecode for baseArray[0]
|
||||
|
||||
+14
@@ -1,3 +1,4 @@
|
||||
// FILE: delegatedPropertyInOtherFile.kt
|
||||
package delegatedPropertyInOtherFile
|
||||
|
||||
import delegatedPropertyInOtherFileOther.*
|
||||
@@ -11,3 +12,16 @@ fun main(a: Array<String>) {
|
||||
|
||||
// EXPRESSION: t.a
|
||||
// RESULT: 12: I
|
||||
|
||||
// FILE: delegatedPropertyInOtherFile/delegatedPropertyInOtherFile2.kt
|
||||
package delegatedPropertyInOtherFileOther
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class WithDelegate {
|
||||
val a: Int by Id(12)
|
||||
}
|
||||
|
||||
class Id(val v: Int) {
|
||||
operator fun getValue(o: Any, property: KProperty<*>): Int = v
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at delegatedPropertyInOtherFile.kt:9
|
||||
LineBreakpoint created at delegatedPropertyInOtherFile.kt:10
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
delegatedPropertyInOtherFile.kt:9
|
||||
delegatedPropertyInOtherFile.kt:10
|
||||
Compile bytecode for t.a
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
-1
@@ -2,7 +2,6 @@ LineBreakpoint created at evDelegatedProperty.kt:13
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
evDelegatedProperty.kt:13
|
||||
Compile bytecode for a.prop
|
||||
frame = main:13, EvDelegatedPropertyKt {evDelegatedProperty}
|
||||
local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = evDelegatedProperty.kt, 9)
|
||||
local = a: evDelegatedProperty.A = {evDelegatedProperty.A@uniqueID} (sp = evDelegatedProperty.kt, 10)
|
||||
|
||||
+80
-3
@@ -1,3 +1,4 @@
|
||||
// FILE: fieldGetters.kt
|
||||
package fieldGetters
|
||||
|
||||
import forTests.FieldsGetters
|
||||
@@ -30,8 +31,6 @@ class K2 {
|
||||
// EXPRESSION: K2().a_field
|
||||
// RESULT: 0: I
|
||||
|
||||
|
||||
|
||||
// EXPRESSION: PublicField().foo
|
||||
// RESULT: "a": Ljava/lang/String;
|
||||
|
||||
@@ -102,4 +101,82 @@ class K2 {
|
||||
// RESULT: "a": Ljava/lang/String;
|
||||
|
||||
// EXPRESSION: PublicFieldAndGetterInParent().foo_field
|
||||
// RESULT: "b": Ljava/lang/String;
|
||||
// RESULT: "b": Ljava/lang/String;
|
||||
|
||||
// FILE: forTests/FieldsGetters.java
|
||||
package forTests;
|
||||
|
||||
public class FieldsGetters {
|
||||
public static class PublicField {
|
||||
public String foo = "a";
|
||||
}
|
||||
|
||||
public static class PackagePrivateField {
|
||||
String foo = "b";
|
||||
}
|
||||
|
||||
public static class ProtectedField {
|
||||
protected String foo = "c";
|
||||
}
|
||||
|
||||
public static class PrivateField {
|
||||
private String foo = "d";
|
||||
}
|
||||
|
||||
public static class PublicFieldGetter {
|
||||
public final String foo = "a";
|
||||
|
||||
public String getFoo() {
|
||||
return "b";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrivateFieldPublicGetter {
|
||||
private final String foo = "c";
|
||||
|
||||
public String getFoo() {
|
||||
return "d";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrivateFieldPrivateGetter {
|
||||
private final String foo = "e";
|
||||
|
||||
public String getFoo() {
|
||||
return "f";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PublicGetter1 extends PublicField {
|
||||
public String getFoo() {
|
||||
return "g";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PublicGetter2 extends PackagePrivateField {
|
||||
public String getFoo() {
|
||||
return "h";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrivateGetter1 extends PrivateField {
|
||||
private String getFoo() {
|
||||
return "g";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PublicGetterOnly {
|
||||
public String getFoo() {
|
||||
return "a";
|
||||
}
|
||||
}
|
||||
|
||||
public static class PublicFieldAndGetterInParent extends PublicGetterOnly {
|
||||
public String foo = "b";
|
||||
}
|
||||
|
||||
public abstract class AbstractGetter {
|
||||
public String foo = "c";
|
||||
public abstract String getFoo();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at fieldGetters.kt:8
|
||||
LineBreakpoint created at fieldGetters.kt:9
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
fieldGetters.kt:8
|
||||
fieldGetters.kt:9
|
||||
Compile bytecode for K1().a
|
||||
Compile bytecode for K1().a_field
|
||||
Compile bytecode for K2().a
|
||||
|
||||
+15
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: fileWithError.kt
|
||||
package fileWithError
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -8,4 +9,17 @@ fun main(args: Array<String>) {
|
||||
// ADDITIONAL_BREAKPOINT: fileWithInternal.kt:Breakpoint
|
||||
|
||||
// EXPRESSION: 1
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: lib/fileWithInternal.kt
|
||||
package fileWithInternal
|
||||
|
||||
fun test() {
|
||||
// Breakpoint
|
||||
val a = fileWithInternal2.MyInternal()
|
||||
}
|
||||
|
||||
// FILE: lib/fileWithInternal2.kt
|
||||
package fileWithInternal2
|
||||
|
||||
internal class MyInternal
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at fileWithInternal.kt:5
|
||||
LineBreakpoint created at fileWithInternal.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
fileWithInternal.kt:5
|
||||
fileWithInternal.kt:6
|
||||
Compile bytecode for 1
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+7
@@ -1,3 +1,4 @@
|
||||
// FILE: inlineFunInMultiFilePackage.kt
|
||||
package inlineFunInMultiFilePackage
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -8,3 +9,9 @@ fun main(args: Array<String>) {
|
||||
// EXPRESSION: multiFilePackage.foo { 1 }
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: multiFilePackage.kt
|
||||
@file:JvmMultifileClass
|
||||
@file:JvmName("NewName")
|
||||
package multiFilePackage
|
||||
|
||||
inline fun foo(f: () -> Int) = f()
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at inlineFunInMultiFilePackage.kt:5
|
||||
LineBreakpoint created at inlineFunInMultiFilePackage.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
inlineFunInMultiFilePackage.kt:5
|
||||
inlineFunInMultiFilePackage.kt:6
|
||||
Compile bytecode for multiFilePackage.foo { 1 }
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+12
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: inlineFunction.kt
|
||||
package inlineFunction
|
||||
|
||||
import inlineFunctionOtherPackage.*
|
||||
@@ -13,4 +14,14 @@ inline fun foo() = 1
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: foo()
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: lib.kt
|
||||
package inlineFunctionOtherPackage
|
||||
|
||||
inline fun myFun(f: () -> Int): Int = f()
|
||||
|
||||
val String.prop: String
|
||||
get() {
|
||||
return "a"
|
||||
}
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at inlineFunction.kt:7
|
||||
LineBreakpoint created at inlineFunction.kt:8
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
inlineFunction.kt:7
|
||||
inlineFunction.kt:8
|
||||
Compile bytecode for myFun { 1 }
|
||||
Compile bytecode for foo()
|
||||
Disconnected from the target VM
|
||||
|
||||
+9
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: test.kt
|
||||
package inlineFunctionBreakpointAnotherFile
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -10,4 +11,11 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
}
|
||||
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunctionWithBreakpoint.kt:inline fun myFun
|
||||
// ADDITIONAL_BREAKPOINT: inlineFunctionWithBreakpoint.kt:inline fun myFun
|
||||
|
||||
// FILE: inlineFunctionWithBreakpoint.kt
|
||||
package inlineFunctionWithBreakpoint
|
||||
|
||||
inline fun myFun(f: (Int) -> Unit) {
|
||||
f(1)
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at inlineFunctionWithBreakpoint.kt:4
|
||||
LineBreakpoint created at inlineFunctionWithBreakpoint.kt:5
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
inlineFunctionWithBreakpoint.kt:4
|
||||
inlineFunctionWithBreakpoint.kt:5
|
||||
Disconnected from the target VM
|
||||
|
||||
Process finished with exit code 0
|
||||
|
||||
Vendored
+18
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: jcBlock.kt
|
||||
package jcBlock
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -16,4 +17,20 @@ fun main(args: Array<String>) {
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: elseVal
|
||||
// RESULT: Unresolved reference: elseVal
|
||||
// RESULT: Unresolved reference: elseVal
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public void block() {
|
||||
int bodyVal = 1;
|
||||
if (true) {
|
||||
int thenVal = 1;
|
||||
int breakpoint = 1;
|
||||
}
|
||||
else {
|
||||
int elseVal = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+5
-5
@@ -1,10 +1,10 @@
|
||||
LineBreakpoint created at jcBlock.kt:6
|
||||
LineBreakpoint created at jcBlock.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcBlock.kt:6
|
||||
JavaClass.java:16
|
||||
JavaClass.java:18
|
||||
JavaClass.java:19
|
||||
jcBlock.kt:7
|
||||
JavaClass.java:6
|
||||
JavaClass.java:8
|
||||
JavaClass.java:9
|
||||
Compile bytecode for bodyVal
|
||||
Compile bytecode for thenVal
|
||||
Disconnected from the target VM
|
||||
|
||||
Vendored
+21
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: jcImports.kt
|
||||
package jcImports
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -10,4 +11,23 @@ fun main(args: Array<String>) {
|
||||
// STEP_OVER: 1
|
||||
|
||||
// EXPRESSION: list.filter { it == 1 }.size
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class JavaClass {
|
||||
public void imports() {
|
||||
ArrayList<Integer> list = createList();
|
||||
int breakpoint = 1;
|
||||
}
|
||||
|
||||
private ArrayList<Integer> createList() {
|
||||
ArrayList<Integer> list = new ArrayList<Integer>();
|
||||
list.add(1);
|
||||
list.add(2);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
Vendored
+4
-4
@@ -1,9 +1,9 @@
|
||||
LineBreakpoint created at jcImports.kt:6
|
||||
LineBreakpoint created at jcImports.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcImports.kt:6
|
||||
JavaClass.java:27
|
||||
JavaClass.java:28
|
||||
jcImports.kt:7
|
||||
JavaClass.java:8
|
||||
JavaClass.java:9
|
||||
Compile bytecode for list.filter { it == 1 }.size
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+12
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: jcLocalVariable.kt
|
||||
package jcLocalVariable
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -10,4 +11,14 @@ fun main(args: Array<String>) {
|
||||
// STEP_OVER: 1
|
||||
|
||||
// EXPRESSION: i
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public void localVariable() {
|
||||
int i = 1;
|
||||
int breakpoint = 1;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
LineBreakpoint created at jcLocalVariable.kt:6
|
||||
LineBreakpoint created at jcLocalVariable.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcLocalVariable.kt:6
|
||||
JavaClass.java:11
|
||||
JavaClass.java:12
|
||||
jcLocalVariable.kt:7
|
||||
JavaClass.java:6
|
||||
JavaClass.java:7
|
||||
Compile bytecode for i
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+12
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: test.kt
|
||||
package jcMarkedObject
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -15,4 +16,14 @@ fun main(args: Array<String>) {
|
||||
// EXPRESSION: i_DebugLabel
|
||||
// RESULT: instance of java.lang.Integer(id=ID): Ljava/lang/Integer;
|
||||
|
||||
// DEBUG_LABEL: i = i
|
||||
// DEBUG_LABEL: i = i
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public void markObject() {
|
||||
Integer i = 1;
|
||||
int breakpoint = 1;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
LineBreakpoint created at jcMarkedObject.kt:6
|
||||
LineBreakpoint created at test.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcMarkedObject.kt:6
|
||||
JavaClass.java:39
|
||||
JavaClass.java:40
|
||||
test.kt:7
|
||||
JavaClass.java:6
|
||||
JavaClass.java:7
|
||||
Compile bytecode for i
|
||||
Compile bytecode for i_DebugLabel
|
||||
Disconnected from the target VM
|
||||
|
||||
Vendored
+14
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: test.kt
|
||||
package jcProperty
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -15,4 +16,16 @@ fun main(args: Array<String>) {
|
||||
// RESULT: 1: I
|
||||
|
||||
// EXPRESSION: javaPrivateProperty
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public int javaProperty = 1;
|
||||
private int javaPrivateProperty = 1;
|
||||
|
||||
public void property() {
|
||||
int breakpoint = 1;
|
||||
}
|
||||
}
|
||||
Vendored
+3
-3
@@ -1,8 +1,8 @@
|
||||
LineBreakpoint created at jcProperty.kt:6
|
||||
LineBreakpoint created at test.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcProperty.kt:6
|
||||
JavaClass.java:47
|
||||
test.kt:7
|
||||
JavaClass.java:9
|
||||
Compile bytecode for this.javaProperty
|
||||
Compile bytecode for javaProperty
|
||||
Compile bytecode for javaPrivateProperty
|
||||
|
||||
Vendored
+11
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: jcSimple.kt
|
||||
package jcSimple
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -9,4 +10,13 @@ fun main(args: Array<String>) {
|
||||
// STEP_INTO: 1
|
||||
|
||||
// EXPRESSION: 1 + 1
|
||||
// RESULT: 2: I
|
||||
// RESULT: 2: I
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public void simple() {
|
||||
int breakpoint = 1;
|
||||
}
|
||||
}
|
||||
Vendored
+3
-3
@@ -1,8 +1,8 @@
|
||||
LineBreakpoint created at jcSimple.kt:6
|
||||
LineBreakpoint created at jcSimple.kt:7
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
jcSimple.kt:6
|
||||
JavaClass.java:7
|
||||
jcSimple.kt:7
|
||||
JavaClass.java:6
|
||||
Compile bytecode for 1 + 1
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
Vendored
+10
@@ -1,3 +1,4 @@
|
||||
// FILE: javaStaticMethods.kt
|
||||
package javaStaticMethods
|
||||
|
||||
import forTests.javaContext.JavaClass.JavaStatic
|
||||
@@ -9,3 +10,12 @@ fun main() {
|
||||
|
||||
// EXPRESSION: JavaStatic.state()
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/javaContext/JavaClass.java
|
||||
package forTests.javaContext;
|
||||
|
||||
public class JavaClass {
|
||||
public interface JavaStatic {
|
||||
static int state() { return 1; }
|
||||
}
|
||||
}
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at javaStaticMethods.kt:7
|
||||
LineBreakpoint created at javaStaticMethods.kt:8
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
javaStaticMethods.kt:7
|
||||
javaStaticMethods.kt:8
|
||||
Compile bytecode for JavaStatic.state()
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
+13
@@ -1,3 +1,4 @@
|
||||
// FILE: privateClass.kt
|
||||
package privateClass
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -32,3 +33,15 @@ class A {
|
||||
|
||||
// EXPRESSION: forTests.MyJavaClass.PrivateJavaClass().prop
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/MyJavaClass.java
|
||||
package forTests;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
public class MyJavaClass {
|
||||
private static class PrivateJavaClass {
|
||||
public final int prop = 1;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at privateClass.kt:5
|
||||
LineBreakpoint created at privateClass.kt:6
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
privateClass.kt:5
|
||||
privateClass.kt:6
|
||||
Compile bytecode for A.PrivateClass()
|
||||
Compile bytecode for A.PrivateClass().prop
|
||||
Compile bytecode for A().PrivateInnerClass()
|
||||
|
||||
Vendored
+20
-1
@@ -1,3 +1,4 @@
|
||||
// FILE: rawTypeskt11831.kt
|
||||
package rawTypeskt11831
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -8,4 +9,22 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
// EXPRESSION: foo
|
||||
// RESULT: 1: I
|
||||
// RESULT: 1: I
|
||||
|
||||
// FILE: forTests/MyJavaClass.java
|
||||
package forTests;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
public class MyJavaClass {
|
||||
public static class RawA<T> {
|
||||
public int foo(List<T> p) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RawADerived extends RawA {
|
||||
|
||||
}
|
||||
}
|
||||
Vendored
+2
-2
@@ -1,7 +1,7 @@
|
||||
LineBreakpoint created at rawTypeskt11831.kt:7
|
||||
LineBreakpoint created at rawTypeskt11831.kt:8
|
||||
Run Java
|
||||
Connected to the target VM
|
||||
rawTypeskt11831.kt:7
|
||||
rawTypeskt11831.kt:8
|
||||
Compile bytecode for foo
|
||||
Disconnected from the target VM
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package forTests
|
||||
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlin.coroutines.startCoroutine
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation<Any?> {
|
||||
companion object : EmptyContinuation()
|
||||
override fun resumeWith(result: Result<Any?>) { result.getOrThrow() }
|
||||
}
|
||||
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
class WaitFinish(
|
||||
private val timeout: Long = 5,
|
||||
private val unit: TimeUnit = TimeUnit.SECONDS
|
||||
) {
|
||||
private val cdl = CountDownLatch(1)
|
||||
|
||||
fun finish() {
|
||||
cdl.countDown()
|
||||
}
|
||||
|
||||
fun waitEnd() {
|
||||
if (!cdl.await(timeout, unit)) {
|
||||
throw java.lang.IllegalStateException("Too long wait in debugger test!")
|
||||
}
|
||||
|
||||
Thread.sleep(10)
|
||||
}
|
||||
}
|
||||
Vendored
+3
-1
@@ -3,4 +3,6 @@ package streams.sequence.sort
|
||||
fun main(args: Array<String>) {
|
||||
//Breakpoint!
|
||||
arrayOf(Person("Bob", 42), Person("Alice", 27)).asSequence().sortedByDescending { it.age }.count()
|
||||
}
|
||||
}
|
||||
|
||||
data class Person(val name: String, val age: Int)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user