From 2b015ae09ab48f53dad39a9967cfa42f47b45fc1 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Wed, 20 May 2015 15:13:28 +0300 Subject: [PATCH] Debugger: support field watchpoints for kotlin --- .../kotlin/idea/JetBundle.properties | 1 + idea/src/META-INF/plugin.xml | 2 + .../KotlinBreakpointHandlerFactory.kt | 30 +++ .../breakpoints/KotlinFieldBreakpointType.kt | 224 ++++++++++++++++++ .../tinyApp/outs/fieldWatchpoints.out | 44 ++++ .../outs/inapplicableFieldWatchpoints.out | 5 + .../tinyApp/src/stepOut/fieldWatchpoints.kt | 65 +++++ .../stepOut/inapplicableFieldWatchpoints.kt | 23 ++ .../idea/debugger/KotlinDebuggerTestBase.kt | 78 ++++++ .../debugger/KotlinSteppingTestGenerated.java | 21 ++ 10 files changed, 493 insertions(+) create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointHandlerFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt create mode 100644 idea/testData/debugger/tinyApp/outs/fieldWatchpoints.out create mode 100644 idea/testData/debugger/tinyApp/outs/inapplicableFieldWatchpoints.out create mode 100644 idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt create mode 100644 idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties index c8eb4361f4e..2e5b286f705 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/JetBundle.properties @@ -338,3 +338,4 @@ kotlin.compiler.js.option.output.copy.dir=O&utput directory for library &runtime # Debugger debugger.filter.ignore.internal.classes=Do not step into specific Kotlin classes debugger.data.view.delegated.properties=Calculate values of delegated properties (may affect program execution) +debugger.field.watchpoints.tab.title=Kotlin Field Watchpoints diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index e93e6e49357..18d34b72cbd 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -406,7 +406,9 @@ + + diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointHandlerFactory.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointHandlerFactory.kt new file mode 100644 index 00000000000..7645f437e85 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinBreakpointHandlerFactory.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.breakpoints + +import com.intellij.debugger.engine.DebugProcessImpl +import com.intellij.debugger.engine.JavaBreakpointHandler +import com.intellij.debugger.engine.JavaBreakpointHandlerFactory +import com.intellij.debugger.ui.breakpoints.JavaLineBreakpointType + +public class KotlinBreakpointHandlerFactory: JavaBreakpointHandlerFactory { + override fun createHandler(process: DebugProcessImpl): JavaBreakpointHandler? { + return KotlinFieldBreakpointHandler(process) + } +} + +public class KotlinFieldBreakpointHandler(process: DebugProcessImpl) : JavaBreakpointHandler(javaClass(), process) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt new file mode 100644 index 00000000000..1d838acf268 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/debugger/breakpoints/KotlinFieldBreakpointType.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger.breakpoints + +import com.intellij.debugger.SourcePosition +import com.intellij.debugger.impl.PositionUtil +import com.intellij.debugger.ui.breakpoints.* +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.* +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.xdebugger.XDebuggerUtil +import com.intellij.xdebugger.breakpoints.XBreakpoint +import com.intellij.xdebugger.breakpoints.XLineBreakpoint +import com.intellij.xdebugger.breakpoints.XLineBreakpointType +import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel +import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider +import org.jetbrains.annotations.TestOnly +import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.idea.JetBundle +import org.jetbrains.kotlin.idea.JetFileType +import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.load.kotlin.PackageClassUtils +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import javax.swing.Icon +import javax.swing.JComponent + +public class KotlinFieldBreakpointType : JavaBreakpointType, XLineBreakpointType( + "kotlin-field", JetBundle.message("debugger.field.watchpoints.tab.title") +) { + private val delegate = JavaFieldBreakpointType() + + override fun createJavaBreakpoint(project: Project, breakpoint: XBreakpoint): Breakpoint<*> { + return KotlinFieldBreakpoint(project, breakpoint) + } + + override fun canPutAt(file: VirtualFile, line: Int, project: Project): Boolean { + val psiFile = PsiManager.getInstance(project).findFile(file) + + if (psiFile == null || psiFile.getVirtualFile().getFileType() != JetFileType.INSTANCE) { + return false + } + + val document = FileDocumentManager.getInstance().getDocument(file) ?: return false + + var canPutAt = false + XDebuggerUtil.getInstance().iterateLine(project, document, line, fun (el: PsiElement): Boolean { + // avoid comments + if (el is PsiWhiteSpace || PsiTreeUtil.getParentOfType(el, javaClass(), false) != null) { + return true + } + + var element = el + var parent = element.getParent() + while (parent != null) { + val offset = parent.getTextOffset() + if (offset >= 0 && document.getLineNumber(offset) != line) break + + element = parent + parent = element.getParent() + } + + if (element is JetProperty || element is JetParameter) { + val bindingContext = (element as JetElement).analyzeAndGetResult().bindingContext + var descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element) + if (descriptor is ValueParameterDescriptor) { + descriptor = bindingContext.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor) + } + if (descriptor is PropertyDescriptor && bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor)) { + canPutAt = true + } + return false + } + + return true + }) + + return canPutAt + } + + override fun getPriority() = 120 + + override fun createBreakpointProperties(file: VirtualFile, line: Int): JavaFieldBreakpointProperties? { + return delegate.createBreakpointProperties(file, line) + } + + override fun addBreakpoint(project: Project?, parentComponent: JComponent?): XLineBreakpoint? { + return delegate.addBreakpoint(project, parentComponent) + } + + override fun isAddBreakpointButtonVisible(): Boolean { + return delegate.isAddBreakpointButtonVisible() + } + + override fun getMutedEnabledIcon(): Icon { + return delegate.getMutedEnabledIcon() + } + + override fun getDisabledIcon(): Icon { + return delegate.getDisabledIcon() + } + + override fun getEnabledIcon(): Icon { + return delegate.getEnabledIcon() + } + + override fun getMutedDisabledIcon(): Icon { + return delegate.getMutedDisabledIcon() + } + + override fun canBeHitInOtherPlaces(): Boolean { + return delegate.canBeHitInOtherPlaces() + } + + override fun getShortText(breakpoint: XLineBreakpoint?): String? { + return delegate.getShortText(breakpoint) + } + + override fun createProperties(): JavaFieldBreakpointProperties? { + return delegate.createProperties() + } + + override fun createCustomPropertiesPanel(): XBreakpointCustomPropertiesPanel>? { + return delegate.createCustomPropertiesPanel() + } + + override fun getDisplayText(breakpoint: XLineBreakpoint?): String? { + return delegate.getDisplayText(breakpoint) + } + + override fun getEditorsProvider(): XDebuggerEditorsProvider? { + return delegate.getEditorsProvider() + } + + override fun createCustomRightPropertiesPanel(project: Project): XBreakpointCustomPropertiesPanel>? { + return delegate.createCustomRightPropertiesPanel(project) + } + + override fun isSuspendThreadSupported(): Boolean { + return delegate.isSuspendThreadSupported() + } +} + +class KotlinFieldBreakpoint(project: Project, breakpoint: XBreakpoint): FieldBreakpoint(project, breakpoint) { + override fun isValid(): Boolean { + if (!BreakpointWithHighlighter.isPositionValid(getXBreakpoint().getSourcePosition())) return false + + return runReadAction { + val field = getField() + field != null && field.isValid() + } + } + + public fun getField(): JetCallableDeclaration? { + val sourcePosition = getSourcePosition() + return getProperty(sourcePosition) + } + + private fun getProperty(sourcePosition: SourcePosition?): JetCallableDeclaration? { + val property: JetProperty? = PositionUtil.getPsiElementAt(getProject(), javaClass(), sourcePosition) + if (property != null) { + return property + } + val parameter: JetParameter? = PositionUtil.getPsiElementAt(getProject(), javaClass(), sourcePosition) + if (parameter != null) { + return parameter + } + return null + } + + override fun reload(psiFile: PsiFile?) { + val property = getProperty(getSourcePosition()) + if (property != null) { + setFieldName(property.getName()) + + if (property is JetProperty && property.isTopLevel()) { + getProperties().myClassName = PackageClassUtils.getPackageClassFqName(property.getContainingJetFile().getPackageFqName()).asString() + } + else { + val jetClass: JetClassOrObject? = PsiTreeUtil.getParentOfType(property, javaClass()) + if (jetClass is JetClassOrObject) { + val fqName = jetClass.getFqName() + if (fqName != null) { + getProperties().myClassName = fqName.asString() + } + } + } + setInstanceFiltersEnabled(false) + } + } + + fun setFieldName(fieldName: String) { + getProperties().myFieldName = fieldName + } + + @TestOnly + fun setWatchAccess(value: Boolean) { + getProperties().WATCH_ACCESS = value + } + + @TestOnly + fun setWatchModification(value: Boolean) { + getProperties().WATCH_MODIFICATION = value + } +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/outs/fieldWatchpoints.out b/idea/testData/debugger/tinyApp/outs/fieldWatchpoints.out new file mode 100644 index 00000000000..aa1bde9a489 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/fieldWatchpoints.out @@ -0,0 +1,44 @@ +KotlinFieldBreakpoint created at fieldWatchpoints.kt:4 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:7 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:17 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:20 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:30 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:32 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:44 +KotlinFieldBreakpoint created at fieldWatchpoints.kt:47 +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! fieldWatchpoints.FieldWatchpointsPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +fieldWatchpoints.kt:18 +fieldWatchpoints.kt:21 +fieldWatchpoints.kt:59 +fieldWatchpoints.kt:5 +fieldWatchpoints.kt:8 +fieldWatchpoints.kt:59 +fieldWatchpoints.kt:11 +fieldWatchpoints.kt:12 +fieldWatchpoints.kt:13 +fieldWatchpoints.kt:60 +fieldWatchpoints.kt:24 +fieldWatchpoints.kt:25 +fieldWatchpoints.kt:26 +fieldWatchpoints.kt:61 +fieldWatchpoints.kt:0 +fieldWatchpoints.kt:0 +fieldWatchpoints.kt:61 +fieldWatchpoints.kt:36 +fieldWatchpoints.kt:37 +fieldWatchpoints.kt:38 +fieldWatchpoints.kt:62 +fieldWatchpoints.kt:45 +fieldWatchpoints.kt:48 +fieldWatchpoints.kt:62 +fieldWatchpoints.kt:44 +fieldWatchpoints.kt:51 +fieldWatchpoints.kt:47 +fieldWatchpoints.kt:52 +fieldWatchpoints.kt:47 +fieldWatchpoints.kt:54 +fieldWatchpoints.kt:63 +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/outs/inapplicableFieldWatchpoints.out b/idea/testData/debugger/tinyApp/outs/inapplicableFieldWatchpoints.out new file mode 100644 index 00000000000..9963536d281 --- /dev/null +++ b/idea/testData/debugger/tinyApp/outs/inapplicableFieldWatchpoints.out @@ -0,0 +1,5 @@ +!JDK_HOME!\bin\java -agentlib:jdwp=transport=dt_socket,address=!HOST_NAME!:!HOST_PORT!,suspend=y,server=n -Dfile.encoding=!FILE_ENCODING! -classpath !APP_PATH!\classes;!KOTLIN_RUNTIME!;!CUSTOM_LIBRARY!;!RT_JAR! inapplicableFieldWatchpoints.InapplicableFieldWatchpointsPackage +Connected to the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' +Disconnected from the target VM, address: '!HOST_NAME!:PORT_NAME!', transport: 'socket' + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt b/idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt new file mode 100644 index 00000000000..c1b709a5eff --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt @@ -0,0 +1,65 @@ +package fieldWatchpoints + +class A { + //FieldWatchpoint! (propVal) + val propVal = 1 + + //FieldWatchpoint! (propVar) + var propVar = 1 + + fun testPublicPropertyInClass() { + propVal + propVar + propVar = 2 + } +} + +//FieldWatchpoint! (topPropVal) +val topPropVal = 1 + +//FieldWatchpoint! (topPropVar) +var topPropVar = 1 + +fun testPublicTopLevelProperty() { + topPropVal + topPropVar + topPropVar = 2 +} + +class B( + //FieldWatchpoint! (bPropVal) + val bPropVal: Int, + //FieldWatchpoint! (bPropVar) + var bPropVar: Int +) { + fun testConstructorProperty() { + bPropVal + bPropVar + bPropVar = 2 + } +} + +class AWithCompanion { + companion object { + //FieldWatchpoint! (compPropVal) + val compPropVal = 1 + + //FieldWatchpoint! (compPropVar) + var compPropVar = 1 + + fun testCompanionProperty() { + compPropVal + compPropVar + compPropVar = 2 + } + } +} + +fun main(args: Array) { + A().testPublicPropertyInClass() + testPublicTopLevelProperty() + B(1, 1).testConstructorProperty() + AWithCompanion.testCompanionProperty() +} + +// STEP_OUT: 36 \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt b/idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt new file mode 100644 index 00000000000..8111ecab697 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt @@ -0,0 +1,23 @@ +package inapplicableFieldWatchpoints + +class A { + //FieldWatchpoint! (propWithGet) + val propWithGet: Int get() = 1 +} + +interface T { + //FieldWatchpoint! (propInInterface) + val propInInterface: Int +} + +fun main(args: Array) { + //FieldWatchpoint! (localVal) + val localVal = 1 +} + +fun foo( + //FieldWatchpoint! (funParam) + funParam: Int +) { + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt index 7ec63154a91..78ca3967e87 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinDebuggerTestBase.kt @@ -16,6 +16,8 @@ package org.jetbrains.kotlin.idea.debugger +import com.intellij.debugger.DebuggerInvocationUtil +import com.intellij.debugger.DebuggerManagerEx import com.intellij.debugger.engine.DebugProcessImpl import com.intellij.debugger.engine.SuspendContextImpl import com.intellij.debugger.engine.MethodFilter @@ -33,6 +35,28 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils.findStringWithPrefixes import kotlin.properties.Delegates import com.intellij.debugger.engine.evaluation.EvaluationContextImpl import com.intellij.debugger.impl.DebuggerContextImpl +import com.intellij.debugger.ui.breakpoints.BreakpointManager +import com.intellij.debugger.ui.breakpoints.FieldBreakpoint +import com.intellij.debugger.ui.breakpoints.JavaFieldBreakpointType +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.ModalityState +import com.intellij.openapi.editor.Document +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.util.Computable +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FilenameIndex +import com.intellij.xdebugger.XDebuggerManager +import com.intellij.xdebugger.XDebuggerUtil +import com.intellij.xdebugger.breakpoints.* +import org.jetbrains.java.debugger.breakpoints.properties.JavaFieldBreakpointProperties +import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpoint +import org.jetbrains.kotlin.idea.debugger.breakpoints.KotlinFieldBreakpointType +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import javax.swing.SwingUtilities abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() { private var oldSettings: DebuggerSettings by Delegates.notNull() @@ -154,4 +178,58 @@ abstract class KotlinDebuggerTestBase : KotlinDebuggerTestCase() { resume(this) } } + + override fun createBreakpoints(file: PsiFile?) { + super.createBreakpoints(file) + + if (file == null) return + + val document = PsiDocumentManager.getInstance(myProject).getDocument(file) ?: return + val breakpointManager = XDebuggerManager.getInstance(myProject).getBreakpointManager() + val breakpointType = javaClass() as Class>, XBreakpointProperties<*>>> + val type = XDebuggerUtil.getInstance().findBreakpointType>>(breakpointType) as KotlinFieldBreakpointType + val virtualFile = file.getVirtualFile() + + val runnable = { + var offset = -1; + while (true) { + offset = document.getText().indexOf("FieldWatchpoint!", offset + 1) + if (offset == -1) break + + val commentLine = document.getLineNumber(offset) + + val comment = document.getText().substring(document.getLineStartOffset(commentLine), document.getLineEndOffset(commentLine)) + + val lineIndex = commentLine + 1 + val fieldName = comment.substringAfter("//FieldWatchpoint! (").substringBefore(")") + + if (!type.canPutAt(virtualFile, lineIndex, myProject)) continue + + val xBreakpoint = runWriteAction { + breakpointManager.addLineBreakpoint( + type as XLineBreakpointType>, + virtualFile.getUrl(), + lineIndex, + type.createBreakpointProperties(virtualFile, lineIndex) + ) + } + + val javaBreakpoint = BreakpointManager.getJavaBreakpoint(xBreakpoint) + if (javaBreakpoint is KotlinFieldBreakpoint) { + javaBreakpoint.setFieldName(fieldName) + javaBreakpoint.setWatchAccess(true) + javaBreakpoint.setWatchModification(true) + BreakpointManager.addBreakpoint(javaBreakpoint) + println("KotlinFieldBreakpoint created at ${file.getVirtualFile().getName()}:$lineIndex", ProcessOutputTypes.SYSTEM) + } + } + } + + if (!SwingUtilities.isEventDispatchThread()) { + DebuggerInvocationUtil.invokeAndWait(myProject, runnable, ModalityState.defaultModalityState()) + } + else { + runnable.invoke() + } + } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java index 74ec07614cd..135a45385df 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/KotlinSteppingTestGenerated.java @@ -272,6 +272,27 @@ public class KotlinSteppingTestGenerated extends AbstractKotlinSteppingTest { } } + @TestMetadata("idea/testData/debugger/tinyApp/src/stepOut") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class StepOut extends AbstractKotlinSteppingTest { + public void testAllFilesPresentInStepOut() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/stepOut"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("fieldWatchpoints.kt") + public void testFieldWatchpoints() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepOut/fieldWatchpoints.kt"); + doStepOutTest(fileName); + } + + @TestMetadata("inapplicableFieldWatchpoints.kt") + public void testInapplicableFieldWatchpoints() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/debugger/tinyApp/src/stepOut/inapplicableFieldWatchpoints.kt"); + doStepOutTest(fileName); + } + } + @TestMetadata("idea/testData/debugger/tinyApp/src/filters") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)