From 863d477966a627aa935eb4884725fc122f290ea1 Mon Sep 17 00:00:00 2001 From: Dai Ha Date: Sun, 16 Aug 2026 18:12:23 +0200 Subject: [PATCH] CB-603: make FakeHerdr.calls thread-safe Background loops call the fake from their own scheduler threads while a test polls called() from the test thread. The list was a plain ArrayList, so a nudge landing mid-stream threw ConcurrentModificationException out of called(). It surfaced while I was verifying CB-598, which nudges more often, but the race is on main today and is unrelated to that change. 824 tests, BUILD SUCCESS. --- .../src/test/java/dev/ltms/bridged/herdr/FakeHerdr.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/bridged/src/test/java/dev/ltms/bridged/herdr/FakeHerdr.java b/bridged/src/test/java/dev/ltms/bridged/herdr/FakeHerdr.java index 2ec0c5a..3da81cb 100644 --- a/bridged/src/test/java/dev/ltms/bridged/herdr/FakeHerdr.java +++ b/bridged/src/test/java/dev/ltms/bridged/herdr/FakeHerdr.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; /** * Recording fake {@link HerdrClient} for unit/acceptance tests. Returns canned frames @@ -22,7 +23,13 @@ public final class FakeHerdr implements HerdrClient { public static final long WORKER_PID = 4242; private final ObjectMapper mapper = new ObjectMapper(); - public final List calls = new ArrayList<>(); + /** + * Thread-safe on purpose. Background loops — {@link dev.ltms.bridged.msg.ReplyPushLoop} and the + * lead heartbeat — call this fake from their own scheduler threads while a test polls + * {@link #called} from the test thread. A plain {@code ArrayList} threw + * {@code ConcurrentModificationException} out of {@code called()} when a nudge landed mid-stream. + */ + public final List calls = new CopyOnWriteArrayList<>(); private boolean healthy = true; private final List extraWorkspaces = new ArrayList<>(); private final List extraAgents = new ArrayList<>();