Files
TheTree_mock/tests/transport/test_runtime_transport.gd
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

203 lines
12 KiB
GDScript

extends SceneTree
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
var _finishing := false
var _checks := 0
var _failures := 0
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _serial := 0
var _main: Control
var _saw_trip := false
var _saw_boarded := false
var _saw_arrival := false
var _saved := false
var _preview_snapshot: Dictionary = {}
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_runtime = root.get_node("SimulationRuntime")
_clock = root.get_node("GameClock")
_state = root.get_node("GameState")
_clock.reset(1, 6, 0)
_state.reset()
_runtime.initialize(_clock, _state, 424242)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_clock.set_paused(true)
root.size = Vector2i(1440, 810)
_main = load("res://scenes/main/main.tscn").instantiate() as Control
_main.set("manual_save_path", "user://tests/transport_%d/manual" % OS.get_process_id())
root.add_child(_main)
await process_frame
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
_expect(_state.get_resource(&"gold") == 5000 and _state.get_resource(&"materials") == 50, "ordinary starting budget")
if not _build(&"guest_elevator", Vector3i(0, 0, 0)) or not _build(&"reception", Vector3i(2, 0, 0)):
_finish(); return
_clock.advance_minutes(300)
_expect(_runtime.elevators.get_report()[&"shafts"].size() == 1, "completed construction registers physical cabin")
if _runtime.elevators.get_report()[&"shafts"].is_empty(): _finish(); return
if not _build(&"guest_elevator", Vector3i(0, 2, 0)) or not _build(&"standard_room", Vector3i(2, 2, 0)):
_finish(); return
_clock.advance_minutes(300)
if not _build(&"standard_room", Vector3i(2, 3, 0)):
_finish(); return
_clock.advance_minutes(60)
var report: Dictionary = _runtime.elevators.get_report()
_expect(report[&"shafts"].size() == 1 and report[&"shafts"][0][&"stops"].size() == 4, "stacked modules share one cabin across four floors")
_expect(_state.get_resource(&"materials") == 2, "buildings fit within 50 materials")
var reception: FacilityInstance
for facility: FacilityInstance in _runtime.facility_service.get_instances():
if facility.definition_id == &"reception": reception = facility
_expect(_cmd(&"hire_staff_candidate", {&"candidate_id": &"staff_akane"}).is_success(), "hire receptionist with earned starting funds")
_expect(_cmd(&"set_staff_work_assignment", {&"staff_id": &"staff_akane", &"facility_id": reception.instance_id, &"template_id": &"DAY"}).is_success(), "assign real commute")
_expect(reception.active_staff_ids.is_empty(), "assignment alone does not open reception")
for index: int in range(250):
_clock.advance_minutes(1)
var current: Dictionary = _runtime.elevators.get_report()
if not current[&"trips"].is_empty(): _saw_trip = true
for trip: Dictionary in current[&"trips"]:
if trip[&"state"] != PathService.ELEVATOR_REQUEST_BOARDED: continue
_saw_boarded = true
if not _saved:
_saved = _verify_mid_ride()
_preview_snapshot = _runtime.create_snapshot()
for guest: GuestRuntime in _runtime.guest_service.get_guests():
if guest.current_cell.y >= 2: _saw_arrival = true
if _saw_boarded and not _main.get_meta(&"captured", false) and OS.get_environment("THE_TREE_TRANSPORT_CAPTURE") == "1":
_main.set_meta(&"captured", true)
await _capture()
if index % 30 == 0: await process_frame
if not _saw_trip:
print("TRANSPORT_DIAGNOSTIC ", _runtime.elevators.get_report(), " STAFF ", _runtime.staff_commute.get_report())
for f: FacilityInstance in _runtime.facility_service.get_instances(): print("FAC ", f.definition_id, " ", f.state, " ", f.active_staff_ids)
for g: GuestRuntime in _runtime.guest_service.get_guests(): print("GUEST ", g.guest_id, " ", g.state, " ", g.current_cell, " ", _runtime._project_guest_candidates(g.guest_id))
_expect(_saw_trip, "natural guest requests an elevator")
_expect(_saw_boarded, "natural guest boards the shared cabin")
_expect(_saw_arrival, "natural guest physically reaches an upper floor")
_expect(_saved, "mid-ride replay tested")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "current completed runtime restores")
if OS.get_environment("THE_TREE_TRANSPORT_PREVIEW") == "1" and _failures == 0:
_expect(_runtime.restore_snapshot(_preview_snapshot), "preview returns to the verified first boarding")
if _failures > 0: _finish(); return
await _capture()
DisplayServer.window_set_title("The Tree · 昇降機と通勤の試遊(保存なし)")
_clock.set_paused(true)
print("Runtime transport: %d checks, %d failures" % [_checks, _failures])
print("TRANSPORT_PREVIEW_READY Space resumes; test saves disabled")
return
_finish()
func _verify_mid_ride() -> bool:
var save_path := "user://tests/transport_%d/ride" % OS.get_process_id()
_expect(bool(_runtime.save_to_path(save_path).get(&"ok", false)), "boarded save writes isolated disk slot")
_expect(bool(_runtime.load_from_path(save_path).get(&"ok", false)), "boarded disk slot reloads")
var snapshot: Dictionary = _runtime.create_snapshot()
_expect(snapshot[&"systems"][&"runtime"][&"runtime_schema_version"] == 14, "runtime schema14 records elevator state and explicit vertical routing")
_expect(_runtime.restore_snapshot(snapshot), "mid-ride Runtime snapshot restores actor/cabin/Path ownership")
var shaft: Dictionary = _runtime.elevators.get_report()[&"shafts"][0]
var busy := _cmd(&"demolish_facility", {&"instance_id": shaft[&"module_ids"][1]})
_expect(not busy.is_success() and busy.details.get(&"reason") == &"ELEVATOR_BUSY", "occupied shaft demolition rejected before spending or topology changes")
_expect(_runtime.restore_snapshot(snapshot), "snapshot resets rejected command metadata")
# Diagnose total reserve depletion while a natural passenger is aboard.
var riders: Array[StringName] = []
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
if trip[&"state"] == PathService.ELEVATOR_REQUEST_BOARDED:
riders.append(trip[&"actor_id"])
_expect(not riders.is_empty(), "depletion drill starts with actual boarded passenger")
_state.set_resource(&"mana", 0)
_clock.advance_minutes(1)
var outage: Dictionary = _runtime.elevators.get_report()
_expect(not outage[&"shafts"][0][&"powered"] and outage[&"trips"].is_empty(), "reserve outage stops cabin and releases trip ownership")
for actor_id: StringName in riders:
_expect(_runtime.path_service.get_active_elevator_request(actor_id).is_empty(), "outage releases Path passenger request")
var safe_arrivals := 0
for event: SimulationDomainEvent in _runtime.domain_events.recent_events():
if event.event_type == &"ELEVATOR_ARRIVED" and event.absolute_minute == _clock.absolute_minute and event.payload.get(&"actor_id", &"") == actor_id and bool(event.payload.get(&"emergency", false)):
if event.payload.get(&"cell") in shaft[&"stops"]:
safe_arrivals += 1
_expect(safe_arrivals == 1, "boarded passenger alights exactly once at a valid shaft stop")
var outage_snapshot: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(outage_snapshot), "emergency alighting and empty reserve survive Runtime save")
_state.set_resource(&"mana", 100)
_clock.advance_minutes(1)
_expect(_runtime.elevators.get_report()[&"shafts"][0][&"powered"], "cabin supply returns when reserve recovers")
_expect(_runtime.restore_snapshot(snapshot), "rewind depletion drill to original passenger state")
for cell: Vector3i in shaft[&"stops"]:
_runtime.path_service.configure_cell_traffic(cell, 4, 0, true)
_state.set_resource(&"mana", 0)
_clock.advance_minutes(1)
for actor_id: StringName in riders:
_expect(_runtime.elevators.get_actor_trip(actor_id).get(&"state") == PathService.ELEVATOR_REQUEST_BOARDED, "no safe landing retains boarded passenger instead of teleporting")
var held_shaft: Dictionary = _runtime.elevators.get_report()[&"shafts"][0]
_clock.advance_minutes(20)
var held_after: Dictionary = _runtime.elevators.get_report()[&"shafts"][0]
_expect(held_after[&"remaining_minutes"] == held_shaft[&"remaining_minutes"], "powerless held cabin does not advance physical journey")
var held_snapshot: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(held_snapshot) and _runtime.create_snapshot() == held_snapshot, "no-landing outage preserves full Runtime save and passenger ownership")
for cell: Vector3i in shaft[&"stops"]:
_runtime.path_service.configure_cell_traffic(cell)
_state.set_resource(&"mana", 100)
_clock.advance_minutes(45)
for actor_id: StringName in riders:
_expect(_runtime.elevators.get_actor_trip(actor_id).is_empty() and _runtime.path_service.get_active_elevator_request(actor_id).is_empty(), "landing repair and power restoration release held trip ownership")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "recovered held cabin remains saveable")
_expect(_runtime.restore_snapshot(snapshot), "restore original ride after no-landing drill")
_clock.advance_minutes(45)
var expected: Dictionary = _runtime.create_snapshot()
_expect(_runtime.restore_snapshot(snapshot), "rewind boarded save for deterministic replay")
_clock.advance_minutes(45)
_expect(_runtime._strict_variants_equal(expected, _runtime.create_snapshot()), "mid-ride save replays all Runtime state identically")
_expect(_runtime.restore_snapshot(snapshot), "restore original ride position after replay test")
var stopped := _cmd(&"set_elevator_operation", {&"shaft_id": shaft[&"shaft_id"], &"operator_open": false})
_expect(stopped.is_success(), "player stops elevator through public command")
_expect(_runtime.elevators.get_report()[&"trips"].is_empty(), "stop releases waiting and disembarks passengers at safe floor")
_expect(_runtime.restore_snapshot(_runtime.create_snapshot()), "stopped elevator save restores")
_expect(_runtime.restore_snapshot(snapshot), "resume original operating snapshot")
return true
func _capture() -> void:
var preview: Control = _main.get("world_preview")
_main.call("_on_facility_selected", _runtime.elevators.get_report()[&"shafts"][0][&"module_ids"][0])
preview.call("set_zoom", 2.0)
preview.set("pan_offset", preview.get("pan_offset") + preview.size * Vector2(0.5, 0.55) - preview.call("cell_to_canvas", Vector3i(2, 1, 0)))
preview.queue_redraw()
await process_frame
await RenderingServer.frame_post_draw
for trip: Dictionary in _runtime.elevators.get_report()[&"trips"]:
if trip[&"state"] != PathService.ELEVATOR_REQUEST_BOARDED: continue
var key := String(trip[&"actor_id"]).replace("guest:", "guest/").replace("staff:", "staff/")
_expect((preview.get("_boarded_actor_ids") as Dictionary).has(key), "live rider identified in cabin rendering")
_expect(not (preview.get("_actor_tracks") as Dictionary).has(key), "live rider is not duplicated on floor")
var path := ProjectSettings.globalize_path("res://artifacts/transport/elevator-cabin.png")
root.get_texture().get_image().save_png(path)
func _build(id: StringName, origin: Vector3i) -> bool:
var result := _cmd(&"build_facility", {&"definition_id": id, &"origin": origin})
_expect(result.is_success(), "build %s at %s %s" % [id, origin, result.message])
return result.is_success()
func _cmd(kind: StringName, payload: Dictionary) -> CommandResult:
_serial += 1
return _runtime.submit_command(kind, payload, &"player", &"", 0, _clock.absolute_minute, StringName("transport.test.%d" % _serial))
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition: _failures += 1
print("%s %s" % ["PASS" if condition else "FAIL", message])
func _finish() -> void:
if _finishing:
return
_finishing = true
if is_instance_valid(_main):
var final_cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, _main)
if not bool(final_cleanup.get(&"passed", false)):
_failures += 1
push_error("ordinary Main final audio cleanup failed")
print("Runtime transport: %d checks, %d failures" % [_checks, _failures])
quit(1 if _failures else 0)