From 59b9e5a4a95bf272a8283deae3e9bf11498bebce Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Fri, 6 Dec 2024 15:05:02 +0530 Subject: [PATCH 1/4] Make the breakpoint verification configurable --- .../debugadapter/BallerinaStackFrame.java | 2 +- .../debugadapter/BreakpointProcessor.java | 3 +- .../debugadapter/JBallerinaDebugServer.java | 69 ++-------- .../debugadapter/JDIEventProcessor.java | 5 +- .../config/ClientConfigHolder.java | 19 +++ .../debugadapter/utils/ServerUtils.java | 120 ++++++++++++++++++ 6 files changed, 155 insertions(+), 63 deletions(-) create mode 100644 misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BallerinaStackFrame.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BallerinaStackFrame.java index 93fdb18c6a86..981cb1940bdc 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BallerinaStackFrame.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BallerinaStackFrame.java @@ -35,8 +35,8 @@ import java.util.Map; import java.util.Optional; -import static org.ballerinalang.debugadapter.JBallerinaDebugServer.isBalStackFrame; import static org.ballerinalang.debugadapter.evaluation.utils.EvaluationUtils.STRAND_VAR_NAME; +import static org.ballerinalang.debugadapter.utils.ServerUtils.isBalStackFrame; import static org.ballerinalang.debugadapter.variable.VariableUtils.isService; import static org.ballerinalang.debugadapter.variable.VariableUtils.removeRedundantQuotes; import static org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper.LAMBDA; diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BreakpointProcessor.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BreakpointProcessor.java index 63facc5aea6f..6a9edc9ff121 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BreakpointProcessor.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/BreakpointProcessor.java @@ -54,6 +54,7 @@ import java.util.concurrent.TimeoutException; import static org.ballerinalang.debugadapter.utils.PackageUtils.getQualifiedClassName; +import static org.ballerinalang.debugadapter.utils.ServerUtils.supportsBreakpointVerification; /** * Implementation of Ballerina breakpoint processor. The existing implementation is capable of processing advanced @@ -202,7 +203,7 @@ void activateUserBreakPoints(ReferenceType referenceType, boolean shouldNotify) bpReq.enable(); // verifies the breakpoint reachability and notifies the client if required. - if (!breakpoint.isVerified()) { + if (supportsBreakpointVerification(context) && !breakpoint.isVerified()) { breakpoint.setVerified(true); if (shouldNotify) { notifyBreakPointChangesToClient(breakpoint); diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JBallerinaDebugServer.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JBallerinaDebugServer.java index ce07af5caa6d..5f2503c3e3e1 100755 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JBallerinaDebugServer.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JBallerinaDebugServer.java @@ -51,6 +51,7 @@ import org.ballerinalang.debugadapter.runner.BProgramRunner; import org.ballerinalang.debugadapter.runner.BSingleFileRunner; import org.ballerinalang.debugadapter.utils.PackageUtils; +import org.ballerinalang.debugadapter.utils.ServerUtils; import org.ballerinalang.debugadapter.variable.BCompoundVariable; import org.ballerinalang.debugadapter.variable.BSimpleVariable; import org.ballerinalang.debugadapter.variable.BVariable; @@ -83,7 +84,6 @@ import org.eclipse.lsp4j.debug.SetBreakpointsArguments; import org.eclipse.lsp4j.debug.SetBreakpointsResponse; import org.eclipse.lsp4j.debug.SetExceptionBreakpointsArguments; -import org.eclipse.lsp4j.debug.Source; import org.eclipse.lsp4j.debug.SourceArguments; import org.eclipse.lsp4j.debug.SourceBreakpoint; import org.eclipse.lsp4j.debug.SourceResponse; @@ -132,10 +132,13 @@ import static org.ballerinalang.debugadapter.completion.util.CompletionUtil.getTriggerCharacters; import static org.ballerinalang.debugadapter.completion.util.CompletionUtil.getVisibleSymbolCompletions; import static org.ballerinalang.debugadapter.completion.util.CompletionUtil.triggerCharactersFound; -import static org.ballerinalang.debugadapter.utils.PackageUtils.BAL_FILE_EXT; import static org.ballerinalang.debugadapter.utils.PackageUtils.GENERATED_VAR_PREFIX; import static org.ballerinalang.debugadapter.utils.PackageUtils.INIT_CLASS_NAME; import static org.ballerinalang.debugadapter.utils.PackageUtils.getQualifiedClassName; +import static org.ballerinalang.debugadapter.utils.ServerUtils.isBalStackFrame; +import static org.ballerinalang.debugadapter.utils.ServerUtils.isBalStrand; +import static org.ballerinalang.debugadapter.utils.ServerUtils.isNoDebugMode; +import static org.ballerinalang.debugadapter.utils.ServerUtils.toBalBreakpoint; /** * JBallerina debug server implementation. @@ -178,7 +181,7 @@ public ExecutionContext getContext() { return context; } - ClientConfigHolder getClientConfigHolder() { + public ClientConfigHolder getClientConfigHolder() { return clientConfigHolder; } @@ -221,12 +224,13 @@ public CompletableFuture initialize(InitializeRequestArguments arg public CompletableFuture setBreakpoints(SetBreakpointsArguments args) { return CompletableFuture.supplyAsync(() -> { SetBreakpointsResponse bpResponse = new SetBreakpointsResponse(); - if (isNoDebugMode()) { + if (isNoDebugMode(context)) { return bpResponse; } BalBreakpoint[] balBreakpoints = Arrays.stream(args.getBreakpoints()) - .map((SourceBreakpoint sourceBreakpoint) -> toBalBreakpoint(sourceBreakpoint, args.getSource())) + .map((SourceBreakpoint sourceBreakpoint) -> toBalBreakpoint(context, sourceBreakpoint, + args.getSource())) .toArray(BalBreakpoint[]::new); LinkedHashMap breakpointsMap = new LinkedHashMap<>(); @@ -345,7 +349,7 @@ public CompletableFuture stackTrace(StackTraceArguments args } else { StackFrame[] validFrames = activeThread.frames().stream() .map(this::toDapStackFrame) - .filter(JBallerinaDebugServer::isValidFrame) + .filter(ServerUtils::isValidFrame) .toArray(StackFrame[]::new); stackTraceResponse.setStackFrames(validFrames); threadStackTraces.put(activeThread.uniqueID(), validFrames); @@ -785,13 +789,6 @@ private StackFrame toDapStackFrame(StackFrameProxyImpl stackFrameProxy) { } } - private BalBreakpoint toBalBreakpoint(SourceBreakpoint sourceBreakpoint, Source source) { - BalBreakpoint breakpoint = new BalBreakpoint(source, sourceBreakpoint.getLine()); - breakpoint.setCondition(sourceBreakpoint.getCondition()); - breakpoint.setLogMessage(sourceBreakpoint.getLogMessage()); - return breakpoint; - } - /** * Returns a map of all currently running threads in the remote VM, against their unique ID. *

@@ -844,47 +841,6 @@ && isBalStrand(threadReference) return balStrandThreads; } - /** - * Validates whether the given DAP thread reference represents a ballerina strand. - *

- * - * @param threadReference DAP thread reference - * @return true if the given DAP thread reference represents a ballerina strand. - */ - private static boolean isBalStrand(ThreadReference threadReference) { - // Todo - Refactor to use thread proxy implementation - try { - return isBalStackFrame(threadReference.frames().get(0)); - } catch (Exception e) { - return false; - } - } - - /** - * Validates whether the given DAP stack frame represents a ballerina call stack frame. - * - * @param frame DAP stack frame - * @return true if the given DAP stack frame represents a ballerina call stack frame. - */ - static boolean isBalStackFrame(com.sun.jdi.StackFrame frame) { - // Todo - Refactor to use stack frame proxy implementation - try { - return frame.location().sourceName().endsWith(BAL_FILE_EXT); - } catch (Exception e) { - return false; - } - } - - /** - * Validates a given ballerina stack frame for its source information. - * - * @param stackFrame ballerina stack frame - * @return true if its a valid ballerina frame - */ - static boolean isValidFrame(StackFrame stackFrame) { - return stackFrame != null && stackFrame.getSource() != null && stackFrame.getLine() > 0; - } - /** * Asynchronously listens to remote debuggee stdout + error streams and redirects the output to the client debug * console. @@ -978,11 +934,6 @@ private void prepareFor(DebugInstruction instruction, int threadId) { context.setPrevInstruction(instruction); } - private boolean isNoDebugMode() { - ClientConfigHolder confHolder = context.getAdapter().getClientConfigHolder(); - return confHolder instanceof ClientLaunchConfigHolder launchConfigHolder && launchConfigHolder.isNoDebugMode(); - } - private Variable[] computeGlobalScopeVariables(VariablesArguments requestArgs) { int stackFrameReference = requestArgs.getVariablesReference(); String classQName = PackageUtils.getQualifiedClassName(suspendedContext, INIT_CLASS_NAME); diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JDIEventProcessor.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JDIEventProcessor.java index 40d31b8b80cd..ad29ed4e8b17 100755 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JDIEventProcessor.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/JDIEventProcessor.java @@ -35,6 +35,7 @@ import org.ballerinalang.debugadapter.breakpoint.BalBreakpoint; import org.ballerinalang.debugadapter.jdi.StackFrameProxyImpl; import org.ballerinalang.debugadapter.jdi.ThreadReferenceProxyImpl; +import org.ballerinalang.debugadapter.utils.ServerUtils; import org.eclipse.lsp4j.debug.ContinuedEventArguments; import org.eclipse.lsp4j.debug.StoppedEventArguments; import org.eclipse.lsp4j.debug.StoppedEventArgumentsReason; @@ -48,8 +49,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; -import static org.ballerinalang.debugadapter.JBallerinaDebugServer.isBalStackFrame; import static org.ballerinalang.debugadapter.utils.PackageUtils.BAL_FILE_EXT; +import static org.ballerinalang.debugadapter.utils.ServerUtils.isBalStackFrame; /** * JDI Event processor implementation. @@ -226,7 +227,7 @@ List filterValidBallerinaFrames(List j if (balStackFrame.getAsDAPStackFrame().isEmpty()) { continue; } - if (JBallerinaDebugServer.isValidFrame(balStackFrame.getAsDAPStackFrame().get())) { + if (ServerUtils.isValidFrame(balStackFrame.getAsDAPStackFrame().get())) { validFrames.add(balStackFrame); } } catch (Exception ignored) { diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/config/ClientConfigHolder.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/config/ClientConfigHolder.java index ebe94c4d886b..c2d294585858 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/config/ClientConfigHolder.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/config/ClientConfigHolder.java @@ -40,6 +40,7 @@ public class ClientConfigHolder { protected static final String ARG_DEBUGGEE_PORT = "debuggeePort"; private static final String ARG_CAPABILITIES = "capabilities"; private static final String ARG_SUPPORT_READONLY_EDITOR = "supportsReadOnlyEditors"; + private static final String ARG_SUPPORT_BP_VERIFICATION = "supportsBreakpointVerification"; private static final String ARG_TERMINAL_KIND = "terminal"; private static final String INTEGRATED_TERMINAL_KIND = "INTEGRATED"; private static final String EXTERNAL_TERMINAL_KIND = "EXTERNAL"; @@ -88,6 +89,15 @@ public Optional getExtendedCapabilities() { extendedClientCapabilities.setSupportsReadOnlyEditors(false); } + Object bpVerificationConfig = capabilities.get(ARG_SUPPORT_BP_VERIFICATION); + if (bpVerificationConfig instanceof Boolean b) { + extendedClientCapabilities.setSupportsBreakpointVerification(b); + } else if (bpVerificationConfig instanceof String s) { + extendedClientCapabilities.setSupportsBreakpointVerification(Boolean.parseBoolean(s)); + } else { + extendedClientCapabilities.setSupportsBreakpointVerification(false); + } + return Optional.ofNullable(extendedClientCapabilities); } @@ -122,6 +132,7 @@ public enum ClientConfigKind { public static class ExtendedClientCapabilities { private boolean supportsReadOnlyEditors = false; + private boolean supportsBreakpointVerification = false; public boolean supportsReadOnlyEditors() { return supportsReadOnlyEditors; @@ -130,5 +141,13 @@ public boolean supportsReadOnlyEditors() { public void setSupportsReadOnlyEditors(boolean supportsReadOnlyEditors) { this.supportsReadOnlyEditors = supportsReadOnlyEditors; } + + public boolean supportsBreakpointVerification() { + return supportsBreakpointVerification; + } + + public void setSupportsBreakpointVerification(boolean supportsBreakpointVerification) { + this.supportsBreakpointVerification = supportsBreakpointVerification; + } } } diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java new file mode 100644 index 000000000000..0dff90b3a007 --- /dev/null +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com). + * + * 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.ballerinalang.debugadapter.utils; + +import com.sun.jdi.ThreadReference; +import org.ballerinalang.debugadapter.ExecutionContext; +import org.ballerinalang.debugadapter.breakpoint.BalBreakpoint; +import org.ballerinalang.debugadapter.config.ClientConfigHolder; +import org.ballerinalang.debugadapter.config.ClientLaunchConfigHolder; +import org.eclipse.lsp4j.debug.Source; +import org.eclipse.lsp4j.debug.SourceBreakpoint; +import org.eclipse.lsp4j.debug.StackFrame; + +import java.util.Objects; + +import static org.ballerinalang.debugadapter.utils.PackageUtils.BAL_FILE_EXT; + +/** + * Ballerina debug server related utility functions. + */ +public class ServerUtils { + + /** + * Checks whether the debug server should run in no-debug mode. + * + * @param context debug context + * @return true if the debug mode is no-debug mode + */ + public static boolean isNoDebugMode(ExecutionContext context) { + ClientConfigHolder confHolder = context.getAdapter().getClientConfigHolder(); + return confHolder instanceof ClientLaunchConfigHolder launchConfigHolder && launchConfigHolder.isNoDebugMode(); + } + + /** + * Validates whether the given DAP thread reference represents a ballerina strand. + * + * @param threadReference DAP thread reference + * @return true if the given DAP thread reference represents a ballerina strand + */ + public static boolean isBalStrand(ThreadReference threadReference) { + // Todo - Refactor to use thread proxy implementation + try { + return isBalStackFrame(threadReference.frames().getFirst()); + } catch (Exception e) { + return false; + } + } + + /** + * Validates whether the given DAP stack frame represents a ballerina call stack frame. + * + * @param frame DAP stack frame + * @return true if the given DAP stack frame represents a ballerina call stack frame + */ + public static boolean isBalStackFrame(com.sun.jdi.StackFrame frame) { + // Todo - Refactor to use stack frame proxy implementation + try { + return frame.location().sourceName().endsWith(BAL_FILE_EXT); + } catch (Exception e) { + return false; + } + } + + /** + * Validates a given ballerina stack frame for its source information. + * + * @param stackFrame ballerina stack frame + * @return true if it's a valid ballerina frame + */ + public static boolean isValidFrame(StackFrame stackFrame) { + return stackFrame != null && stackFrame.getSource() != null && stackFrame.getLine() > 0; + } + + /** + * Converts a given DAP source breakpoint instance to a ballerina breakpoint instance. + * + * @param context debug context + * @param sourceBp source breakpoint + * @param source source + * @return ballerina breakpoint + */ + public static BalBreakpoint toBalBreakpoint(ExecutionContext context, SourceBreakpoint sourceBp, Source source) { + BalBreakpoint breakpoint = new BalBreakpoint(source, sourceBp.getLine()); + breakpoint.setCondition(sourceBp.getCondition()); + breakpoint.setLogMessage(sourceBp.getLogMessage()); + // If the debug client doesn't support breakpoint verification, mark the breakpoint as verified by default. + if (!supportsBreakpointVerification(context)) { + breakpoint.setVerified(true); + } + + return breakpoint; + } + + /** + * Checks whether the connected debug client supports breakpoint verification. + * + * @param context debug context + * @return true if the connected debug client supports breakpoint verification + */ + public static boolean supportsBreakpointVerification(ExecutionContext context) { + ClientConfigHolder configHolder = context.getAdapter().getClientConfigHolder(); + + return Objects.nonNull(configHolder) && configHolder.getExtendedCapabilities() + .map(ClientConfigHolder.ExtendedClientCapabilities::supportsBreakpointVerification) + .orElse(false); + } +} From 4b18717f97c4c8834b8bc0fa53999e8f8cafba5d Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Fri, 6 Dec 2024 17:52:53 +0530 Subject: [PATCH 2/4] Add tests --- .../adapter/BreakpointVerificationTest.java | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/BreakpointVerificationTest.java b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/BreakpointVerificationTest.java index 4e4603e10776..6875f39333c1 100644 --- a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/BreakpointVerificationTest.java +++ b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/BreakpointVerificationTest.java @@ -33,6 +33,7 @@ import java.nio.file.Path; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -63,13 +64,17 @@ public void setup() { @Test(description = "Test to assert runtime verification on breakpoints which were added before starting a " + "debug session") - public void testInitialBreakpointVerification() throws BallerinaTestException { + public void testBasicBreakpointVerification() throws BallerinaTestException { // Adds breakpoints for all the lines in the source. for (int line = 1; line <= SRC_LINE_COUNT; line++) { debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, line)); } - debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); + HashMap extendedCapabilities = new HashMap<>(); + extendedCapabilities.put("supportsBreakpointVerification", true); + HashMap launchConfigs = new HashMap<>(); + launchConfigs.put("capabilities", extendedCapabilities); + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN, launchConfigs); Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); debugHitInfo = debugTestRunner.waitForDebugHit(10000); @@ -82,7 +87,7 @@ public void testInitialBreakpointVerification() throws BallerinaTestException { // retrieves all the 'breakpoint' events received from the server, which indicates breakpoint verification // status changes. List changedBreakpoints = debugTestRunner.waitForModifiedBreakpoints(2000); - assertBreakpointChanges(changedBreakpoints); + assertBreakpoints(changedBreakpoints); } @Test(description = "Test to assert runtime verification on breakpoints which are getting added on-the-fly " + @@ -90,7 +95,12 @@ public void testInitialBreakpointVerification() throws BallerinaTestException { public void testOnTheFlyBreakpointVerification() throws BallerinaTestException { // adds one initial breakpoint and run debug session until the breakpoint is reached. debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 27)); - debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); + + HashMap extendedCapabilities = new HashMap<>(); + extendedCapabilities.put("supportsBreakpointVerification", true); + HashMap launchConfigs = new HashMap<>(); + launchConfigs.put("capabilities", extendedCapabilities); + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN, launchConfigs); Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); Assert.assertEquals(debugHitInfo.getLeft(), new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 27)); @@ -108,10 +118,41 @@ public void testOnTheFlyBreakpointVerification() throws BallerinaTestException { breakpoint.getLine(), breakpoint.isVerified())) .toList(); - assertBreakpointChanges(breakPoints); + assertBreakpoints(breakPoints); } - private void assertBreakpointChanges(List breakpoints) { + @Test(description = "Test to assert runtime verification on breakpoints when the debug client doesn't support " + + "breakpoint verification") + public void testNegativeBreakpointVerification() throws BallerinaTestException { + // Adds breakpoints for all the lines in the source. + for (int line = 1; line <= SRC_LINE_COUNT; line++) { + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, line)); + } + + HashMap extendedCapabilities = new HashMap<>(); + extendedCapabilities.put("supportsBreakpointVerification", false); + HashMap launchConfigs = new HashMap<>(); + launchConfigs.put("capabilities", extendedCapabilities); + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN, launchConfigs); + Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + Assert.assertEquals(debugHitInfo.getLeft(), new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 27)); + + // retrieves all the 'breakpoint' events received from the server, which indicates breakpoint verification + // status changes. + try { + List changedBreakpoints = debugTestRunner.waitForModifiedBreakpoints(2000); + } catch (BallerinaTestException ignored) { + // expected exception because the debug client doesn't support breakpoint verification. + } + } + + private void assertBreakpoints(List breakpoints) { // if statement assertVerifiedBreakpoints(breakpoints, IF_STATEMENT_START, 7, true, true, true, true, false, true, false); // while statement @@ -120,7 +161,6 @@ private void assertBreakpointChanges(List breakpoints) assertVerifiedBreakpoints(breakpoints, MATCH_STMT_START, 5, true, true, true, false, false); // documentation statement assertVerifiedBreakpoints(breakpoints, DOCUMENTATION_START, 4, false, false, false, false); - // enum declaration assertVerifiedBreakpoints(breakpoints, ENUM_DCLN_START, 5, false, false, false, false, false); // class definition From 36af680ba38488986bcf4df56844dd145691f2fc Mon Sep 17 00:00:00 2001 From: Nipuna Ranasinghe Date: Fri, 6 Dec 2024 17:57:22 +0530 Subject: [PATCH 3/4] Fix verification when the feature is enabled --- .../debugadapter/breakpoint/BalBreakpoint.java | 8 +++++++- .../org/ballerinalang/debugadapter/utils/ServerUtils.java | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/breakpoint/BalBreakpoint.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/breakpoint/BalBreakpoint.java index d12a65ebc3ce..b3e4da50e020 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/breakpoint/BalBreakpoint.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/breakpoint/BalBreakpoint.java @@ -40,6 +40,7 @@ public class BalBreakpoint { private String condition; private LogMessage logMessage; private boolean isVerified; + private boolean supportsVerification; private static final AtomicInteger nextID = new AtomicInteger(0); @@ -48,6 +49,7 @@ public BalBreakpoint(Source source, int line) { this.source = source; this.line = line; this.isVerified = false; + this.supportsVerification = false; } public Integer getLine() { @@ -91,11 +93,15 @@ public Breakpoint getAsDAPBreakpoint() { breakpoint.setId(id); breakpoint.setLine(line); breakpoint.setSource(source); - breakpoint.setVerified(isVerified); + breakpoint.setVerified(!supportsVerification || isVerified); return breakpoint; } private boolean isTemplate(String logMessage) { return logMessage != null && Pattern.compile(INTERPOLATION_REGEX).matcher(logMessage).find(); } + + public void setSupportsVerification(boolean supportsVerification) { + this.supportsVerification = supportsVerification; + } } diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java index 0dff90b3a007..17a6e9fdadc7 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java @@ -97,8 +97,8 @@ public static BalBreakpoint toBalBreakpoint(ExecutionContext context, SourceBrea breakpoint.setCondition(sourceBp.getCondition()); breakpoint.setLogMessage(sourceBp.getLogMessage()); // If the debug client doesn't support breakpoint verification, mark the breakpoint as verified by default. - if (!supportsBreakpointVerification(context)) { - breakpoint.setVerified(true); + if (supportsBreakpointVerification(context)) { + breakpoint.setSupportsVerification(true); } return breakpoint; From 0c56743ab0e69b8478221b4d29e52eddf98de1d2 Mon Sep 17 00:00:00 2001 From: Nipuna Ransinghe Date: Tue, 10 Dec 2024 15:07:13 +0530 Subject: [PATCH 4/4] Update misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java Co-authored-by: Nipuna Fernando <59343084+nipunayf@users.noreply.github.com> --- .../java/org/ballerinalang/debugadapter/utils/ServerUtils.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java index 17a6e9fdadc7..6efda07cec62 100644 --- a/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java +++ b/misc/debug-adapter/modules/debug-adapter-core/src/main/java/org/ballerinalang/debugadapter/utils/ServerUtils.java @@ -30,6 +30,8 @@ /** * Ballerina debug server related utility functions. + * + * @since 2201.11.0 */ public class ServerUtils {