diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-27 15:23:17 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-27 15:23:17 +0200 |
| commit | 69c5dbf50c36ae622c02b943cc6c471254bb2fa9 (patch) | |
| tree | 01173b3404af9b2f156c78a7aff72367b074cb39 /src | |
| parent | ca9d7633d76871cb0ea00dd46af351daf8ef4895 (diff) | |
Strengthen replay schedule regression checks
Diffstat (limited to 'src')
| -rw-r--r-- | src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java | 40 | ||||
| -rw-r--r-- | src/test/java/simulator/builder/SimulationBuilderTest.java | 78 |
2 files changed, 113 insertions, 5 deletions
diff --git a/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java b/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java index 81ceeb8..7bb6038 100644 --- a/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java +++ b/src/test/java/core/VSTaskManagerCrashRecoveryIntegrationTest.java @@ -75,12 +75,46 @@ class VSTaskManagerCrashRecoveryIntegrationTest { loadedSimulatorToStop = loaded.getSimulator(); VSSimulatorVisualization visualization = loaded.getVisualization(); - runUntil(visualization, 20); + assertFalse(visualization.getProcess(0).isCrashed(), + "process 0 should start alive after replay load"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should start alive after replay load"); + + runUntil(visualization, 4); + assertFalse(visualization.getProcess(0).isCrashed(), + "process 0 should stay alive before its crash point"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should stay alive before process 0 crashes"); + + runUntil(visualization, 6); + assertTrue(visualization.getProcess(0).isCrashed(), + "process 0 should crash immediately after its scheduled crash point"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should still be alive while process 0 is crashed"); + + runUntil(visualization, 10); + assertTrue(visualization.getProcess(0).isCrashed(), + "process 0 should remain crashed before its recover point"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should still be alive before its later crash"); + + runUntil(visualization, 11); + assertFalse(visualization.getProcess(0).isCrashed(), + "process 0 should recover immediately after its scheduled recover point"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should still be alive before its crash point"); + + runUntil(visualization, 15); + assertFalse(visualization.getProcess(0).isCrashed(), + "process 0 should stay recovered before process 1 crashes"); + assertFalse(visualization.getProcess(1).isCrashed(), + "process 1 should stay alive before its later crash point"); + runUntil(visualization, 16); assertFalse(visualization.getProcess(0).isCrashed(), - "process 0 should recover after replay load"); + "process 0 should remain recovered after replay load"); assertTrue(visualization.getProcess(1).isCrashed(), - "process 1 should still crash later in the replay"); + "process 1 should crash immediately after its later replay point"); } @Test diff --git a/src/test/java/simulator/builder/SimulationBuilderTest.java b/src/test/java/simulator/builder/SimulationBuilderTest.java index c2d6511..6d16fa1 100644 --- a/src/test/java/simulator/builder/SimulationBuilderTest.java +++ b/src/test/java/simulator/builder/SimulationBuilderTest.java @@ -2,10 +2,17 @@ package simulator.builder; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.io.File; import java.nio.file.*; import java.nio.charset.StandardCharsets; +import core.VSInternalProcess; +import simulator.VSSimulator; +import simulator.VSSimulatorVisualization; +import testing.HeadlessLoader; + /** * Tests for the SimulationBuilder framework */ @@ -76,8 +83,7 @@ class SimulationBuilderTest { String filename = TEST_DIR + "test-raft.dat"; SimulationBuilder builder = SimulationFactory.createRaftSimulation(); - builder - .save(filename); + VSSimulator simulator = builder.save(filename).getSimulator(); File file = new File(filename); assertTrue(file.exists(), "Simulation file should be created"); @@ -92,6 +98,42 @@ class SimulationBuilderTest { assertTrue(content.contains("VSProcessRecoverEvent"), "Should contain recovery event"); assertTrue(countOccurrences(content, "VSProcessCrashEvent") >= 2, "Should contain two crash events for different processes"); + + HeadlessLoader.LoadedSimulation loaded = + HeadlessLoader.load(filename, simulator.getPrefs()); + VSSimulator loadedSimulator = loaded.getSimulator(); + VSSimulatorVisualization visualization = loaded.getVisualization(); + try { + VSInternalProcess process0 = visualization.getProcess(0); + VSInternalProcess process2 = visualization.getProcess(2); + + runUntil(visualization, 3499); + assertFalse(process0.isCrashed(), "leader should stay alive before 3500ms"); + assertFalse(process2.isCrashed(), "process 2 should stay alive before 3500ms"); + + runUntil(visualization, 3501); + assertTrue(process0.isCrashed(), "leader should crash immediately after 3500ms"); + assertFalse(process2.isCrashed(), "process 2 should still be alive after leader crash"); + + runUntil(visualization, 12000); + assertTrue(process0.isCrashed(), "leader should remain crashed before 12000ms recovery executes"); + assertFalse(process2.isCrashed(), "process 2 should stay alive before 20000ms"); + + runUntil(visualization, 12001); + assertFalse(process0.isCrashed(), "leader should recover immediately after 12000ms"); + assertFalse(process2.isCrashed(), "process 2 should still be alive after leader recovery"); + + runUntil(visualization, 20000); + assertFalse(process0.isCrashed(), "leader should remain recovered before 20000ms crash executes"); + assertFalse(process2.isCrashed(), "process 2 should stay alive before its crash point"); + + runUntil(visualization, 20001); + assertFalse(process0.isCrashed(), "leader should remain recovered at 20000ms"); + assertTrue(process2.isCrashed(), "process 2 should crash immediately after 20000ms"); + } finally { + loadedSimulator.getSimulatorCanvas().stopThread(); + simulator.getSimulatorCanvas().stopThread(); + } } @Test @@ -140,4 +182,36 @@ class SimulationBuilderTest { return count; } + + private void runUntil(VSSimulatorVisualization visualization, long targetTime) + throws Exception { + setBooleanField(visualization, "isPaused", false); + setBooleanField(visualization, "hasFinished", false); + setDoubleField(visualization, "clockSpeed", 1.0d); + + Method updateSimulator = VSSimulatorVisualization.class.getDeclaredMethod( + "updateSimulator", long.class, long.class); + updateSimulator.setAccessible(true); + + long wallTime = visualization.getTime(); + while (visualization.getTime() < targetTime) { + long nextWallTime = wallTime + 1L; + updateSimulator.invoke(visualization, nextWallTime, wallTime); + wallTime = nextWallTime; + } + } + + private void setBooleanField(Object target, String fieldName, boolean value) + throws Exception { + Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.setBoolean(target, value); + } + + private void setDoubleField(Object target, String fieldName, double value) + throws Exception { + Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.setDouble(target, value); + } } |
