extends SceneTree ## Asset-free contract, dependency and timing regression for WMO placements. const RESOLVER_SCRIPT := preload("res://src/render/wmo/wmo_placement_resolver.gd") const RESOLVER_PATH := "res://src/render/wmo/wmo_placement_resolver.gd" const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd" func _initialize() -> void: var failures: Array[String] = [] _verify_path_normalization(failures) _verify_positive_unique_id(failures) _verify_synthetic_unique_keys(failures) _verify_default_transform(failures) _verify_position_rotation_and_scale(failures) _verify_unclamped_scale(failures) _verify_node_property_equivalence(failures) _verify_source_boundaries(failures) _verify_fresh_stateless_results(failures) var elapsed_milliseconds := _verify_bounded_timing(failures) if not failures.is_empty(): for failure in failures: push_error("WMO_PLACEMENT_RESOLVER: %s" % failure) quit(1) return print( "WMO_PLACEMENT_RESOLVER PASS cases=9 iterations=20000 elapsed_ms=%.3f" % elapsed_milliseconds ) quit(0) func _verify_path_normalization(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() _expect_equal_string( resolver.normalize_relative_path("World\\WMO\\Stormwind\\Keep.WMO"), "world/wmo/stormwind/keep.wmo", "slash and case normalization", failures ) _expect_equal_string(resolver.normalize_relative_path(""), "", "empty path", failures) func _verify_positive_unique_id(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() _expect_equal_string( resolver.resolve_unique_key({"unique_id": 11785}, "31:49", 6), "uid:11785", "positive unique ID", failures ) func _verify_synthetic_unique_keys(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() for placement in [{}, {"unique_id": 0}, {"unique_id": -4}]: _expect_equal_string( resolver.resolve_unique_key(placement, "31:49", 6), "tile:31:49:6", "synthetic unique key", failures ) func _verify_default_transform(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() var transform: Transform3D = resolver.resolve_world_transform({}) _expect_basis_near(transform.basis, Basis.IDENTITY, "default basis", failures) _expect_vector_near(transform.origin, Vector3.ZERO, "default origin", failures) func _verify_position_rotation_and_scale(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() var position := Vector3(10.0, 20.0, 30.0) var rotation := Vector3(0.2, -0.4, 0.6) var transform: Transform3D = resolver.resolve_world_transform({ "pos": position, "rot": rotation, "scale": 2.5, }) _expect_basis_near( transform.basis, Basis.from_euler(rotation).scaled(Vector3.ONE * 2.5), "rotated scaled basis", failures ) _expect_vector_near(transform.origin, position, "world origin", failures) func _verify_unclamped_scale(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() var zero_transform: Transform3D = resolver.resolve_world_transform({"scale": 0.0}) _expect_basis_near( zero_transform.basis, Basis.IDENTITY.scaled(Vector3.ZERO), "zero scale", failures ) var negative_transform: Transform3D = resolver.resolve_world_transform({"scale": -2.0}) _expect_basis_near( negative_transform.basis, Basis.IDENTITY.scaled(Vector3.ONE * -2.0), "negative scale", failures ) func _verify_node_property_equivalence(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() for scale_value in [2.5, 0.0, -2.0]: var placement := { "pos": Vector3(10.0, 20.0, 30.0), "rot": Vector3(0.2, -0.4, 0.6), "scale": scale_value, } var historical_node := Node3D.new() historical_node.position = placement["pos"] historical_node.rotation = placement["rot"] historical_node.scale = Vector3.ONE * scale_value var historical_transform := historical_node.transform historical_node.free() var resolved_transform: Transform3D = resolver.resolve_world_transform(placement) _expect_basis_near( resolved_transform.basis, historical_transform.basis, "Node3D property equivalence scale %s" % scale_value, failures ) _expect_vector_near( resolved_transform.origin, historical_transform.origin, "Node3D origin equivalence scale %s" % scale_value, failures ) func _verify_source_boundaries(failures: Array[String]) -> void: var loader_source := _read_text(LOADER_PATH, failures) var resolver_source := _read_text(RESOLVER_PATH, failures) _expect_true( loader_source.contains("WMO_PLACEMENT_RESOLVER_SCRIPT.new()"), "loader composes resolver", failures ) _expect_equal_int( loader_source.count("_wmo_placement_resolver.normalize_relative_path("), 3, "three normalization adapters", failures ) _expect_equal_int( loader_source.count("_wmo_placement_resolver.resolve_unique_key("), 1, "one identity adapter", failures ) _expect_equal_int( loader_source.count("_wmo_placement_resolver.resolve_world_transform("), 3, "three transform adapters", failures ) _expect_true( not loader_source.contains("func _normalize_wmo_rel_path("), "loader omits normalization formula", failures ) _expect_true( not loader_source.contains("func _wmo_unique_key("), "loader omits identity formula", failures ) for forbidden_dependency in [ "Node", "ResourceLoader", "RenderingServer", "WorkerThreadPool", "Mutex", "FileAccess", ]: _expect_true( not resolver_source.contains(forbidden_dependency), "resolver omits %s" % forbidden_dependency, failures ) func _verify_fresh_stateless_results(failures: Array[String]) -> void: var resolver := RESOLVER_SCRIPT.new() var placement := {"pos": Vector3(1.0, 2.0, 3.0), "scale": 4.0} var first: Transform3D = resolver.resolve_world_transform(placement) first.origin = Vector3.ZERO var second: Transform3D = resolver.resolve_world_transform(placement) _expect_vector_near(second.origin, Vector3(1.0, 2.0, 3.0), "stateless result", failures) func _verify_bounded_timing(failures: Array[String]) -> float: var resolver := RESOLVER_SCRIPT.new() var placement := { "unique_id": 11785, "pos": Vector3(10.0, 20.0, 30.0), "rot": Vector3(0.1, 0.2, 0.3), "scale": 1.5, } var started_microseconds := Time.get_ticks_usec() for iteration in range(20000): resolver.normalize_relative_path("World\\WMO\\Stormwind\\Keep.WMO") resolver.resolve_unique_key(placement, "31:49", iteration) resolver.resolve_world_transform(placement) var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0 _expect_true(elapsed_milliseconds < 1000.0, "20000 placement triples 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_basis_near( actual_basis: Basis, expected_basis: Basis, label: String, failures: Array[String] ) -> void: if not actual_basis.is_equal_approx(expected_basis): failures.append("%s basis mismatch" % label) func _expect_vector_near( actual_value: Vector3, expected_value: Vector3, label: String, failures: Array[String] ) -> void: if not actual_value.is_equal_approx(expected_value): failures.append("%s expected %s, got %s" % [label, expected_value, actual_value]) func _expect_equal_string( 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_equal_int( 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_true(actual_value: bool, label: String, failures: Array[String]) -> void: if not actual_value: failures.append("%s expected true" % label)