189 lines
6.0 KiB
GDScript
189 lines
6.0 KiB
GDScript
extends SceneTree
|
|
|
|
const SimulationManagerScript := preload("res://simulation/SimulationManager.gd")
|
|
const BenchmarkScript := preload("res://simulation/benchmark/SimulationScalingBenchmark.gd")
|
|
|
|
|
|
class CoverageDroppingManager:
|
|
extends "res://simulation/SimulationManager.gd"
|
|
|
|
var drop_after_simulation_ticks := -1
|
|
var simulation_ticks_seen := 0
|
|
var removed_combatant_id: StringName = &""
|
|
|
|
func simulate_tick() -> void:
|
|
super()
|
|
simulation_ticks_seen += 1
|
|
if simulation_ticks_seen != drop_after_simulation_ticks or npcs.is_empty():
|
|
return
|
|
removed_combatant_id = SimulationIds.npc_combatant_id(npcs[-1].id)
|
|
conflict_system.combatants.erase(removed_combatant_id)
|
|
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var first := _run_case(9001)
|
|
var repeated := _run_case(9001)
|
|
var different_seed := _run_case(9002)
|
|
_check(not first.is_empty(), "The benchmark fixture should produce one result")
|
|
if first.is_empty():
|
|
_finish()
|
|
return
|
|
_check(first["fixture_valid"], "The prepared benchmark state should pass schema validation")
|
|
_check(
|
|
(
|
|
int(first["schema_version"]) == 2
|
|
and SimulationScalingBenchmark.SCHEMA_VERSION == 2
|
|
and (
|
|
int(first["simulation_state_schema_version"])
|
|
== SimulationStateRecord.SCHEMA_VERSION
|
|
)
|
|
and StringName(first["workload_id"]) == &"full_fidelity_combatant_headless_arrival_v2"
|
|
and (
|
|
SimulationScalingBenchmark.WORKLOAD_ID
|
|
== &"full_fidelity_combatant_headless_arrival_v2"
|
|
)
|
|
and int(first["population"]) == 12
|
|
and int(first["npc_combatant_count"]) == 12
|
|
and bool(first["npc_combatant_coverage_valid"])
|
|
and int(first["fixture_npc_combatant_count"]) == 12
|
|
and bool(first["fixture_combatants_valid"])
|
|
and int(first["history_seed_events"]) == 24
|
|
and int(first["measured_ticks"]) == 8
|
|
and int(first["npc_updates"]) == 96
|
|
and is_equal_approx(float(first["tick_interval"]), 1.2)
|
|
and int(first["end_tick"]) - int(first["start_tick"]) == 8
|
|
),
|
|
"The result should identify the exact workload and measured update count"
|
|
)
|
|
_check(
|
|
(
|
|
int(first["elapsed_usec"]) > 0
|
|
and int(first["simulation_usec"]) > 0
|
|
and int(first["arrival_usec"]) > 0
|
|
and int(first["start_state_bytes"]) > 0
|
|
and int(first["end_state_bytes"]) >= int(first["start_state_bytes"])
|
|
and int(first["arrivals_processed"]) > 0
|
|
and int(first["events_recorded"]) > 0
|
|
and int(first["end_event_count"]) >= 24
|
|
and String(first["final_checksum"]).length() == 64
|
|
),
|
|
"The benchmark should report timing, state growth, updates, history, and a checksum"
|
|
)
|
|
for deterministic_key in [
|
|
"final_checksum",
|
|
"start_state_bytes",
|
|
"end_state_bytes",
|
|
"state_growth_bytes",
|
|
"start_event_count",
|
|
"end_event_count",
|
|
"events_recorded",
|
|
"start_known_reference_count",
|
|
"end_known_reference_count",
|
|
"arrivals_processed",
|
|
"warmup_arrivals",
|
|
"tick_interval",
|
|
"npc_combatant_count",
|
|
"npc_combatant_coverage_valid",
|
|
]:
|
|
_check(
|
|
first[deterministic_key] == repeated[deterministic_key],
|
|
"Repeated benchmark runs should preserve %s" % deterministic_key
|
|
)
|
|
_check(
|
|
first["final_checksum"] != different_seed["final_checksum"],
|
|
"A different fixture seed should produce a different deterministic checksum"
|
|
)
|
|
_check(
|
|
_coverage_loss_is_rejected(2, 3, 2),
|
|
"Coverage lost on the final warmup tick should reject the benchmark result"
|
|
)
|
|
_check(
|
|
_coverage_loss_is_rejected(2, 3, 5),
|
|
"Coverage lost on the final measured tick should reject the benchmark result"
|
|
)
|
|
_finish()
|
|
|
|
|
|
func _run_case(seed_value: int) -> Dictionary:
|
|
var manager := SimulationManagerScript.new()
|
|
manager.simulation_seed = seed_value
|
|
manager.debug_logs = false
|
|
manager.set_process(false)
|
|
root.add_child(manager)
|
|
var benchmark := BenchmarkScript.new()
|
|
if not benchmark.prepare_manager(manager, 12, 24, seed_value):
|
|
manager.free()
|
|
return {}
|
|
var fixture_valid := SimulationStateRecord.from_json(manager.serialize_state()) != null
|
|
var fixture_npc_combatant_count := 0
|
|
var fixture_combatants_valid := true
|
|
for npc in manager.npcs:
|
|
var combatant: CombatantStateRecord = manager.conflict_system.get_combatant(
|
|
SimulationIds.npc_combatant_id(npc.id)
|
|
)
|
|
if (
|
|
combatant == null
|
|
or combatant.get_npc_id() != npc.id
|
|
or combatant.get_display_name() != npc.npc_name
|
|
or not combatant.get_position().is_equal_approx(npc.position)
|
|
):
|
|
fixture_combatants_valid = false
|
|
else:
|
|
fixture_npc_combatant_count += 1
|
|
var result: Dictionary = benchmark.measure_manager(manager, 12, 24, 2, 8)
|
|
result["fixture_valid"] = fixture_valid
|
|
result["fixture_npc_combatant_count"] = fixture_npc_combatant_count
|
|
result["fixture_combatants_valid"] = fixture_combatants_valid
|
|
manager.free()
|
|
return result
|
|
|
|
|
|
func _coverage_loss_is_rejected(
|
|
warmup_ticks: int, measured_ticks: int, drop_after_simulation_ticks: int
|
|
) -> bool:
|
|
var manager := CoverageDroppingManager.new()
|
|
manager.simulation_seed = 9010
|
|
manager.debug_logs = false
|
|
manager.set_process(false)
|
|
root.add_child(manager)
|
|
var benchmark := BenchmarkScript.new()
|
|
var prepared := benchmark.prepare_manager(manager, 4, 0, manager.simulation_seed)
|
|
if not prepared:
|
|
manager.free()
|
|
return false
|
|
var target_id := SimulationIds.npc_combatant_id(manager.npcs[-1].id)
|
|
var target_existed := manager.conflict_system.get_combatant(target_id) != null
|
|
manager.drop_after_simulation_ticks = drop_after_simulation_ticks
|
|
var result: Dictionary = benchmark.measure_manager(manager, 4, 0, warmup_ticks, measured_ticks)
|
|
var rejected_after_exact_drop := (
|
|
target_existed
|
|
and manager.simulation_ticks_seen == drop_after_simulation_ticks
|
|
and manager.removed_combatant_id == target_id
|
|
and manager.conflict_system.get_combatant(target_id) == null
|
|
and result.is_empty()
|
|
)
|
|
manager.free()
|
|
return rejected_after_exact_drop
|
|
|
|
|
|
func _check(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append(message)
|
|
|
|
|
|
func _finish() -> void:
|
|
if failures.is_empty():
|
|
print("[TEST] Simulation scaling benchmark passed: valid fixture -> stable metrics")
|
|
quit(0)
|
|
return
|
|
for failure in failures:
|
|
push_error("[TEST] " + failure)
|
|
quit(1)
|