summaryrefslogtreecommitdiff
path: root/src/main/java/testing/HeadlessSimulationRunner.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/testing/HeadlessSimulationRunner.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/testing/HeadlessSimulationRunner.java')
-rw-r--r--src/main/java/testing/HeadlessSimulationRunner.java141
1 files changed, 119 insertions, 22 deletions
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