Skip to content

Save Load System Test Plan

Luqishu Wu–––– edited this page Oct 7, 2026 · 6 revisions

Team 5 | Save/Load System Test Plan — automated coverage plus a walked-through manual scenario checklist with evidence | Related: Save/Load System | Sprint 3 work reaches main via #405


Testing policy

Same rule as the rest of Team 5's work: tests ship with the code in the same PR, and whoever implements a piece of Save/Load is responsible for its tests — not left for someone else to backfill later.

Note: there is no repo-wide enforced JaCoCo coverage gate in build.gradle (no jacocoTestCoverageVerification rule exists) — coverage numbers here are reported, not enforced.


Automated test coverage

The full list, and what each one actually proves, lives on the Save/Load System page's Testing section — not duplicated here to avoid the two pages drifting out of sync. Summary:

  • GameStateSnapshotProvider — capture correctness (player, map, connections-as-IDs).
  • GameStateSnapshotProviderTest.capturesPietyAsCurrentNodeHeightForSchemaCompleteness() (#303) — the player's level is captured from the current node's height.
  • saveCloseRelaunchAndRestoreRecoversTheWholeRun() — full round-trip using real SaveGameService/SaveGameRestoreService, fresh objects simulating an actual relaunch.
  • shouldIgnoreLegacyCompletedEncounterIds() — backward compatibility with pre-schema-cleanup save files, verified at the actual file-parsing layer.
  • SaveGameRestoreService — every RestoreError case, including card-progress validation.
  • SaveErrorMessages — every SaveError maps to a real message.
  • PlayerRunStateTest — durable player state apply/capture.
  • saveRestorePreservesGeneratedMapTopologyAndNodeStates() — round-trip against a real MapGenerationController-produced map, not just a small hand-built fixture.
  • RunStateTest.restoringRunFixesNodeStuckMidBattle(), restoringRunWithNoActiveEncounterLeavesNodesUnaffected() and firstBattleOfARunAlwaysLeavesACompletedNeighbourToFallBackOn() (#315) — a node interrupted by a mid-battle save is recovered on restore (node state and currentNode), a normal restore is left untouched, and the very first battle of a run still has a completed neighbour to fall back to.
  • AutosaveCoordinatorTest — slot-4 isolation, successful encounter checkpoints, stale-run rejection, idempotent callbacks, previous-checkpoint preservation before reward selection, non-blocking repository failures, and save/relaunch/load after a reward is applied.
  • RewardDisplayTest.checkpointsAppliedRewardBeforeNavigationAndOnlyOnce() — confirms reward state is applied before both the checkpoint callback and navigation events.
  • PlayerItemEffectsSaveTest — round-trips all current owned item types through the real JSON repository and verifies the derived effects are rebuilt after relaunch; also covers an older save with no ownedItems field.
  • PlayerSaveDataTest — the custom serializer, including the legacy piety fallback and that ownedItems is written and read.
  • Card discovery tests (#379) — CardDiscoveryService.mergeProgress only upgrades LOCKED to SEEN and never re-locks a card, validates before applying, and restore rejects invalid card progress (INVALID_PROGRESS_STATE).

Run locally from source/:

./gradlew build
./gradlew :core:test --tests "*PlayerItemEffectsSaveTest*" --tests "*SaveGameRestoreServiceTest*"

Manual test scenarios

Automated tests prove the logic is correct in isolation. These scenarios prove the whole thing actually works end to end, in the real running game, against the real UI — the kind of thing unit tests can't catch (a crash on click, a screen that doesn't visually match, a button that's silently broken). Scenarios 1 to 5 were run during Save/Load's menu integration, and screenshots from those sessions are the evidence. Scenarios 6 to 11 are currently covered by automated tests, and each says what manual evidence is still outstanding.

Current manual evidence video

Save/Load manual test evidence video

This recorded walkthrough shows the current Save/Load behaviour in the running game and provides manual evidence for the current UI flow, including opening the Load Game screen, checking that the Save/Load panel uses the current menu theme, and exercising the Save/Load interaction through the game UI.

Scenario 1 — Save and reload preserves state

  1. Start a new run, enter a node, save to a slot.
  2. Return to the main menu, load that slot.
  3. Confirm the game resumes at the same map position, with the same deck/health/gold.

Evidence: confirmed working — save round trip verified live during the menu integration test session (screenshots: Save/Load panel showing "Game saved." then "Save loaded successfully. Ready to resume from the Map.").

Scenario 2 — Load Game from the main menu doesn't crash

  1. From the main menu, click Load Game.
  2. Confirm the Save/Load screen opens without error.

Evidence: this was a real, reproducible crash before the fix (GdxRuntimeException: Asset not loaded, background texture never registered) — screenshot of the crash stack trace, and a follow-up screenshot of the working screen after the fix, both on file from that session.

Scenario 3 — Save/Load screen visually matches the rest of the menu

  1. Open Load Game from the main menu.
  2. Confirm background, labels, and buttons match the "Fall of the Pantheon" theme rather than the default engine skin.

Evidence: before/after screenshots on file — default flat-earth skin (yellow background, stock green pill buttons) vs. the themed version (matching background, MenuTheme-colored rounded-pill buttons).

Scenario 4 — Repeated save/load/delete doesn't leak memory

  1. Open Load Game.
  2. Save, load, and delete repeatedly across all three slots.
  3. Confirm no growing GPU memory usage from repeated UI refreshes.

Evidence: caught in review before merge — the panel was regenerating ~40 new textures per refresh before the fix. Verified after the fix that the same three pill drawables are reused across every refresh instead of rebuilt.

Scenario 5 — Abandoning a battle mid-fight doesn't strand the map

  1. Enter a combat node, play at least one card.
  2. Exit via the pause menu (Escape → Return to Main Menu → confirm).
  3. Return to the map and confirm the node reverted to its pre-battle state, and other nodes are still selectable — not permanently stuck.

Evidence: confirmed working live, including the specific edge case (abandoning on the very first move, before any prior position exists) that was caught and fixed in review before merge. Screenshot on file of the map after abandoning, showing normal node selection still working.

Scenario 6 — Missing or corrupted save file fails safely

  1. Open the Save/Load screen from the main menu.
  2. Attempt to load a save slot that has no save file, or replace a slot file with invalid JSON before loading.
  3. Confirm the game does not crash.
  4. Confirm the Save/Load panel reports a readable load failure message.
  5. Confirm the current run state is not partially overwritten.

Expected result: the load operation fails safely. The player remains in the current state, the Save/Load UI stays usable, and the failure is reported through the Save/Load error handling path rather than crashing the game.

Evidence: covered by automated Save/Load failure-path tests, including malformed JSON, missing-slot, and readable-error-message coverage. A local focused Save/Load and Bestiary test run, plus a full Gradle build, passed on 16/09.

Scenario 7 — Bestiary progress survives Save/Load

  1. Start a run and encounter or defeat an enemy that updates Bestiary progress.
  2. Open the Bestiary from the Library and confirm the enemy entry has advanced from locked to encountered or defeated.
  3. Save the game.
  4. Return to the main menu or restart the run flow, then load the saved slot.
  5. Open the Bestiary again.
  6. Confirm the same enemy entry still shows the saved progress state.

Expected result: Bestiary progress is persisted through the shared Save/Load file. Encountered and defeated states remain available after loading, while unrelated locked entries remain locked.

Evidence: covered by automated snapshot, repository round-trip, restore, and end-to-end Save/Load tests for Bestiary progress. A local focused Save/Load and Bestiary test run, plus a full Gradle build, passed on 16/09.

Scenario 8 — Rewarded battle creates a durable Autosave

  1. Load or create a valid Autosave checkpoint, then begin and win another battle.
  2. Before choosing a reward, try the generic click/key-to-continue action and confirm the victory screen does not exit.
  3. Choose a gold, item or card reward and confirm navigation can now continue.
  4. Fully close and relaunch the game before starting another encounter.
  5. Load Autosave (slot 4).
  6. Confirm the claimed reward, player state, deck, completed map node, and Bestiary progress match the post-battle checkpoint.
  7. Repeat by closing before reward selection and confirm the previous valid Autosave remains.

Expected result: a reward is applied before slot 4 is written; the map transition does not perform a duplicate write; closing before selection neither consumes nor duplicates a reward.

Automated evidence: RewardDisplayTest and AutosaveCoordinatorTest cover ordering, idempotency, relaunch, stale requests, and failure handling. Add a screenshot or short video of the manual sequence before marking the manual evidence complete.

Scenario 9 — Owned items and effects survive relaunch

  1. Acquire at least one persistent item, then save the run.
  2. Fully close and relaunch the game.
  3. Load the save and enter a screen that creates the player entity.
  4. Confirm the owned item is still listed and its derived effect (for example maximum energy, gold bonus, or shop discount) matches the pre-save value.

Expected result: PlayerRunState.ownedItems is restored from stable ItemType names and applyTo() rebuilds each derived effect exactly once. A save created before ownedItems existed loads with an empty item list rather than failing.

Automated evidence: PlayerItemEffectsSaveTest covers every current item type and the legacy missing-field case through a real repository round trip.

Scenario 10 — Saving mid-battle does not strand the map

  1. Start a run and enter a combat node. Do not finish the fight.
  2. Open the pause menu (Escape) and save to a slot.
  3. Return to the main menu, then load that slot.
  4. Confirm you land on the map and the node you were fighting at can be clicked and entered again.
  5. Repeat on the very first battle of a run, before any other node has been completed.

Expected result: the interrupted node returns to a selectable state and the next nodes stay reachable. Before the fix (#315), the node stayed unselectable and the run could not progress.

Automated evidence: RunStateTest covers the interrupted node, the unaffected normal restore, and the first-battle case. Manual evidence: Pending manual execution on the current main build. PR #390 is now merged, so this scenario is no longer blocked by the pause-menu save integration.

Scenario 11 — Card discovery survives a load and never re-locks

  1. Discover a few cards (see them in a shop, event or reward) and note the Card Library count.
  2. Save, then discover another card.
  3. Load the earlier save.
  4. Open the Card Library and confirm the later card is still discovered.

Expected result: loading an older save merges its card progress, so no discovered card is re-locked. Bestiary progress still follows the loaded save.

Automated evidence: the card discovery tests (#379). Manual evidence: Pending manual execution on the current main build.


What's still missing

  • Additional targeted screenshots can still be added beside individual scenarios, but a current Save/Load manual evidence video is now linked above.
  • Direct screenshot or video evidence can still be added for the corrupted/missing save-file scenario after reproducing it in the running game.
  • Direct screenshot or video evidence can still be added for the Bestiary + Save/Load interaction scenario after reproducing it in the running game.
  • Direct screenshot or video evidence is still required for Scenario 8's post-reward Autosave sequence. Automated coverage is complete, but this manual UI flow must not be claimed as run until it has actually been performed.
  • Scenarios 10 and 11 still need a manual run and supporting evidence. PR #390 is already in main, so Scenario 10 is ready to execute.

Table of Contents

Home

Game Engine

Getting Started

Entities and Components

Service Locator

Loading Resources

Logging

Unit Testing

Debug Terminal

Input Handling

UI

Animations

Audio

AI

Physics

Game Screens and Areas

Terrain

Concurrency & Threading

Settings

Encounters
Tutorials and Contextual Help (Team 2)
Cards (Team 6)
Story (Team 6)
Level design, battle system, turn order, spawn (Team 3)
Cards, Save/Load & Bestiary (Team 5)
Rewards and Battle Settlement (Team 7)

Troubleshooting

MacOS Setup Guide

Clone this wiki locally