Files
TheTree_mock/tests/s1/test_world_service.gd
T

200 lines
9.7 KiB
GDScript

extends SceneTree
const WorldServiceType = preload("res://scripts/world/world_service.gd")
const DigestType = preload("res://scripts/core/simulation_digest.gd")
var _checks := 0
var _failures := 0
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
print("S1: World Service")
_test_bounds_zones_and_footprints()
_test_occupancy_validation_and_release()
_test_walkability_excavation_and_revisions()
_test_connected_support_validation()
_test_snapshot_round_trip_and_atomic_restore()
_test_snapshot_order_is_deterministic()
if _failures == 0:
print("PASS: %d checks" % _checks)
else:
push_error("FAIL: %d of %d checks failed" % [_failures, _checks])
quit(_failures)
func _test_bounds_zones_and_footprints() -> void:
var world = WorldServiceType.new()
_expect(world.reset(_bounds(-2, 2, -2, 2)), "dictionary bounds are accepted")
_expect(world.is_in_bounds(Vector3i(-2, -2, 0)), "FRONT minimum corner is in bounds")
_expect(world.is_in_bounds(Vector3i(2, 2, 1)), "BACK maximum corner is in bounds")
_expect(not world.is_in_bounds(Vector3i(0, 0, 2)), "unknown layer is out of bounds")
_expect(not world.is_in_bounds(Vector3i(3, 0, 0)), "x outside bounds is rejected")
_expect(world.get_zone(Vector3i(0, 1, 0)) == world.ZONE_ABOVE_GROUND, "positive y is above ground")
_expect(world.get_zone(0) == world.ZONE_BOUNDARY, "zero y is boundary")
_expect(world.get_zone(Vector2i(0, -1)) == world.ZONE_UNDERGROUND, "negative y is underground")
var footprint := world.cells_for_footprint(Vector3i(-1, -1, 1), Vector2i(2, 2))
_expect(
footprint == [
Vector3i(-1, -1, 1), Vector3i(0, -1, 1),
Vector3i(-1, 0, 1), Vector3i(0, 0, 1),
],
"rectangular footprint has stable row-major ordering"
)
_expect(world.cells_for_footprint(Vector3i.ZERO, Vector2i.ZERO).is_empty(), "empty footprint is rejected")
var before := world.get_bounds()
_expect(not world.reset(Rect2i(0, 0, 0, 2)), "invalid reset is rejected")
_expect(world.get_bounds() == before, "failed reset preserves the current world")
func _test_occupancy_validation_and_release() -> void:
var world = WorldServiceType.new()
world.reset(_bounds(-3, 3, -2, 2))
var room_cells := world.cells_for_footprint(Vector3i(0, 0, 0), Vector2i(2, 1))
_expect(world.occupy(&"room-b", room_cells, &"facility_small"), "valid entity occupies its footprint")
_expect(world.get_occupant(Vector3i(0, 0, 0)) == &"room-b", "cell points back to occupant")
_expect(world.get_occupant_kind(Vector3i(1, 0, 0)) == &"facility_small", "occupant kind is retained")
_expect(world.get_entity_cells(&"room-b") == room_cells, "entity points to canonical cells")
var returned_cells := world.get_entity_cells(&"room-b")
returned_cells.clear()
_expect(world.get_entity_cells(&"room-b") == room_cells, "returned cell arrays cannot mutate world state")
var overlap := world.validate_occupancy([Vector3i(1, 0, 0)], &"room-a")
_expect(not overlap[&"ok"] and overlap[&"errors"].has(world.ERROR_OCCUPIED), "overlapping occupation reports OCCUPIED")
var revision_before_failure: int = world.revision
_expect(not world.occupy(&"room-a", [Vector3i(1, 0, 0)], &"facility"), "overlapping occupy is atomic")
_expect(world.revision == revision_before_failure, "failed occupy does not change revision")
_expect(not world.occupy(&"room-b", [Vector3i(2, 0, 0)], &"facility"), "same entity cannot occupy twice")
var malformed := world.validate_occupancy([
Vector3i(0, 1, 0),
Vector3i(0, 1, 0),
Vector3i(0, 1, 1),
Vector3i(9, 1, 0),
Vector2i.ZERO,
])
_expect(malformed[&"errors"].has(world.ERROR_DUPLICATE_CELL), "duplicate cell is reported")
_expect(malformed[&"errors"].has(world.ERROR_LAYER_MISMATCH), "layer crossing is reported")
_expect(malformed[&"errors"].has(world.ERROR_OUT_OF_BOUNDS), "out-of-bounds cell is reported")
_expect(malformed[&"errors"].has(world.ERROR_INVALID_CELL_TYPE), "non-Vector3i cell is reported")
_expect(world.release(&"room-b"), "release succeeds for existing entity")
_expect(world.get_occupant(Vector3i(0, 0, 0)).is_empty(), "release clears cell-side occupation")
_expect(world.get_entity_cells(&"room-b").is_empty(), "release clears entity-side occupation")
_expect(not world.release(&"room-b"), "double release is rejected")
func _test_walkability_excavation_and_revisions() -> void:
var world = WorldServiceType.new()
world.reset(_bounds(-1, 1, -2, 2))
var path_cell := Vector3i(0, 1, 0)
var cave_front := Vector3i(0, -1, 0)
var cave_back := Vector3i(0, -1, 1)
_expect(not world.is_walkable(path_cell), "walkability defaults to false")
_expect(world.set_walkable(path_cell, true), "walkability can be enabled")
_expect(world.is_walkable(path_cell), "enabled walkability is readable")
_expect(world.dirty_revision == world.revision, "dirty revision exposes the current world revision")
var walk_revision: int = world.revision
_expect(world.set_walkable(path_cell, true), "same walkability is a successful no-op")
_expect(world.revision == walk_revision, "walkability no-op does not dirty world")
_expect(world.is_excavated(Vector3i(0, 0, 0)), "boundary is usable without excavation")
_expect(not world.is_excavated(cave_front), "underground defaults to unexcavated")
_expect(world.set_excavated(cave_front, true), "underground spatial cell can be excavated")
_expect(world.is_excavated(cave_front) and world.is_excavated(cave_back), "excavation is shared by FRONT and BACK")
_expect(world.excavation_revision == world.revision, "excavation tracks its dirty revision")
_expect(world.get_dirty_cells().has(cave_front) and world.get_dirty_cells().has(cave_back), "both layers become dirty")
var dirty := world.consume_dirty_cells()
_expect(not dirty.is_empty() and world.get_dirty_cells().is_empty(), "dirty cells can be consumed deterministically")
_expect(not world.set_walkable(Vector3i(99, 0, 0), true), "out-of-bounds walkability mutation is rejected")
func _test_connected_support_validation() -> void:
var world = WorldServiceType.new()
world.reset(_bounds(-5, 5, 0, 2))
_expect(world.set_support(Vector3i(0, 0, 0), 3), "explicit support capacity is stored")
_expect(world.occupy(&"existing-room", [Vector3i(1, 0, 0)], &"facility_small"), "existing structural load is stored")
var supported := world.validate_support([Vector3i(2, 0, 0), Vector3i(3, 0, 0)], 1)
_expect(supported[&"ok"], "component capacity covers existing and proposed load")
_expect(supported[&"required_weight"] == 3, "support report includes existing structural load")
_expect(supported[&"available_capacity"] == 3, "support report exposes finite capacity")
var overloaded := world.validate_support([
Vector3i(2, 0, 0), Vector3i(3, 0, 0), Vector3i(4, 0, 0),
], 1)
_expect(not overloaded[&"ok"] and overloaded[&"errors"].has(world.ERROR_UNSUPPORTED), "overloaded component is rejected")
_expect(overloaded[&"unsupported_cells"].size() == 3, "all proposed cells in failed component are identified")
_expect(world.set_support(Vector3i(-2, 0, 1), 0, true), "infinite structural anchor is stored")
var anchored := world.validate_support([Vector3i(-1, 0, 1), Vector3i(0, 0, 1)], 99)
_expect(anchored[&"ok"] and anchored[&"available_capacity"] == -1, "connected anchor supports any finite weight")
_expect(not world.validate_support([Vector3i(0, 0, 0)], 0)[&"ok"], "non-positive proposed weight is rejected")
func _test_snapshot_round_trip_and_atomic_restore() -> void:
var source = WorldServiceType.new()
source.reset(_bounds(-3, 3, -3, 3))
source.occupy(&"warehouse", [Vector3i(1, 0, 1), Vector3i(0, 0, 1)], &"facility_medium")
source.set_walkable(Vector3i(-1, 0, 0), true)
source.set_excavated(Vector3i(2, -2, 0), true)
source.set_excavated(Vector3i(-2, 1, 0), false)
source.set_support(Vector3i(0, 0, 0), 5, false)
source.set_support(Vector3i(-1, 0, 1), 0, true)
var snapshot := source.create_snapshot()
var restored = WorldServiceType.new()
restored.reset(Rect2i(0, 0, 1, 1))
_expect(restored.restore_snapshot(snapshot), "valid world snapshot restores")
_expect(restored.create_snapshot() == snapshot, "world snapshot round trip is exact")
_expect(DigestType.compute(restored.create_snapshot()) == DigestType.compute(snapshot), "round trip preserves deterministic digest")
var corrupt: Dictionary = snapshot.duplicate(true)
corrupt[&"occupancies"].append({
&"entity_id": &"overlap",
&"kind": &"facility",
&"cells": [Vector3i(0, 0, 1)],
})
var before_failed_restore := restored.create_snapshot()
_expect(not restored.restore_snapshot(corrupt), "overlapping snapshot is rejected")
_expect(restored.create_snapshot() == before_failed_restore, "failed restore is fully atomic")
func _test_snapshot_order_is_deterministic() -> void:
var first = WorldServiceType.new()
var second = WorldServiceType.new()
first.reset(_bounds(-2, 2, -1, 1))
second.reset(_bounds(-2, 2, -1, 1))
first.occupy(&"z-entity", [Vector3i(1, 0, 0)], &"decoration")
first.occupy(&"a-entity", [Vector3i(-1, 0, 0)], &"decoration")
second.occupy(&"a-entity", [Vector3i(-1, 0, 0)], &"decoration")
second.occupy(&"z-entity", [Vector3i(1, 0, 0)], &"decoration")
first.set_walkable(Vector3i(1, 1, 1), true)
first.set_walkable(Vector3i(-1, -1, 0), true)
second.set_walkable(Vector3i(-1, -1, 0), true)
second.set_walkable(Vector3i(1, 1, 1), true)
_expect(first.create_snapshot() == second.create_snapshot(), "snapshot ignores Dictionary insertion order")
_expect(DigestType.compute(first.create_snapshot()) == DigestType.compute(second.create_snapshot()), "equivalent worlds have identical digest")
func _bounds(min_x: int, max_x: int, min_y: int, max_y: int) -> Dictionary:
return {
&"min_x": min_x,
&"max_x": max_x,
&"min_y": min_y,
&"max_y": max_y,
}
func _expect(condition: bool, label: String) -> void:
_checks += 1
if condition:
print(" ✓ %s" % label)
return
_failures += 1
push_error(" ✗ %s" % label)