Skip to content

Music Test Plan

Faraj edited this page Oct 8, 2026 · 1 revision

Overview

This test plan covers the game's music system:

  • Per-room track selection — the hub theme, the battle theme and the final boss theme (Music).
  • The roomEntered contract that tells the music which room the player is standing in, and re-fires when a room is cleared.
  • Volume, which follows the settings slider and the mute-when-unfocused rule rather than a fixed level.
  • Loading and disposal of the three tracks.

Before this sprint one placeholder track looped in every room, including the hub. Music is now chosen by what the player is doing: the calm hub theme whenever there is nothing left to fight, the battle theme while a dungeon room still has enemies, and the boss theme in the final dungeon.

Testing is divided into automated unit tests (what the code must do) and manual gameplay tests (what the player must hear). Every behaviour listed in the automated plan is enforced by a test in the repository — the test class is named above each table.

Test Environment

  • Unit testing: JUnit 5 and Mockito, run headlessly with the engine's GameExtension
  • Game testing: Desktop application
  • Movement: WASD — Interact: E
  • Debug terminal (F1) commands: room clear to empty the current room without fighting, which is the fastest way to hear the battle-to-calm transition
  • Settings: the music volume slider and the "mute when unfocused" option both feed AudioLevels.music(), which the music system reads every frame

Run all automated tests with:

cd source
./gradlew :core:test

Coverage is written to source/core/build/reports/jacoco/test/html/index.html by:

cd source
./gradlew :core:jacocoTestReport

Coverage

Last verified run: 8 Oct 2026 on task/232-hecate-npc — 2326 tests, 0 failures project-wide, of which 10 tests in 1 class cover music.

Quality Gate Coverage

Reference: SonarCloud project overview · coverage by component

The badges above are project-wide. The figures below are the music code only.

Per-file coverage

Figures are from a local JaCoCo run of this branch, which is the same measurement SonarCloud consumes. SonarCloud's own published figures lag by one analysis until this branch is merged.

File Lines Line coverage Branch coverage
rooms/RoomAssets.java 67 100.0% 90.9%

RoomAssets owns every music decision, so it is the only file whose coverage is meaningful for this feature. Two other files carry a few lines each:

  • rooms/RoomManager.java fires the roomEntered event that drives the music. The class is large and mostly unrelated to music, so its overall figure (52.7% line coverage) is not a useful measure of this feature.
  • screens/MainGameScreen.java holds the one-line listener that connects the two. Screen classes are not unit tested anywhere in this project.

Aggregate for RoomAssets (JaCoCo, source/core/build/reports/jacoco/test/html/index.html):

Counter Covered Missed Coverage
Instructions 519 0 100.0%
Branches 20 2 90.9%
Lines 67 0 100.0%
Methods 11 0 100.0%

Tests per class

Class Tests
RoomAssetsTest 10
Total 10

Automated Unit Test Plan

Track Selection

The music system is told the player's situation — which dungeon they are in, and whether the room still has enemies — and picks a track from it. The hub is identified by having no dungeon, not by its room id, so renaming the hub in rooms.json cannot silently break the music. A dungeon with its own theme is one entry in a map rather than a new branch.

RoomAssetsTest

Test Purpose Expected Result
shouldLoadAssets Load every asset a room needs. Textures, atlases, music and sounds are all handed to the resource service.
shouldNotStartMusicUntilARoomAsksForIt Load assets without entering a room. No track is even looked up. Which track plays depends on the room, so loading alone must not pick one.
shouldStartTheTrackTheRoomAsksFor Ask for the hub's music. The track is set looping, set to the saved volume, and played.
shouldFightOnlyWhileADungeonRoomStillHasEnemies Enter a dungeon room with enemies alive, then clear it. The battle theme plays while enemies remain; clearing the room stops it and returns to the calm theme.
shouldTreatTheHubAsCalmEvenIfSomethingClaimsItIsNotCleared Ask for hub music with cleared false. The calm theme plays regardless. The hub has no enemies, so its state can never force a fight track.
shouldPlayTheBossThemeInTheFinalDungeon Enter the final dungeon with the boss alive, then beat it. The boss theme plays instead of the ordinary battle theme, and beating the boss returns to calm like any other cleared room.
shouldNotRestartATrackThatIsAlreadyPlaying Ask twice for the track already playing. Nothing is restarted, so crossing rooms of one dungeon never resets the music to its opening bar.
shouldUnloadOnDispose Dispose the room assets. Every track is stopped and all four asset groups are unloaded.

Volume

Volume is read live from the settings every frame rather than fixed when a track starts, so moving the slider or unfocusing the window takes effect immediately on whichever track the current room happens to be playing.

