extends SceneTree var failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: await process_frame var bus: Node = load("res://autoload/event_bus.gd").new() bus.name = "EventBus" root.add_child(bus) var manager: Node = load("res://autoload/time_phase_manager.gd").new() manager.name = "TimePhaseManager" root.add_child(manager) var system: Node = load("res://autoload/time_anchor_system.gd").new() system.name = "TimeAnchorSystem" system.set("periodic_scheduling_enabled", false) root.add_child(system) await process_frame var scheduled: Array = [] var resolved: Array = [] bus.connect("time_anchor_scheduled", func(anchor: Dictionary) -> void: scheduled.append(anchor) ) bus.connect("time_anchor_resolved", func(anchor: Dictionary, held: bool, judgement: Dictionary) -> void: resolved.append([anchor, held, judgement]) ) # --- Perfect input on the anchor beat holds the phase. --- system.call("schedule_time_anchor", 4, &"chart") _expect_int(scheduled.size(), 1, "scheduling should broadcast time_anchor_scheduled") bus.emit_signal("judgement_made", &"perfect", 0.0, 4) _expect_int(resolved.size(), 1, "qualifying judgement should resolve the anchor") if resolved.size() >= 1: _expect_bool(bool(resolved[0][1]), true, "perfect on the anchor beat should hold") _expect_equal(manager.get("current_time_phase"), &"past", "held anchor should not switch the phase") # --- Early miss does not break; a later qualifying fact rescues. --- system.call("schedule_time_anchor", 8, &"chart") bus.emit_signal("judgement_made", &"miss", 120.0, 8) _expect_int(resolved.size(), 1, "miss must not resolve the anchor early") bus.emit_signal("judgement_made", &"good", 40.0, 8) _expect_int(resolved.size(), 2, "later qualifying input should rescue the anchor") if resolved.size() >= 2: _expect_bool(bool(resolved[1][1]), true, "rescued anchor should be held") # --- Off-beat judgements are ignored; deadline breaks and switches phase. --- system.call("schedule_time_anchor", 12, &"chart") bus.emit_signal("judgement_made", &"good", 0.0, 11) _expect_int(resolved.size(), 2, "judgement on a non-anchor beat should not resolve") system.call("check_deadlines_for_song_time", 12 * 0.5 + 0.5) _expect_int(resolved.size(), 3, "passing the deadline should break the anchor") if resolved.size() >= 3: _expect_bool(bool(resolved[2][1]), false, "deadline resolution should be broken") _expect_equal(manager.get("current_time_phase"), &"future", "broken anchor should toggle the phase") # --- min_grade tightening. --- system.call("schedule_time_anchor", 16, &"chart", null, &"good") bus.emit_signal("judgement_made", &"bad", 90.0, 16) _expect_int(resolved.size(), 3, "bad should not satisfy a min_grade good anchor") bus.emit_signal("judgement_made", &"perfect", 5.0, 16) _expect_int(resolved.size(), 4, "perfect should satisfy a min_grade good anchor") # --- Resolved anchors never respond again. --- bus.emit_signal("judgement_made", &"perfect", 0.0, 4) _expect_int(resolved.size(), 4, "resolved anchors must not resolve twice") # --- chart_reset discards armed anchors without resolving. --- system.call("schedule_time_anchor", 20, &"chart") var phase_before_reset: StringName = manager.get("current_time_phase") bus.emit_signal("chart_reset", &"test_chart") _expect_int(int(system.call("anchored_beat_count")), 0, "chart_reset should discard all anchors") _expect_int(resolved.size(), 4, "chart_reset must not resolve anchors") _expect_equal(manager.get("current_time_phase"), phase_before_reset, "chart_reset alone must not switch the phase") # --- Same-beat dedup: second schedule returns the existing anchor. --- system.call("schedule_time_anchor", 24, &"periodic") var scheduled_count := scheduled.size() system.call("schedule_time_anchor", 24, &"chart") _expect_int(scheduled.size(), scheduled_count, "same-beat schedule should dedup, not double-broadcast") var summaries: Array = system.call("armed_anchor_summaries") _expect_int(summaries.size(), 1, "one armed anchor expected after dedup") if summaries.size() == 1: _expect_equal((summaries[0] as Dictionary).get("source"), &"chart", "explicit chart anchor should win the collision") _finish() func _expect_equal(actual: Variant, expected: Variant, label: String) -> void: if actual != expected: failures.append("%s: expected %s, got %s" % [label, expected, actual]) func _expect_int(actual: int, expected: int, label: String) -> void: if actual != expected: failures.append("%s: expected %d, got %d" % [label, expected, actual]) func _expect_bool(actual: bool, expected: bool, label: String) -> void: if actual != expected: failures.append("%s: expected %s, got %s" % [label, expected, actual]) func _finish() -> void: if failures.is_empty(): print("PASS time anchor resolution") quit(0) else: for failure: String in failures: push_error(failure) quit(1)