From 73567cc1e5a3a02d4c60c534122e8db9e8a82949 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 27 Mar 2026 15:43:46 +0200 Subject: Narrow replay task visibility fix --- saved-simulations/raft.dat | Bin 15305 -> 15305 bytes src/main/java/core/VSTaskManager.java | 7 +- .../java/simulator/builder/SimulationBuilder.java | 2 +- .../VSTaskManagerCrashRecoveryIntegrationTest.java | 88 +++++++++++++++++++++ 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/saved-simulations/raft.dat b/saved-simulations/raft.dat index c54c0c5..9f6c7ad 100644 Binary files a/saved-simulations/raft.dat and b/saved-simulations/raft.dat differ diff --git a/src/main/java/core/VSTaskManager.java b/src/main/java/core/VSTaskManager.java index c78af5b..bd95a4e 100644 --- a/src/main/java/core/VSTaskManager.java +++ b/src/main/java/core/VSTaskManager.java @@ -553,12 +553,7 @@ public class VSTaskManager implements VSSerializable { for (int i = 0; i < numTasks; ++i) { VSTask task = new VSTask(serialize, objectInputStream); - /* - * Serialized tasks define the replay state of a loaded simulation. - * Register them as programmed so they remain visible in the Event - * view and survive reset/replay after deserialization. - */ - addTask(task, PROGRAMMED); + addTask(task, task.isProgrammed()); } /** For later backwards compatibility, to add more stuff */ diff --git a/src/main/java/simulator/builder/SimulationBuilder.java b/src/main/java/simulator/builder/SimulationBuilder.java index cf5f962..ac78a68 100644 --- a/src/main/java/simulator/builder/SimulationBuilder.java +++ b/src/main/java/simulator/builder/SimulationBuilder.java @@ -308,7 +308,7 @@ public class SimulationBuilder { // Create task VSTask task = new VSTask(st.time, process, st.event, st.isGlobalTimed ? VSTask.GLOBAL : VSTask.LOCAL); - taskManager.addTask(task); + taskManager.addTask(task, VSTaskManager.PROGRAMMED); } } diff --git a/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java b/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java index 34908bb..eb4bc7b 100644 --- a/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java +++ b/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.FileOutputStream; +import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Files; @@ -15,6 +17,8 @@ import org.junit.jupiter.api.Test; import events.implementations.VSProcessCrashEvent; import events.implementations.VSProcessRecoverEvent; +import prefs.VSSerializablePrefs; +import serialize.VSSerialize; import simulator.VSSimulator; import simulator.VSSimulatorVisualization; import simulator.builder.SimulationBuilder; @@ -147,6 +151,57 @@ class VSTaskManagerCrashRecoveryIntegrationTest { "process 2 later crash event should remain visible after it executes"); } + @Test + @DisplayName("Loading preserves programmed replay tasks but not ordinary runtime tasks") + void loadingPreservesProgrammedReplayTasksWithoutPromotingRuntimeTasks() + throws Exception { + Path replayFile = Files.createTempFile("programmed-replay", ".dat"); + Path runtimeFile = Files.createTempFile("runtime-task", ".dat"); + + VSSimulator replaySimulator = new SimulationBuilder() + .withProcesses(3) + .withDuration(1000) + .addCrashEvent(0, 5) + .save(replayFile.toString()) + .getSimulator(); + simulatorToStop = replaySimulator; + + HeadlessLoader.LoadedSimulation replayLoaded = + HeadlessLoader.load(replayFile.toString(), replaySimulator.getPrefs()); + loadedSimulatorToStop = replayLoaded.getSimulator(); + + VSTaskManager replayTaskManager = replayLoaded.getVisualization().getTaskManager(); + assertEquals(1, replayTaskManager.getProcessGlobalTasks( + replayLoaded.getVisualization().getProcess(0)).size(), + "builder-authored replay task should remain visible after load"); + + loadedSimulatorToStop.getSimulatorCanvas().stopThread(); + loadedSimulatorToStop = null; + + VSSimulator runtimeSimulator = new SimulationBuilder() + .withProcesses(3) + .withDuration(1000) + .getSimulator(); + simulatorToStop = runtimeSimulator; + + VSSimulatorVisualization runtimeVisualization = runtimeSimulator.getSimulatorCanvas(); + VSInternalProcess runtimeProcess = runtimeVisualization.getProcess(0); + VSTask runtimeTask = new VSTask(5, runtimeProcess, new VSProcessCrashEvent(), VSTask.GLOBAL); + runtimeVisualization.getTaskManager().addTask(runtimeTask); + assertFalse(runtimeTask.isProgrammed(), "runtime task should remain non-programmed before save"); + + saveSimulation(runtimeFile, runtimeSimulator); + + HeadlessLoader.LoadedSimulation runtimeLoaded = + HeadlessLoader.load(runtimeFile.toString(), runtimeSimulator.getPrefs()); + loadedSimulatorToStop = runtimeLoaded.getSimulator(); + + VSTaskManager runtimeTaskManager = runtimeLoaded.getVisualization().getTaskManager(); + assertEquals(0, runtimeTaskManager.getProcessGlobalTasks( + runtimeLoaded.getVisualization().getProcess(0)).size(), + "ordinary non-programmed runtime task must stay hidden after load"); + } + @Test @DisplayName("Live GUI-style event injection supports recover and later crash") void liveEventInjectionSupportsRecoverAndLaterCrash() throws Exception { @@ -214,4 +269,37 @@ class VSTaskManagerCrashRecoveryIntegrationTest { simulator.getSimulatorCanvas().stopThread(); } } + + private void saveSimulation(Path file, VSSimulator simulator) throws Exception { + VSSerialize serialize = new VSSerialize(); + try (FileOutputStream fos = new FileOutputStream(file.toFile()); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + VSSerializablePrefs serializablePrefs = new VSSerializablePrefs(); + + for (String key : simulator.getPrefs().getIntegerKeySet()) { + serializablePrefs.initInteger(key, simulator.getPrefs().getInteger(key)); + } + for (String key : simulator.getPrefs().getBooleanKeySet()) { + serializablePrefs.initBoolean(key, simulator.getPrefs().getBoolean(key)); + } + for (String key : simulator.getPrefs().getStringKeySet()) { + serializablePrefs.initString(key, simulator.getPrefs().getString(key)); + } + for (String key : simulator.getPrefs().getFloatKeySet()) { + serializablePrefs.initFloat(key, simulator.getPrefs().getFloat(key)); + } + for (String key : simulator.getPrefs().getColorKeySet()) { + serializablePrefs.initColor(key, simulator.getPrefs().getColor(key)); + } + for (String key : simulator.getPrefs().getVectorKeySet()) { + serializablePrefs.initVector(key, simulator.getPrefs().getVector(key)); + } + for (String key : simulator.getPrefs().getLongKeySet()) { + serializablePrefs.initLong(key, simulator.getPrefs().getLong(key)); + } + + serializablePrefs.serialize(serialize, oos); + simulator.serialize(serialize, oos); + } + } } -- cgit v1.2.3