Files
open-wc/src/tools/verify_adt_water_scene_finalizer.gd
T

195 lines
6.2 KiB
GDScript

extends SceneTree
## Synthetic main-thread scene, ownership, boundary and timing regression for
## ADT water finalization.
const FINALIZER_SCRIPT := preload("res://src/render/liquid/adt_water_scene_finalizer.gd")
const FINALIZER_PATH := "res://src/render/liquid/adt_water_scene_finalizer.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
func _initialize() -> void:
var failures: Array[String] = []
_verify_invalid_and_dry_input(failures)
_verify_build_attach_geometry_and_owner(failures)
_verify_ownership_boundaries(failures)
var elapsed_milliseconds := _verify_bounded_timing(failures)
if not failures.is_empty():
for failure in failures:
push_error("ADT_WATER_SCENE_FINALIZER: %s" % failure)
quit(1)
return
print(
"ADT_WATER_SCENE_FINALIZER PASS cases=8 iterations=100 elapsed_ms=%.3f"
% elapsed_milliseconds
)
quit(0)
func _verify_invalid_and_dry_input(failures: Array[String]) -> void:
var finalizer: RefCounted = FINALIZER_SCRIPT.new()
var tile_root := Node3D.new()
_expect_null(
finalizer.call("attach_water_scene", {}, Vector3.ZERO, tile_root, null),
"empty water data returns null",
failures
)
_expect_equal(tile_root.get_child_count(), 0, "empty data does not mutate tile", failures)
_expect_null(
finalizer.call("attach_water_scene", _synthetic_water_data(), Vector3.ZERO, null, null),
"missing tile root returns null",
failures
)
_expect_null(
finalizer.call(
"attach_water_scene",
{"chunks": [{"origin": Vector3.ZERO, "liquids": []}]},
Vector3.ZERO,
tile_root,
null
),
"dry parsed ADT returns null",
failures
)
_expect_equal(tile_root.get_child_count(), 0, "dry data does not mutate tile", failures)
tile_root.free()
func _verify_build_attach_geometry_and_owner(failures: Array[String]) -> void:
var finalizer: RefCounted = FINALIZER_SCRIPT.new()
var editor_scene_root := Node3D.new()
var tile_root := Node3D.new()
get_root().add_child(editor_scene_root)
editor_scene_root.add_child(tile_root)
var tile_origin := Vector3(10.0, 20.0, 30.0)
var water_root: Node3D = finalizer.call(
"attach_water_scene",
_synthetic_water_data(tile_origin),
tile_origin,
tile_root,
editor_scene_root
)
_expect_not_null(water_root, "synthetic liquid builds a water root", failures)
if water_root != null:
_expect_true(water_root.get_parent() == tile_root, "water root attached once", failures)
_expect_equal(tile_root.get_child_count(), 1, "tile owns one water root", failures)
_expect_true(water_root.owner == editor_scene_root, "water root editor owner", failures)
_expect_equal(water_root.get_child_count(), 1, "one liquid surface grouped", failures)
if water_root.get_child_count() == 1:
var liquid_mesh_instance := water_root.get_child(0) as MeshInstance3D
_expect_not_null(liquid_mesh_instance, "liquid child is MeshInstance3D", failures)
if liquid_mesh_instance != null:
_expect_true(
liquid_mesh_instance.owner == editor_scene_root,
"liquid child editor owner",
failures
)
var liquid_mesh := liquid_mesh_instance.mesh as ArrayMesh
_expect_not_null(liquid_mesh, "liquid surface has ArrayMesh", failures)
if liquid_mesh != null:
_expect_equal(
liquid_mesh.surface_get_array_len(0),
4,
"one enabled liquid cell has four vertices",
failures
)
_expect_equal(
liquid_mesh.surface_get_array_index_len(0),
6,
"one enabled liquid cell has six indices",
failures
)
editor_scene_root.free()
func _verify_ownership_boundaries(failures: Array[String]) -> void:
var finalizer_source := FileAccess.get_file_as_string(FINALIZER_PATH)
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
_expect_true(
finalizer_source.contains("ADT_BUILDER_SCRIPT.build_tile_water_scene"),
"finalizer retains existing ADTBuilder implementation",
failures
)
_expect_true(
finalizer_source.contains("tile_root.add_child(water_root)"),
"finalizer owns water subtree attachment",
failures
)
_expect_equal(
loader_source.count("_adt_water_scene_finalizer.attach_water_scene("),
2,
"both loader water paths delegate finalization",
failures
)
_expect_false(
loader_source.contains("_builder.build_tile_water_scene"),
"loader no longer builds water scenes directly",
failures
)
_expect_true(
loader_source.contains("state[\"water_loaded\"] = true"),
"loader retains water-loaded timing",
failures
)
_expect_true(
loader_source.contains("WATER_FINALIZE"),
"loader retains finalization permit ownership",
failures
)
func _verify_bounded_timing(failures: Array[String]) -> float:
var finalizer: RefCounted = FINALIZER_SCRIPT.new()
var tile_root := Node3D.new()
var started_microseconds := Time.get_ticks_usec()
for _iteration in range(100):
finalizer.call("attach_water_scene", {}, Vector3.ZERO, tile_root, null)
var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
_expect_true(elapsed_milliseconds < 1000.0, "100 empty attempts under 1 second", failures)
_expect_equal(tile_root.get_child_count(), 0, "timing loop creates no nodes", failures)
tile_root.free()
return elapsed_milliseconds
func _synthetic_water_data(tile_origin: Vector3 = Vector3.ZERO) -> Dictionary:
var liquid_mask := PackedByteArray()
liquid_mask.resize(64)
liquid_mask.fill(0)
liquid_mask[0] = 1
var liquid_heights := PackedFloat32Array()
liquid_heights.resize(81)
liquid_heights.fill(tile_origin.y + 5.0)
return {
"chunks": [{
"origin": tile_origin,
"liquids": [{
"liquid_id": 7,
"mask": liquid_mask,
"heights": liquid_heights,
}],
}],
}
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)
func _expect_null(value: Variant, label: String, failures: Array[String]) -> void:
_expect_true(value == null, label, failures)
func _expect_not_null(value: Variant, label: String, failures: Array[String]) -> void:
_expect_true(value != null, label, failures)
func _expect_equal(actual: int, expected: int, label: String, failures: Array[String]) -> void:
if actual != expected:
failures.append("%s expected=%d actual=%d" % [label, expected, actual])