diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-21 21:27:31 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-21 21:27:31 +0300 |
| commit | 0841f0f9a1e3f3708d8c511a6290344e73607aab (patch) | |
| tree | 4ae7fe60878b693d6975093acb491d977d247acd /src/main/java | |
| parent | ce82046a11521b0537ac2150a07a4de54aec883a (diff) | |
Move test scripts to scripts/ directory and fix simulation completion
- Moved test-protocols.sh, test-quick.sh, test-verbose.sh to scripts/
- Updated references in README.md and docs/testing-guide.md
- Fixed HeadlessSimulationRunner to properly run simulations to completion
- Fixed message delivery timing (now respects 500-2000ms delays)
- Added proper process time synchronization
- Fixed HeadlessProtocolRunner to exit cleanly
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'src/main/java')
4 files changed, 149 insertions, 54 deletions
diff --git a/src/main/java/simulator/engine/AbstractSimulationEngine.java b/src/main/java/simulator/engine/AbstractSimulationEngine.java index 21be5c7..45b2522 100644 --- a/src/main/java/simulator/engine/AbstractSimulationEngine.java +++ b/src/main/java/simulator/engine/AbstractSimulationEngine.java @@ -16,7 +16,7 @@ public abstract class AbstractSimulationEngine implements SimulationEngine { protected final VSPrefs prefs; protected final List<VSInternalProcess> processes; protected final List<SimulationVisualizer> visualizers; - protected final VSTaskManager taskManager; + protected VSTaskManager taskManager; protected VSLogging loging; protected long time; @@ -38,19 +38,16 @@ public abstract class AbstractSimulationEngine implements SimulationEngine { @Override public void sendMessage(VSMessage message) { + // Calculate proper delivery time + long deliveryTime = calculateDeliveryTime(message); + // Schedule message delivery to all processes (broadcast model) - scheduleMessageDelivery(message, time); + scheduleMessageDelivery(message, deliveryTime); // Notify visualizers for (SimulationVisualizer visualizer : visualizers) { visualizer.onMessageSent(message); } - - // Log the message - if (loging != null) { - loging.log("Message sent; ID: " + message.getMessageID() + - "; Protocol: " + message.getName()); - } } protected abstract long calculateDeliveryTime(VSMessage message); @@ -100,6 +97,14 @@ public abstract class AbstractSimulationEngine implements SimulationEngine { return taskManager; } + /** + * Set the task manager for this engine. + * Used when integrating with existing simulation infrastructure. + */ + public void setTaskManager(VSTaskManager taskManager) { + this.taskManager = taskManager; + } + @Override public long getTime() { return time; diff --git a/src/main/java/simulator/engine/HeadlessSimulationEngine.java b/src/main/java/simulator/engine/HeadlessSimulationEngine.java index 36a64a7..fa6dde8 100644 --- a/src/main/java/simulator/engine/HeadlessSimulationEngine.java +++ b/src/main/java/simulator/engine/HeadlessSimulationEngine.java @@ -24,23 +24,12 @@ public class HeadlessSimulationEngine extends AbstractSimulationEngine { return time; // Deliver immediately if process not found } - // Calculate network delay - long networkDelay = prefs.getLong("sim.network.delay"); - long variability = prefs.getLong("sim.network.variability"); + // Use the process's getDurationTime method to get the message duration + // This respects the message.sendingtime.min and message.sendingtime.max preferences + long durationTime = source.getDurationTime(); - // Add random variability - if (variability > 0) { - long variance = (long)(Math.random() * variability * 2) - variability; - networkDelay += variance; - } - - // Ensure minimum delay - if (networkDelay < 0) { - networkDelay = 0; - } - - // Calculate delivery time based on source process time - return source.getTime() + networkDelay; + // Calculate delivery time based on source process's global time + duration + return source.getGlobalTime() + durationTime; } @Override @@ -49,6 +38,13 @@ public class HeadlessSimulationEngine extends AbstractSimulationEngine { VSInternalProcess sendingProcess = (VSInternalProcess) message.getSendingProcess(); boolean recvOwn = prefs.getBoolean("sim.message.own.recv"); + // Debug logging + if (loging != null) { + loging.log("Message " + message.getMessageID() + " scheduled for delivery at time " + + deliveryTime + " (sent at globalTime=" + sendingProcess.getGlobalTime() + + ", duration=" + (deliveryTime - sendingProcess.getGlobalTime()) + "ms)"); + } + // Schedule delivery to all processes for (VSInternalProcess receiverProcess : processes) { if (receiverProcess.equals(sendingProcess)) { @@ -62,12 +58,6 @@ public class HeadlessSimulationEngine extends AbstractSimulationEngine { VSMessageReceiveEvent receiveEvent = new VSMessageReceiveEvent(message); VSTask task = new VSTask(deliveryTime, receiverProcess, receiveEvent, VSTask.GLOBAL); taskManager.addTask(task); - - if (loging != null) { - loging.log("Message scheduled for delivery to process " + - receiverProcess.getProcessNum() + "; ID: " + - message.getMessageID() + "; Time: " + deliveryTime); - } } } diff --git a/src/main/java/testing/HeadlessProtocolRunner.java b/src/main/java/testing/HeadlessProtocolRunner.java index d68d743..daf96aa 100644 --- a/src/main/java/testing/HeadlessProtocolRunner.java +++ b/src/main/java/testing/HeadlessProtocolRunner.java @@ -38,7 +38,7 @@ public class HeadlessProtocolRunner { try { long startTime = System.currentTimeMillis(); - SimulationResult result = runner.runSimulation(simFile, 5000); // 5 second timeout + SimulationResult result = runner.runSimulation(simFile, 10000); // 10 second timeout long duration = System.currentTimeMillis() - startTime; if (verbose) { @@ -59,6 +59,9 @@ public class HeadlessProtocolRunner { } finally { runner.shutdown(); } + + // Exit explicitly when running single simulation + System.exit(0); } private static void runAllSimulations(boolean verbose) throws Exception { @@ -87,7 +90,7 @@ public class HeadlessProtocolRunner { try { long startTime = System.currentTimeMillis(); - SimulationResult result = runner.runSimulation(simFile.getPath(), 3000); // 3 second timeout + SimulationResult result = runner.runSimulation(simFile.getPath(), 10000); // 10 second timeout long duration = System.currentTimeMillis() - startTime; System.out.println("✓ PASSED in " + duration + "ms"); diff --git a/src/main/java/testing/HeadlessSimulationRunner.java b/src/main/java/testing/HeadlessSimulationRunner.java index ef50995..9d2274c 100644 --- a/src/main/java/testing/HeadlessSimulationRunner.java +++ b/src/main/java/testing/HeadlessSimulationRunner.java @@ -70,12 +70,16 @@ public class HeadlessSimulationRunner { } installLogCapture(); - System.out.println("Running simulation for " + maxTime + "ms..."); + // Get the simulation's configured end time + long untilTime = viz.getUntilTime(); + long actualMaxTime = Math.min(maxTime, untilTime); + + System.out.println("Running simulation for up to " + actualMaxTime + "ms (until time: " + untilTime + "ms)..."); // Run simulation Future<Void> runFuture = executor.submit(() -> { try { - runSimulationSteps(maxTime); + runSimulationSteps(actualMaxTime); } catch (Exception e) { System.err.println("Error during simulation: " + e.getMessage()); e.printStackTrace(); @@ -85,7 +89,7 @@ public class HeadlessSimulationRunner { // Wait for completion or timeout try { - runFuture.get(maxTime * 2, TimeUnit.MILLISECONDS); + runFuture.get(actualMaxTime * 2, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { System.out.println("Simulation timeout - stopping..."); runFuture.cancel(true); @@ -113,31 +117,126 @@ public class HeadlessSimulationRunner { .getDeclaredField("time"); timeField.setAccessible(true); - // Find runTasks method with correct signature - Method runTasksMethod = VSTaskManager.class - .getDeclaredMethod("runTasks", long.class, long.class, long.class); - runTasksMethod.setAccessible(true); + // Get simulatorTime field for accurate time tracking + Field simulatorTimeField = VSSimulatorVisualization.class + .getDeclaredField("simulatorTime"); + simulatorTimeField.setAccessible(true); + + // Get isPaused and hasFinished fields + Field isPausedField = VSSimulatorVisualization.class + .getDeclaredField("isPaused"); + isPausedField.setAccessible(true); + Field hasFinishedField = VSSimulatorVisualization.class + .getDeclaredField("hasFinished"); + hasFinishedField.setAccessible(true); + + // Get clockSpeed field and ensure it's set + Field clockSpeedField = VSSimulatorVisualization.class + .getDeclaredField("clockSpeed"); + clockSpeedField.setAccessible(true); + double clockSpeed = clockSpeedField.getDouble(viz); + if (clockSpeed == 0) { + // Set default clock speed if not initialized + clockSpeed = 1.0; + clockSpeedField.setDouble(viz, clockSpeed); + } + + // Get task queue fields for checking if tasks remain + Field globalTasksField = VSTaskManager.class.getDeclaredField("globalTasks"); + globalTasksField.setAccessible(true); + + // Set isPaused to false to allow simulation to run + isPausedField.setBoolean(viz, false); + hasFinishedField.setBoolean(viz, false); long startTime = timeField.getLong(viz); long currentTime = startTime; + long endTime = startTime + maxTime; + int noActivityCount = 0; + int lastLogCount = 0; + long lastActiveTime = 0; - while (currentTime - startTime < maxTime) { - // Update time - timeField.setLong(viz, currentTime); - - // Sync process times + // Call updateSimulator method to advance simulation + Method updateSimulatorMethod = VSSimulatorVisualization.class + .getDeclaredMethod("updateSimulator", long.class, long.class); + updateSimulatorMethod.setAccessible(true); + + System.out.println("Starting simulation at time " + startTime + ", running until " + endTime); + + while (currentTime < endTime) { + // Sync all process times BEFORE running tasks for (int i = 0; i < viz.getNumProcesses(); i++) { - viz.getProcess(i).syncTime(currentTime); + VSInternalProcess process = viz.getProcess(i); + if (process != null) { + process.syncTime(currentTime); + } } - // Run tasks (step, offset, lastGlobalTime) - runTasksMethod.invoke(taskManager, currentTime, 0L, currentTime - 1); + // Update simulation time - this also runs tasks internally + updateSimulatorMethod.invoke(viz, currentTime, currentTime - 1); + long simulatorTime = simulatorTimeField.getLong(viz); + + // Check if there's been any activity + int currentLogCount = logCapture.getTotalLogCount(); + boolean hasActivity = currentLogCount > lastLogCount || + hasPendingActivity(taskManager, globalTasksField, simulatorTime); + + if (hasActivity) { + noActivityCount = 0; + lastLogCount = currentLogCount; + 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); + break; + } + } // Advance time by 1ms currentTime++; + timeField.setLong(viz, currentTime); - // Small delay to prevent CPU spinning - Thread.sleep(1); + // No delay needed - let simulation run at full speed + } + + // Set isPaused back to true when done + isPausedField.setBoolean(viz, true); + } + + private boolean hasPendingActivity(VSTaskManager taskManager, Field globalTasksField, long currentTime) { + try { + // Check global tasks + Queue<?> globalTasks = (Queue<?>) globalTasksField.get(taskManager); + if (globalTasks != null && !globalTasks.isEmpty()) { + return true; // If any global tasks exist, keep running + } + + // Check process-specific tasks + for (int i = 0; i < viz.getNumProcesses(); i++) { + VSInternalProcess process = viz.getProcess(i); + if (process != null) { + Queue<VSTask> tasks = process.getTasks(); + if (tasks != null && !tasks.isEmpty()) { + return true; // If any process tasks exist, keep running + } + } + } + + // Check for messages in transit + Field messageLinesField = VSSimulatorVisualization.class.getDeclaredField("messageLines"); + messageLinesField.setAccessible(true); + LinkedList<?> messageLines = (LinkedList<?>) messageLinesField.get(viz); + if (messageLines != null && !messageLines.isEmpty()) { + return true; // Messages are still being delivered + } + + return false; + } catch (Exception e) { + // If we can't check, assume there might be activity + return true; } } @@ -202,6 +301,9 @@ public class HeadlessSimulationRunner { // Create a headless simulation engine HeadlessSimulationEngine engine = new HeadlessSimulationEngine(prefs, logCapture); + // Set the task manager from the visualization + engine.setTaskManager(viz.getTaskManager()); + // Copy processes to engine for (int i = 0; i < viz.getNumProcesses(); i++) { VSInternalProcess process = viz.getProcess(i); @@ -213,10 +315,5 @@ public class HeadlessSimulationRunner { process.setMessageHandler(handler); } } - - // Note: Task manager state is not copied because: - // - Global tasks are in VSTaskManager.globalTasks - // - Local tasks are stored in each VSInternalProcess.tasks - // - The engine already has references to the processes which contain their tasks } }
\ No newline at end of file |
