Files
TheTree_mock/tests/transport/test_elevator_service.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

305 lines
18 KiB
GDScript

extends SceneTree
var _checks := 0
var _failures := 0
var _path: PathService
var _elevators: ElevatorService
var _config: Dictionary
var _minute := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var clock := root.get_node_or_null("GameClock")
if clock != null: clock.set_paused(true)
_test_paired_boarding()
_test_grouping()
_test_capacity_and_time()
_test_access_and_shared_cabin()
_test_restore_and_emergency()
_test_mutations_and_cancellation()
_test_snapshot_rejections()
print("Elevator service: %d checks, %d failures" % [_checks, _failures])
quit(0 if _failures == 0 else 1)
func _setup(layer := 0, floors := 4) -> void:
_path = PathService.new()
var cells: Array[Vector3i] = []
for y: int in range(floors + 2):
cells.append(Vector3i(0, y, layer))
_path.rebuild_graph(cells)
_elevators = ElevatorService.new()
_elevators.configure(_path)
_config = {&"shaft_id": StringName("transport.shaft.%d.0.0.%d" % [layer, 2 if layer == 0 else 3]),
&"module_ids": [&"module.0", &"module.1"], &"stops": [], &"width": 2 if layer == 0 else 3,
&"capacity": 4 if layer == 0 else 8, &"travel_minutes_per_floor": 10 if layer == 0 else 5,
&"access_id": &"" if layer == 0 else &"staff"}
for y: int in range(floors): (_config[&"stops"] as Array).append(Vector3i(0, y, layer))
_expect(_elevators.synchronize([_config], 0)[&"ok"], "shaft initializes")
_minute = 0
func _request(actor: StringName, from: int, to: int, layer := 0) -> Dictionary:
var access: Array[StringName] = []
if layer == 1: access.append(&"staff")
return _elevators.request_trip(actor, Vector3i(0, from, layer), Vector3i(0, to, layer), _minute, access)
func _tick(count := 1) -> Array[Dictionary]:
var arrivals: Array[Dictionary] = []
for i: int in range(count):
_minute += 1
arrivals.append_array(_elevators.advance_one_minute(_minute))
return arrivals
func _car() -> Dictionary:
return _elevators.get_report()[&"shafts"][0]
func _test_grouping() -> void:
var registry := DefinitionRegistry.new()
registry.rebuild()
var records: Array = []
for row: Array in [[&"g2", &"guest_elevator", Vector3i(0, 2, 0)], [&"g0", &"guest_elevator", Vector3i(0, 0, 0)], [&"g6", &"guest_elevator", Vector3i(0, 6, 0)], [&"b0", &"service_elevator", Vector3i(0, 0, 1)], [&"other", &"reception", Vector3i(6, 0, 0)]]:
records.append({&"instance_id": row[0], &"definition_id": row[1], &"origin": row[2]})
var configs := ElevatorService.configurations_from_facilities(records, registry)
_expect(configs.size() == 3, "adjacent modules form one shaft, gaps and layers form separate shafts")
_expect(configs[0][&"stops"] == [Vector3i(0, 0, 0), Vector3i(0, 1, 0), Vector3i(0, 2, 0), Vector3i(0, 3, 0)], "each occupied integer floor gets a landing")
_expect(configs[0][&"module_ids"] == [&"g0", &"g2"], "construction ordering does not change module/stop ordering")
_expect(configs[2][&"capacity"] == 8 and configs[2][&"travel_minutes_per_floor"] == 5 and configs[2][&"access_id"] == &"staff", "BACK shaft has eight seats, five minutes per floor and staff access")
records.reverse()
_expect(ElevatorService.configurations_from_facilities(records, registry) == configs, "configuration generation is deterministic")
func _test_capacity_and_time() -> void:
_setup()
_expect(_path.create_snapshot()[&"connectors"].size() == 6, "four stops expose six bidirectional direct links")
for i: int in range(5): _expect(_request(StringName("guest/%d" % i), 0, 3)[&"ok"], "guest queues without moving")
_expect(_car()[&"passenger_count"] == 0 and _car()[&"waiting_count"] == 5, "requests do not board before simulation advances")
_expect(_tick().is_empty() and _car()[&"passenger_count"] == 4 and _car()[&"waiting_count"] == 1, "shared cabin boards at most four FRONT actors")
_expect(_car()[&"phase"] == &"BOARDING" and _car()[&"remaining_minutes"] == 6, "doors take two minutes plus one per boarding actor")
_expect(_tick(6).is_empty() and _car()[&"phase"] == &"TRAVEL", "boarding duration elapses before cabin travels")
_expect(_tick(15).is_empty() and is_equal_approx(_car()[&"visual_floor"], 1.5), "cabin interpolates actual half journey without arriving")
_expect(_tick(15).is_empty() and _car()[&"phase"] == &"ALIGHTING", "three FRONT floors take thirty minutes before alighting")
_expect(_tick(5).is_empty(), "passengers remain owned while doors are unloading")
var arrivals := _tick()
_expect(arrivals.size() == 4 and _minute == 43, "first four arrivals happen only after both door intervals and travel")
for arrival: Dictionary in arrivals:
_expect(arrival[&"cell"] == Vector3i(0, 3, 0) and not arrival[&"emergency"] and _path.get_active_elevator_request(arrival[&"actor_id"]).is_empty(), "arrival transfers actor once and releases Path ownership")
_expect(_car()[&"phase"] == &"PICKUP" and _car()[&"waiting_count"] == 1, "remaining guest waits for same cabin to return")
arrivals = _tick(66)
_expect(arrivals.size() == 1 and arrivals[0][&"actor_id"] == &"guest/4" and _car()[&"phase"] == &"IDLE", "fifth guest waits for real return and completes without starvation")
_expect(_tick(10).is_empty(), "completed request never arrives twice")
var read := _elevators.get_report()
read[&"shafts"][0][&"boarded"].append(&"tamper")
_expect(_car()[&"boarded"].is_empty(), "report returns defensive copies")
func _test_access_and_shared_cabin() -> void:
_setup(1)
_expect(not _elevators.request_trip(&"guest/no_access", Vector3i(0, 0, 1), Vector3i(0, 3, 1), 0)[&"ok"], "BACK elevator rejects actor without staff access")
for i: int in range(9): _expect(_request(StringName("staff/%d" % i), 0, 3, 1)[&"ok"], "authorized staff queues")
_tick()
_expect(_car()[&"passenger_count"] == 8 and _car()[&"waiting_count"] == 1, "BACK cabin shares eight seats across requests")
_tick(10)
_expect(_car()[&"phase"] == &"TRAVEL" and _car()[&"remaining_minutes"] == 15, "BACK shaft uses five minutes per floor")
_tick(15)
_expect(_car()[&"phase"] == &"ALIGHTING", "BACK travel duration is physical")
_expect(_tick(10).size() == 8, "BACK eight passengers alight together after paid time")
_setup()
_request(&"a", 0, 3)
_request(&"b", 1, 2)
_tick()
_expect(_car()[&"passenger_count"] == 1 and _car()[&"waiting_count"] == 1, "different floor pair cannot create a second cabin")
_expect(_elevators.get_actor_trip(&"b")[&"state"] == PathService.ELEVATOR_REQUEST_QUEUED, "other connector actor remains waiting while shared car moves")
_expect(_elevators.owns_connector(_elevators.get_actor_trip(&"a")[&"connector_id"]), "transport identifies its owned path step")
_expect(not _elevators.owns_connector(&"door.other"), "unrelated door connector is not transport-owned")
var all := _tick(100)
_expect(all.size() == 2 and all[0][&"actor_id"] == &"a" and all[1][&"actor_id"] == &"b", "FIFO serves different floor pair after first journey")
func _test_restore_and_emergency() -> void:
_setup()
_request(&"rider", 0, 3)
_tick(20)
_expect(_car()[&"phase"] == &"TRAVEL" and _car()[&"visual_floor"] > 1.0, "midride fixture is physically between floors")
var state := _elevators.create_snapshot()
var path_state := _path.create_snapshot()
var future := _tick(40)
var final_state := _elevators.create_snapshot()
_expect(_path.restore_snapshot(path_state), "Path queue/boarded state restores")
_expect(_elevators.restore_snapshot(state), "midride save restores against matching Path ownership: %s" % _elevators.last_error)
_minute = state[&"current_minute"]
_expect(_tick(40) == future and _elevators.create_snapshot() == final_state, "restored trip produces identical arrivals and final snapshot")
_setup()
_request(&"aboard", 0, 3)
_request(&"waiting", 2, 3)
_tick(20)
var released := _elevators.set_powered(_config[&"shaft_id"], false, _minute)
_expect(released.size() == 2, "power failure releases both waiting and boarded owners")
_expect(released[0][&"actor_id"] == &"aboard" and released[0][&"cell"] == Vector3i(0, 2, 0) and released[0][&"emergency"], "power failure unloads boarded actor at nearest valid floor")
_expect(released[1][&"actor_id"] == &"waiting" and released[1][&"cell"] == Vector3i(0, 2, 0) and released[1][&"cancelled"], "waiting cancellation stays on original landing for replan")
_expect(_car()[&"operator_open"] and not _car()[&"powered"] and not _car()[&"operational"], "power loss does not alter player operation policy")
_expect(_path.get_active_elevator_request(&"aboard").is_empty() and _elevators.get_actor_trip(&"waiting").is_empty(), "emergency leaves no orphan owner")
_expect(not _request(&"stopped", 0, 3)[&"ok"], "stopped elevator rejects new boarding")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "stopped elevator restores with Path outage state")
_elevators.set_operator_open(_config[&"shaft_id"], false, _minute)
_elevators.set_powered(_config[&"shaft_id"], true, _minute)
_expect(not _car()[&"operator_open"] and _car()[&"powered"] and not _car()[&"operational"], "power restoration preserves player-requested stop")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "player-stopped powered elevator state restores")
_elevators.set_operator_open(_config[&"shaft_id"], true, _minute)
_expect(_car()[&"operational"] and _request(&"resumed", 2, 0)[&"ok"], "player reopens restored elevator")
_expect(_tick(27).size() == 1, "reopened cabin resumes from emergency landing with real travel")
_setup()
_request(&"blocked_destination", 0, 3)
_tick(20)
_path.configure_cell_traffic(Vector3i(0, 3, 0), 4, 0, true)
released = _tick()
_expect(released.size() == 1 and released[0][&"emergency"] and released[0][&"cell"] != Vector3i(0, 3, 0), "blocked destination causes emergency at a valid landing")
_setup()
_request(&"held", 0, 3)
_request(&"waiting_held", 2, 3)
_tick(15)
for cell: Vector3i in _config[&"stops"]: _path.configure_cell_traffic(cell, 4, 0, true)
released = _elevators.set_operator_open(_config[&"shaft_id"], false, _minute)
_expect(released.size() == 1 and released[0][&"actor_id"] == &"waiting_held" and released[0][&"cancelled"], "all-landings closure still cancels waiting actors at their original position")
_expect(_car()[&"passenger_count"] == 1 and not _car()[&"operational"] and not _path.get_active_elevator_request(&"held").is_empty(), "no valid landing holds boarded actor safely with ownership")
var held_remaining: int = _car()[&"remaining_minutes"]
_tick(20)
_expect(_car()[&"remaining_minutes"] == held_remaining and _elevators.restore_snapshot(_elevators.create_snapshot()), "held cabin survives elapsed time and save restore without advancing or losing its rider")
for cell: Vector3i in _config[&"stops"]: _path.configure_cell_traffic(cell)
_elevators.set_operator_open(_config[&"shaft_id"], true, _minute)
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "repaired cabin resumes with an adjusted physical phase clock")
_expect(_tick(40).size() == 1, "held actor arrives exactly once after landing repair")
func _test_mutations_and_cancellation() -> void:
_setup()
_request(&"waiter", 0, 3)
var before := _elevators.create_snapshot()
_expect(not _elevators.synchronize([], _minute)[&"ok"] and _elevators.create_snapshot() == before, "queued rider prevents destructive shaft modification atomically")
var larger := _config.duplicate(true)
larger[&"module_ids"].append(&"module.2")
larger[&"stops"].append(Vector3i(0, 4, 0))
larger[&"stops"].append(Vector3i(0, 5, 0))
_expect(not _elevators.synchronize([larger], _minute)[&"ok"], "extension waits until cabin and queues are idle")
_expect(_elevators.cancel_actor(&"waiter") and _path.get_active_elevator_request(&"waiter").is_empty(), "waiting actor removal cancels Path queue ownership")
_expect(_elevators.synchronize([larger], _minute)[&"ok"] and _car()[&"stops"].size() == 6, "idle upward extension joins existing physical cabin")
_expect(_request(&"despawn", 0, 3)[&"ok"], "new request uses fresh request ID after cancellation")
_tick(2)
_expect(_elevators.cancel_actor(&"despawn") and _path.get_active_elevator_request(&"despawn").is_empty() and _car()[&"passenger_count"] == 0, "boarded actor despawn removes only seat and ownership")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "saved doors remain valid after only passenger despawns")
_expect(_tick(50).is_empty() and _car()[&"phase"] == &"IDLE", "empty cabin safely finishes its motion without resurrecting despawned actor")
_expect(_elevators.synchronize([], _minute)[&"ok"] and _path.create_snapshot()[&"connectors"].is_empty(), "idle demolition removes all owned connectors")
_setup()
_request(&"duplicate", 0, 3)
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "queued snapshot restores with matching floor, request, and Path unit")
var replay := _request(&"duplicate", 0, 3)
_expect(replay[&"ok"] and replay[&"replay"] and _car()[&"waiting_count"] == 1, "same actor route request replays without duplicate queue")
_expect(not _request(&"duplicate", 0, 2)[&"ok"], "actor cannot own another simultaneous destination")
_expect(_elevators.advance_one_minute(8).is_empty() and _elevators.current_minute == 0, "skipped simulation minutes are rejected")
_expect(_tick().is_empty() and _elevators.advance_one_minute(1).is_empty() and _car()[&"remaining_minutes"] == 3, "duplicate minute cannot advance cabin twice")
func _test_snapshot_rejections() -> void:
_setup()
_request(&"saved", 0, 3)
_tick(10)
var saved := _elevators.create_snapshot()
_expect(_elevators.restore_snapshot(saved), "valid active snapshot restores")
var bad := saved.duplicate(true)
bad[&"shafts"][0][&"config"][&"capacity"] = 5
_reject(bad, saved, "tampered FRONT capacity")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"config"][&"travel_minutes_per_floor"] = 1
_reject(bad, saved, "tampered floor speed")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"remaining_minutes"] -= 1
_reject(bad, saved, "tampered cabin countdown")
bad = saved.duplicate(true)
bad[&"trips"][0][&"to"] = Vector3i(0, 2, 0)
_reject(bad, saved, "tampered destination")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"boarded"] = []
_reject(bad, saved, "missing passenger seat")
bad = saved.duplicate(true)
bad[&"trips"] = []
_reject(bad, saved, "missing active trip")
bad = saved.duplicate(true)
bad[&"request_sequence"] = 0
_reject(bad, saved, "active request sequence reuse")
bad = saved.duplicate(true)
bad[&"shafts"][0][&"powered"] = false
_reject(bad, saved, "inconsistent power and operation")
bad = saved.duplicate(true)
bad[&"unexpected"] = true
_reject(bad, saved, "unknown snapshot field")
_tick(50)
saved = _elevators.create_snapshot()
bad = saved.duplicate(true)
bad[&"request_sequence"] = 0
_reject(bad, saved, "retired request sequence reuse")
_expect(_elevators.restore_snapshot(saved), "completed trip history permits valid restore")
func _reject(bad: Dictionary, before: Dictionary, label: String) -> void:
_expect(not _elevators.restore_snapshot(bad) and _elevators.create_snapshot() == before, "%s is rejected atomically" % label)
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition: print("PASS: %s" % label)
else:
_failures += 1
push_error("FAIL: %s" % label)
func _test_paired_boarding() -> void:
_setup(1)
for i: int in range(7): _request(StringName("a%d" % i), 0, 3, 1)
_expect(_elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])[&"ok"], "rescue pair reserves two real seats")
var before := _elevators.create_snapshot()
_expect(_elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])[&"replay"] and _elevators.create_snapshot() == before, "pair retry is idempotent")
_tick()
_expect(_car()[&"passenger_count"] == 7 and _car()[&"waiting_count"] == 2, "one remaining seat never splits the rescue pair")
_expect(_elevators.get_actor_trip(&"medic")[&"state"] == _elevators.get_actor_trip(&"patient")[&"state"], "pair retains the same queued state")
var state := _elevators.create_snapshot()
var paths := _path.create_snapshot()
var arrivals := _tick(150)
var paired: Array[Dictionary] = []
for arrival: Dictionary in arrivals:
if arrival[&"actor_id"] in [&"medic", &"patient"]: paired.append(arrival)
_expect(paired.size() == 2 and paired[0][&"cell"] == paired[1][&"cell"], "both rescue passengers arrive at the same landing")
_expect(_path.restore_snapshot(paths) and _elevators.restore_snapshot(state), "queued pair and seat ownership restore")
_minute = state[&"current_minute"]
_expect(_tick(150) == arrivals, "queued pair replays exact arrivals")
_setup(1)
_elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])
_expect(_elevators.cancel_actor(&"patient") and _elevators.get_report()[&"trips"].is_empty() and _path.get_active_elevator_request(&"medic").is_empty(), "queued cancellation releases both passengers")
_elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])
_tick(10)
state = _elevators.create_snapshot(); paths = _path.create_snapshot()
_expect(not _elevators.cancel_actor(&"patient") and _elevators.create_snapshot() == state, "onboard pair cannot teleport out through cancellation")
var corrupt := state.duplicate(true)
corrupt[&"trips"][0][&"companion_id"] = &"missing"
_expect(not _elevators.restore_snapshot(corrupt) and _elevators.create_snapshot() == state, "orphan companion save is rejected atomically")
_expect(_elevators.restore_snapshot(state), "boarded pair restores")
var released := _elevators.set_powered(_config[&"shaft_id"], false, _minute)
_expect(released.size() == 2 and released[0][&"cell"] == released[1][&"cell"] and released[0][&"emergency"] and released[1][&"emergency"], "power loss unloads the pair together")
_expect(_elevators.restore_snapshot(_elevators.create_snapshot()), "emergency release leaves a valid snapshot")
_setup(1)
_request(&"patient", 0, 2, 1)
state = _elevators.create_snapshot(); paths = _path.create_snapshot()
_expect(not _elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])[&"ok"] and _elevators.create_snapshot() == state and _path.create_snapshot() == paths, "conflicting pair reservation leaves all ownership unchanged")
_setup(1)
for i: int in range(ElevatorService.QUEUE_CAPACITY - 1): _request(StringName("crowd_%d" % i), 0, 3, 1)
state = _elevators.create_snapshot(); paths = _path.create_snapshot()
_expect(_car()[&"waiting_count"] == ElevatorService.QUEUE_CAPACITY - 1, "queue fixture has exactly one free request slot")
_expect(not _elevators.request_pair(&"medic", &"patient", Vector3i(0,0,1), Vector3i(0,3,1), 0, [&"staff"])[&"ok"] and _elevators.create_snapshot() == state and _path.create_snapshot() == paths, "second-member queue rejection rolls back first member and request sequence")