extends SceneTree ## P16-01-DISMISS F7/F8, writer phase. Plays the ledger day script and writes ## ordinary save files before and right after the dismissal command (before the ## next minute), a minute later and around the day 2 00:00 close; and saves the ## command boundary of A6 (the medic dismissed while carrying the patient on ## foot), A7 (the patient dismissed while the medic rides the elevator alone) and ## A8 (the actor of a foreground Event with reserved cost dismissed). The reader ## suite (test_general_dismissal_restart_2_read.gd) loads them in a separate ## engine process. The handoff is the declared generated report ## res://artifacts/p16-01-dismiss/restart-handoff.json ## (tools/regression/suite-policy.json fixed_generated_report_routes). const Scenes := preload("res://tests/helpers/p16_dismissal_fixture.gd") const CONTINUE_MINUTES := 5 var _checks := 0 var _failures := 0 var _scenes: Scenes func _initialize() -> void: call_deferred("_run") func _run() -> void: print("P16-01-DISMISS: restart writer") _scenes = Scenes.new(_expect) var straight := _scenes.new_ledger_fixture() _scenes.play_ledger(straight, 360, Scenes.LAST_MINUTE, Callable()) var straight_runtime: Variant = straight[&"runtime"] _expect(_scenes.count_category(straight_runtime, FinancePolicy.SEVERANCE_CATEGORY) == 1 and straight_runtime.staff_service.get_departed_unpaid_wages(Scenes.DISMISSED) == 0, "uninterrupted script: one severance and the held wages paid at the day 2 close") var saving := _scenes.new_ledger_fixture() var ledger_saves: Array = [] _scenes.play_ledger(saving, 360, Scenes.LAST_MINUTE, func(minute: int, phase: String) -> void: if [minute, phase] in Scenes.SAVE_POINTS: var name := "ledger-%d-%s" % [minute, phase] var path := Scenes.WRITER_SAVE % name _expect(bool(saving[&"runtime"].save_to_path(path).get(&"ok", false)), "save file %s" % name) ledger_saves.append(_package(name, path, {"minute": minute, "phase": phase}))) _expect(_scenes.economy_bytes(saving[&"runtime"]) == _scenes.economy_bytes(straight_runtime), "writing the save files leaves the ledger as without saving") var boundaries: Array = [] for scene: Array in [["A6", "rescue_transport/MOVING/-/single", &"medic"], ["A7", "rescue_pickup/MOVING/BOARDED/single", &"patient"], ["A6-pickup", "rescue_pickup/MOVING/-/single", &"medic"], ["A6-pair-aboard", "rescue_transport/MOVING/BOARDED/pair", &"medic"], ["A7-pair-aboard", "rescue_transport/MOVING/BOARDED/pair", &"patient"]]: var fixture := _scenes.new_rescue_fixture() _expect(_scenes.advance_to_rescue_phase(fixture, scene[1]), "%s: the rescue reaches %s" % [scene[0], scene[1]]) boundaries.append(_boundary(fixture, scene[0], scene[2])) _scenes.free_fixture(fixture) var event_fixture := _scenes.new_event_fixture(16_100) boundaries.append(_boundary(event_fixture, "A8", &"event_actor")) _scenes.free_fixture(event_fixture) var pair_fixture := _scenes.new_event_pair_fixture(16_101) boundaries.append(_boundary(pair_fixture, "A8-continue-without", &"event_partner")) _scenes.free_fixture(pair_fixture) var handoff := { "schema": Scenes.HANDOFF_SCHEMA, "writer_pid": OS.get_process_id(), "writer_source_sha256": FileAccess.get_sha256(Scenes.WRITER_SOURCE), "ledger_seed": Scenes.LEDGER_SEED, "last_minute": Scenes.LAST_MINUTE, "straight_economy_sha256": Scenes.sha256(_scenes.economy_bytes(straight_runtime)), "straight_state_base64": Marshalls.raw_to_base64(var_to_bytes(_scenes.state_value(straight_runtime, straight[&"clock"]))), "ledger_saves": ledger_saves, "boundaries": boundaries, } DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(Scenes.HANDOFF_PATH).get_base_dir()) var file := FileAccess.open(Scenes.HANDOFF_PATH, FileAccess.WRITE) _expect(file != null, "handoff file opens for writing") if file != null: file.store_string(JSON.stringify(handoff, "\t") + "\n") file.close() print("P16_RESTART_WRITER pid=%d handoff_sha256=%s" % [OS.get_process_id(), FileAccess.get_sha256(Scenes.HANDOFF_PATH)]) _scenes.free_fixture(straight) _scenes.free_fixture(saving) await process_frame if _failures == 0: print("PASS: %d checks" % _checks) else: push_error("FAIL: %d of %d checks failed" % [_failures, _checks]) quit(_failures) ## Dismisses the employee, saves at once (the command boundary, before the next ## minute), then records the state then and CONTINUE_MINUTES later. func _boundary(fixture: Dictionary, label: String, staff_id: StringName) -> Dictionary: var runtime: Variant = fixture[&"runtime"] var result := _scenes.dismiss(fixture, staff_id) _expect(result.is_success(), "%s: the employee is dismissed (%s)" % [label, result.message]) var dangling: Array[String] = _scenes.dangling_references(runtime, staff_id) _expect(dangling.is_empty(), "%s: no live reference remains at the command boundary %s" % [label, dangling]) var name := "boundary-%s" % label var path := Scenes.WRITER_SAVE % name _expect(bool(runtime.save_to_path(path).get(&"ok", false)), "%s: save right after the command" % label) var at_command := _scenes.state_value(runtime, fixture[&"clock"]) fixture[&"clock"].advance_minutes(CONTINUE_MINUTES) var later := _scenes.state_value(runtime, fixture[&"clock"]) return _package(name, path, {"label": label, "staff_id": String(staff_id), "continue_minutes": CONTINUE_MINUTES, "at_command_base64": Marshalls.raw_to_base64(var_to_bytes(at_command)), "later_base64": Marshalls.raw_to_base64(var_to_bytes(later))}) func _package(name: String, path: String, extra: Dictionary) -> Dictionary: var directory := ProjectSettings.globalize_path(path) _expect(DirAccess.dir_exists_absolute(directory) and DirAccess.get_directories_at(directory).is_empty(), "save package %s holds only files" % name) var files := {} for file_name: String in DirAccess.get_files_at(directory): var bytes := FileAccess.get_file_as_bytes(directory.path_join(file_name)) files[file_name] = {"sha256": Scenes.sha256(bytes), "bytes": bytes.size(), "base64": Marshalls.raw_to_base64(bytes)} _expect(files.has("manifest.json") and files.has("world.bin"), "save package %s has its manifest and world" % name) var package := extra.duplicate(true) package["name"] = name package["files"] = files return package func _expect(condition: bool, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message)