extends SceneTree var failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: _check_rhythm_autoload() _check_rhythm_manager_contract() _check_debug_scene() _finish() func _check_rhythm_autoload() -> void: _expect(ProjectSettings.has_setting("autoload/EventBus"), "EventBus autoload should be configured") _expect(ProjectSettings.has_setting("autoload/RhythmManager"), "RhythmManager autoload should be configured") if ProjectSettings.has_setting("autoload/RhythmManager"): _expect(str(ProjectSettings.get_setting("autoload/RhythmManager")).contains("res://autoload/rhythm_manager.gd"), "RhythmManager autoload path") func _check_rhythm_manager_contract() -> void: var rhythm_script: Script = load("res://autoload/rhythm_manager.gd") _expect(rhythm_script != null, "RhythmManager script should load") if rhythm_script == null: return var rhythm: Node = rhythm_script.new() _expect_float(float(rhythm.get("bpm")), 129.2, "RhythmManager default BPM should match ev_past1.mp3") _expect(_file_contains("res://autoload/rhythm_manager.gd", "res://assets/audio/ev_past1.mp3"), "RhythmManager should load ev_past1.mp3 by default") _expect(rhythm.has_method("input_to_song_time"), "RhythmManager should expose input_to_song_time") _expect(rhythm.has_method("judge"), "RhythmManager should expose judge") var rating: Dictionary = rhythm.call("judge", 0.0) _expect(rating.has("nearest_beat"), "judge result should include nearest_beat") _expect(rating.has("diff"), "judge result should include diff") rhythm.free() func _check_debug_scene() -> void: var debug_scene: PackedScene = load("res://scenes/main/clock_debug.tscn") _expect(debug_scene != null, "clock_debug scene should load") if debug_scene == null: return var debug_root := debug_scene.instantiate() _expect(debug_root.get_script() != null, "clock_debug root script should load") _expect(debug_root.has_method("_print_clock_sample"), "clock_debug should expose manual sample printing") debug_root.free() func _expect(condition: bool, label: String) -> void: if not condition: failures.append(label) func _file_contains(path: String, needle: String) -> bool: if not FileAccess.file_exists(path): return false var file := FileAccess.open(path, FileAccess.READ) if file == null: return false var text := file.get_as_text() file.close() return text.contains(needle) func _expect_float(actual: float, expected: float, label: String) -> void: if absf(actual - expected) > 0.001: failures.append("%s: expected %.3f, got %.3f" % [label, expected, actual]) func _finish() -> void: if failures.is_empty(): print("PASS rhythm scene") quit(0) else: for failure: String in failures: push_error(failure) quit(1)