231 lines
6.9 KiB
GDScript
231 lines
6.9 KiB
GDScript
extends SceneTree
|
|
|
|
## Asset-free M2 resource observation identity, adoption, lifetime, boundary and
|
|
## bounded-timing regression.
|
|
|
|
const SNAPSHOT_SCRIPT := preload(
|
|
"res://src/render/m2/m2_build_resource_snapshot.gd"
|
|
)
|
|
const SNAPSHOT_PATH := "res://src/render/m2/m2_build_resource_snapshot.gd"
|
|
const DISPATCH_PATH := "res://src/render/m2/m2_build_dispatch_planner.gd"
|
|
const STATIC_OBSERVER_PATH := "res://src/render/m2/m2_static_build_resource_observer.gd"
|
|
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
|
|
|
|
|
func _initialize() -> void:
|
|
var failures: Array[String] = []
|
|
_verify_initial_observation(failures)
|
|
_verify_static_adoption_and_replacement(failures)
|
|
_verify_detached_diagnostics(failures)
|
|
_verify_engine_lifetime(failures)
|
|
_verify_loader_and_dependency_boundaries(failures)
|
|
var elapsed_milliseconds := _verify_bounded_timing(failures)
|
|
if not failures.is_empty():
|
|
for failure in failures:
|
|
push_error("M2_BUILD_RESOURCE_SNAPSHOT: %s" % failure)
|
|
quit(1)
|
|
return
|
|
print(
|
|
"M2_BUILD_RESOURCE_SNAPSHOT PASS cases=12 iterations=20000 elapsed_ms=%.3f"
|
|
% elapsed_milliseconds
|
|
)
|
|
quit(0)
|
|
|
|
|
|
func _verify_initial_observation(failures: Array[String]) -> void:
|
|
var prototype := Node3D.new()
|
|
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new(
|
|
"world/example.m2",
|
|
prototype,
|
|
true
|
|
)
|
|
_expect_string(
|
|
String(snapshot.call("normalized_relative_path")),
|
|
"world/example.m2",
|
|
"normalized path retained",
|
|
failures
|
|
)
|
|
_expect_same(
|
|
snapshot.call("animated_prototype"),
|
|
prototype,
|
|
"animated prototype identity retained",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
bool(snapshot.call("has_animated_prototype")),
|
|
"animated availability",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
bool(snapshot.call("animation_request_pending")),
|
|
"animation pending retained",
|
|
failures
|
|
)
|
|
_expect_false(bool(snapshot.call("has_static_mesh")), "static defaults empty", failures)
|
|
_expect_false(
|
|
bool(snapshot.call("static_model_missing")),
|
|
"missing defaults false",
|
|
failures
|
|
)
|
|
prototype.free()
|
|
|
|
|
|
func _verify_static_adoption_and_replacement(failures: Array[String]) -> void:
|
|
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/static.m2", null, false)
|
|
var first_mesh := ArrayMesh.new()
|
|
var second_mesh := ArrayMesh.new()
|
|
snapshot.call("adopt_static_observation", first_mesh, true)
|
|
_expect_same(snapshot.call("static_mesh"), first_mesh, "first Mesh identity", failures)
|
|
_expect_true(bool(snapshot.call("has_static_mesh")), "static available", failures)
|
|
_expect_true(
|
|
bool(snapshot.call("static_model_missing")),
|
|
"contradictory missing flag retained",
|
|
failures
|
|
)
|
|
snapshot.call("adopt_static_observation", second_mesh, false)
|
|
_expect_same(
|
|
snapshot.call("static_mesh"),
|
|
second_mesh,
|
|
"replacement Mesh identity",
|
|
failures
|
|
)
|
|
_expect_false(
|
|
bool(snapshot.call("static_model_missing")),
|
|
"replacement missing flag",
|
|
failures
|
|
)
|
|
snapshot.call("adopt_static_observation", null, true)
|
|
_expect_false(bool(snapshot.call("has_static_mesh")), "Mesh may clear", failures)
|
|
_expect_true(bool(snapshot.call("static_model_missing")), "terminal missing adopted", failures)
|
|
|
|
|
|
func _verify_detached_diagnostics(failures: Array[String]) -> void:
|
|
var prototype := Node3D.new()
|
|
var mesh := ArrayMesh.new()
|
|
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/diag.m2", prototype, false)
|
|
snapshot.call("adopt_static_observation", mesh, true)
|
|
var diagnostics: Dictionary = snapshot.call("diagnostic_snapshot")
|
|
_expect_true(
|
|
diagnostics == {
|
|
"normalized_relative_path": "world/diag.m2",
|
|
"has_animated_prototype": true,
|
|
"animation_request_pending": false,
|
|
"has_static_mesh": true,
|
|
"static_model_missing": true,
|
|
},
|
|
"diagnostic scalar contract",
|
|
failures
|
|
)
|
|
_expect_false(diagnostics.values().has(prototype), "diagnostics omit prototype", failures)
|
|
_expect_false(diagnostics.values().has(mesh), "diagnostics omit Mesh", failures)
|
|
diagnostics["has_static_mesh"] = false
|
|
var fresh: Dictionary = snapshot.call("diagnostic_snapshot")
|
|
_expect_true(bool(fresh["has_static_mesh"]), "diagnostics detached", failures)
|
|
prototype.free()
|
|
|
|
|
|
func _verify_engine_lifetime(failures: Array[String]) -> void:
|
|
var prototype := Node3D.new()
|
|
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/lifetime.m2", prototype, false)
|
|
snapshot = null
|
|
_expect_true(is_instance_valid(prototype), "snapshot release keeps Node valid", failures)
|
|
prototype.free()
|
|
|
|
|
|
func _verify_loader_and_dependency_boundaries(failures: Array[String]) -> void:
|
|
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
|
|
var dispatch_source := FileAccess.get_file_as_string(DISPATCH_PATH)
|
|
var static_observer_source := FileAccess.get_file_as_string(STATIC_OBSERVER_PATH)
|
|
var snapshot_source := FileAccess.get_file_as_string(SNAPSHOT_PATH)
|
|
for delegated_token in [
|
|
"M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(",
|
|
"resource_snapshot.call(\"animated_prototype\")",
|
|
"resource_snapshot.call(\"static_mesh\")",
|
|
]:
|
|
_expect_true(
|
|
loader_source.contains(delegated_token),
|
|
"loader uses %s" % delegated_token,
|
|
failures
|
|
)
|
|
_expect_true(
|
|
static_observer_source.contains("\"adopt_static_observation\""),
|
|
"static observer adopts snapshot",
|
|
failures
|
|
)
|
|
_expect_true(
|
|
dispatch_source.contains("resource_snapshot.call(\"animation_request_pending\")")
|
|
and dispatch_source.contains("resource_snapshot.call(\"has_static_mesh\")"),
|
|
"dispatch planner consumes typed snapshot",
|
|
failures
|
|
)
|
|
for forbidden_token in [
|
|
"queue_free",
|
|
"free(",
|
|
"ResourceLoader",
|
|
"RenderingServer",
|
|
"FileAccess",
|
|
"WorkerThreadPool",
|
|
"Thread",
|
|
"Mutex",
|
|
]:
|
|
_expect_false(
|
|
snapshot_source.contains(forbidden_token),
|
|
"snapshot omits %s ownership" % forbidden_token,
|
|
failures
|
|
)
|
|
|
|
|
|
func _verify_bounded_timing(failures: Array[String]) -> float:
|
|
var mesh := ArrayMesh.new()
|
|
var started_microseconds := Time.get_ticks_usec()
|
|
for iteration in range(20000):
|
|
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new(
|
|
"world/%d.m2" % (iteration % 64),
|
|
null,
|
|
iteration % 5 == 0
|
|
)
|
|
snapshot.call(
|
|
"adopt_static_observation",
|
|
mesh if iteration % 3 == 0 else null,
|
|
iteration % 7 == 0
|
|
)
|
|
snapshot.call("diagnostic_snapshot")
|
|
var elapsed_milliseconds := (
|
|
float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
|
)
|
|
_expect_true(
|
|
elapsed_milliseconds < 1000.0,
|
|
"20,000 snapshot cycles remain bounded",
|
|
failures
|
|
)
|
|
return elapsed_milliseconds
|
|
|
|
|
|
func _expect_string(
|
|
actual: String,
|
|
expected: String,
|
|
label: String,
|
|
failures: Array[String]
|
|
) -> void:
|
|
if actual != expected:
|
|
failures.append("%s expected=%s actual=%s" % [label, expected, actual])
|
|
|
|
|
|
func _expect_same(
|
|
actual: Variant,
|
|
expected: Variant,
|
|
label: String,
|
|
failures: Array[String]
|
|
) -> void:
|
|
if not is_same(actual, expected):
|
|
failures.append(label)
|
|
|
|
|
|
func _expect_true(condition: bool, label: String, failures: Array[String]) -> void:
|
|
if not condition:
|
|
failures.append(label)
|
|
|
|
|
|
func _expect_false(condition: bool, label: String, failures: Array[String]) -> void:
|
|
_expect_true(not condition, label, failures)
|