Enemy_Memory_System Test Plan Springt3 - UQcsse3200/2026-studio-3 GitHub Wiki
Enemy Memory Component Test Plan Sprint 3
Scope: Per-combat player-behaviour storage and immutable PlayerMemory snapshots
Implementation and tests: memory-system refactor commit e9d65e7e
Package correction: commit d2840e2c
Integration: PR #337
Purpose
EnemyMemoryComponent is the central per-combat storage component of the Enemy Memory System.
PlayerTrackerComponent observes successful player actions and calls:
recordCardPlayed(
CardType cardType,
boolean gainedBlock);
When the player turn ends, the tracker calls:
settlePlayerTurn();
Enemy behaviour reads memory through:
PlayerMemory snapshot();
The component stores:
- Total attack cards played during the combat;
- Total skill cards played during the combat;
- Number of cards played during the current turn;
- Number of cards played during the previous completed turn;
- Whether the player gained Block during the current turn;
- Consecutive completed turns without gaining positive Block.
This test plan covers memory storage and settlement behaviour. Event listening, card-instance resolution and Block-effect detection belong to PlayerTrackerComponentTest.
Automated test coverage
EnemyMemoryComponentTest currently contains 12 JUnit tests.
| Test class | Tests | What they check |
|---|---|---|
EnemyMemoryComponentTest |
12 | Initial state, null rejection, card classification, turn settlement, previous-turn counts, no-Block streaks, lifetime totals and immutable snapshot independence. |
The tests use a real EnemyMemoryComponent and do not require mocks. Inputs are provided directly through the component's public API.
Test groups
| Group | Tests | Coverage |
|---|---|---|
| Initial state and validation | 2 | Empty memory and null CardType rejection |
| Card counting | 3 | Attack/skill totals, other card types and hidden current-turn count |
| Turn settlement | 2 | Previous-turn count and replacement on later turns |
| Block-streak behaviour | 4 | Streak growth, reset, whole-turn Block memory and per-turn flag reset |
| Snapshot and lifetime behaviour | 2 | Totals across turns and independent immutable snapshots |
Some tests cover more than one group, so the grouped count is not intended to be summed as a separate test total.
Acceptance-criteria traceability
| # | Required behaviour | Automated evidence | Result or gap |
|---|---|---|---|
| 1 | A new component begins with empty player memory. | shouldStartWithEmptyMemory() compares the first snapshot with PlayerMemory.empty(). |
Covered. |
| 2 | A null card type is rejected. | shouldRejectNullCardType() |
Covered. |
| 3 | Attack and skill cards update their respective lifetime counters. | shouldCountAttackAndSkillCards() records two attacks and one skill. |
Covered. |
| 4 | Other valid card types count toward the current turn without changing attack or skill totals. | shouldCountOtherTypesAsCardsPlayedDuringTurn() records POWER, STATUS and CURSE. |
Covered. |
| 5 | The current-turn card count is not exposed as cardsPlayedLastTurn before settlement. |
shouldNotExposeCurrentTurnCountBeforeSettlement() |
Covered. |
| 6 | Settling a turn copies the current count into cardsPlayedLastTurn. |
shouldNotExposeCurrentTurnCountBeforeSettlement() checks the value before and after settlement. |
Covered. |
| 7 | A later settlement replaces, rather than accumulates, the previous-turn card count. | shouldReplaceLastTurnCardCount() checks one card followed by a two-card turn. |
Covered. |
| 8 | A completed turn without Block increases the no-Block streak. | shouldIncreaseNoBlockStreak() settles three empty turns and expects a streak of three. |
Covered. |
| 9 | Gaining positive Block resets the no-Block streak. | shouldResetNoBlockStreakAfterBlock() |
Covered. |
| 10 | Once Block is recorded, later non-Block cards during the same turn do not erase it. | shouldRememberBlockForEntireTurn() |
Covered. |
| 11 | The per-turn Block flag resets after settlement. | shouldResetBlockFlagBetweenTurns() checks a defended turn followed by an undefended turn. |
Covered. |
| 12 | Lifetime attack and skill totals persist across multiple turns. | shouldKeepLifetimeTotalsAcrossTurns() |
Covered. |
| 13 | Every card type contributes to cardsPlayedLastTurn. |
shouldCountOtherTypesAsCardsPlayedDuringTurn() and shouldKeepLifetimeTotalsAcrossTurns() |
Covered for attack, skill, power, status and curse inputs. |
| 14 | A snapshot is independent of later component mutations. | shouldReturnIndependentSnapshots() preserves the first snapshot after recording another card. |
Covered. |
| 15 | A returned snapshot cannot mutate the component. | PlayerMemory is an immutable Java record. |
Structural guarantee; no mutation API exists. |
| 16 | Settlement resets the current card counter. | Later-turn replacement tests imply this behaviour. | Indirectly covered; there is no dedicated assertion against the private current-turn counter. |
| 17 | Settlement resets gainedBlockThisTurn. |
shouldResetBlockFlagBetweenTurns() |
Covered through observable behaviour. |
State-transition coverage
recordCardPlayed()
|
+-- every card --> cardsPlayedThisTurn + 1
|
+-- ATTACK --> attackCardsPlayed + 1
|
+-- SKILL --> skillCardsPlayed + 1
|
+-- gainedBlock = true --> gainedBlockThisTurn = true
settlePlayerTurn()
|
+-- cardsPlayedLastTurn = cardsPlayedThisTurn
|
+-- gained Block --> no-Block streak = 0
|
+-- no Block --> no-Block streak + 1
|
+-- cardsPlayedThisTurn = 0
|
+-- gainedBlockThisTurn = false
The tests exercise both settlement branches:
- Turn contained positive Block;
- Turn contained no positive Block.
Snapshot coverage
The snapshot method creates:
new PlayerMemory(
attackCardsPlayed,
skillCardsPlayed,
consecutiveTurnsWithoutBlock,
cardsPlayedLastTurn);
shouldReturnIndependentSnapshots() checks that:
- A snapshot is created before recording a card;
- The component is then modified;
- A second snapshot contains the new data;
- The first snapshot remains unchanged;
- The two snapshots are different instances.
This verifies that enemy AI receives a point-in-time value rather than direct access to mutable counters.
How to run the tests
Use JDK 21 and run commands from the repository's source directory.
On Windows PowerShell:
.\gradlew.bat :core:test --tests "com.csse3200.game.components.enemy.Memory.EnemyMemoryComponentTest"
.\gradlew.bat test
.\gradlew.bat spotlessCheck
.\gradlew.bat :core:jacocoTestReport
On macOS/Linux:
./gradlew :core:test --tests "com.csse3200.game.components.enemy.Memory.EnemyMemoryComponentTest"
./gradlew test
./gradlew spotlessCheck
./gradlew :core:jacocoTestReport
The JaCoCo HTML report is generated at:
source/core/build/reports/jacoco/test/html/index.html
The Team 1 test policy targets at least 90% instruction coverage and 80% branch coverage. Coverage should be checked for both EnemyMemoryComponent and its integration boundary, PlayerTrackerComponent.
Recorded results and limits
The direct component suite contains 12 focused tests. This page does not attach a new Gradle or JaCoCo result for the latest Feature-1 head, so results should be regenerated after updating the branch.
These tests verify storage behaviour through the public component API. They do not directly test:
- Registration of battle-event listeners;
- Runtime card-instance resolution;
- Detection of
BLOCKinsideCardConfig; - Rejection of failed card-play attempts;
- Battle phase transitions;
- Injection of memory into
EnemyAIContext; - AI reactions to the returned memory;
- Persistence between battles;
- Achievement aggregation;
- Map-derived memory.
Those responsibilities are covered by other components or are outside the current feature scope.
PlayerTrackerComponentTest provides adjacent coverage for event translation, card classification and real-turn settlement. EnemyAIContextTest and individual AI tests provide downstream coverage for memory consumption.
Useful follow-up regression tests are:
- A dedicated empty-turn settlement test that checks both previous-turn count and no-Block streak together;
- Large counter values near integer limits, if overflow protection becomes a project requirement;
- Full integration from a successful
BattleControllercard event to an AI decision.