Files
Ransom-s-wake/tests/unit/run_site_domain_tests.gd
T
2026-10-06 19:45:21 +02:00

101 lines
4.1 KiB
GDScript

extends SceneTree
const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd")
const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd")
const TerrainHeightField = preload("res://game/domain/sites/terrain_height_field.gd")
const TEST_COUNT := 20
var _failures: Array[String] = []
func _init() -> void:
_test_terrain_data_validation()
_test_height_field_sampling()
if _failures.is_empty():
print("Site domain unit tests: %d passed, 0 failed." % TEST_COUNT)
quit(0)
else:
for failure: String in _failures:
push_error(failure)
print("Site domain unit tests: %d failed." % _failures.size())
quit(1)
func _make_terrain(columns: int, rows: int) -> SiteTerrainData:
var data := SiteTerrainData.new()
data.columns = columns
data.rows = rows
data.cell_size_m = 2.0
data.origin_x_m = -10.0
data.origin_z_m = 100.0
# Packed arrays are passed by value in GDScript: build locally, then assign.
var elevations := PackedFloat32Array()
var materials := PackedByteArray()
elevations.resize(columns * rows)
materials.resize(columns * rows)
for row in rows:
for column in columns:
var index := row * columns + column
# Plane sloping down toward +Z (south): -20 at row 0, -2 m per row.
elevations[index] = -20.0 - 2.0 * float(row)
materials[index] = (
SonarMaterial.Value.ROCK if column == 0 else SonarMaterial.Value.SAND
)
data.elevations_m = elevations
data.materials = materials
return data
func _test_terrain_data_validation() -> void:
var data := _make_terrain(3, 4)
_expect(data.validation_error().is_empty(), "regular terrain data is valid")
_expect(data.index_of(2, 3) == 11, "index is row-major with columns along X")
var tiny := _make_terrain(1, 4)
_expect(not tiny.validation_error().is_empty(), "terrain needs at least two columns")
var mismatched := _make_terrain(3, 4)
mismatched.elevations_m.resize(5)
_expect(not mismatched.validation_error().is_empty(), "elevation array must match the grid")
var non_finite := _make_terrain(3, 4)
non_finite.elevations_m[0] = INF
_expect(not non_finite.validation_error().is_empty(), "elevations must be finite")
var metal_terrain := _make_terrain(3, 4)
metal_terrain.materials[1] = SonarMaterial.Value.METAL
_expect(not metal_terrain.validation_error().is_empty(), "terrain cells are rock or sand only")
var bad_cell := _make_terrain(3, 4)
bad_cell.cell_size_m = 0.0
_expect(not bad_cell.validation_error().is_empty(), "cell size must be positive")
var bad_reflectivity := _make_terrain(3, 4)
bad_reflectivity.sand_reflectivity = 1.5
_expect(not bad_reflectivity.validation_error().is_empty(), "reflectivity stays within 0..1")
_expect(TerrainHeightField.from_data(bad_reflectivity) == null, "invalid data yields no field")
func _test_height_field_sampling() -> void:
var field := TerrainHeightField.from_data(_make_terrain(3, 4))
_expect(field != null, "valid data yields a field")
if field == null:
return
_expect(field.bounds_m().is_equal_approx(Rect2(-10.0, 100.0, 4.0, 6.0)), "bounds cover (columns-1) by (rows-1) cells")
_expect(field.cell_world_position(2, 3).is_equal_approx(Vector3(-6.0, -26.0, 106.0)), "cell position follows origin and cell size")
_expect(is_equal_approx(field.elevation_at(-10.0, 100.0), -20.0), "elevation at a vertex is exact")
_expect(is_equal_approx(field.elevation_at(-9.0, 101.0), -21.0), "elevation is bilinear between vertices")
_expect(is_equal_approx(field.elevation_at(-50.0, 1000.0), -26.0), "sampling outside clamps to the nearest edge")
_expect(field.material_at(-10.4, 100.0) == SonarMaterial.Value.ROCK, "material is the nearest cell")
_expect(field.material_at(-6.0, 103.0) == SonarMaterial.Value.SAND, "material of an interior cell")
_expect(field.normal_at_cell(1, 1).is_equal_approx(Vector3(0.0, 1.0, 1.0).normalized()), "normal leans against the downhill direction")
_expect(not field.contains(-11.0, 100.0) and field.contains(-8.0, 103.0), "contains follows the bounds")
_expect(is_equal_approx(field.reflectivity_for(SonarMaterial.Value.SAND), 0.45), "sand reflectivity comes from the data")
func _expect(condition: bool, label: String) -> void:
if not condition:
_failures.append(label)