extends SceneTree var _checks := 0 var _failures := 0 var _prefix := "" var _runtime: Variant var _source: Dictionary func _initialize() -> void: call_deferred("_run") func _run() -> void: _prefix = "user://tests/save_preflight_%d" % OS.get_process_id() _runtime = root.get_node("SimulationRuntime") var clock: Variant = root.get_node("GameClock") var state: Variant = root.get_node("GameState") clock.reset(1,6,0); state.reset(); _runtime.initialize(clock,state,42602); clock.set_paused(true) _source = _runtime.create_snapshot() _test_read_only_preview_and_recovery() _test_manifest_types_and_versions() _test_resource_limits_and_corruption() _test_runtime_schema_rejection() _test_envelope_rejection_and_legacy_defaults() _measure_fixture_sizes() print("Save preflight: %d checks, %d failures" % [_checks, _failures]) quit(_failures) func _test_read_only_preview_and_recovery() -> void: var path := _prefix + "/read_only" _expect(SaveService.save_snapshot(path,_source)[&"ok"],"write valid disposable Runtime save") var before := _slot_fingerprint(path) var inspected := SaveService.inspect_snapshot(path) _expect(inspected[&"ok"] and inspected[&"read_only"] and inspected[&"snapshot"] == _source,"inspection returns exact formal snapshot") _expect(_slot_fingerprint(path) == before,"valid inspection leaves every package byte unchanged") _write_marker(path); before = _slot_fingerprint(path) inspected = SaveService.inspect_snapshot(path) _expect(inspected[&"ok"] and _slot_fingerprint(path) == before,"inspection preserves stale marker and formal bytes") var live: Dictionary = _runtime.create_snapshot() var preview: Dictionary = _runtime.preview_facility_dimensions_at_path(path) _expect(preview[&"ok"] and _runtime.create_snapshot() == live,"dimension preview preserves live Runtime") _expect(_slot_fingerprint(path) == before,"dimension preview does not reconcile transaction artifacts") _expect(SaveService.load_snapshot(path)[&"ok"] and not FileAccess.file_exists(ProjectSettings.globalize_path(path) + SaveService.SAVE_TRANSACTION_SUFFIX),"ordinary load retains interrupted-save reconciliation") var absolute := ProjectSettings.globalize_path(path) _expect(SaveService.save_snapshot(path+".tmp",_source)[&"ok"],"write verified interrupted candidate") _expect(DirAccess.rename_absolute(absolute,absolute+".bak") == OK,"hold formal source at crash boundary") _write_marker(path); before = _slot_fingerprint(path) inspected = SaveService.inspect_snapshot(path) _expect(not inspected[&"ok"] and inspected[&"code"] == &"SAVE_NOT_FOUND" and inspected[&"read_only"],"inspection refuses to promote missing formal") preview = _runtime.preview_facility_dimensions_at_path(path) _expect(not preview[&"ok"] and _runtime.create_snapshot() == live,"missing-formal preview refuses without live mutation") _expect(_slot_fingerprint(path) == before,"rejected preview preserves candidate, backup and marker SHA") var recovered := SaveService.load_snapshot(path) _expect(recovered[&"ok"] and recovered[&"interrupted_save_reconciled"] and recovered[&"snapshot"] == _source,"ordinary load still promotes verified candidate") SaveService.delete_save(path) func _test_manifest_types_and_versions() -> void: var path := _prefix+"/manifest" _expect(SaveService.save_snapshot(path,_source)[&"ok"],"write manifest fixture") var manifest_path := ProjectSettings.globalize_path(path).path_join(SaveService.MANIFEST_FILE) var original := FileAccess.get_file_as_string(manifest_path) for key: String in ["format_version","uncompressed_size","checksum_world"]: var values: Array = [true,"1",1.5,null] if key == "format_version": values.append_array([-1,4294967296.0]) if key == "uncompressed_size": values.append_array([0,-1]) if key == "checksum_world": values = [true,123,null,"bad_hash"] for value: Variant in values: var manifest: Dictionary = JSON.parse_string(original) manifest[key] = value _write_text(manifest_path,JSON.stringify(manifest)) _assert_read_rejection(path,&"INVALID_MANIFEST","malformed %s %s" % [key,str(value)]) var missing: Dictionary = JSON.parse_string(original) missing.erase("format_version"); _write_text(manifest_path,JSON.stringify(missing)) _assert_read_rejection(path,&"INVALID_MANIFEST","missing format version") missing = JSON.parse_string(original) missing.erase("uncompressed_size"); _write_text(manifest_path,JSON.stringify(missing)) _expect(SaveService.inspect_snapshot(path)[&"ok"],"legacy missing duplicate size retains bounded binary compatibility") _write_text(manifest_path,original) for version: int in [0,2]: var manifest: Dictionary = JSON.parse_string(original) manifest["format_version"] = version; _write_text(manifest_path,JSON.stringify(manifest)) var world := FileAccess.open(ProjectSettings.globalize_path(path).path_join(SaveService.WORLD_FILE),FileAccess.READ_WRITE) world.seek(4); world.store_32(version); world = null _assert_read_rejection(path,&"UNSUPPORTED_FORMAT","old or unknown package version %d" % version) var before := _slot_fingerprint(path) var migration := SaveService.migrate_save(path,SaveMigrationRegistry.new()) _expect(not migration[&"ok"] and _slot_fingerprint(path) == before,"unregistered old migration or future downgrade preserves source") SaveService.delete_save(path) func _test_resource_limits_and_corruption() -> void: var path := _prefix+"/limits" _expect(SaveService.save_snapshot(path,_source)[&"ok"],"write resource boundary fixture") var absolute := ProjectSettings.globalize_path(path) var world_path := absolute.path_join(SaveService.WORLD_FILE) var manifest_path := absolute.path_join(SaveService.MANIFEST_FILE) var original_world := FileAccess.get_file_as_bytes(world_path) var original_manifest := FileAccess.get_file_as_string(manifest_path) for size: int in [0,-1,SaveService.MAX_WORLD_BYTES,SaveService.MAX_WORLD_BYTES+1]: _write_bytes(world_path,original_world) var world := FileAccess.open(world_path,FileAccess.READ_WRITE) world.seek(8); world.store_64(size); world = null # The inclusive limit reaches the independent manifest mismatch before # decompression; over-limit values reject before any expansion allocation. var code: StringName = &"SIZE_MISMATCH" if size == SaveService.MAX_WORLD_BYTES else &"SAVE_SIZE_LIMIT_EXCEEDED" _assert_read_rejection(path,code,"advertised expansion boundary %d" % size) _write_bytes(world_path,original_world) var world := FileAccess.open(world_path,FileAccess.READ_WRITE) world.seek(SaveService.MAX_WORLD_BYTES+16); world.store_8(0); world = null _assert_read_rejection(path,&"SAVE_SIZE_LIMIT_EXCEEDED","oversized compressed file rejected before bulk read") _write_bytes(world_path,original_world) _write_text(manifest_path," ".repeat(SaveService.MAX_MANIFEST_BYTES+1)) _assert_read_rejection(path,&"SAVE_SIZE_LIMIT_EXCEEDED","oversized manifest rejected before text read") _write_text(manifest_path,"{invalid JSON") _assert_read_rejection(path,&"INVALID_MANIFEST","malformed JSON") _write_text(manifest_path,original_manifest) var corrupt := original_world.duplicate() corrupt[corrupt.size()-1] = corrupt[corrupt.size()-1] ^ 0xff _write_bytes(world_path,corrupt); _assert_read_rejection(path,&"CHECKSUM_MISMATCH","corrupt world checksum") _write_bytes(world_path,original_world.slice(0,15)); _assert_read_rejection(path,&"INVALID_WORLD_HEADER","truncated binary header") _write_bytes(world_path,original_world) var before := _slot_fingerprint(path) var too_large := SaveService.save_snapshot(path,_source,{&"custom_note":"x".repeat(SaveService.MAX_MANIFEST_BYTES)}) _expect(not too_large[&"ok"] and too_large[&"code"] == &"SAVE_SIZE_LIMIT_EXCEEDED","writer refuses manifest beyond reader resource limit") _expect(_slot_fingerprint(path) == before,"oversized save preserves formal and backup SHA") SaveService.delete_save(path) func _test_runtime_schema_rejection() -> void: for mutation: String in ["unknown","missing","string","extra"]: var candidate := _source.duplicate(true) var data: Dictionary = candidate[&"systems"][&"runtime"] match mutation: "unknown": data[&"runtime_schema_version"] = 999 "missing": data.erase(&"facilities") "string": data[&"runtime_schema_version"] = "13" "extra": data[&"unknown_feature"] = {} var original := candidate.duplicate(true) var live: Dictionary = _runtime.create_snapshot() _expect(not _runtime.restore_snapshot(candidate),"Runtime rejects %s current-schema payload" % mutation) _expect(candidate == original and _runtime.create_snapshot() == live,"Runtime rejection preserves source and live state for %s" % mutation) func _measure_fixture_sizes() -> void: var raw := var_to_bytes(_source) var world := WorldService.new() var harness := preload("res://tests/performance/perf_mvp_001_harness.gd").new() var cells: Array[Vector3i] = harness._populate_world(world,Vector2i(200,50)) var world_raw := var_to_bytes(world.create_snapshot()) _expect(cells.size() == 20000,"resource sizing uses existing PERF 20000-cell world fixture") print("SAVE_RESOURCE_SIZE_REPORT=%s" % JSON.stringify({ "starter_runtime_raw_bytes":raw.size(),"starter_runtime_compressed_bytes":raw.compress(FileAccess.COMPRESSION_ZSTD).size(), "world_cells":cells.size(),"world_only_raw_bytes":world_raw.size(),"world_only_compressed_bytes":world_raw.compress(FileAccess.COMPRESSION_ZSTD).size(), "world_limit_bytes":SaveService.MAX_WORLD_BYTES,"manifest_limit_bytes":SaveService.MAX_MANIFEST_BYTES,"full_runtime_20000_cell_acceptance":false, })) func _test_envelope_rejection_and_legacy_defaults() -> void: var clock: Variant = root.get_node("GameClock") var state: Variant = root.get_node("GameState") var source := SimulationSnapshot.capture(clock,state) for row: Array in [ [&"schema_version","1"],[&"schema_version",true],[&"schema_version",1.5], [&"absolute_minute","360"],[&"absolute_minute",-1],[&"absolute_minute",360.5], [&"speed",true],[&"speed",1.5],[&"speed",0],[&"paused","false"], [&"snapshot_version",-1],[&"snapshot_version","0"],[&"snapshot_version",1.5], ]: var candidate := source.duplicate(true) if row[0] == &"schema_version": candidate[row[0]] = row[1] else: candidate[&"clock"][row[0]] = row[1] var original := candidate.duplicate(true) var live: Dictionary = _runtime.create_snapshot() _expect(not SimulationSnapshot.validate(candidate)[&"ok"],"envelope rejects %s = %s" % [row[0],str(row[1])]) _expect(not SimulationSnapshot.restore(candidate,clock,state) and not _runtime.restore_snapshot(candidate),"invalid envelope refuses both restore entrypoints") _expect(candidate == original and _runtime.create_snapshot() == live,"envelope rejection preserves source and complete live Runtime") var json_numeric := source.duplicate(true) json_numeric[&"schema_version"] = 1.0 json_numeric[&"clock"][&"absolute_minute"] = 360.0 json_numeric[&"clock"][&"speed"] = 1.0 json_numeric[&"clock"][&"snapshot_version"] = 0.0 _expect(SimulationSnapshot.validate(json_numeric)[&"ok"],"exact integral JSON numbers remain valid generic envelopes") var legacy := source.duplicate(true) legacy[&"clock"].erase(&"paused"); legacy[&"clock"].erase(&"snapshot_version") _expect(SimulationSnapshot.validate(legacy)[&"ok"],"absent legacy pause/version retain compatible defaults") legacy[&"clock"][&"speed"] = 4 _expect(SimulationSnapshot.restore(legacy,clock,state) and clock.speed == 3,"legacy x4 still resumes at x3 through validated envelope") _expect(_runtime.restore_snapshot(_source),"restore original live state after legacy compatibility probe") func _assert_read_rejection(path: String,code: StringName,label: String) -> void: var before := _slot_fingerprint(path) var inspected := SaveService.inspect_snapshot(path) _expect(not inspected[&"ok"] and inspected[&"code"] == code,label+": correct rejection") _expect(_slot_fingerprint(path) == before,label+": all disk SHA unchanged") func _slot_fingerprint(path: String) -> Dictionary: var absolute := ProjectSettings.globalize_path(path) var parent := DirAccess.open(absolute.get_base_dir()) var result := {} if parent == null: return result for file: String in parent.get_files(): if file.begins_with(absolute.get_file()): result[file] = FileAccess.get_sha256(absolute.get_base_dir().path_join(file)) for directory: String in parent.get_directories(): if directory.begins_with(absolute.get_file()): result[directory+"/"] = _tree_fingerprint(absolute.get_base_dir().path_join(directory)) return result func _tree_fingerprint(path: String) -> Dictionary: var dir := DirAccess.open(path) var result := {} for file: String in dir.get_files(): result[file] = FileAccess.get_sha256(path.path_join(file)) for child: String in dir.get_directories(): result[child+"/"] = _tree_fingerprint(path.path_join(child)) return result func _write_marker(path: String) -> void: _write_text(ProjectSettings.globalize_path(path)+SaveService.SAVE_TRANSACTION_SUFFIX,SaveService.SAVE_TRANSACTION_MARKER) func _write_text(path: String,value: String) -> void: var file := FileAccess.open(path,FileAccess.WRITE) file.store_string(value) func _write_bytes(path: String,value: PackedByteArray) -> void: var file := FileAccess.open(path,FileAccess.WRITE) file.store_buffer(value) func _expect(ok: bool,label: String) -> void: _checks += 1 if not ok: _failures += 1 push_error(label)