204 lines
7.5 KiB
GDScript
204 lines
7.5 KiB
GDScript
extends SceneTree
|
|
|
|
## Asset-free ownership, lifecycle, dependency and timing regression for WMOs.
|
|
|
|
const REGISTRY_SCRIPT := preload("res://src/render/wmo/wmo_placement_registry.gd")
|
|
const REGISTRY_PATH := "res://src/render/wmo/wmo_placement_registry.gd"
|
|
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
|
|
|
|
|
func _initialize() -> void:
|
|
var failures: Array[String] = []
|
|
_verify_invalid_and_first_reference(failures)
|
|
_verify_idempotent_and_shared_references(failures)
|
|
_verify_owner_checked_final_release(failures)
|
|
_verify_global_reference_and_clear(failures)
|
|
_verify_detached_sorted_diagnostics(failures)
|
|
_verify_ownership_boundaries(failures)
|
|
var elapsed_milliseconds := _verify_bounded_timing(failures)
|
|
if not failures.is_empty():
|
|
for failure in failures:
|
|
push_error("WMO_PLACEMENT_REGISTRY: %s" % failure)
|
|
quit(1)
|
|
return
|
|
print(
|
|
"WMO_PLACEMENT_REGISTRY PASS cases=10 iterations=100 elapsed_ms=%.3f"
|
|
% elapsed_milliseconds
|
|
)
|
|
quit(0)
|
|
|
|
|
|
func _verify_invalid_and_first_reference(failures: Array[String]) -> void:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
_expect_false(registry.call("add_reference", "", "tile-a"), "empty placement rejected", failures)
|
|
_expect_false(registry.call("add_reference", "uid:7", ""), "empty reference rejected", failures)
|
|
_expect_true(registry.call("add_reference", "uid:7", "tile-a"), "first reference registers", failures)
|
|
_expect_true(registry.call("contains", "uid:7"), "registered placement contained", failures)
|
|
_expect_equal(int(registry.call("active_count")), 1, "first active count", failures)
|
|
|
|
|
|
func _verify_idempotent_and_shared_references(failures: Array[String]) -> void:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
registry.call("add_reference", "uid:11", "tile-a")
|
|
_expect_false(
|
|
registry.call("add_reference", "uid:11", "tile-a"),
|
|
"same reference idempotent",
|
|
failures
|
|
)
|
|
_expect_false(
|
|
registry.call("add_reference", "uid:11", "tile-b"),
|
|
"shared reference retains placement",
|
|
failures
|
|
)
|
|
_expect_equal(int(registry.call("active_count")), 1, "shared active count", failures)
|
|
_expect_false(
|
|
registry.call("release_reference", "uid:11", "tile-a"),
|
|
"non-final release retains placement",
|
|
failures
|
|
)
|
|
_expect_true(registry.call("contains", "uid:11"), "shared placement remains", failures)
|
|
|
|
|
|
func _verify_owner_checked_final_release(failures: Array[String]) -> void:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
registry.call("add_reference", "uid:19", "tile-owner")
|
|
_expect_false(
|
|
registry.call("release_reference", "uid:19", "tile-other"),
|
|
"non-owner release ignored",
|
|
failures
|
|
)
|
|
_expect_false(
|
|
registry.call("release_reference", "uid:404", "tile-owner"),
|
|
"unknown placement release ignored",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
registry.call("release_reference", "uid:19", "tile-owner"),
|
|
"final owner release removes placement",
|
|
failures
|
|
)
|
|
_expect_false(registry.call("contains", "uid:19"), "released placement absent", failures)
|
|
|
|
|
|
func _verify_global_reference_and_clear(failures: Array[String]) -> void:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
_expect_true(
|
|
registry.call("add_reference", "global", "__global__"),
|
|
"global reference registers",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
registry.call("release_reference", "global", "__global__"),
|
|
"global final release removes",
|
|
failures
|
|
)
|
|
registry.call("add_reference", "uid:3", "tile-a")
|
|
registry.call("clear")
|
|
registry.call("clear")
|
|
_expect_equal(int(registry.call("active_count")), 0, "clear idempotent", failures)
|
|
|
|
|
|
func _verify_detached_sorted_diagnostics(failures: Array[String]) -> void:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
registry.call("add_reference", "uid:20", "tile-b")
|
|
registry.call("add_reference", "uid:10", "tile-c")
|
|
registry.call("add_reference", "uid:10", "tile-a")
|
|
var snapshot: Array = registry.call("diagnostic_snapshot")
|
|
_expect_equal(snapshot.size(), 2, "diagnostic record count", failures)
|
|
_expect_string_equal(String(snapshot[0]["placement_key"]), "uid:10", "placement sort", failures)
|
|
_expect_string_array(snapshot[0]["reference_keys"], ["tile-a", "tile-c"], "reference sort", failures)
|
|
(snapshot[0]["reference_keys"] as Array).append("mutated")
|
|
var fresh_snapshot: Array = registry.call("diagnostic_snapshot")
|
|
_expect_string_array(
|
|
fresh_snapshot[0]["reference_keys"],
|
|
["tile-a", "tile-c"],
|
|
"diagnostics detached",
|
|
failures
|
|
)
|
|
|
|
|
|
func _verify_ownership_boundaries(failures: Array[String]) -> void:
|
|
var loader_source := _read_text(LOADER_PATH, failures)
|
|
var registry_source := _read_text(REGISTRY_PATH, failures)
|
|
_expect_true(
|
|
loader_source.contains("WMO_PLACEMENT_REGISTRY_SCRIPT.new()"),
|
|
"loader composes registry",
|
|
failures
|
|
)
|
|
_expect_true(loader_source.contains("_wmo_placement_registry.add_reference("), "loader delegates add", failures)
|
|
_expect_true(loader_source.contains("_wmo_placement_registry.release_reference("), "loader delegates release", failures)
|
|
_expect_true(loader_source.contains("_wmo_placement_registry.clear()"), "loader delegates clear", failures)
|
|
_expect_true(
|
|
loader_source.contains("_wmo_instance_nodes_by_unique_key"),
|
|
"loader retains node map",
|
|
failures
|
|
)
|
|
_expect_false(loader_source.contains("var _wmo_registry"), "legacy mixed registry removed", failures)
|
|
for forbidden_token in ["extends Node", "Node3D", "Resource", "RID", "Thread", "Mutex", "Semaphore"]:
|
|
_expect_false(
|
|
registry_source.contains(forbidden_token),
|
|
"registry omits %s ownership" % forbidden_token,
|
|
failures
|
|
)
|
|
|
|
|
|
func _verify_bounded_timing(failures: Array[String]) -> float:
|
|
var registry: RefCounted = REGISTRY_SCRIPT.new()
|
|
var started_microseconds := Time.get_ticks_usec()
|
|
for iteration in range(100):
|
|
for placement_index in range(256):
|
|
var placement_key := "uid:%d" % placement_index
|
|
registry.call("add_reference", placement_key, "tile-a")
|
|
registry.call("add_reference", placement_key, "tile-b")
|
|
for placement_index in range(256):
|
|
var placement_key := "uid:%d" % placement_index
|
|
registry.call("release_reference", placement_key, "tile-a")
|
|
registry.call("release_reference", placement_key, "tile-b")
|
|
_expect_equal(int(registry.call("active_count")), 0, "timing cycle %d clears" % iteration, failures)
|
|
var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
|
_expect_true(elapsed_milliseconds < 1000.0, "100 shared-reference cycles remain bounded", failures)
|
|
return elapsed_milliseconds
|
|
|
|
|
|
func _read_text(path: String, failures: Array[String]) -> String:
|
|
var file := FileAccess.open(path, FileAccess.READ)
|
|
if file == null:
|
|
failures.append("cannot open %s" % path)
|
|
return ""
|
|
return file.get_as_text()
|
|
|
|
|
|
func _expect_equal(actual_value: int, expected_value: int, label: String, failures: Array[String]) -> void:
|
|
if actual_value != expected_value:
|
|
failures.append("%s expected %d, got %d" % [label, expected_value, actual_value])
|
|
|
|
|
|
func _expect_string_equal(
|
|
actual_value: String,
|
|
expected_value: String,
|
|
label: String,
|
|
failures: Array[String]
|
|
) -> void:
|
|
if actual_value != expected_value:
|
|
failures.append("%s expected %s, got %s" % [label, expected_value, actual_value])
|
|
|
|
|
|
func _expect_string_array(
|
|
actual_values: Array,
|
|
expected_values: Array,
|
|
label: String,
|
|
failures: Array[String]
|
|
) -> void:
|
|
if actual_values != expected_values:
|
|
failures.append("%s expected %s, got %s" % [label, expected_values, actual_values])
|
|
|
|
|
|
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
|
if not actual_value:
|
|
failures.append("%s expected true" % label)
|
|
|
|
|
|
func _expect_false(actual_value: bool, label: String, failures: Array[String]) -> void:
|
|
if actual_value:
|
|
failures.append("%s expected false" % label)
|