From d1a0b8d6d4ccec5aa02c6de6045078f2e22bcd9e Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Wed, 29 Apr 2026 23:00:26 +0200 Subject: [PATCH 1/6] test(incarnation-sapere): harden LsaNode concurrency test --- .../sapere/nodes/LsaNodeConcurrencyTest.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index 3625454a18..5a48f263dc 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -23,7 +23,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -53,10 +56,11 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); final CountDownLatch latch = new CountDownLatch(numberOfThreads); final AtomicBoolean exceptionOccurred = new AtomicBoolean(false); + final List> tasks = new java.util.ArrayList<>(numberOfThreads); // Start threads that modify the node while others read from it for (int i = 0; i < numberOfThreads; i++) { final int threadId = i; - executor.submit(() -> { + tasks.add(executor.submit(() -> { try { for (int j = 0; j < numberOfOperations; j++) { if (threadId % 2 == 0) { @@ -69,7 +73,7 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { assertNotNull(lsaSpace); } else { // Writer threads - final ILsaMolecule newMolecule = new LsaMolecule("thread" + threadId + "_" + j); + final ILsaMolecule newMolecule = new LsaMolecule("thread" + threadId + "x" + j); node.setConcentration(newMolecule); // Sometimes remove molecules to simulate real concurrent modification if (j % 10 == 0 && node.getMoleculeCount() > MIN_MOLECULES) { @@ -81,17 +85,25 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { } } } - } catch (final IllegalStateException | java.util.ConcurrentModificationException e) { - exceptionOccurred.set(true); - e.printStackTrace(); } finally { latch.countDown(); } - }); + return null; + })); } // Wait for all threads to complete assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS), "Test should complete within 30 seconds"); executor.shutdown(); + for (final Future task : tasks) { + try { + task.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + exceptionOccurred.set(true); + } catch (final ExecutionException | TimeoutException e) { + exceptionOccurred.set(true); + } + } // No exceptions should have occurred (especially no ConcurrentModificationException) assertFalse(exceptionOccurred.get(), "No exceptions should occur during concurrent access"); // Verify the node is still in a valid state From 0f13ed4f7561d7ccd1847ddfac74764594e23f21 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Thu, 30 Apr 2026 09:05:34 +0200 Subject: [PATCH 2/6] Update alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index 5a48f263dc..aa1b8787c9 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -92,7 +92,7 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { })); } // Wait for all threads to complete - assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS), "Test should complete within 30 seconds"); + assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS), "Test should complete within " + TIMEOUT_SECONDS + " seconds"); executor.shutdown(); for (final Future task : tasks) { try { From e7545e69a44614c0916d1a1881d098e63752b14e Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Thu, 30 Apr 2026 09:06:11 +0200 Subject: [PATCH 3/6] Update alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../model/sapere/nodes/LsaNodeConcurrencyTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index aa1b8787c9..5184cc8f95 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -100,7 +100,11 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { } catch (final InterruptedException e) { Thread.currentThread().interrupt(); exceptionOccurred.set(true); - } catch (final ExecutionException | TimeoutException e) { + } catch (final ExecutionException e) { + exceptionOccurred.set(true); + } catch (final TimeoutException e) { + task.cancel(true); + executor.shutdownNow(); exceptionOccurred.set(true); } } From edee5ff0ffc2af96e82aaefe14e2149663420021 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:13:23 +0000 Subject: [PATCH 4/6] test(incarnation-sapere): rethrow ExecutionException cause, fix ArrayList import, ensure executor shutdown in finally Agent-Logs-Url: https://github.com/AlchemistSimulator/Alchemist/sessions/6651c551-7d20-4a2c-aebc-a2f43f4054f8 Co-authored-by: DanySK <1991673+DanySK@users.noreply.github.com> --- .../sapere/nodes/LsaNodeConcurrencyTest.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index 5184cc8f95..762dcfa746 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -18,6 +18,7 @@ import it.unibo.alchemist.model.positions.Euclidean2DPosition; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -27,7 +28,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -55,8 +55,7 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { final int numberOfOperations = 100; final ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads); final CountDownLatch latch = new CountDownLatch(numberOfThreads); - final AtomicBoolean exceptionOccurred = new AtomicBoolean(false); - final List> tasks = new java.util.ArrayList<>(numberOfThreads); + final List> tasks = new ArrayList<>(numberOfThreads); // Start threads that modify the node while others read from it for (int i = 0; i < numberOfThreads; i++) { final int threadId = i; @@ -92,24 +91,21 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { })); } // Wait for all threads to complete - assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS), "Test should complete within " + TIMEOUT_SECONDS + " seconds"); - executor.shutdown(); - for (final Future task : tasks) { - try { - task.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - exceptionOccurred.set(true); - } catch (final ExecutionException e) { - exceptionOccurred.set(true); - } catch (final TimeoutException e) { - task.cancel(true); - executor.shutdownNow(); - exceptionOccurred.set(true); + try { + assertTrue(latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS), "Test should complete within " + TIMEOUT_SECONDS + " seconds"); + for (final Future task : tasks) { + try { + task.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (final ExecutionException e) { + throw new AssertionError("Task failed with exception", e.getCause()); + } catch (final TimeoutException e) { + task.cancel(true); + throw new AssertionError("Task timed out after " + TIMEOUT_SECONDS + " seconds", e); + } } + } finally { + executor.shutdownNow(); } - // No exceptions should have occurred (especially no ConcurrentModificationException) - assertFalse(exceptionOccurred.get(), "No exceptions should occur during concurrent access"); // Verify the node is still in a valid state final Map> finalContents = node.getContents(); assertNotNull(finalContents); From 58f6cfc0da006e16faf372a836e6a9c07702783a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Apr 2026 07:16:57 +0000 Subject: [PATCH 5/6] test(incarnation-sapere): handle InterruptedException in task.get() explicitly Agent-Logs-Url: https://github.com/AlchemistSimulator/Alchemist/sessions/6651c551-7d20-4a2c-aebc-a2f43f4054f8 Co-authored-by: DanySK <1991673+DanySK@users.noreply.github.com> --- .../alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index 762dcfa746..a2bbdf508c 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -96,6 +96,9 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { for (final Future task : tasks) { try { task.get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError("Test interrupted while waiting for task", e); } catch (final ExecutionException e) { throw new AssertionError("Task failed with exception", e.getCause()); } catch (final TimeoutException e) { From 22643a850d22bacbd045fecb59fd00ffe4b4e8c4 Mon Sep 17 00:00:00 2001 From: Danilo Pianini Date: Thu, 30 Apr 2026 11:26:49 +0200 Subject: [PATCH 6/6] Update alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../model/sapere/nodes/LsaNodeConcurrencyTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java index a2bbdf508c..41ada2f4c4 100644 --- a/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java +++ b/alchemist-incarnation-sapere/src/test/java/it/unibo/alchemist/model/sapere/nodes/LsaNodeConcurrencyTest.java @@ -108,6 +108,14 @@ void testConcurrentGetContentsAndModification() throws InterruptedException { } } finally { executor.shutdownNow(); + try { + if (!executor.awaitTermination(TIMEOUT_SECONDS, TimeUnit.SECONDS)) { + throw new AssertionError("Executor did not terminate within " + TIMEOUT_SECONDS + " seconds"); + } + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new AssertionError("Interrupted while waiting for executor termination", e); + } } // Verify the node is still in a valid state final Map> finalContents = node.getContents();