test(M01): add coordinate golden fixtures
Work-Package: M01-QAR-COORD-FIXTURES-001 Agent: sindo-main-codex
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002` |
|
||||
| Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002`, `M01-QAR-COORD-FIXTURES-001` |
|
||||
| Owners | Foundation/domain |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m01-coordinate-tile-axis-fix`, 2026-07-13 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m01-coordinate-fixtures`, 2026-07-13 |
|
||||
| Profiles/capabilities | All profiles; contract version 2 |
|
||||
|
||||
## Purpose
|
||||
@@ -85,6 +85,7 @@ Forbidden dependencies:
|
||||
| Output | `CanonicalWowWorldPosition` | `CoordinateMapper` | Gameplay/content/adapters | New immutable value | Caller-owned reference |
|
||||
| Output | `AdtTileCoordinate`, `AdtTileLocalPosition`, `AdtChunkCoordinate` | `CoordinateMapper` | Streaming/content adapters | New immutable values | Caller-owned references |
|
||||
| Output | `GodotWorldPosition`, `GodotWorldYaw` | `CoordinateMapper` | Future render/scene adapter | New immutable values | Caller-owned references |
|
||||
| Test input | `coordinate_golden_points.json` schema 1 | Pinned public server row plus private numeric observations | Headless fixture verifier | Versioned repository fixture | Test-process lifetime |
|
||||
|
||||
Side effects:
|
||||
|
||||
@@ -95,6 +96,10 @@ Side effects:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
ServerFixture[Pinned AzerothCore spawn] --> FixtureVerifier[Golden fixture verifier]
|
||||
RawADTFixture[Raw ADT numeric metadata] --> FixtureVerifier
|
||||
ClientFixture[Build 12340 observations] --> FixtureVerifier
|
||||
FixtureVerifier --> Mapper
|
||||
Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition]
|
||||
ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical
|
||||
Canonical -->|tileX from Y; tileY from X| Tile[AdtTileCoordinate]
|
||||
@@ -145,8 +150,11 @@ There is no cancellation because every conversion is constant-time scalar math.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
This package adds no persisted format, cache or migration. Existing dictionaries,
|
||||
native `Vector3` payloads and renderer conversion helpers remain compatible.
|
||||
Production code adds no persisted format, cache or migration. Test schema 1 in
|
||||
`coordinate_golden_points.json` persists explicit source provenance, coordinate
|
||||
contract version 2, tolerances, source-space values and expected outputs. It
|
||||
contains no proprietary bytes, database dump or original-client image. Existing
|
||||
dictionaries, native `Vector3` payloads and renderer conversion helpers remain compatible.
|
||||
Consumer migration is ordered after this contract and must remove old manual
|
||||
conversions only after its producer/consumer tests pass.
|
||||
|
||||
@@ -163,9 +171,12 @@ raw three-number arrays are not an accepted new persistence contract.
|
||||
## Verification
|
||||
|
||||
- Unit/contract tests: `src/tools/verify_coordinate_mapper.gd`.
|
||||
- Cross-source golden test: `src/tools/verify_coordinate_golden_fixtures.gd`.
|
||||
- Integration/E2E: consumer migration and `StreamingFocus` packages remain pending.
|
||||
- Fidelity evidence: five build-12340 camera positions from the M00 manifest map
|
||||
within `0.002` yard; independent reference formulas are recorded in ADR 0001.
|
||||
within `0.002` yard; a pinned AzerothCore human-warrior spawn and one raw MCNK
|
||||
header observation independently confirm tile `(32,48)` and chunk `(3,12)`;
|
||||
independent reference formulas are recorded in ADR 0001.
|
||||
- Performance budgets: constant-time arithmetic; no scene or file access.
|
||||
- Manual diagnostics: not required for the pure contract; renderer checkpoints
|
||||
are required when consumers migrate.
|
||||
@@ -186,7 +197,9 @@ raw three-number arrays are not an accepted new persistence contract.
|
||||
| ADT tile/local/chunk ownership | Implemented | Center/extent boundaries plus raw `Azeroth_31_49.adt` filename and MCNK direction evidence | Migrate streaming consumers |
|
||||
| World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate |
|
||||
| Scene-free production contract | Implemented | Source boundary and headless test | Add dependency search gate later in M01 |
|
||||
| Visual/server-spawn integration | Planned | M01 target criteria | Add golden server spawn after adapter migration |
|
||||
| Cross-source coordinate fixture | Implemented | Pinned AzerothCore spawn, raw ADT MCNK metadata and five build-12340 points pass one headless verifier | Add live adapter/renderer integration |
|
||||
| TrinityCore populated spawn equivalence | Not verified | The base development schema has no pinned populated row used by this fixture | Pin and verify a TDB snapshot before claiming parity |
|
||||
| Visual/server-spawn integration | Planned | M01 target criteria | Route the fixture through migrated adapters and renderer |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
@@ -197,6 +210,8 @@ raw three-number arrays are not an accepted new persistence contract.
|
||||
this package deliberately does not generalize them into world yaw.
|
||||
- Finite-number validation belongs to external format adapters and is not yet implemented.
|
||||
- Arithmetic agreement does not prove terrain, placement, camera direction or visual parity.
|
||||
- The golden server row is verified against pinned AzerothCore data only; no
|
||||
TrinityCore equivalence claim is made without a pinned populated TDB snapshot.
|
||||
|
||||
## Source map
|
||||
|
||||
@@ -208,6 +223,8 @@ raw three-number arrays are not an accepted new persistence contract.
|
||||
| `src/domain/coordinates/adt_chunk_coordinate.gd` | MCNK indices and bounds |
|
||||
| `src/domain/coordinates/*_yaw.gd` | Immutable typed world-facing yaw values |
|
||||
| `src/tools/verify_coordinate_mapper.gd` | Headless fixtures and boundary regressions |
|
||||
| `src/tests/fixtures/coordinate_golden_points.json` | Versioned cross-source values, provenance and tolerances |
|
||||
| `src/tools/verify_coordinate_golden_fixtures.gd` | Cross-source fixture schema and mapper validation |
|
||||
| `docs/adr/0001-canonical-world-coordinates.md` | Normative axes, units, boundaries and rollout decision |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"coordinate_contract_version": 2,
|
||||
"tolerances": {
|
||||
"position_yards": 0.002,
|
||||
"yaw_radians": 0.000001
|
||||
},
|
||||
"sources": {
|
||||
"azerothcore_playercreateinfo": {
|
||||
"status": "verified",
|
||||
"repository": "azerothcore/database-wotlk",
|
||||
"revision": "68fcf0098b16388093989627f37be530fc91f07d",
|
||||
"path": "sql/base/playercreateinfo.sql",
|
||||
"url": "https://raw.githubusercontent.com/azerothcore/database-wotlk/68fcf0098b16388093989627f37be530fc91f07d/sql/base/playercreateinfo.sql"
|
||||
},
|
||||
"trinitycore_playercreateinfo": {
|
||||
"status": "not_verified",
|
||||
"repository": "TrinityCore/TrinityCore",
|
||||
"branch": "3.3.5",
|
||||
"reason": "The base development schema does not provide a pinned populated playercreateinfo row; verify against a pinned TDB snapshot before claiming equivalence."
|
||||
},
|
||||
"azeroth_32_48_adt": {
|
||||
"status": "verified_private_metadata",
|
||||
"file_name": "Azeroth_32_48.adt",
|
||||
"sha256": "D66E27FD82B461AE38B78562952AED428E619777A3F3E35DD8FF97444644770A",
|
||||
"distribution": "Numeric metadata only; proprietary file remains outside Git."
|
||||
},
|
||||
"original_client_build_12340": {
|
||||
"status": "verified_private_observation",
|
||||
"build": 12340,
|
||||
"manifest": "res://src/tools/render_baseline_manifest.json",
|
||||
"evidence_document": "docs/M00_FINAL_PAIRED_EVIDENCE.md"
|
||||
}
|
||||
},
|
||||
"server_spawns": [
|
||||
{
|
||||
"name": "azerothcore_human_warrior_start",
|
||||
"source": "azerothcore_playercreateinfo",
|
||||
"backend": "AzerothCore",
|
||||
"table": "playercreateinfo",
|
||||
"key": {"race": 1, "class": 1},
|
||||
"map_id": 0,
|
||||
"map_directory": "Azeroth",
|
||||
"zone_id": 12,
|
||||
"server_position": [-8949.95, -132.493, 83.5312],
|
||||
"server_yaw_radians": 0.0,
|
||||
"expected_adt_placement": [26016.616666667, 83.5312, 17199.159666667],
|
||||
"expected_adt_tile": [32, 48],
|
||||
"expected_adt_chunk": [3, 12],
|
||||
"expected_adt_tile_local": [132.493, 83.5312, 416.616666667],
|
||||
"expected_godot_position": [17199.159666667, 83.5312, 26016.616666667],
|
||||
"expected_godot_yaw_radians": 0.0
|
||||
}
|
||||
],
|
||||
"raw_adt_chunks": [
|
||||
{
|
||||
"name": "azeroth_32_48_mcnk_3_12",
|
||||
"source": "azeroth_32_48_adt",
|
||||
"map_directory": "Azeroth",
|
||||
"adt_tile": [32, 48],
|
||||
"mcnk_index": [3, 12],
|
||||
"raw_header_canonical_origin": [-8933.33203125, -100.0, 84.238929749],
|
||||
"sample_position": "chunk_center",
|
||||
"expected_godot_chunk_center": [17183.333333333, 84.238929749, 26016.665364583]
|
||||
}
|
||||
],
|
||||
"original_client_points": [
|
||||
{
|
||||
"name": "elwynn_terrain_overview",
|
||||
"source": "original_client_build_12340",
|
||||
"client_position": [-9153.334, 386.666, 180.0],
|
||||
"expected_godot_position": [16680.0, 180.0, 26220.0]
|
||||
},
|
||||
{
|
||||
"name": "elwynn_adt_boundary",
|
||||
"source": "original_client_build_12340",
|
||||
"client_position": [-9523.334, 46.666, 105.0],
|
||||
"expected_godot_position": [17020.0, 105.0, 26590.0]
|
||||
},
|
||||
{
|
||||
"name": "goldshire_dense_m2",
|
||||
"source": "original_client_build_12340",
|
||||
"client_position": [-9400.0, 110.0, 150.0],
|
||||
"expected_godot_position": [16956.666, 150.0, 26466.666]
|
||||
},
|
||||
{
|
||||
"name": "goldshire_inn_large_wmo",
|
||||
"source": "original_client_build_12340",
|
||||
"client_position": [-9385.0, 53.0, 72.0],
|
||||
"expected_godot_position": [17013.666, 72.0, 26451.666]
|
||||
},
|
||||
{
|
||||
"name": "elwynn_waterfall_liquid",
|
||||
"source": "original_client_build_12340",
|
||||
"client_position": [-9300.0, 585.0, 190.0],
|
||||
"expected_godot_position": [16481.666, 190.0, 26366.666]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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