106 lines
6.8 KiB
GDScript
106 lines
6.8 KiB
GDScript
extends SceneTree
|
|
|
|
var _checks := 0
|
|
var _failures := 0
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
func _run() -> void:
|
|
_test_policy_and_connectors()
|
|
_test_recovery_and_diagnostics()
|
|
_test_snapshots()
|
|
print("Path vertical policy: %d checks, %d failures" % [_checks, _failures])
|
|
quit(_failures)
|
|
|
|
func _path() -> PathService:
|
|
var path := PathService.new()
|
|
path.rebuild_graph([Vector3i.ZERO, Vector3i(1, 0, 0), Vector3i(0, 1, 0), Vector3i(1, 1, 0), Vector3i(0, 0, 1), Vector3i(0, 1, 1)])
|
|
return path
|
|
|
|
func _test_policy_and_connectors() -> void:
|
|
var path := _path()
|
|
var bottom := Vector3i.ZERO
|
|
var top := Vector3i(0, 1, 0)
|
|
_expect(not path.get_vertical_walk_requires_connector(), "standalone Path retains legacy cardinal movement by default")
|
|
var old_route := path.find_path(bottom, top)
|
|
_expect(old_route == [bottom, top] and path.cached_path_count() == 1, "legacy upper-floor route can be cached")
|
|
var graph_before := path.graph_revision
|
|
_expect(path.set_vertical_walk_requires_connector(true), "strict vertical policy enables")
|
|
_expect(path.graph_revision == graph_before + 1 and path.cached_path_count() == 0, "policy change invalidates previously cached floor climbing")
|
|
_expect(path.find_path(bottom, top).is_empty() and path.last_no_route_reason == PathService.ROUTE_GOAL_UNREACHABLE, "adjacent floors require an explicit vertical connection")
|
|
_expect(path.calculate_path_cost(old_route) == -1 and path.get_edge_report(bottom, top).is_empty(), "cost and movement edge resolution cannot bypass strict routing")
|
|
_expect(path.find_path(bottom, Vector3i(1, 0, 0)).size() == 2, "horizontal walking remains available")
|
|
var raw_vertical := false
|
|
for edge: Dictionary in path.get_neighbors(bottom):
|
|
if edge[&"to"] == top and edge[&"movement_type"] == PathService.MOVEMENT_WALK: raw_vertical = true
|
|
_expect(raw_vertical, "raw structural neighbors remain available to legacy-stair migration")
|
|
var snapshot := path.create_snapshot()
|
|
_expect(path.set_vertical_walk_requires_connector(true) and path.create_snapshot() == snapshot, "reapplying the same policy is an exact no-op")
|
|
_expect(path.configure_connector(&"stairs", bottom, top, 150), "explicit public stairs register")
|
|
_expect(path.find_path(bottom, top) == [bottom, top] and path.get_step_connector(bottom, top) == &"stairs", "explicit stairs work through strict policy")
|
|
_expect(path.calculate_path_cost([bottom, top]) == 150, "stairs keep their defined movement cost")
|
|
path.set_connector_enabled(&"stairs", false)
|
|
_expect(path.find_path(bottom, top).is_empty() and path.last_no_route_reason == PathService.ROUTE_CONNECTOR_UNAVAILABLE, "closed stairs cannot fall back to implicit floor climbing")
|
|
path.configure_connector(&"service_stairs", Vector3i(0, 0, 1), Vector3i(0, 1, 1), 150, true, &"staff")
|
|
_expect(path.find_path(Vector3i(0, 0, 1), Vector3i(0, 1, 1), [1], []).is_empty(), "service stairs retain staff-only access")
|
|
_expect(path.find_path(Vector3i(0, 0, 1), Vector3i(0, 1, 1), [1], [&"staff"]).size() == 2, "authorized staff use explicit BACK stairs")
|
|
path.configure_elevator(&"lift", bottom, top)
|
|
_expect(path.get_step_connector(bottom, top) == &"lift", "an operational elevator remains an explicit vertical route")
|
|
path.set_connector_enabled(&"lift", false)
|
|
_expect(path.find_path(bottom, top).is_empty(), "a stopped lift never exposes an ordinary vertical edge")
|
|
path.set_vertical_walk_blockers(&"shaft", [bottom, top])
|
|
path.set_vertical_walk_requires_connector(false)
|
|
_expect(path.get_edge_report(bottom, top).is_empty(), "physical shaft blockers survive opting out of the global floor policy")
|
|
|
|
func _test_recovery_and_diagnostics() -> void:
|
|
var path := PathService.new()
|
|
var bottom := Vector3i.ZERO
|
|
var top := Vector3i(0, 1, 0)
|
|
path.rebuild_graph([bottom, top])
|
|
path.set_vertical_walk_requires_connector(true)
|
|
path.set_cell_temporary_block(bottom, true)
|
|
_expect(not path.find_nearest_walkable_recovery(bottom).get(&"found", false), "recovery cannot choose an unreachable floor solely because it is nearby")
|
|
path.configure_connector(&"stairs", bottom, top, 150)
|
|
_expect(path.find_nearest_walkable_recovery(bottom).get(&"cell") == top, "a real public stair makes the upper recovery proposal structurally reachable")
|
|
path.set_connector_enabled(&"stairs", false)
|
|
_expect(not path.find_nearest_walkable_recovery(bottom).get(&"found", false), "recovery respects an unavailable vertical connector")
|
|
path.set_cell_temporary_block(bottom, false)
|
|
path.remove_connector(&"stairs")
|
|
path.rebuild_graph([bottom, top, Vector3i(1, 0, 0), Vector3i(1, 1, 0)])
|
|
path.set_cell_temporary_block(Vector3i(1, 0, 0), true)
|
|
_expect(path.find_path(bottom, top).is_empty() and path.last_no_route_reason == PathService.ROUTE_GOAL_UNREACHABLE, "diagnostics do not invent a blocked walking route between floors")
|
|
path.set_vertical_walk_requires_connector(false)
|
|
_expect(path.find_path(bottom, top) == [bottom, top], "explicit legacy opt-out restores standalone vertical walking")
|
|
|
|
func _test_snapshots() -> void:
|
|
var path := _path()
|
|
path.set_vertical_walk_requires_connector(true)
|
|
path.set_vertical_walk_blockers(&"shaft", [Vector3i.ZERO, Vector3i(0, 1, 0)])
|
|
var saved := path.create_snapshot()
|
|
_expect(saved[&"schema_version"] == 5 and saved[&"vertical_walk_requires_connector"] == true and saved[&"transit_actor_ids"] == [], "Path v5 saves explicit policy and transit occupancy")
|
|
var restored := _path()
|
|
_expect(restored.restore_snapshot(saved) and restored.create_snapshot() == saved, "strict policy and shaft ownership restore exactly")
|
|
for value: Variant in [0, 1, "true", null]:
|
|
var bad := saved.duplicate(true)
|
|
bad[&"vertical_walk_requires_connector"] = value
|
|
_expect(not restored.restore_snapshot(bad) and restored.create_snapshot() == saved, "non-bool vertical policy rejects atomically: %s" % str(value))
|
|
var missing := saved.duplicate(true)
|
|
missing.erase(&"vertical_walk_requires_connector")
|
|
_expect(not restored.restore_snapshot(missing), "v5 cannot silently default a missing policy")
|
|
var legacy := saved.duplicate(true)
|
|
legacy[&"schema_version"] = 4
|
|
_expect(not restored.restore_snapshot(legacy), "outer-only legacy downgrade cannot hide new policy fields")
|
|
legacy.erase(&"vertical_walk_requires_connector")
|
|
legacy.erase(&"transit_actor_ids")
|
|
_expect(restored.restore_snapshot(legacy) and not restored.get_vertical_walk_requires_connector(), "v4 defaults to its original vertical rules for Runtime-managed migration")
|
|
_expect(restored.get_vertical_walk_blockers() == path.get_vertical_walk_blockers(), "v4 keeps existing shaft ownership independently of v5 policy")
|
|
var migrated := restored.create_snapshot()
|
|
_expect(restored.restore_snapshot(migrated) and restored.create_snapshot() == migrated, "legacy migration is canonical and does not repeat on second restore")
|
|
|
|
func _expect(ok: bool, label: String) -> void:
|
|
_checks += 1
|
|
if not ok:
|
|
_failures += 1
|
|
push_error(label)
|