Card Cool Down Testing - UQcsse3200/2026-studio-3 GitHub Wiki
Methodology
Tests were designed against each method's control flow graph (CFG): for
every if/loop condition in the methods under test, at least one test was written to exercise each
outcome (true and false, or "found" and "not found"). Coverage numbers below are JaCoCo branch
coverage, gathered from a fresh ./gradlew :core:test :core:jacocoTestReport run. Branch coverage
is the relevant metric here since the logic in both features is almost entirely decision-driven
(guards, membership checks, loop termination) rather than long straight-line computation.
Cooldown: CardCooldownTracker
flowchart TD
A["trackDiscard(instance, rounds)"] --> B{"rounds > 0?"}
B -- yes --> C["add Entry(instance, rounds)"]
B -- no --> D["do nothing"]
E["tickRoundAndRetrieve()"] --> F{"more entries?"}
F -- no --> G["return retrieved"]
F -- yes --> H["roundsRemaining--"]
H --> I{"roundsRemaining <= 0?"}
I -- no --> F
I -- yes --> J{"retrieveInstanceFromDiscard succeeds?"}
J -- yes --> K["add cardId to retrieved, remove entry"]
J -- no --> L["remove entry only"]
K --> F
L --> F
M["isOnCooldown(instance)"] --> N{"any entry.instance.equals(instance)?"}
N -- yes --> O["return true"]
N -- no --> P["return false"]
| Branch | Covered by | Outcome tested |
|---|---|---|
rounds > 0 → true |
shouldTrackCooldownPerInstanceNotPerCardId |
entry gets tracked |
rounds > 0 → false |
(not directly tested) | never reached via the public API: CardPlayService always calls trackDiscard with CardCooldown.roundsFor(card), which is always ≥ 1 by construction. A defensive guard, not a live code path. |
roundsRemaining <= 0 → false (still counting) |
shouldOnlyRetrieveTheSpecificInstanceOnCooldown (first tick of a 2-round cooldown returns empty) |
tick decrements without retrieving |
roundsRemaining <= 0 → true |
both cooldown tests | retrieval fires once the count reaches zero |
retrieveInstanceFromDiscard succeeds |
both cooldown tests | normal retrieval path |
retrieveInstanceFromDiscard fails |
(not tested) | would require the instance to have left the discard pile through some other path between trackDiscard and the tick; not currently reachable in normal play |
isOnCooldown match / no match |
shouldTrackCooldownPerInstanceNotPerCardId |
asserts true for the discarded instance, false for its still-in-hand duplicate |
Measured coverage: CardCooldownTracker 24/25 lines (96%), 11/14 branches (78%), 4/4 methods
(100%). The two uncovered branches are the rounds > 0 → false guard and the
retrieveInstanceFromDiscard failure path, both defensive and not reachable through the code paths
CardPlayService actually exercises.