test(M01): add coordinate golden fixtures
Work-Package: M01-QAR-COORD-FIXTURES-001 Agent: sindo-main-codex
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
extends SceneTree
|
||||
|
||||
## Validates the non-proprietary M01 coordinate fixture against coordinate
|
||||
## contract version 2 and the existing M00 build-12340 manifest. The script does
|
||||
## not read server databases, proprietary ADT bytes or original-client images.
|
||||
|
||||
const CoordinateMapperScript = preload("res://src/domain/coordinates/coordinate_mapper.gd")
|
||||
const CanonicalWowWorldPositionScript = preload("res://src/domain/coordinates/canonical_wow_world_position.gd")
|
||||
const ServerWorldPositionScript = preload("res://src/domain/coordinates/server_world_position.gd")
|
||||
const ServerWorldYawScript = preload("res://src/domain/coordinates/server_world_yaw.gd")
|
||||
|
||||
const FIXTURE_PATH := "res://src/tests/fixtures/coordinate_golden_points.json"
|
||||
const RENDER_MANIFEST_PATH := "res://src/tools/render_baseline_manifest.json"
|
||||
const SUPPORTED_FIXTURE_SCHEMA_VERSION := 1
|
||||
const REQUIRED_COORDINATE_CONTRACT_VERSION := 2
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var failures: Array[String] = []
|
||||
var fixture := _load_json_object(FIXTURE_PATH, failures)
|
||||
var render_manifest := _load_json_object(RENDER_MANIFEST_PATH, failures)
|
||||
if fixture.is_empty() or render_manifest.is_empty():
|
||||
_report_and_quit(failures)
|
||||
return
|
||||
|
||||
_expect_equal(fixture.get("schema_version", -1), SUPPORTED_FIXTURE_SCHEMA_VERSION, "fixture schema version", failures)
|
||||
_expect_equal(fixture.get("coordinate_contract_version", -1), REQUIRED_COORDINATE_CONTRACT_VERSION, "coordinate contract version", failures)
|
||||
var position_tolerance_yards := float(fixture.get("tolerances", {}).get("position_yards", 0.0))
|
||||
var yaw_tolerance_radians := float(fixture.get("tolerances", {}).get("yaw_radians", 0.0))
|
||||
_expect_true(position_tolerance_yards > 0.0, "positive position tolerance", failures)
|
||||
_expect_true(yaw_tolerance_radians > 0.0, "positive yaw tolerance", failures)
|
||||
_verify_source_provenance(fixture.get("sources", {}), failures)
|
||||
_verify_server_spawns(fixture.get("server_spawns", []), position_tolerance_yards, yaw_tolerance_radians, failures)
|
||||
_verify_raw_adt_chunks(fixture.get("raw_adt_chunks", []), position_tolerance_yards, failures)
|
||||
_verify_original_client_points(fixture.get("original_client_points", []), render_manifest, position_tolerance_yards, failures)
|
||||
|
||||
if not failures.is_empty():
|
||||
_report_and_quit(failures)
|
||||
return
|
||||
print("COORDINATE_GOLDEN_FIXTURES PASS server_spawns=1 raw_adt_chunks=1 original_client_points=5")
|
||||
quit(0)
|
||||
|
||||
|
||||
func _verify_source_provenance(sources: Dictionary, failures: Array[String]) -> void:
|
||||
var azerothcore_source: Dictionary = sources.get("azerothcore_playercreateinfo", {})
|
||||
_expect_equal(azerothcore_source.get("status", ""), "verified", "AzerothCore source status", failures)
|
||||
_expect_equal(String(azerothcore_source.get("revision", "")).length(), 40, "AzerothCore pinned revision length", failures)
|
||||
_expect_true(String(azerothcore_source.get("url", "")).contains(String(azerothcore_source.get("revision", ""))), "AzerothCore URL contains pinned revision", failures)
|
||||
|
||||
var trinitycore_source: Dictionary = sources.get("trinitycore_playercreateinfo", {})
|
||||
_expect_equal(trinitycore_source.get("status", ""), "not_verified", "TrinityCore source status", failures)
|
||||
_expect_true(not String(trinitycore_source.get("reason", "")).is_empty(), "TrinityCore gap reason", failures)
|
||||
|
||||
var adt_source: Dictionary = sources.get("azeroth_32_48_adt", {})
|
||||
_expect_equal(adt_source.get("status", ""), "verified_private_metadata", "ADT source status", failures)
|
||||
_expect_equal(String(adt_source.get("sha256", "")).length(), 64, "ADT SHA-256 length", failures)
|
||||
|
||||
|
||||
func _verify_server_spawns(server_spawns: Array, position_tolerance_yards: float, yaw_tolerance_radians: float, failures: Array[String]) -> void:
|
||||
_expect_equal(server_spawns.size(), 1, "server spawn count", failures)
|
||||
for spawn_variant in server_spawns:
|
||||
if not (spawn_variant is Dictionary):
|
||||
failures.append("server spawn must be an object")
|
||||
continue
|
||||
var spawn: Dictionary = spawn_variant
|
||||
var name := String(spawn.get("name", "server_spawn"))
|
||||
var server_position_values := _require_three_numbers(spawn.get("server_position", null), "%s server_position" % name, failures)
|
||||
if server_position_values.is_empty():
|
||||
continue
|
||||
var server_position = ServerWorldPositionScript.new(server_position_values[0], server_position_values[1], server_position_values[2])
|
||||
var canonical_position = CoordinateMapperScript.server_to_canonical(server_position)
|
||||
var godot_position = CoordinateMapperScript.canonical_to_godot(canonical_position)
|
||||
var adt_placement = CoordinateMapperScript.canonical_to_adt_placement(canonical_position)
|
||||
var adt_tile = CoordinateMapperScript.canonical_to_adt_tile(canonical_position)
|
||||
var adt_local = CoordinateMapperScript.canonical_to_adt_tile_local(canonical_position)
|
||||
var adt_chunk = CoordinateMapperScript.adt_tile_local_to_chunk(adt_tile, adt_local)
|
||||
|
||||
_expect_three_near([godot_position.x_units, godot_position.y_units, godot_position.z_units], spawn.get("expected_godot_position", null), position_tolerance_yards, "%s Godot position" % name, failures)
|
||||
_expect_three_near([adt_placement.x_offset_yards, adt_placement.height_yards, adt_placement.z_offset_yards], spawn.get("expected_adt_placement", null), position_tolerance_yards, "%s ADT placement" % name, failures)
|
||||
_expect_three_near([adt_local.east_yards, adt_local.height_yards, adt_local.south_yards], spawn.get("expected_adt_tile_local", null), position_tolerance_yards, "%s ADT local" % name, failures)
|
||||
_expect_pair([adt_tile.tile_x, adt_tile.tile_y], spawn.get("expected_adt_tile", null), "%s ADT tile" % name, failures)
|
||||
_expect_pair([adt_chunk.chunk_x, adt_chunk.chunk_y], spawn.get("expected_adt_chunk", null), "%s ADT chunk" % name, failures)
|
||||
|
||||
var server_yaw = ServerWorldYawScript.new(float(spawn.get("server_yaw_radians", 0.0)))
|
||||
var godot_yaw = CoordinateMapperScript.canonical_to_godot_yaw(CoordinateMapperScript.server_to_canonical_yaw(server_yaw))
|
||||
_expect_near(godot_yaw.radians, float(spawn.get("expected_godot_yaw_radians", 0.0)), yaw_tolerance_radians, "%s Godot yaw" % name, failures)
|
||||
|
||||
|
||||
func _verify_raw_adt_chunks(raw_adt_chunks: Array, position_tolerance_yards: float, failures: Array[String]) -> void:
|
||||
_expect_equal(raw_adt_chunks.size(), 1, "raw ADT chunk count", failures)
|
||||
for chunk_variant in raw_adt_chunks:
|
||||
if not (chunk_variant is Dictionary):
|
||||
failures.append("raw ADT chunk must be an object")
|
||||
continue
|
||||
var chunk: Dictionary = chunk_variant
|
||||
var name := String(chunk.get("name", "raw_adt_chunk"))
|
||||
var origin_values := _require_three_numbers(chunk.get("raw_header_canonical_origin", null), "%s raw origin" % name, failures)
|
||||
if origin_values.is_empty():
|
||||
continue
|
||||
var half_chunk_size_yards := CoordinateMapperScript.ADT_CHUNK_SIZE_YARDS * 0.5
|
||||
var chunk_center = CanonicalWowWorldPositionScript.new(
|
||||
origin_values[0] - half_chunk_size_yards,
|
||||
origin_values[1] - half_chunk_size_yards,
|
||||
origin_values[2]
|
||||
)
|
||||
var tile_coordinate = CoordinateMapperScript.canonical_to_adt_tile(chunk_center)
|
||||
var local_position = CoordinateMapperScript.canonical_to_adt_tile_local(chunk_center)
|
||||
var chunk_coordinate = CoordinateMapperScript.adt_tile_local_to_chunk(tile_coordinate, local_position)
|
||||
_expect_pair([tile_coordinate.tile_x, tile_coordinate.tile_y], chunk.get("adt_tile", null), "%s ADT tile" % name, failures)
|
||||
_expect_pair([chunk_coordinate.chunk_x, chunk_coordinate.chunk_y], chunk.get("mcnk_index", null), "%s MCNK index" % name, failures)
|
||||
var godot_center = CoordinateMapperScript.canonical_to_godot(chunk_center)
|
||||
_expect_three_near([godot_center.x_units, godot_center.y_units, godot_center.z_units], chunk.get("expected_godot_chunk_center", null), position_tolerance_yards, "%s Godot center" % name, failures)
|
||||
|
||||
|
||||
func _verify_original_client_points(original_client_points: Array, render_manifest: Dictionary, position_tolerance_yards: float, failures: Array[String]) -> void:
|
||||
_expect_equal(original_client_points.size(), 5, "original-client point count", failures)
|
||||
var manifest_checkpoint_by_name: Dictionary = {}
|
||||
for checkpoint_variant in render_manifest.get("checkpoints", []):
|
||||
if checkpoint_variant is Dictionary:
|
||||
manifest_checkpoint_by_name[String(checkpoint_variant.get("name", ""))] = checkpoint_variant
|
||||
|
||||
for point_variant in original_client_points:
|
||||
if not (point_variant is Dictionary):
|
||||
failures.append("original-client point must be an object")
|
||||
continue
|
||||
var point: Dictionary = point_variant
|
||||
var name := String(point.get("name", "original_client_point"))
|
||||
var client_position_values := _require_three_numbers(point.get("client_position", null), "%s client position" % name, failures)
|
||||
if client_position_values.is_empty():
|
||||
continue
|
||||
var canonical_position = CanonicalWowWorldPositionScript.new(client_position_values[0], client_position_values[1], client_position_values[2])
|
||||
var godot_position = CoordinateMapperScript.canonical_to_godot(canonical_position)
|
||||
_expect_three_near([godot_position.x_units, godot_position.y_units, godot_position.z_units], point.get("expected_godot_position", null), position_tolerance_yards, "%s mapped position" % name, failures)
|
||||
|
||||
var manifest_checkpoint: Dictionary = manifest_checkpoint_by_name.get(name, {})
|
||||
_expect_true(not manifest_checkpoint.is_empty(), "%s exists in render manifest" % name, failures)
|
||||
if not manifest_checkpoint.is_empty():
|
||||
_expect_three_near(client_position_values, manifest_checkpoint.get("reference_wow_camera", null), position_tolerance_yards, "%s manifest client position" % name, failures)
|
||||
_expect_three_near(point.get("expected_godot_position", null), manifest_checkpoint.get("camera", null), position_tolerance_yards, "%s manifest Godot position" % name, failures)
|
||||
|
||||
|
||||
func _require_three_numbers(value_variant, label: String, failures: Array[String]) -> Array[float]:
|
||||
if not (value_variant is Array) or value_variant.size() != 3:
|
||||
failures.append("%s must contain three numbers" % label)
|
||||
return []
|
||||
return [float(value_variant[0]), float(value_variant[1]), float(value_variant[2])]
|
||||
|
||||
|
||||
func _expect_three_near(actual_values, expected_variant, tolerance: float, label: String, failures: Array[String]) -> void:
|
||||
var expected_values := _require_three_numbers(expected_variant, label, failures)
|
||||
if expected_values.is_empty() or not (actual_values is Array) or actual_values.size() != 3:
|
||||
return
|
||||
for component_index in range(3):
|
||||
_expect_near(float(actual_values[component_index]), expected_values[component_index], tolerance, "%s[%d]" % [label, component_index], failures)
|
||||
|
||||
|
||||
func _expect_pair(actual_values: Array, expected_variant, label: String, failures: Array[String]) -> void:
|
||||
if not (expected_variant is Array) or expected_variant.size() != 2:
|
||||
failures.append("%s must contain two integers" % label)
|
||||
return
|
||||
_expect_equal(actual_values[0], int(expected_variant[0]), "%s[0]" % label, failures)
|
||||
_expect_equal(actual_values[1], int(expected_variant[1]), "%s[1]" % label, failures)
|
||||
|
||||
|
||||
func _expect_near(actual_value: float, expected_value: float, tolerance: float, label: String, failures: Array[String]) -> void:
|
||||
if absf(actual_value - expected_value) > tolerance:
|
||||
failures.append("%s expected %.9f, got %.9f" % [label, expected_value, actual_value])
|
||||
|
||||
|
||||
func _expect_equal(actual_value, expected_value, 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_true(actual_value: bool, label: String, failures: Array[String]) -> void:
|
||||
if not actual_value:
|
||||
failures.append("%s expected true" % label)
|
||||
|
||||
|
||||
func _load_json_object(path: String, failures: Array[String]) -> Dictionary:
|
||||
var file := FileAccess.open(path, FileAccess.READ)
|
||||
if file == null:
|
||||
failures.append("cannot open JSON fixture: %s" % path)
|
||||
return {}
|
||||
var parsed = JSON.parse_string(file.get_as_text())
|
||||
if not (parsed is Dictionary):
|
||||
failures.append("JSON fixture is not an object: %s" % path)
|
||||
return {}
|
||||
return parsed
|
||||
|
||||
|
||||
func _report_and_quit(failures: Array[String]) -> void:
|
||||
if failures.is_empty():
|
||||
failures.append("fixture validation stopped without a diagnostic")
|
||||
for failure in failures:
|
||||
push_error("COORDINATE_GOLDEN_FIXTURES: %s" % failure)
|
||||
quit(1)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cr8f88swvk002
|
||||
Reference in New Issue
Block a user