RoomAssetsTest

Test Purpose Expected Result
shouldKeepEveryLoadedTrackOnTheSavedVolume Apply the volume while all three tracks are loaded. Each track is set to AudioLevels.music(), so the setting reaches whichever one is playing.
shouldIgnoreVolumeChangesForTracksThatAreNotLoaded Apply the volume before the tracks are loaded. No track is fetched, so a volume change during loading cannot crash the game.

Manual Gameplay Test Plan

Setup for every test below: run the game with ./gradlew desktop:run. The debug terminal is F1, and room clear empties the current room without fighting.

Hub

Test Steps Expected Result
Plays on spawn Start a new game. The calm hub theme begins as the player appears, with no placeholder track underneath it.
Survives interaction Talk to an NPC in the hub and open a menu. The hub theme keeps playing through the dialogue and the menu; the frozen world does not silence it.
Returns after a run Enter a dungeon, then come back to the hub. The hub theme returns.

Dungeons

Test Steps Expected Result
Battle theme on entry Walk into a dungeon room with enemies. The battle theme replaces the hub theme as the room loads.
Calm on clear Defeat every enemy in the room, or run room clear. The battle theme stops and the calm theme returns, without waiting for a room change.
No restart between rooms Walk from one contested room of a dungeon to another. The battle theme continues from where it was rather than restarting.
Cleared rooms stay calm Re-enter a room already cleared. The calm theme plays; the battle theme does not start again.
Boss theme Enter the final boss room. The boss theme plays instead of the ordinary battle theme.
Boss theme holds across stages Fight the boss through all three of its stages. The boss theme keeps playing; it does not drop to calm between stages.
Calm after the boss Defeat the boss. The music returns to the calm theme.

Volume and Focus

Test Steps Expected Result
Slider takes effect live Open settings mid-run and move the music slider. The volume changes immediately, on whichever track is playing.
Mute when unfocused Enable "mute when unfocused", then click away from the window. The music silences, and returns when the window is focused again.
Applies to every track Set a distinctive volume in the hub, then enter a dungeon. The battle theme starts at the same volume rather than reverting to a default.

Mixing

Test Steps Expected Result
Levels match between tracks Move between the hub and a dungeon room several times. Neither track is noticeably louder than the other at the same slider setting.
Loops cleanly Stay in one room past the end of its track. The track loops without an audible gap or a jarring restart.

Known Coverage Gaps

Two branches are uncovered, both defensive:

Location Uncovered Why
RoomAssets.playMusicFor() The track == null half of the early return Guards a track that is not loaded. The resource service is always registered in the game and in every test, so only the loaded path is exercised.
RoomAssets.music() The resourceService == null half of the guard Guards a lookup after the service locator is torn down. The containsAsset half of the same condition is covered.

Areas Not Covered by Automated Tests

  • The tests assert which Music object is played, looped and stopped. They do not assert that any sound is produced; audio output is verified manually.
  • Mixing is judged by ear, not asserted. Relative loudness between the three tracks, and whether the battle theme sits well under combat sound effects, are manual checks.
  • Loop quality is manual. libGDX restarts a looping track from its beginning and has no concept of a loop point, so a track with an intro replays that intro on every repeat. The boss theme has a 13.8 second intro that is not part of its intended loop; this is documented in assets/licenses/AUDIO-ATTRIBUTION.md and is only audible in a fight long enough to loop.
  • roomEntered is fired by RoomManager and consumed in MainGameScreen. Neither end is unit tested — RoomManager's music announcement is exercised only through gameplay, and screen classes are not unit tested anywhere in this project. The event's payload is simple (room id, dungeon id, cleared) and a wrong value shows up immediately as the wrong track.
  • Music does not duck or pause for dialogue and cutscenes. The world freezes but the track continues, which was a deliberate choice rather than an untested behaviour.

AI Declaration

This page was written with LLM assistance (Claude Code on Opus 5), generated from the music test suite, the JaCoCo report and the project's pull requests, and reviewed before publishing.

Table of Contents

Home

Test Plan

Team 2

Features

Components
Player
Items and Inventory
Enemies
Interactable NPCs
Cutscenes and Dialogue
Music
Bosses
Rooms
Weapons
Abilities & Spells
Game Screens and Areas
Dungeons

Game Engine

Getting Started

Entities and Components

Service Locator

Loading Resources

Logging

Unit Testing

Debug Terminal

Input Handling

UI

Animations

Audio

AI

Physics

Terrain

Concurrency & Threading

Settings

Troubleshooting

MacOS Setup Guide

Clone this wiki locally