Files
TheTree_mock/tests/ui/test_selection_lifecycle.gd
T
Takeshi 5dde0c5ec2 Record original-alignment implementation and traceability checkpoint
Capture current source, content data, tests, asset licenses, and original-specification traceability. Original implementation remains incomplete. The accepted matching UI generation passed 154 suites and 12,253 checks, with eight rendered staff-contract screens reviewed. Latest native Windows and full-original acceptance remain outstanding.
2026-09-30 23:52:24 +09:00

151 lines
7.7 KiB
GDScript

extends SceneTree
var _checks := 0
var _failures := 0
var _runtime: Variant
var _clock: Variant
var _state: Variant
var _main: Control
var _path := "user://tests/selection_lifecycle_%d/slot" % OS.get_process_id()
func _initialize() -> void:
call_deferred("_run")
func _expect(ok: bool, message: String) -> void:
_checks += 1
if not ok:
_failures += 1
push_error(message)
func _mount() -> void:
_main = load("res://scenes/main/main.tscn").instantiate()
_main.set("manual_save_path", _path)
root.add_child(_main)
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
await process_frame
await process_frame
func _build(id: StringName, cell: Vector3i) -> StringName:
_main.call("_set_layer", cell.z)
_main.call("_select_build_definition", id)
var before: int = _runtime.construction_service.get_jobs().size()
_main.call("_on_map_cell_clicked", cell)
var jobs: Array = _runtime.construction_service.get_jobs()
_expect(jobs.size() == before + 1, "normal palette creates %s construction" % id)
return jobs.back().job_id if jobs.size() == before + 1 else &""
func _round_trip(kind: StringName, id: StringName, heading: String) -> void:
_main.call("_save_manual")
var package := SaveService.load_snapshot(_path)
_expect(package.get(&"ok", false), "%s selection has a valid package" % kind)
_main.set("_selected_entity_id", &"")
_main.set("_selected_entity_kind", &"")
_main.call("_load_manual", true)
_expect(_main.get("_selected_entity_id") == id and _main.get("_selected_entity_kind") == kind, "%s selection restores its actual target" % kind)
_expect(heading in _main.get("selected_heading_label").text, "%s inspector restores the target details" % kind)
var expected_highlight := &"" if kind == &"excavation" else id
_expect(_main.get("world_preview").selected_entity_id == expected_highlight, "%s map selection uses the appropriate highlight" % kind)
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, 42530)
_clock.set_paused(true)
root.size = Vector2i(1440, 810)
await _mount()
var reception_job := _build(&"reception", Vector3i(-8, 0, 0))
if reception_job.is_empty():
_finish()
return
_main.call("_clear_build_selection")
_main.call("_on_construction_selected", reception_job)
_round_trip(&"construction", reception_job, "工事")
_clock.advance_minutes(120)
var reception_id: StringName = _runtime.construction_service.get_facilities()[0][&"instance_id"]
_main.call("_on_facility_selected", reception_id)
_round_trip(&"facility", reception_id, "受付")
var stair_job := _build(&"guest_stairs", Vector3i(0, 1, 0))
_clock.advance_minutes(60)
_main.call("_clear_build_selection")
var stair_id := &""
for row: Dictionary in _runtime.construction_service.get_facilities():
if row[&"definition_id"] == &"guest_stairs":
stair_id = row[&"instance_id"]
_expect(not stair_job.is_empty() and not stair_id.is_empty(), "paid stair construction finishes without a FacilityService instance")
if not stair_id.is_empty():
_main.call("_on_facility_selected", stair_id)
_round_trip(&"facility", stair_id, "客用階段")
# Rank is an explicit boundary fixture; this suite does not claim natural rank progression.
_runtime.submit_command(&"update_progression_metrics", {&"metrics": {&"reputation": 25}}, &"player", &"", 100, -1, &"selection.rank.reputation")
_runtime.submit_command(&"change_resources", {&"deltas": {&"gold": 10000}}, &"player", &"", 100, -1, &"selection.rank.capital")
_expect(_runtime.submit_command(&"set_world_tree_rank", {&"rank": 2}, &"player", &"", 100, -1, &"selection.rank.promote").is_success(), "boundary fixture unlocks excavation")
_main.call("_set_excavation_mode", true)
_main.call("_on_map_cell_clicked", Vector3i(0, -1, 0))
var excavations: Array = _runtime.excavation_service.get_jobs()
_expect(not excavations.is_empty(), "normal excavation UI creates a saved job")
if not excavations.is_empty():
_round_trip(&"excavation", excavations.back()[&"job_id"], "地下掘削")
_main.call("_on_facility_selected", reception_id)
_expect(_runtime.save_to_path(_path).get(&"ok", false), "save selection through Runtime without explicit metadata")
_main.queue_free()
await process_frame
_expect(_runtime.get_signal_connection_list(&"checkpoint_presentation_requested").is_empty(), "destroyed UI leaves no presentation callback")
_expect(_runtime.save_to_path(_path + "_headless").get(&"ok", false), "Runtime still saves with no main scene")
var headless := SaveService.load_snapshot(_path + "_headless")
_expect(not headless.get(&"manifest", {}).has("interface_selection"), "UI-free save has no stale presentation")
await _mount()
_expect(_runtime.get_signal_connection_list(&"checkpoint_presentation_requested").size() == 1, "new main scene has exactly one presentation callback")
_expect(_runtime.load_from_path(_path).get(&"ok", false) and _main.get("_selected_entity_id") == reception_id, "new main scene receives direct-load selection")
# Rotation must preserve each candidate's own selection, rather than the newest UI state.
for selected: StringName in [reception_id, stair_id]:
_main.call("_on_facility_selected", selected)
_expect(_runtime.queue_checkpoint(_path + "_generations", {}, true).get(&"ok", false), "queue an autosave generation")
_clock.advance_minutes(1)
_expect(_runtime.load_recovery_candidate(_path + "_generations", &"autosave", 1).get(&"ok", false) and _main.get("_selected_entity_id") == reception_id, "older generation restores its reception selection")
_expect(_runtime.load_recovery_candidate(_path + "_generations", &"autosave", 0).get(&"ok", false) and _main.get("_selected_entity_id") == stair_id, "newest generation restores its staircase selection")
await _test_legacy()
_finish()
func _test_legacy() -> void:
# Materialize a genuine pre-policy world connection through the normal migration contract.
_clock.reset(1, 6, 0)
_state.reset()
_runtime.initialize(_clock, _state, 42531)
_clock.set_paused(true)
_runtime.world_service.set_walkable(Vector3i(0, 1, 0), true)
_expect(_runtime.rebuild_path_graph_from_world(), "old-world boundary fixture has two adjacent floors")
var old: Dictionary = _runtime.create_snapshot()
var data: Dictionary = old[&"systems"][&"runtime"]
data[&"runtime_schema_version"] = 7
data.erase(&"elevators")
data.erase(&"legacy_stairs")
preload("res://tests/fixtures/pre_upkeep_runtime.gd").strip_empty_upkeep(data)
data[&"path"][&"schema_version"] = 3
for field: StringName in [&"vertical_walk_blockers", &"vertical_walk_requires_connector", &"transit_actor_ids"]:
data[&"path"].erase(field)
_expect(_runtime.restore_snapshot(old), "genuine old world migrates into owned legacy stairs")
_main.get("world_preview").set_runtime(_runtime)
var records: Array = _main.get("world_preview").get_legacy_stair_records()
_expect(not records.is_empty(), "migrated legacy connection is selectable")
if not records.is_empty():
var id: StringName = records[0][&"id"]
_main.call("_on_legacy_stair_selected", id)
_round_trip(&"legacy_stairs", id, "既設の階段")
var capture := OS.get_environment("THE_TREE_SELECTION_LIFECYCLE_CAPTURE")
if not capture.is_empty():
for frame: int in 5:
await process_frame
await RenderingServer.frame_post_draw
_expect(root.get_texture().get_image().save_png(capture) == OK, "capture migrated staircase selection after actual load")
func _finish() -> void:
if is_instance_valid(_main):
_main.queue_free()
for suffix: String in ["", "_headless", "_generations"]:
SaveService.delete_save(_path + suffix)
print("Selection lifecycle: %d checks, %d failures" % [_checks, _failures])
quit(1 if _failures else 0)