From 695adc1f6bfb0a0eeef4dd6c035475ea2826871f Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 21 Jun 2025 20:10:38 +0300 Subject: Complete GUI decoupling implementation for headless testing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement MessageHandler pattern to decouple message sending from visualization - Add HeadlessLoader to load simulations without GUI components - Create HeadlessProtocolRunner for clean protocol test execution - Update VSInternalProcess to use MessageHandler for message routing - Add null checks in VSSimulator for headless mode compatibility - Update VSSimulatorVisualization paint() to check for headless mode - Remove obsolete test scripts and documentation - Update test-protocols.sh to remove GUI error suppression options - Consolidate testing documentation in docs/testing-guide.md All protocol tests now run cleanly in headless mode without GUI errors, enabling proper CI/CD integration and automated testing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../simulator/engine/AbstractSimulationEngine.java | 204 +++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/main/java/simulator/engine/AbstractSimulationEngine.java (limited to 'src/main/java/simulator/engine/AbstractSimulationEngine.java') diff --git a/src/main/java/simulator/engine/AbstractSimulationEngine.java b/src/main/java/simulator/engine/AbstractSimulationEngine.java new file mode 100644 index 0000000..21be5c7 --- /dev/null +++ b/src/main/java/simulator/engine/AbstractSimulationEngine.java @@ -0,0 +1,204 @@ +package simulator.engine; + +import core.*; +import prefs.VSPrefs; +import simulator.VSLogging; +import events.internal.VSMessageReceiveEvent; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Abstract base implementation of SimulationEngine that provides common + * functionality for both headless and visual simulation engines. + */ +public abstract class AbstractSimulationEngine implements SimulationEngine { + + protected final VSPrefs prefs; + protected final List processes; + protected final List visualizers; + protected final VSTaskManager taskManager; + protected VSLogging loging; + + protected long time; + protected boolean isPaused; + protected boolean isResetted; + protected boolean hasFinished; + + public AbstractSimulationEngine(VSPrefs prefs, VSLogging loging) { + this.prefs = prefs; + this.loging = loging; + this.processes = new ArrayList<>(); + this.visualizers = new CopyOnWriteArrayList<>(); + this.taskManager = new VSTaskManager(prefs, null); // We'll inject visualization later + this.time = 0; + this.isPaused = true; + this.isResetted = true; + this.hasFinished = false; + } + + @Override + public void sendMessage(VSMessage message) { + // Schedule message delivery to all processes (broadcast model) + scheduleMessageDelivery(message, time); + + // 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); + + protected abstract void scheduleMessageDelivery(VSMessage message, long deliveryTime); + + @Override + public void addProcess(VSInternalProcess process) { + processes.add(process); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onProcessAdded(process); + } + } + + @Override + public void removeProcess(VSInternalProcess process) { + processes.remove(process); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onProcessRemoved(process); + } + } + + @Override + public List getProcesses() { + return new ArrayList<>(processes); + } + + @Override + public VSInternalProcess getProcess(int index) { + if (index >= 0 && index < processes.size()) { + return processes.get(index); + } + return null; + } + + @Override + public int getNumProcesses() { + return processes.size(); + } + + @Override + public VSTaskManager getTaskManager() { + return taskManager; + } + + @Override + public long getTime() { + return time; + } + + @Override + public void setTime(long time) { + this.time = time; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onTimeChanged(time); + } + } + + @Override + public void reset() { + // Reset state + isResetted = true; + isPaused = true; + hasFinished = false; + time = 0; + + // Reset all processes + for (VSInternalProcess process : processes) { + process.reset(); + } + + // Reset task manager + taskManager.reset(); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationReset(); + } + } + + @Override + public void play() { + isPaused = false; + isResetted = false; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationStarted(); + } + } + + @Override + public void pause() { + isPaused = true; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationPaused(); + } + } + + @Override + public boolean isPaused() { + return isPaused; + } + + @Override + public boolean isResetted() { + return isResetted; + } + + @Override + public boolean hasFinished() { + return hasFinished; + } + + @Override + public void setFinished(boolean finished) { + this.hasFinished = finished; + + if (finished) { + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationFinished(); + } + } + } + + @Override + public void addVisualizer(SimulationVisualizer visualizer) { + visualizers.add(visualizer); + } + + @Override + public void removeVisualizer(SimulationVisualizer visualizer) { + visualizers.remove(visualizer); + } + + /** + * Set the logging instance. + */ + public void setLogging(VSLogging loging) { + this.loging = loging; + } +} \ No newline at end of file -- cgit v1.2.3