summaryrefslogtreecommitdiff
path: root/src/main/java/testing/HeadlessSimulationRunner.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/testing/HeadlessSimulationRunner.java')
-rw-r--r--src/main/java/testing/HeadlessSimulationRunner.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/main/java/testing/HeadlessSimulationRunner.java b/src/main/java/testing/HeadlessSimulationRunner.java
new file mode 100644
index 0000000..c3b699e
--- /dev/null
+++ b/src/main/java/testing/HeadlessSimulationRunner.java
@@ -0,0 +1,188 @@
+package testing;
+
+import simulator.*;
+import core.*;
+import prefs.*;
+import events.*;
+import serialize.VSSerialize;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * Runs DS-Sim simulations in headless mode without GUI dependencies.
+ * Captures logs and provides verification capabilities for automated testing.
+ */
+public class HeadlessSimulationRunner {
+ private final VSDefaultPrefs prefs;
+ private VSSimulator simulator;
+ private VSSimulatorVisualization viz;
+ private LogCapture logCapture;
+ private final ExecutorService executor;
+ private boolean printLogs = false;
+
+ public HeadlessSimulationRunner() {
+ this.prefs = new VSDefaultPrefs();
+ this.prefs.fillWithDefaults();
+ VSRegisteredEvents.init(prefs);
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+
+ /**
+ * Run a simulation from a saved file for a specified duration.
+ *
+ * @param simulationFile Path to the saved simulation .dat file
+ * @param maxTime Maximum simulation time in milliseconds
+ * @return SimulationResult containing logs and metrics
+ */
+ public SimulationResult runSimulation(String simulationFile, long maxTime)
+ throws Exception {
+ return runSimulation(simulationFile, maxTime, null);
+ }
+
+ /**
+ * Run a simulation with an optional log listener.
+ */
+ public SimulationResult runSimulation(String simulationFile, long maxTime, LogListener listener)
+ throws Exception {
+ System.out.println("Loading simulation: " + simulationFile);
+
+ try {
+ // Use the new headless loader
+ HeadlessLoader.LoadedSimulation loaded = HeadlessLoader.load(simulationFile, prefs);
+ simulator = loaded.getSimulator();
+ viz = loaded.getVisualization();
+
+ // Install log capture
+ logCapture = new LogCapture();
+ logCapture.setPrintLogs(printLogs);
+ if (listener != null) {
+ logCapture.addListener(listener);
+ }
+ installLogCapture();
+
+ System.out.println("Running simulation for " + maxTime + "ms...");
+
+ // Run simulation
+ Future<Void> runFuture = executor.submit(() -> {
+ try {
+ runSimulationSteps(maxTime);
+ } catch (Exception e) {
+ System.err.println("Error during simulation: " + e.getMessage());
+ e.printStackTrace();
+ }
+ return null;
+ });
+
+ // Wait for completion or timeout
+ try {
+ runFuture.get(maxTime * 2, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ System.out.println("Simulation timeout - stopping...");
+ runFuture.cancel(true);
+ }
+
+ System.out.println("Simulation complete. Captured " +
+ logCapture.getTotalLogCount() + " log entries.");
+
+ return new SimulationResult(
+ logCapture.getCapturedLogs(),
+ logCapture.getProcessLogs(),
+ getSimulationMetrics()
+ );
+ } catch (Exception e) {
+ System.err.println("Failed to load simulation: " + e.getMessage());
+ throw e;
+ }
+ }
+
+ private void runSimulationSteps(long maxTime) throws Exception {
+ VSTaskManager taskManager = viz.getTaskManager();
+
+ // Get necessary fields via reflection
+ Field timeField = VSSimulatorVisualization.class
+ .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);
+
+ long startTime = timeField.getLong(viz);
+ long currentTime = startTime;
+
+ while (currentTime - startTime < maxTime) {
+ // Update time
+ timeField.setLong(viz, currentTime);
+
+ // Sync process times
+ for (int i = 0; i < viz.getNumProcesses(); i++) {
+ viz.getProcess(i).syncTime(currentTime);
+ }
+
+ // Run tasks (step, offset, lastGlobalTime)
+ runTasksMethod.invoke(taskManager, currentTime, 0L, currentTime - 1);
+
+ // Advance time by 1ms
+ currentTime++;
+
+ // Small delay to prevent CPU spinning
+ Thread.sleep(1);
+ }
+ }
+
+ private void installLogCapture() throws Exception {
+ // Set simulatorVisualization reference in logCapture
+ logCapture.setSimulatorCanvas(viz);
+
+ // Install on visualization
+ Field logingField = VSSimulatorVisualization.class
+ .getDeclaredField("loging");
+ logingField.setAccessible(true);
+ logingField.set(viz, logCapture);
+
+ // Install on all processes
+ for (int i = 0; i < viz.getNumProcesses(); i++) {
+ VSInternalProcess process = viz.getProcess(i);
+ if (process != null) {
+ Field processLogingField = VSAbstractProcess.class
+ .getDeclaredField("loging");
+ processLogingField.setAccessible(true);
+ processLogingField.set(process, logCapture);
+ }
+ }
+ }
+
+ private SimulationMetrics getSimulationMetrics() {
+ return new SimulationMetrics(
+ viz.getNumProcesses(),
+ logCapture.getTotalLogCount(),
+ logCapture.getProcessMessageCounts()
+ );
+ }
+
+ public void setPrintLogs(boolean printLogs) {
+ this.printLogs = printLogs;
+ if (logCapture != null) {
+ logCapture.setPrintLogs(printLogs);
+ }
+ }
+
+ public void addLogListener(LogListener listener) {
+ if (logCapture != null) {
+ logCapture.addListener(listener);
+ }
+ }
+
+ public void shutdown() {
+ executor.shutdown();
+ try {
+ if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
+ executor.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ executor.shutdownNow();
+ }
+ }
+} \ No newline at end of file