Repository navigation
Enemy_Memory_System Test Plan Springt3
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
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.
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.
| 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.
| # | 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. |
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.
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.
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:jacocoTestReportOn macOS/Linux:
./gradlew :core:test --tests "com.csse3200.game.components.enemy.Memory.EnemyMemoryComponentTest"
./gradlew test
./gradlew spotlessCheck
./gradlew :core:jacocoTestReportThe 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.
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.