Encounter Composition Test Plan Sprint 3 - UQcsse3200/2026-studio-3 GitHub Wiki

Scope: Issue #276 — Encounter composition and level extension

Implementation and tests: commits 8769936d, dc38c170, e17e44b3, ca9b3834, 7ce10149 and 65cff37c

Integration: Team 1 Sprint 3 PR #374, merged into main as c71c9e6c

Feature documentation: Encounter Composition (Multi-Enemy Battles)

Purpose

Before this work, every battle used the same hardcoded enemies: a Bone Crawler and a Lesser Shade. Encounter composition adds a hand-authored encounter table, configs/encounters.json, and an EncounterComposer that picks an enemy line-up from the map node's RoomType, the current progression and a seed. The seed is derived from a run-level seed and the node ID, and the run seed is stored in the save, so a node produces the same battle after a reload.

This page records the tests added with issue #276 and maps them to its acceptance criteria.

Automated test coverage

Issue #276 added 58 JUnit tests across six new test classes:

Test class New tests What they check
EncounterConfigsTest 18 Loading and validation: null entries, missing IDs, duplicate IDs (first kept), missing room types, non-positive weights, invalid progression ranges, empty enemy lists and blank enemy IDs are skipped; matching() filters by room type and inclusive progression bounds; omitted JSON fields fall back to defaults. Uses the fixture assets/test/encounters/valid.json.
EncounterTableContentTest 6 Checks the real encounters.json: it loads, no entry is skipped, only known enemies are referenced, every battle room type has a candidate at every progression, the boss stays out of ordinary rooms, and the default line-up only uses known enemies.
EncounterComposerTest 12 Determinism (same seed, same line-up, independent of earlier calls); every fallback case; candidates with unknown enemies are never picked; the returned line-up is unmodifiable; weight distribution; a regression test for SplitMix64 seed mixing.
BattleEncounterSelectorTest 11 Room type is read from the active node (missing or unknown nodes count as COMBAT); each node gets its own seed; the same node gets different seeds in different runs; the encounter survives a reload; ELITE and FINAL routing; the start room falls back to the default line-up; the boss never appears in an ordinary room.
RunStateEncounterSeedTest 8 Seed lifecycle: no seed before a run starts, a seed is created at run start, each run gets a new seed, the seed is cleared when the run ends, a saved seed is put back on restore, old saves keep a fallback seed, and setting a map alone does not create a seed.
EncounterSeedSaveTest 3 The seed is written into the save, the same seed is restored after a relaunch, and old saves without a seed get a fresh one.

EncounterSeedSaveTest also contains two run-progress-flag tests, shouldWriteRunProgressFlagsIntoSave() and shouldRestoreRunProgressFlagsAfterRelaunch(). They belong to a different feature and are not counted here.

Acceptance-criteria traceability

# Required behaviour Automated evidence Result or gap
1 The same (RoomType, progression, seed) always produces the same enemy list. shouldReturnSameLineUpForSameSeed() and shouldNotDependOnEarlierCalls(). Covered.
2 A RoomType.ELITE node never draws an ordinary combination, and vice versa. shouldMatchOnlySameRoomType(), shouldPickEliteLineUpForEliteRoom(), shouldPickBossForFinalRoom(), shouldNeverPutBossInOrdinaryRoom() and shouldKeepBossOutOfOrdinaryRooms(). Covered.
3 With no matching candidate, the composer returns the default combination instead of throwing. shouldFallBackWhenTableIsMissing(), shouldFallBackWhenNothingMatches(), shouldFallBackWhenOnlyCandidateHasUnknownEnemy() and shouldFallBackToDefaultLineUpForStartRoom(). Covered.
4 BattleGameArea contains no configuration loading and no randomness, and spawns exactly the IDs it is given. Code inspection: the line-up arrives through the constructor's List<String> enemyIds, and the class has no encounter-table access and no Random. Verified by code inspection; no dedicated JUnit test. BattleGameArea still reads enemies.json for each enemy's stats; that lookup predates #276 and does not decide which enemies appear.
5 ForestGameArea behaves identically for all existing callers when the hook is not used. Code inspection: spawnsDefaultEnemy() returns true by default, and only BattleGameArea overrides it. Verified by code inspection; no regression test was found.
6 Quitting and reloading a save produces the same line-up on the same node. shouldKeepEncounterAfterReload(), shouldWriteSeedIntoSave(), shouldRestoreSameSeedAfterRelaunch() and shouldPutBackSavedSeed(). Covered.

Additional defensive coverage checks that bad table entries are skipped instead of failing the whole load, and that saves from before this feature receive a fallback seed. The regression test shouldSpreadConsecutiveSeedsAcrossEqualWeights() guards against java.util.Random giving correlated first values for consecutive seeds: without the mix() step, two equal weights over seeds 0–999 split 0:1000. The weight tests assert ranges rather than exact counts, so they do not depend on Random's internals.

How to run the tests

Use JDK 21 and run commands from the repository's source directory. On Windows PowerShell:

cd source
.\gradlew.bat :core:test --tests "com.csse3200.game.entities.configs.EncounterConfigsTest" --tests "com.csse3200.game.entities.configs.EncounterTableContentTest" --tests "com.csse3200.game.components.battle.EncounterComposerTest" --tests "com.csse3200.game.components.battle.BattleEncounterSelectorTest" --tests "com.csse3200.game.maps.RunStateEncounterSeedTest" --tests "com.csse3200.game.save.EncounterSeedSaveTest"
.\gradlew.bat test
.\gradlew.bat :core:jacocoTestReport

On macOS/Linux, use ./gradlew in place of .\gradlew.bat. The JaCoCo HTML report is at source/core/build/reports/jacoco/test/html/index.html when viewed from the repository root. The Team 1 test policy targets at least 90% instruction and 80% branch coverage; check the generated report for the affected classes rather than treating a passing test task alone as proof of coverage.

Recorded results and limits

For PR #374, the public unit-test CI job and Java-format CI job completed successfully. This page does not attach local Gradle or JaCoCo output, so regenerate the report when checking a newer branch.

Acceptance criteria 4 and 5 are verified by code inspection only. BattleGameArea and ForestGameArea depend on rendering and the ResourceService, which makes them hard to construct in a unit test; a regression test for each remains a useful addition. The issue's Extras (three-enemy encounters and avoiding repeats on one floor) are not claimed here. Manual in-game play-test results were not recorded for this page.

⚠️ **GitHub.com Fallback** ⚠️