diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 27690a92c..b8a2c3527 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,11 +8,14 @@ on: jobs: build: - runs-on: ubuntu-22.04 + strategy: + matrix: + os: [ubuntu-22.04, macos-14, windows-2022] steps: - uses: actions/checkout@v3 - name: Install Webkit + GTK bindings run: sudo apt-get install -yq libwebkit2gtk-4.0-dev + if: runner.os == 'Linux' - name: Set up Maven 3.9.5 uses: stCarolas/setup-maven@d6af6abeda15e98926a57b5aa970a96bb37f97d1 with: diff --git a/plugins/org.eclipse.epsilon.eol.dap/src/org/eclipse/epsilon/eol/dap/EpsilonDebugAdapter.java b/plugins/org.eclipse.epsilon.eol.dap/src/org/eclipse/epsilon/eol/dap/EpsilonDebugAdapter.java index eca030eeb..905843f3b 100644 --- a/plugins/org.eclipse.epsilon.eol.dap/src/org/eclipse/epsilon/eol/dap/EpsilonDebugAdapter.java +++ b/plugins/org.eclipse.epsilon.eol.dap/src/org/eclipse/epsilon/eol/dap/EpsilonDebugAdapter.java @@ -360,7 +360,7 @@ protected ThreadState attachTo(IEolModule module) { * Latch used for suspending all threads. Should only be used through {@link #suspend()} * and {@link #resumeAllThreads()}. */ - private AtomicBoolean suspendedLatch = new AtomicBoolean(false); + private final AtomicBoolean suspendedLatch = new AtomicBoolean(false); /** * Abstraction over the state of the program while stopped. Used to keep track @@ -910,25 +910,31 @@ private String getStackFrameName(int position, Frame frame) { } protected void suspend(int threadId, ModuleElement ast, SuspendReason reason) throws InterruptedException { - switch (reason) { - case STEP: - sendStopped(threadId, StoppedEventArgumentsReason.STEP); - break; - case BREAKPOINT: - sendStopped(threadId, StoppedEventArgumentsReason.BREAKPOINT); - break; - default: - throw new IllegalArgumentException("Unknown suspend reason"); - } - synchronized (suspendedLatch) { + /* + * Note: the synchronized region must cover the sending of stopped messages, + * as otherwise there is the risk that someone reacting to the "stopped" message + * may try to release the suspendedLatch before it has actually been set. + */ + switch (reason) { + case STEP: + sendStopped(threadId, StoppedEventArgumentsReason.STEP); + break; + case BREAKPOINT: + sendStopped(threadId, StoppedEventArgumentsReason.BREAKPOINT); + break; + default: + throw new IllegalArgumentException("Unknown suspend reason"); + } + suspendedLatch.set(true); do { - suspendedLatch.wait(); + final int timeoutMillis = 500; + suspendedLatch.wait(timeoutMillis); } while (suspendedLatch.get()); } } - + protected void resumeAllThreads() { synchronized (suspendedLatch) { suspendedLatch.set(false); diff --git a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/AbstractEpsilonDebugAdapterTest.java b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/AbstractEpsilonDebugAdapterTest.java index 9172bfe58..e7e3bfe33 100644 --- a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/AbstractEpsilonDebugAdapterTest.java +++ b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/AbstractEpsilonDebugAdapterTest.java @@ -11,6 +11,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; @@ -55,8 +56,11 @@ public abstract class AbstractEpsilonDebugAdapterTest { + /** Timeout used for various assertions in this base class. */ + private static final int TIMEOUT_SECONDS = 5; + @Rule - public Timeout globalTimeout = Timeout.seconds(10); + public Timeout globalTimeout = Timeout.seconds(TIMEOUT_SECONDS * 2); protected class TestClient implements IDebugProtocolClient { private Semaphore isStopped = new Semaphore(0); @@ -148,27 +152,29 @@ public void teardown() { adapter.disconnect(new DisconnectArguments()); } - protected void assertStoppedBecauseOf(final String reason) throws InterruptedException { - client.isStopped.tryAcquire(5, TimeUnit.SECONDS); - assertNotNull("The script should have stopped within 5s", client.stoppedArgs); - assertEquals("The debugger should say it stopped because of " + reason, reason, client.stoppedArgs.getReason()); + protected void assertStoppedBecauseOf(final String expectedReason) throws InterruptedException { + boolean acquired = client.isStopped.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertTrue("The script should have stopped within " + TIMEOUT_SECONDS + " seconds", acquired); + assertNotNull("The script should have provided stopping arguments", client.stoppedArgs); + assertEquals("The script should stop for the expected reason" , expectedReason, client.stoppedArgs.getReason()); } protected void assertProgramCompletedSuccessfully() throws InterruptedException { - client.isExited.tryAcquire(5, TimeUnit.SECONDS); - assertNotNull("The script should have exited within 5s", client.exitedArgs); + boolean acquired = client.isExited.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertTrue("The script should have exited within " + TIMEOUT_SECONDS + " seconds", acquired); + assertNotNull("The script should have provided exiting arguments", client.exitedArgs); assertEquals("The script should have completed its execution successfully", 0, client.exitedArgs.getExitCode()); } protected void assertProgramFailed() throws InterruptedException { - client.isExited.tryAcquire(5, TimeUnit.SECONDS); - assertNotNull("The script should have exited within 5s", client.exitedArgs); + client.isExited.tryAcquire(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertNotNull("The script should have exited within " + TIMEOUT_SECONDS + " seconds", client.exitedArgs); assertEquals("The script should have completed its execution with an error", 1, client.exitedArgs.getExitCode()); } protected StackTraceResponse getStackTrace() throws Exception { - ThreadsResponse threads = adapter.threads().get(5, TimeUnit.SECONDS); - assertEquals("The debugger should report one thread", 1, threads.getThreads().length); + ThreadsResponse threads = adapter.threads().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + assertEquals("The debugger should report one thread within " + TIMEOUT_SECONDS + " seconds", 1, threads.getThreads().length); final int threadId = threads.getThreads()[0].getId(); return getStackTrace(threadId); @@ -176,7 +182,7 @@ protected StackTraceResponse getStackTrace() throws Exception { protected StackTraceResponse getStackTrace(final int threadId) throws Exception { final StackTraceArguments stackTraceArgs = createStackTraceArgs(threadId); - StackTraceResponse stackTrace = adapter.stackTrace(stackTraceArgs).get(5, TimeUnit.SECONDS); + StackTraceResponse stackTrace = adapter.stackTrace(stackTraceArgs).get(TIMEOUT_SECONDS, TimeUnit.SECONDS); return stackTrace; } diff --git a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/StandaloneEolTest.java b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/StandaloneEolTest.java index 7ad024c9e..9d09d7024 100644 --- a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/StandaloneEolTest.java +++ b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/StandaloneEolTest.java @@ -171,7 +171,7 @@ public void breakThenStepOut() throws Exception { // First step out should stop the program at the line after the operation final StepOutArguments args = new StepOutArguments(); args.setThreadId(adapter.threads().get().getThreads()[0].getId()); - adapter.stepOut(args); + adapter.stepOut(args).get(); assertStoppedBecauseOf(StoppedEventArgumentsReason.STEP); StackTraceResponse stackTrace = getStackTrace(); @@ -179,7 +179,7 @@ public void breakThenStepOut() throws Exception { 2, stackTrace.getStackFrames()[0].getLine()); // If we continue, we'll stop again at the breakpoint - adapter.continue_(new ContinueArguments()); + adapter.continue_(new ContinueArguments()).get(); assertStoppedBecauseOf(StoppedEventArgumentsReason.BREAKPOINT); // Second step out should reach the 'return' statement diff --git a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/TupleTest.java b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/TupleTest.java index 50caa6c9a..c74bd76b6 100644 --- a/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/TupleTest.java +++ b/tests/org.eclipse.epsilon.eol.dap.test/src/org/eclipse/epsilon/eol/dap/test/eol/TupleTest.java @@ -61,7 +61,7 @@ public void canShowElements() throws Exception { assertEquals("1234", valueVariable.getValue()); assertEquals("Integer", valueVariable.getType()); - adapter.continue_(new ContinueArguments()); + adapter.continue_(new ContinueArguments()).get(); assertProgramCompletedSuccessfully(); } }