Debugger tests: mark objects using NodeDescriptorImpl.getMarkup. Primitive values cannot be marked.

This commit is contained in:
Natalia Ukhorskaya
2016-08-01 18:24:12 +03:00
parent 252b8ec6db
commit d823d6f4ab
5 changed files with 36 additions and 39 deletions
@@ -20,8 +20,10 @@ import com.intellij.debugger.DebuggerManagerEx
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory import com.intellij.debugger.engine.evaluation.CodeFragmentFactory
import com.intellij.debugger.engine.evaluation.CodeFragmentKind import com.intellij.debugger.engine.evaluation.CodeFragmentKind
import com.intellij.debugger.engine.evaluation.TextWithImports import com.intellij.debugger.engine.evaluation.TextWithImports
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.impl.DebuggerContextImpl
import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl
import com.intellij.ide.highlighter.JavaFileType import com.intellij.ide.highlighter.JavaFileType
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
@@ -141,7 +143,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
private fun getWrappedContextElement(project: Project, context: PsiElement?) private fun getWrappedContextElement(project: Project, context: PsiElement?)
= wrapContextIfNeeded(project, getContextElement(context)) = wrapContextIfNeeded(project, context, getContextElement(context))
override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment { override fun createPresentationCodeFragment(item: TextWithImports, context: PsiElement?, project: Project): JavaCodeFragment {
val kotlinCodeFragment = createCodeFragment(item, context, project) val kotlinCodeFragment = createCodeFragment(item, context, project)
@@ -202,7 +204,7 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
companion object { companion object {
val LABEL_VARIABLE_VALUE_KEY: Key<Value> = Key.create<Value>("_label_variable_value_key_") val LABEL_VARIABLE_VALUE_KEY: Key<Value> = Key.create<Value>("_label_variable_value_key_")
val DEBUG_LABEL_SUFFIX: String = "_DebugLabel" val DEBUG_LABEL_SUFFIX: String = "_DebugLabel"
@TestOnly val DEBUG_FRAME_FOR_TESTS: Key<StackFrameProxyImpl> = Key.create("DEBUG_FRAME_FOR_TESTS") @TestOnly val DEBUG_CONTEXT_FOR_TESTS: Key<DebuggerContextImpl> = Key.create("DEBUG_CONTEXT_FOR_TESTS")
fun getContextElement(elementAt: PsiElement?): KtElement? { fun getContextElement(elementAt: PsiElement?): KtElement? {
if (elementAt == null) return null if (elementAt == null) return null
@@ -283,17 +285,19 @@ class KotlinCodeFragmentFactory: CodeFragmentFactory() {
} }
} }
private fun wrapContextIfNeeded(project: Project, originalContext: KtElement?): KtElement? { private fun wrapContextIfNeeded(project: Project, originalContext: PsiElement?, newContext: KtElement?): KtElement? {
val session = XDebuggerManager.getInstance(project).currentSession as? XDebugSessionImpl val markupMap: Map<*, ValueMarkup>? =
?: return originalContext if (ApplicationManager.getApplication().isUnitTestMode)
NodeDescriptorImpl.getMarkupMap(originalContext?.getCopyableUserData(DEBUG_CONTEXT_FOR_TESTS)?.debugProcess)
else
(XDebuggerManager.getInstance(project).currentSession as? XDebugSessionImpl)?.valueMarkers?.allMarkers
val markupMap = session.valueMarkers?.allMarkers if (markupMap == null || markupMap.isEmpty()) return newContext
if (markupMap == null || markupMap.isEmpty()) return originalContext
val (text, labels) = createCodeFragmentForLabeledObjects(project, markupMap) val (text, labels) = createCodeFragmentForLabeledObjects(project, markupMap)
if (text.isEmpty()) return originalContext if (text.isEmpty()) return newContext
return createWrappingContext(text, labels, originalContext, project) return createWrappingContext(text, labels, newContext, project)
} }
// internal for test // internal for test
+2 -3
View File
@@ -1,10 +1,9 @@
LineBreakpoint created at ldifferentTypes.kt:92 LineBreakpoint created at ldifferentTypes.kt:88
!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ldifferentTypes.LdifferentTypesKt !JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !OUTPUT_PATH!;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! ldifferentTypes.LdifferentTypesKt
Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket'
ldifferentTypes.kt:92 ldifferentTypes.kt:88
Compile bytecode for str_DebugLabel Compile bytecode for str_DebugLabel
Compile bytecode for strArray_DebugLabel Compile bytecode for strArray_DebugLabel
Compile bytecode for primitive_DebugLabel
Compile bytecode for primitiveArray_DebugLabel Compile bytecode for primitiveArray_DebugLabel
Compile bytecode for localObj_DebugLabel Compile bytecode for localObj_DebugLabel
Compile bytecode for localObjArray_DebugLabel Compile bytecode for localObjArray_DebugLabel
@@ -1,11 +1,11 @@
package lIdentifier package lIdentifier
fun main(args: Array<String>) { fun main(args: Array<String>) {
val a = 1 val a = "1"
val b = 1 val b = "1"
val c = 1 val c = "1"
val d = 1 val d = "1"
val e = 1 val e = "1"
//Breakpoint! //Breakpoint!
val f = 1 val f = 1
} }
@@ -17,4 +17,4 @@ fun main(args: Array<String>) {
// DEBUG_LABEL: e = e-e // DEBUG_LABEL: e = e-e
// EXPRESSION: a // EXPRESSION: a
// RESULT: 1: I // RESULT: "1": Ljava/lang/String;
@@ -10,10 +10,6 @@ fun main(args: Array<String>) {
// DEBUG_LABEL: strArray = strArray // DEBUG_LABEL: strArray = strArray
val strArray = arrayOf("str") val strArray = arrayOf("str")
// EXPRESSION: primitive_DebugLabel
// RESULT: 1: I
// DEBUG_LABEL: primitive = primitive
val primitive = 1
// EXPRESSION: primitiveArray_DebugLabel // EXPRESSION: primitiveArray_DebugLabel
// RESULT: instance of java.lang.Integer[1] (id=ID): [Ljava/lang/Integer; // RESULT: instance of java.lang.Integer[1] (id=ID): [Ljava/lang/Integer;
// DEBUG_LABEL: primitiveArray = primitiveArray // DEBUG_LABEL: primitiveArray = primitiveArray
@@ -33,7 +33,6 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiExpression import com.intellij.psi.PsiExpression
import com.intellij.xdebugger.impl.XDebugSessionImpl import com.intellij.xdebugger.impl.XDebugSessionImpl
import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl
@@ -135,6 +134,8 @@ abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
doOnBreakpoint { doOnBreakpoint {
val exceptions = linkedMapOf<String, Throwable>() val exceptions = linkedMapOf<String, Throwable>()
try { try {
createMarkers(fileText)
for ((expression, expected) in expressions) { for ((expression, expected) in expressions) {
mayThrow(exceptions, expression) { mayThrow(exceptions, expression) {
evaluate(expression, CodeFragmentKind.EXPRESSION, expected) evaluate(expression, CodeFragmentKind.EXPRESSION, expected)
@@ -428,30 +429,24 @@ abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
return mainFile.parentFile?.listFiles()?.filter { it.name.startsWith(mainFileName) && it.name != mainFileName } ?: Collections.emptyList() return mainFile.parentFile?.listFiles()?.filter { it.name.startsWith(mainFileName) && it.name != mainFileName } ?: Collections.emptyList()
} }
private fun createContextElement(context: SuspendContextImpl): PsiElement { private fun createMarkers(fileText: String) {
val contextElement = ContextUtil.getContextElement(debuggerContext)!! val labelsAsText = findLinesWithPrefixesRemoved(fileText, "// DEBUG_LABEL: ")
Assert.assertTrue("KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. ContextElement = ${contextElement.text}", if (labelsAsText.isEmpty()) return
KotlinCodeFragmentFactory().isContextAccepted(contextElement))
val labelsAsText = findLinesWithPrefixesRemoved(contextElement.containingFile.text, "// DEBUG_LABEL: ") val markupMap = NodeDescriptorImpl.getMarkupMap(debugProcess)
if (labelsAsText.isEmpty()) return contextElement
val markupMap = hashMapOf<com.sun.jdi.Value, ValueMarkup>()
for (labelAsText in labelsAsText) { for (labelAsText in labelsAsText) {
val labelParts = labelAsText.split("=") val labelParts = labelAsText.split("=")
assert(labelParts.size == 2) { "Wrong format for DEBUG_LABEL directive: // DEBUG_LABEL: {localVariableName} = {labelText}"} assert(labelParts.size == 2) { "Wrong format for DEBUG_LABEL directive: // DEBUG_LABEL: {localVariableName} = {labelText}"}
val localVariableName = labelParts[0].trim() val localVariableName = labelParts[0].trim()
val labelName = labelParts[1].trim() val labelName = labelParts[1].trim()
val localVariable = context.frameProxy!!.visibleVariableByName(localVariableName) val localVariable = debuggerContext.frameProxy!!.visibleVariableByName(localVariableName)
assert(localVariable != null) { "Couldn't find localVariable for label: name = $localVariableName" } assert(localVariable != null) { "Couldn't find localVariable for label: name = $localVariableName" }
val localVariableValue = context.frameProxy!!.getValue(localVariable) val localVariableValue = debuggerContext.frameProxy!!.getValue(localVariable) as? ObjectReference
assert(localVariableValue != null) { "Local variable $localVariableName should be an ObjectReference" } assert(localVariableValue != null) { "Local variable $localVariableName should be an ObjectReference" }
localVariableValue!! localVariableValue!!
markupMap.put(localVariableValue, ValueMarkup(labelName, null, labelName)) markupMap?.put(localVariableValue, ValueMarkup(labelName, null, labelName))
} }
val (text, labels) = KotlinCodeFragmentFactory.createCodeFragmentForLabeledObjects(contextElement.project, markupMap)
return KotlinCodeFragmentFactory().createWrappingContext(text, labels, KotlinCodeFragmentFactory.getContextElement(contextElement), project)!!
} }
private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String?) { private fun SuspendContextImpl.evaluate(text: String, codeFragmentKind: CodeFragmentKind, expectedResult: String?) {
@@ -461,9 +456,12 @@ abstract class AbstractKotlinEvaluateExpressionTest : KotlinDebuggerTestBase() {
private fun SuspendContextImpl.evaluate(item: TextWithImportsImpl, expectedResult: String?) { private fun SuspendContextImpl.evaluate(item: TextWithImportsImpl, expectedResult: String?) {
runReadAction { runReadAction {
val sourcePosition = ContextUtil.getSourcePosition(this) val sourcePosition = ContextUtil.getSourcePosition(this)
val contextElement = createContextElement(this)
contextElement.putCopyableUserData(KotlinCodeFragmentFactory.DEBUG_FRAME_FOR_TESTS, this@AbstractKotlinEvaluateExpressionTest.evaluationContext.frameProxy) val contextElement = ContextUtil.getContextElement(debuggerContext)!!
Assert.assertTrue("KotlinCodeFragmentFactory should be accepted for context element otherwise default evaluator will be called. ContextElement = ${contextElement.text}",
KotlinCodeFragmentFactory().isContextAccepted(contextElement))
contextElement.putCopyableUserData(KotlinCodeFragmentFactory.DEBUG_CONTEXT_FOR_TESTS, this@AbstractKotlinEvaluateExpressionTest.debuggerContext)
try { try {