diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-22 11:58:00 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-22 11:58:00 +0300 |
| commit | 0b5afe8839241dec66ba832cf42860ec69b87df8 (patch) | |
| tree | e100d2d6204f8c04dc33418ae9f193fa6b1a83c2 /src/main/java/testing | |
| parent | b0fc02ce45cb51ce7c8d607d4773808cfa9b6c87 (diff) | |
Fix message delivery in headless test environment
- Fixed HeadlessSimulationEngine to use correct task manager from receiving process
- Reduced message delays for testing (10-50ms instead of 500-2000ms)
- Fixed process ID method call (getProcessID not getProcessId)
- Improved message delivery scheduling to ensure tasks go to the right task manager
This resolves message delivery issues where messages were sent but not received.
BasicMulticast test now passes, but 12 protocol tests still failing.
š¤ Generated with Claude Code
https://claude.ai/code
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'src/main/java/testing')
| -rw-r--r-- | src/main/java/testing/HeadlessProtocolRunner.java | 15 | ||||
| -rw-r--r-- | src/main/java/testing/HeadlessSimulationRunner.java | 55 | ||||
| -rw-r--r-- | src/main/java/testing/LogCapture.java | 29 | ||||
| -rw-r--r-- | src/main/java/testing/ProtocolVerifier.java | 65 | ||||
| -rw-r--r-- | src/main/java/testing/SimulationMetrics.java | 6 |
5 files changed, 156 insertions, 14 deletions
diff --git a/src/main/java/testing/HeadlessProtocolRunner.java b/src/main/java/testing/HeadlessProtocolRunner.java index daf96aa..a6098de 100644 --- a/src/main/java/testing/HeadlessProtocolRunner.java +++ b/src/main/java/testing/HeadlessProtocolRunner.java @@ -50,6 +50,21 @@ public class HeadlessProtocolRunner { System.out.println(" Log entries: " + result.getMetrics().getTotalLogCount()); System.out.println(" Messages per process: " + result.getMetrics().getProcessMessageCounts()); + // Count total messages sent + int totalMessages = result.getMetrics().getTotalMessageCount(); + System.out.println(" Total messages sent: " + totalMessages); + + // Check if any messages were sent + if (totalMessages == 0) { + System.err.println("\nā ļø WARNING: No messages were sent during simulation!"); + System.err.println(" This indicates the protocol may not be functioning correctly."); + if (!verbose) { + System.err.println(" Re-run with -Dds.sim.verbose=true for detailed output."); + } + // Mark as failure + throw new RuntimeException("Protocol test failed: No messages sent"); + } + System.out.println(); } catch (Exception e) { System.err.println("ā FAILED: " + e.getMessage()); diff --git a/src/main/java/testing/HeadlessSimulationRunner.java b/src/main/java/testing/HeadlessSimulationRunner.java index 9d2274c..6279fa9 100644 --- a/src/main/java/testing/HeadlessSimulationRunner.java +++ b/src/main/java/testing/HeadlessSimulationRunner.java @@ -26,6 +26,11 @@ public class HeadlessSimulationRunner { public HeadlessSimulationRunner() { this.prefs = new VSDefaultPrefs(); this.prefs.fillWithDefaults(); + + // Set reasonable message delays for testing (10-50ms instead of 500-2000ms) + this.prefs.initLong("message.sendingtime.min", 10); + this.prefs.initLong("message.sendingtime.max", 50); + VSRegisteredEvents.init(prefs); this.executor = Executors.newSingleThreadExecutor(); } @@ -55,14 +60,20 @@ public class HeadlessSimulationRunner { simulator = loaded.getSimulator(); viz = loaded.getVisualization(); + // Update message delays on all processes after loading + for (int i = 0; i < viz.getNumProcesses(); i++) { + VSInternalProcess process = viz.getProcess(i); + if (process != null) { + process.initLong("message.sendingtime.min", 10); + process.initLong("message.sendingtime.max", 50); + } + } + if (simulator == null || viz == null) { throw new IllegalStateException("Failed to load simulation"); } - // Set up headless message handlers for all processes - setupHeadlessMessageHandlers(viz); - - // Install log capture + // Install log capture first logCapture = new LogCapture(); logCapture.setPrintLogs(printLogs); if (listener != null) { @@ -70,6 +81,9 @@ public class HeadlessSimulationRunner { } installLogCapture(); + // Set up headless message handlers for all processes (after log capture is ready) + setupHeadlessMessageHandlers(viz); + // Get the simulation's configured end time long untilTime = viz.getUntilTime(); long actualMaxTime = Math.min(maxTime, untilTime); @@ -187,10 +201,11 @@ public class HeadlessSimulationRunner { lastActiveTime = currentTime; } else { noActivityCount++; - // If no activity for 3000ms (3 seconds) of simulation time, stop - // This accounts for message delivery times of 500-2000ms plus some buffer - if (noActivityCount > 3000 && (currentTime - lastActiveTime) > 3000) { - System.out.println("No activity detected for 3 seconds - simulation complete at time " + simulatorTime); + // If no activity for 5000ms (5 seconds) of simulation time, stop + // This accounts for message delivery times of 500-2000ms plus extra buffer + // to ensure all messages are delivered + if (noActivityCount > 5000 && (currentTime - lastActiveTime) > 5000) { + System.out.println("No activity detected for 5 seconds - simulation complete at time " + simulatorTime); break; } } @@ -208,10 +223,19 @@ public class HeadlessSimulationRunner { private boolean hasPendingActivity(VSTaskManager taskManager, Field globalTasksField, long currentTime) { try { - // Check global tasks + // Check global tasks - but also check if any are scheduled for future times Queue<?> globalTasks = (Queue<?>) globalTasksField.get(taskManager); if (globalTasks != null && !globalTasks.isEmpty()) { - return true; // If any global tasks exist, keep running + // Check if any tasks are scheduled for the future + for (Object obj : globalTasks) { + VSTask task = (VSTask) obj; + if (task.getTaskTime() > currentTime) { + // There's a future task scheduled, keep running + return true; + } + } + // If all tasks are in the past or present, they should execute now + return true; } // Check process-specific tasks @@ -220,12 +244,19 @@ public class HeadlessSimulationRunner { if (process != null) { Queue<VSTask> tasks = process.getTasks(); if (tasks != null && !tasks.isEmpty()) { - return true; // If any process tasks exist, keep running + // Check if any tasks are scheduled for the future + for (VSTask task : tasks) { + if (task.getTaskTime() > process.getTime()) { + return true; + } + } + // If all tasks are ready to run, keep going + return true; } } } - // Check for messages in transit + // Check for messages in transit (visualization lines) Field messageLinesField = VSSimulatorVisualization.class.getDeclaredField("messageLines"); messageLinesField.setAccessible(true); LinkedList<?> messageLines = (LinkedList<?>) messageLinesField.get(viz); diff --git a/src/main/java/testing/LogCapture.java b/src/main/java/testing/LogCapture.java index 97bb127..ddd0ad0 100644 --- a/src/main/java/testing/LogCapture.java +++ b/src/main/java/testing/LogCapture.java @@ -30,6 +30,17 @@ public class LogCapture extends VSLogging { this.printLogs = printLogs; } + public void setSimulatorCanvas(VSSimulatorVisualization viz) { + // Store reference for process count + try { + Field field = VSLogging.class.getDeclaredField("simulatorVisualization"); + field.setAccessible(true); + field.set(this, viz); + } catch (Exception e) { + // Ignore + } + } + public void setLogPrefix(String prefix) { this.logPrefix = prefix; } @@ -136,9 +147,23 @@ public class LogCapture extends VSLogging { public Map<Integer, Integer> getProcessMessageCounts() { Map<Integer, Integer> counts = new HashMap<>(); - for (Map.Entry<Integer, List<LogEntry>> entry : processLogs.entrySet()) { - counts.put(entry.getKey(), entry.getValue().size()); + + // Initialize counts for all processes + VSSimulatorVisualization viz = getSimulatorVisualization(); + if (viz != null) { + for (int i = 0; i < viz.getNumProcesses(); i++) { + counts.put(i, 0); + } } + + // Count messages from all captured logs + for (LogEntry log : capturedLogs) { + if (log.getMessage().contains("Message sent")) { + int processNum = log.getProcessNum(); + counts.put(processNum, counts.getOrDefault(processNum, 0) + 1); + } + } + return counts; } diff --git a/src/main/java/testing/ProtocolVerifier.java b/src/main/java/testing/ProtocolVerifier.java index 19ed1f2..e5338d4 100644 --- a/src/main/java/testing/ProtocolVerifier.java +++ b/src/main/java/testing/ProtocolVerifier.java @@ -80,6 +80,29 @@ public class ProtocolVerifier { } /** + * Expect at least n messages to be sent during the simulation. + */ + public ProtocolVerifier expectAtLeastNMessages(int minMessages) { + rules.add(new MessageCountRule(minMessages, Integer.MAX_VALUE)); + return this; + } + + /** + * Expect exactly n messages to be sent during the simulation. + */ + public ProtocolVerifier expectExactlyNMessages(int count) { + rules.add(new MessageCountRule(count, count)); + return this; + } + + /** + * Expect messages to be sent (at least 1). + */ + public ProtocolVerifier expectMessages() { + return expectAtLeastNMessages(1); + } + + /** * Verify all rules against the provided logs. */ public VerificationResult verify(List<LogEntry> logs) { @@ -240,4 +263,46 @@ public class ProtocolVerifier { return new RuleResult(passed, message, matches); } } + + /** + * Rule that verifies message count. + */ + private static class MessageCountRule implements VerificationRule { + private final int minCount; + private final int maxCount; + private final String description; + + public MessageCountRule(int minCount, int maxCount) { + this.minCount = minCount; + this.maxCount = maxCount; + this.description = String.format( + "Message count should be %s", + minCount == maxCount ? + String.valueOf(minCount) : + minCount + "-" + (maxCount == Integer.MAX_VALUE ? "ā" : maxCount) + ); + } + + @Override + public RuleResult verify(List<LogEntry> logs) { + int messageCount = 0; + List<LogEntry> messageLogs = new ArrayList<>(); + + // Count all "Message sent" logs + for (LogEntry log : logs) { + if (log.getMessage().contains("Message sent")) { + messageCount++; + messageLogs.add(log); + } + } + + boolean passed = messageCount >= minCount && messageCount <= maxCount; + String message = String.format( + "%s (found %d messages)", + description, messageCount + ); + + return new RuleResult(passed, message, messageLogs); + } + } }
\ No newline at end of file diff --git a/src/main/java/testing/SimulationMetrics.java b/src/main/java/testing/SimulationMetrics.java index 2b80631..dc8fc39 100644 --- a/src/main/java/testing/SimulationMetrics.java +++ b/src/main/java/testing/SimulationMetrics.java @@ -44,4 +44,10 @@ public class SimulationMetrics { return (double) totalProcessMessages / numProcesses; } + + public int getTotalMessageCount() { + return processMessageCounts.values().stream() + .mapToInt(Integer::intValue) + .sum(); + } }
\ No newline at end of file |
