summaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-21 21:27:31 +0300
committerPaul Buetow <paul@buetow.org>2025-06-21 21:27:31 +0300
commit0841f0f9a1e3f3708d8c511a6290344e73607aab (patch)
tree4ae7fe60878b693d6975093acb491d977d247acd /src/main/java
parentce82046a11521b0537ac2150a07a4de54aec883a (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')
-rw-r--r--src/main/java/simulator/engine/AbstractSimulationEngine.java21
-rw-r--r--src/main/java/simulator/engine/HeadlessSimulationEngine.java34
-rw-r--r--src/main/java/testing/HeadlessProtocolRunner.java7
-rw-r--r--src/main/java/testing/HeadlessSimulationRunner.java141
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