diff --git a/hoppys-adventure-arcade/scenes/credits/credits.json b/hoppys-adventure-arcade/scenes/credits/credits.json index 4c850734..c3497983 100644 --- a/hoppys-adventure-arcade/scenes/credits/credits.json +++ b/hoppys-adventure-arcade/scenes/credits/credits.json @@ -6,7 +6,88 @@ "lines": [ "Music by Erik Thorelli" ], - "tracks": [] + "tracks": [ + { + "title": "Send in the Rabbit", + "used_by": "Booth attract mode" + }, + { + "title": "Send in the Rabbit (Instrumental)", + "used_by": "Title screen" + }, + { + "title": "Cable Car Hop", + "used_by": "Bay Area Blitz" + }, + { + "title": "Merge Conflict Monster", + "used_by": "Bay Area Blitz boss" + }, + { + "title": "Zero-G Deploy", + "used_by": "Orbital Ops" + }, + { + "title": "The Deprecated API", + "used_by": "Orbital Ops boss" + }, + { + "title": "The Enchanted Repo", + "used_by": "The Enchanted Repo" + }, + { + "title": "The Legacy Monolith", + "used_by": "The Enchanted Repo boss" + }, + { + "title": "Stage Clear", + "used_by": "Platformer results" + }, + { + "title": "CLI / IDE District", + "used_by": "Code City Brawl" + }, + { + "title": "Security Sector", + "used_by": "Code City Brawl" + }, + { + "title": "Git Repository HQ", + "used_by": "Code City Brawl" + }, + { + "title": "District Boss", + "used_by": "Code City Brawl: Stack Overflow and Zero-Day" + }, + { + "title": "The Grand Merge Conflict", + "used_by": "Code City Brawl final boss" + }, + { + "title": "The Generation Floor", + "used_by": "Heroes vs. Villains" + }, + { + "title": "AI SLOP", + "used_by": "Heroes vs. Villains boss" + }, + { + "title": "The Review Gauntlet", + "used_by": "Heroes vs. Villains" + }, + { + "title": "Agent Army and the 10,000-Line PR", + "used_by": "Heroes vs. Villains bosses" + }, + { + "title": "The Agentic Core", + "used_by": "Heroes vs. Villains" + }, + { + "title": "Machine-Speed Hacker", + "used_by": "Heroes vs. Villains final boss" + } + ] } ] } diff --git a/hoppys-adventure-arcade/tests/credits_screen.gd b/hoppys-adventure-arcade/tests/credits_screen.gd index b553bc5e..6d99e760 100644 --- a/hoppys-adventure-arcade/tests/credits_screen.gd +++ b/hoppys-adventure-arcade/tests/credits_screen.gd @@ -4,6 +4,13 @@ extends SceneTree ## license; a track added to credits.json shows up; it scrolls by itself and by hand; Esc and the ## Back button return to the hub. Run headless with --script res://tests/credits_screen.gd. const Credits := preload("res://scenes/credits/credits.gd") +## The original tracks that play in the game (Assets/Audio/**/PROMPTS.md), kept apart from credits.json +## so a track dropped from the credits fails the suite. +const ORIGINAL_TRACKS := ["Send in the Rabbit", "Send in the Rabbit (Instrumental)", "Cable Car Hop", + "Merge Conflict Monster", "Zero-G Deploy", "The Deprecated API", "The Enchanted Repo", "The Legacy Monolith", + "Stage Clear", "CLI / IDE District", "Security Sector", "Git Repository HQ", "District Boss", + "The Grand Merge Conflict", "The Generation Floor", "AI SLOP", "The Review Gauntlet", + "Agent Army and the 10,000-Line PR", "The Agentic Core", "Machine-Speed Hacker"] var failures := 0 var router: Node @@ -54,6 +61,19 @@ func run() -> void: check(not written.is_empty() and written[0].title == "Original music" and "Music by Erik Thorelli" in written[0].lines and written[0].tracks is Array, "credits.json opens with Original music, by Erik Thorelli, and a tracks list to extend") + var music_tracks: Array = written[0].tracks if not written.is_empty() else [] + var track_titles := {} + for track: Variant in music_tracks: + if track is Dictionary and track.get("title", "") is String and track.get("title", "") != "" \ + and track.get("used_by", "") is String and track.get("used_by", "") != "": + track_titles[track.title] = "%s ยท %s" % [track.title, track.used_by] + check(music_tracks.size() > 0 and track_titles.size() == music_tracks.size(), + "Every original track has a unique title and the part of the game it plays in (%d tracks)" % music_tracks.size()) + var expected_titles := {} + for title in ORIGINAL_TRACKS: + expected_titles[title] = true + check(track_titles.keys().size() == ORIGINAL_TRACKS.size() and track_titles.keys().all(func(t: String) -> bool: return expected_titles.has(t)), + "credits.json lists exactly the %d original tracks that play in the game" % ORIGINAL_TRACKS.size()) var all_credits: Array = [] for section in generated: all_credits.append_array(section.credits) @@ -76,6 +96,9 @@ func run() -> void: var shown := texts(screen) check("ORIGINAL MUSIC" in shown and "Music by Erik Thorelli" in shown, "The screen credits the original music to Erik Thorelli") + var unlisted := track_titles.keys().filter(func(title: String) -> bool: return not track_titles[title] in shown) + check(not track_titles.is_empty() and unlisted.is_empty(), + "The screen lists every original track title%s" % ("" if unlisted.is_empty() else " (missing: %s)" % ", ".join(unlisted))) var missing: Array = [] var unlicensed: Array = [] for entry: Dictionary in all_credits: