142 lines
4.8 KiB
GDScript
142 lines
4.8 KiB
GDScript
extends SceneTree
|
|
|
|
const SurvivalModelScript = preload("res://src/survival_model.gd")
|
|
const SonarModelScript = preload("res://src/sonar_model.gd")
|
|
|
|
var _failures: Array[String] = []
|
|
|
|
|
|
func _init() -> void:
|
|
_test_oxygen_drains_while_idle()
|
|
_test_battery_only_drains_under_thrust()
|
|
_test_nominal_depth_is_safe()
|
|
_test_excess_depth_damages_hull()
|
|
_test_collision_damage_is_applied()
|
|
_test_battery_depletion_is_not_immediate_death()
|
|
_test_alert_thresholds()
|
|
_test_return_estimate()
|
|
_test_critical_debug_state()
|
|
_test_sonar_deduplicates_nearby_echoes()
|
|
_test_sonar_keeps_strongest_echo()
|
|
_test_sonar_intensity_falls_with_distance()
|
|
_test_sonar_does_not_reveal_unobserved_space()
|
|
|
|
if _failures.is_empty():
|
|
print("Prototype tests: 13 passed, 0 failed.")
|
|
quit(0)
|
|
else:
|
|
for failure in _failures:
|
|
push_error(failure)
|
|
print("POC A tests: %d failed." % _failures.size())
|
|
quit(1)
|
|
|
|
|
|
func _test_oxygen_drains_while_idle() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.tick(10.0, 0.0, 12.0)
|
|
_expect_near(model.oxygen, 590.0, 0.001, "oxygen drains while idle")
|
|
|
|
|
|
func _test_battery_only_drains_under_thrust() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.tick(10.0, 0.0, 12.0)
|
|
_expect_near(model.battery, 100.0, 0.001, "battery remains stable at idle")
|
|
model.tick(10.0, 1.0, 12.0)
|
|
_expect_near(model.battery, 94.0, 0.001, "battery drains under full thrust")
|
|
|
|
|
|
func _test_nominal_depth_is_safe() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.tick(30.0, 0.0, 60.0)
|
|
_expect_near(model.hull, 100.0, 0.001, "nominal depth does not damage hull")
|
|
|
|
|
|
func _test_excess_depth_damages_hull() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.tick(2.0, 0.0, 74.0)
|
|
_expect(model.hull < 100.0, "depth above critical damages hull")
|
|
|
|
|
|
func _test_collision_damage_is_applied() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.tick(0.1, 0.0, 12.0, 12.0)
|
|
_expect_near(model.hull, 88.0, 0.001, "collision damage is applied exactly once")
|
|
|
|
|
|
func _test_battery_depletion_is_not_immediate_death() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.battery = 0.0
|
|
_expect(model.loss_reason().is_empty(), "empty battery is not immediate death")
|
|
|
|
|
|
func _test_alert_thresholds() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
_expect(
|
|
model.alert_level(30.0, 100.0) == SurvivalModel.AlertLevel.CAUTION,
|
|
"30 percent is caution"
|
|
)
|
|
_expect(
|
|
model.alert_level(15.0, 100.0) == SurvivalModel.AlertLevel.RETURN,
|
|
"15 percent recommends return"
|
|
)
|
|
_expect(
|
|
model.alert_level(5.0, 100.0) == SurvivalModel.AlertLevel.CRITICAL,
|
|
"5 percent is critical"
|
|
)
|
|
|
|
|
|
func _test_return_estimate() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
var short_trip: float = model.estimated_return_energy(100.0, 0.0)
|
|
var long_trip: float = model.estimated_return_energy(300.0, 20.0)
|
|
_expect(long_trip > short_trip, "return estimate grows with distance and ascent")
|
|
|
|
|
|
func _test_critical_debug_state() -> void:
|
|
var model = SurvivalModelScript.new()
|
|
model.force_critical_state()
|
|
_expect(model.oxygen == 30.0, "critical debug state lowers oxygen")
|
|
_expect(model.battery == 12.0, "critical debug state lowers battery")
|
|
_expect(model.hull == 28.0, "critical debug state lowers hull")
|
|
|
|
|
|
func _test_sonar_deduplicates_nearby_echoes() -> void:
|
|
var model = SonarModelScript.new()
|
|
model.record_echo(Vector3(1.0, 2.0, 3.0), 0.7, SonarModelScript.MaterialCategory.ROCK)
|
|
model.record_echo(Vector3(1.1, 2.1, 3.1), 0.5, SonarModelScript.MaterialCategory.ROCK)
|
|
_expect(model.observed_point_count() == 1, "nearby sonar echoes share one spatial cell")
|
|
|
|
|
|
func _test_sonar_keeps_strongest_echo() -> void:
|
|
var model = SonarModelScript.new()
|
|
model.record_echo(Vector3.ZERO, 0.25, SonarModelScript.MaterialCategory.ROCK)
|
|
model.record_echo(Vector3(0.1, 0.1, 0.1), 0.85, SonarModelScript.MaterialCategory.METAL)
|
|
var point: Dictionary = model.observed_points()[0]
|
|
_expect_near(float(point["intensity"]), 0.85, 0.001, "stronger sonar echo replaces weak echo")
|
|
_expect(
|
|
int(point["material"]) == SonarModelScript.MaterialCategory.METAL,
|
|
"stronger sonar echo updates material"
|
|
)
|
|
|
|
|
|
func _test_sonar_intensity_falls_with_distance() -> void:
|
|
var model = SonarModelScript.new()
|
|
var near_echo: float = model.calculate_intensity(10.0, 100.0, 1.0, 1.0)
|
|
var far_echo: float = model.calculate_intensity(80.0, 100.0, 1.0, 1.0)
|
|
_expect(near_echo > far_echo, "sonar intensity falls with distance")
|
|
|
|
|
|
func _test_sonar_does_not_reveal_unobserved_space() -> void:
|
|
var model = SonarModelScript.new()
|
|
_expect(model.observed_points().is_empty(), "unobserved sonar space starts empty")
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
if not condition:
|
|
_failures.append(label)
|
|
|
|
|
|
func _expect_near(actual: float, expected: float, tolerance: float, label: String) -> void:
|
|
if absf(actual - expected) > tolerance:
|
|
_failures.append("%s: expected %.3f, got %.3f" % [label, expected, actual])
|