feat: add validated quicksave persistence

This commit is contained in:
2026-07-05 18:10:33 +02:00
parent 2fcf641152
commit 08b7bee0e5
10 changed files with 264 additions and 0 deletions
+17
View File
@@ -99,6 +99,23 @@ func _run() -> void:
"Active position callback should synchronize simulation state"
)
var saved_state: String = manager.serialize_state()
var visual_before_restore: Node3D = view.active_npc_visuals[npc.id]
npc.hunger = 99.0
_check(
manager.restore_state_from_json(saved_state),
"Visual lifecycle scenario should restore valid state"
)
var visual_after_restore: Node3D = view.active_npc_visuals[npc.id]
_check(
visual_after_restore != visual_before_restore,
"State restoration should rebuild active NPC presentation"
)
_check(
visual_after_restore.global_position.is_equal_approx(npc.position),
"Rebuilt presentation should use the restored authoritative position"
)
if failures.is_empty():
print("[TEST] NPC visual lifecycle passed")
quit(0)
+75
View File
@@ -0,0 +1,75 @@
extends SceneTree
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var test_directory := "user://save_slot_test_%s" % Time.get_ticks_usec()
var store := SaveSlotStore.new(test_directory)
var manager: Node = load("res://simulation/SimulationManager.gd").new()
manager.debug_logs = false
root.add_child(manager)
manager.set_process(false)
for step in 8:
manager.simulate_tick()
var saved_checksum: String = manager.get_state_checksum()
_check(store.save(manager), "A valid simulation should save atomically")
_check(store.has_slot(), "The validated quicksave should be discoverable")
for step in 5:
manager.simulate_tick()
_check(
manager.get_state_checksum() != saved_checksum,
"The scenario should change after the save point"
)
_check(store.load_into(manager), "A valid quicksave should load")
_check(
manager.get_state_checksum() == saved_checksum,
"Loading should restore the exact saved simulation"
)
var final_path := store.get_slot_path()
var backup_path := final_path + ".bak"
_check(
DirAccess.rename_absolute(final_path, backup_path) == OK,
"Test should reproduce the atomic replacement recovery window"
)
_check(
store.load_into(manager),
"A preserved previous save should recover an interrupted replacement"
)
_check(
DirAccess.rename_absolute(backup_path, final_path) == OK,
"Test should restore the primary save path"
)
var unchanged_checksum: String = manager.get_state_checksum()
var file := FileAccess.open(final_path, FileAccess.WRITE)
file.store_string("{\"schema\":\"broken\"}")
file.close()
_check(not store.load_into(manager), "Invalid save data should be rejected")
_check(
manager.get_state_checksum() == unchanged_checksum,
"A rejected save must not partially mutate the simulation"
)
_check(
not store.save(manager, "../escape"),
"Slot names must not escape the save directory"
)
store.delete_slot()
DirAccess.remove_absolute(ProjectSettings.globalize_path(test_directory))
if failures.is_empty():
print("[TEST] Save-slot persistence passed")
quit(0)
return
for failure in failures:
push_error("[TEST] " + failure)
quit(1)
func _check(condition: bool, message: String) -> void:
if not condition:
failures.append(message)
+1
View File
@@ -0,0 +1 @@
uid://c7ow4dj42sfhl