144 lines
8.7 KiB
GDScript
144 lines
8.7 KiB
GDScript
extends SceneTree
|
|
# Reuse reviewed entire boundary inputs. Formal public saves, separate owned Writer/Reader.
|
|
var _runtime: Variant
|
|
var _clock: Variant
|
|
var _state: Variant
|
|
var _checks := 0
|
|
var _failures := 0
|
|
var _folder := ""
|
|
var _mode := ""
|
|
var _cases: Array = []
|
|
var _slots: Dictionary = {}
|
|
var _results: Array = []
|
|
var _resends: Array[Dictionary] = []
|
|
func _initialize() -> void: call_deferred("_run")
|
|
func _expect(ok: bool, label: String) -> void:
|
|
_checks += 1
|
|
if not ok: _failures += 1; push_error(label)
|
|
func _json(path: String) -> Variant:
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
_expect(file != null, "read sealed metadata " + path.get_file())
|
|
if file == null: return null
|
|
var value: Variant = JSON.parse_string(file.get_as_text()); file.close(); return value
|
|
func _input(row: Dictionary) -> Dictionary:
|
|
var path := String(row["path"])
|
|
_expect(FileAccess.get_sha256(path) == String(row["sha256"]), "exact original input SHA")
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
_expect(file != null, "original typed input exists")
|
|
if file == null: return {}
|
|
var data := file.get_buffer(file.get_length()); file.close()
|
|
_expect(data.size() == int(row["bytes"]), "entire typed input length")
|
|
var value: Variant = bytes_to_var(data)
|
|
_expect(value is Dictionary and var_to_bytes(value) == data, "original entire typed input roundtrip")
|
|
return value if value is Dictionary else {}
|
|
func _proof(label: String, value: Variant) -> void:
|
|
var file := FileAccess.open(_folder.path_join(_mode + "-" + label + ".var"), FileAccess.WRITE)
|
|
_expect(file != null, "whole typed output " + label)
|
|
if file != null: file.store_buffer(var_to_bytes(value)); file.flush(); file.close()
|
|
func _reset() -> void:
|
|
_cleanup()
|
|
_runtime = load("res://autoload/simulation_runtime.gd").new()
|
|
_clock = load("res://autoload/game_clock.gd").new()
|
|
_state = load("res://autoload/game_state.gd").new()
|
|
_clock.reset(1, 6, 0); _state.reset()
|
|
_expect(bool(_runtime.initialize(_clock, _state, 424242).get(&"valid", false)), "ordinary Runtime initializes")
|
|
_clock.set_paused(true)
|
|
func _cleanup() -> void:
|
|
if _runtime != null: _runtime.free()
|
|
if _clock != null: _clock.free()
|
|
if _state != null: _state.free()
|
|
_runtime = null; _clock = null; _state = null
|
|
func _restore_expected(expected: Dictionary, label: String) -> void:
|
|
_expect(_runtime.restore_snapshot(expected), "public whole Runtime restore " + label)
|
|
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected), "whole restore equality " + label)
|
|
func _write_save(label: String, expected: Dictionary) -> void:
|
|
_restore_expected(expected, label)
|
|
var path := "user://saves/p04_boundary/" + label
|
|
var result := SaveService.save_snapshot(path, _runtime.create_snapshot())
|
|
_expect(bool(result.get(&"ok", false)), "formal public package save " + label)
|
|
var inspected := SaveService.inspect_snapshot(path)
|
|
_expect(bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)) and var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(expected), "formal whole typed inspect " + label)
|
|
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected), "save leaves whole Runtime unchanged " + label)
|
|
_slots[label] = ProjectSettings.globalize_path(path)
|
|
_proof(label + "-persisted", expected)
|
|
func _copy_package(original: String, label: String) -> void:
|
|
var target := ProjectSettings.globalize_path("user://saves/p04_boundary/" + label)
|
|
_expect(DirAccess.make_dir_recursive_absolute(target) == OK, "fresh Reader package " + label)
|
|
var dir := DirAccess.open(original)
|
|
_expect(dir != null, "Writer entire package available")
|
|
if dir == null: return
|
|
var names := dir.get_files(); names.sort()
|
|
_expect(dir.get_directories().is_empty() and names == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "exact complete four-member save package")
|
|
for member: String in names:
|
|
_expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy complete member " + member)
|
|
_expect(FileAccess.get_sha256(original.path_join(member)) == FileAccess.get_sha256(target.path_join(member)), "literal copied member SHA " + member)
|
|
_slots[label] = target
|
|
func _load_save(label: String, expected: Dictionary) -> void:
|
|
var result := SaveService.load_snapshot(String(_slots[label]))
|
|
_expect(bool(result.get(&"ok", false)), "formal public package load " + label)
|
|
if not bool(result.get(&"ok", false)): return
|
|
_expect(var_to_bytes(result[&"snapshot"]) == var_to_bytes(expected), "loaded entire typed package matches original " + label)
|
|
_restore_expected(result[&"snapshot"], label)
|
|
_proof(label + "-loaded", _runtime.create_snapshot())
|
|
func _run() -> void:
|
|
_folder = OS.get_environment("THE_TREE_BOUNDARY_RESTART_EVIDENCE")
|
|
_mode = OS.get_environment("THE_TREE_BOUNDARY_RESTART_MODE")
|
|
if _folder.is_empty() or _mode not in ["writer", "reader"]: quit(2); return
|
|
_expect(DirAccess.make_dir_recursive_absolute(_folder) == OK, "fresh formal output")
|
|
var manifest: Dictionary = _json(OS.get_environment("THE_TREE_BOUNDARY_RESTART_INPUT"))
|
|
var inputs: Dictionary = manifest["whole_typed_inputs"]
|
|
_reset()
|
|
if _mode == "writer":
|
|
for point: String in inputs: _write_save(point, _input(inputs[point]))
|
|
else:
|
|
var writer: Dictionary = _json(_folder.path_join("writer.json"))
|
|
_expect(int(writer["pid"]) != OS.get_process_id() and String(writer["profile"]) != OS.get_user_data_dir(), "distinct actual Writer/Reader PID and private profiles")
|
|
for label: String in writer["slots"]: _copy_package(String(writer["slots"][label]), label)
|
|
for point: String in inputs: _load_save(point, _input(inputs[point]))
|
|
_load_save("before", _input(inputs["before"]))
|
|
_assert_request_resend(manifest, "loaded-before")
|
|
var receipts: Array = []
|
|
_runtime.economy_service.transaction_posted.connect(func(id: StringName, entries: Array):
|
|
var rows: Array = []
|
|
for row: LedgerEntry in entries: rows.append(row.to_dictionary())
|
|
receipts.append({&"id": id, &"minute": _clock.absolute_minute, &"rows": rows})
|
|
)
|
|
for point: String in ["midnight", "paused", "after", "next"]:
|
|
if point == "after":
|
|
var id := StringName(manifest["kitchen_id"])
|
|
var command: CommandResult = _runtime.submit_command(&"set_facility_policy", {&"facility_id": id, &"patch": {&"business_open": true}}, &"player", &"", 10, -1, &"p04.dependency.reopen")
|
|
_expect(command.is_success(), "same public dependency reopen after formal restart")
|
|
_clock.advance_one_minute()
|
|
var expected := _input(inputs[point])
|
|
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected), "all typed state after formal restart equals original native " + point)
|
|
_proof("replayed-" + point, _runtime.create_snapshot())
|
|
if point in ["paused", "after"]: _assert_request_resend(manifest, "loaded-" + point)
|
|
var sale_count := 0
|
|
for receipt: Dictionary in receipts:
|
|
for row: Dictionary in receipt[&"rows"]:
|
|
if row[&"category"] == &"SERVICE_SALE":
|
|
sale_count += 1
|
|
_expect(receipt[&"minute"] == 1442 and row[&"amount"] == int(manifest["original_price"]), "exact original service sale after formal restart")
|
|
_expect(sale_count == 1, "one sale notification across paused midnight and recovery")
|
|
_results.append({&"receipts": receipts, &"sale_count": sale_count})
|
|
_proof("receipts", receipts)
|
|
_finish()
|
|
func _assert_request_resend(manifest: Dictionary, label: String) -> void:
|
|
var before: Dictionary = _runtime.create_snapshot()
|
|
var guests: Array = []
|
|
for guest: String in manifest["guest_ids"]: guests.append(StringName(guest))
|
|
var result: CommandResult = _runtime.submit_command(&"create_service_request", {&"facility_id": StringName(manifest["facility_id"]), &"request_id": StringName(manifest["request_id"]), &"guest_ids": guests}, &"player", &"", 10, -1, StringName(manifest["request_key"]))
|
|
_expect(result.code == CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY, "formal restart retains processed request idempotency " + label)
|
|
var after: Dictionary = _runtime.create_snapshot()
|
|
_expect(var_to_bytes(before) == var_to_bytes(after), "formal restart request resend preserves entire typed Runtime " + label)
|
|
_proof("duplicate-" + label + "-before", before); _proof("duplicate-" + label + "-after", after)
|
|
_resends.append({&"label": label, &"minute": _clock.absolute_minute, &"result": result.to_dictionary(), &"request_key": manifest["request_key"]})
|
|
|
|
func _finish() -> void:
|
|
var file := FileAccess.open(_folder.path_join(_mode + ".json"), FileAccess.WRITE)
|
|
if file != null:
|
|
file.store_string(JSON.stringify({&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), &"slots": _slots, &"resends": _resends, &"results": _results, &"checks": _checks, &"failures": _failures}, "\t")); file.flush(); file.close()
|
|
_cleanup()
|
|
print("Dependency formal restart: %d checks, %d failures" % [_checks, _failures])
|
|
quit(1 if _failures else 0)
|