From 177731145835b5436116865e4d6787708080e974 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:05:37 +0200 Subject: [PATCH 1/3] move original expedition cacheing to before simulation exit --- src/virtualship/make_realistic/problems/simulator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/virtualship/make_realistic/problems/simulator.py b/src/virtualship/make_realistic/problems/simulator.py index 6a5e5778..f951572f 100644 --- a/src/virtualship/make_realistic/problems/simulator.py +++ b/src/virtualship/make_realistic/problems/simulator.py @@ -285,7 +285,6 @@ def execute( continue self._log_problem(problem, wp_i, problem_hash, hash_fpath, log_delay) - self._cache_original_expedition(self.expedition) def _log_problem( self, @@ -360,6 +359,11 @@ def _log_problem( ) if not has_contingency: + # cache original expedition for reference before the user edits the schedule + # (checkpoint.yaml can be overwritten if multiple problems occur) + self._cache_original_expedition(self.expedition) + + # exit simulation sys.exit(0) def _has_contingency(self, problem: ProblemType, problem_wp_i: int | None) -> bool: From b287ce97ae07094a4d60ffc6c74c6cc0a6420f44 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:08:05 +0200 Subject: [PATCH 2/3] add test for the new timing of the cacheing --- .../make_realistic/problems/test_simulator.py | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/make_realistic/problems/test_simulator.py b/tests/make_realistic/problems/test_simulator.py index c3b790b0..f4a10e92 100644 --- a/tests/make_realistic/problems/test_simulator.py +++ b/tests/make_realistic/problems/test_simulator.py @@ -1,7 +1,10 @@ import json import random +import time from datetime import datetime, timedelta +import pytest + from virtualship.instruments.types import InstrumentType from virtualship.make_realistic.problems.scenarios import ( GENERAL_PROBLEMS, @@ -18,7 +21,7 @@ ShipConfig, Waypoint, ) -from virtualship.utils import REPORT +from virtualship.utils import CACHE, EXPEDITION_ORIGINAL, REPORT def _make_simple_expedition( @@ -372,3 +375,26 @@ def test_instrument_not_present_doesnt_select_instrument_problem(tmp_path): # any incompatible waypoint x instrument problem combinations should have been replaced by a general problem else: assert isinstance(problem, GeneralProblem) + + +def test_original_expedition_cached_when_problem_halts_simulation( + tmp_path, monkeypatch +): + """When a problem cannot be absorbed by contingency, the original expedition should be cached before the simulation exits.""" + monkeypatch.setattr(time, "sleep", lambda _: None) + + expedition = _make_simple_expedition(num_waypoints=2) + (tmp_path / CACHE).mkdir() + simulator = ProblemSimulator(expedition, str(tmp_path)) + + # no contingency for a pre-departure problem, so simulation should halt + with pytest.raises(SystemExit): + simulator._log_problem( + _get_pre_departure_problem(), + 0, + "sample", + tmp_path / "problem_sample.json", + log_delay=0.0, + ) + + assert (tmp_path / CACHE / EXPEDITION_ORIGINAL).exists() From 3ca0587088c9fa04066052694c3e5e63c437b2d5 Mon Sep 17 00:00:00 2001 From: j-atkins <106238905+j-atkins@users.noreply.github.com> Date: Wed, 30 Sep 2026 18:17:33 +0200 Subject: [PATCH 3/3] change to always cache the original file --- .../make_realistic/problems/simulator.py | 7 ++-- .../make_realistic/problems/test_simulator.py | 40 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/virtualship/make_realistic/problems/simulator.py b/src/virtualship/make_realistic/problems/simulator.py index f951572f..b02486c5 100644 --- a/src/virtualship/make_realistic/problems/simulator.py +++ b/src/virtualship/make_realistic/problems/simulator.py @@ -351,6 +351,9 @@ def _log_problem( _save_checkpoint(checkpoint, self.expedition_dir) self.expedition.to_yaml(self.expedition_dir / CACHE / EXPEDITION_LATEST) + # cache original expedition for user reference + self._cache_original_expedition(self.expedition) + self._tabular_outputter( problem_str=problem.message, impact_str=impact_str, @@ -359,10 +362,6 @@ def _log_problem( ) if not has_contingency: - # cache original expedition for reference before the user edits the schedule - # (checkpoint.yaml can be overwritten if multiple problems occur) - self._cache_original_expedition(self.expedition) - # exit simulation sys.exit(0) diff --git a/tests/make_realistic/problems/test_simulator.py b/tests/make_realistic/problems/test_simulator.py index f4a10e92..6432185d 100644 --- a/tests/make_realistic/problems/test_simulator.py +++ b/tests/make_realistic/problems/test_simulator.py @@ -377,24 +377,36 @@ def test_instrument_not_present_doesnt_select_instrument_problem(tmp_path): assert isinstance(problem, GeneralProblem) -def test_original_expedition_cached_when_problem_halts_simulation( - tmp_path, monkeypatch -): - """When a problem cannot be absorbed by contingency, the original expedition should be cached before the simulation exits.""" +@pytest.mark.parametrize("halts_simulation", [True, False]) +def test_original_expedition_always_cached(tmp_path, monkeypatch, halts_simulation): + """The original expedition should be cached whenever a problem occurs.""" monkeypatch.setattr(time, "sleep", lambda _: None) - expedition = _make_simple_expedition(num_waypoints=2) + if halts_simulation: + # no contingency for a pre-departure problem, so simulation should halt + expedition = _make_simple_expedition(num_waypoints=2) + problem, problem_wp_i = _get_pre_departure_problem(), 0 + else: + # short distance between waypoints, so enough contingency to avoid the problem + expedition = _make_simple_expedition(num_waypoints=2, distance_scale=0.01) + problem = next( + c for c in GENERAL_PROBLEMS if not getattr(c, "pre_departure", False) + ) + problem_wp_i = 1 + (tmp_path / CACHE).mkdir() simulator = ProblemSimulator(expedition, str(tmp_path)) + log_problem_args = ( + problem, + problem_wp_i, + "sample", + tmp_path / "problem_sample.json", + ) - # no contingency for a pre-departure problem, so simulation should halt - with pytest.raises(SystemExit): - simulator._log_problem( - _get_pre_departure_problem(), - 0, - "sample", - tmp_path / "problem_sample.json", - log_delay=0.0, - ) + if halts_simulation: + with pytest.raises(SystemExit): + simulator._log_problem(*log_problem_args, log_delay=0.0) + else: + simulator._log_problem(*log_problem_args, log_delay=0.0) assert (tmp_path / CACHE / EXPEDITION_ORIGINAL).exists()