Skip to content

Testing

Valkerran edited this page Oct 2, 2026 · 3 revisions

Testing

Two xUnit projects. Both must be green before any commit — CI runs both on every push and pull request.

dotnet test PCEdit.SaveFileHandler.Tests/PCEdit.SaveFileHandler.Tests.csproj
dotnet test PCEdit.App.Core.Tests/PCEdit.App.Core.Tests.csproj
Project Covers
PCEdit.SaveFileHandler.Tests Framing, JSON records, key order, decimal format, BOM behaviour, planet hash
PCEdit.App.Core.Tests Workspace, inventory editor, codecs, catalogs, ViewModels, localization parity

The load-save invariant

The most important assertions in the repo. An unedited load → save must produce the same bytes, so that a diff after an edit shows only the edit:

Test What it pins
RoundTrip_RealSampleSaveFile_ReserializesCharacterForCharacter Whole-file, string-exact
RoundTrip_RealSampleSaveFile_PreservesEveryKeyAndValue No key or value lost, ordering aside
SaveThenLoad_OfAnUnchangedSave_IsByteIdenticalOnDisk The same, through real file I/O
RoundTrip_PreservesKeyOrderCharacterForCharacter WorldObject key order replayed exactly
Serialize_ReproducesTheGameFraming The \r, \r@\r, \r@ framing

Byte-order-mark tests

The Game Pass corruption bug (v1.1.1) has a test per branch:

  • Save_ToANewPath_WritesUtf8WithBom — "Save As" matches the Steam game's own output.
  • Save_OverAFileThatHasABom_KeepsTheBom
  • Save_OverAFileThatHasNoBom_KeepsItBomLess
  • Save_OverARealBomLessGamePassSave_KeepsItBomLess — against the raw WGS fixture, on real bytes.

Fixtures

Fixture Game Platform BOM Role
Standard-2.json 2.008 Steam yes The backward-compatibility regression; single planet
mini-save.json 2.008 hand-authored yes Rare object shapes (count, linkedWo, container text, a logistics container, a deliberately unknown key)
Humble-2.102.json 2.102 Steam yes Single planet (Humble)
Humble-2.103.json 2.103 Steam yes Current game version: the same Humble world, re-saved by 2.103
Interplanetary-2.102.json 2.102 Xbox / Game Pass no The BOM-less path, and the only multi-planet save

Rules for handling them:

  • All are byte-exact and marked -text in .gitattributes. Nothing may normalise their line endings.
  • Never let a load → save round-trip overwrite one. Tests copy to a temp path first.
  • Interplanetary-2.102.json must never gain a BOM. It is the only proof the Game Pass path works on real bytes.
  • Interplanetary-2.102.json cannot join SaveThenLoad_OfAnUnchangedSave_IsByteIdenticalOnDisk — that theory saves to a path that does not exist yet, which is "Save As", and correctly emits a BOM.
  • mini-save.json has no trailing newline. Regenerate it with a byte-writing script, never an editor.

SaveFileFixtures (in the handler tests) builds a save with a distinct, greppable marker value in every property, so a round trip can assert each value lands back in the exact property it started in — that is what pins the section-order contract.

What else is covered

Format details GameDecimalFormatTests (a whole number serializes as N.0), GameFormatKeyMappingTests (the abbreviated key spellings a symmetric model round-trip cannot catch — demandGrps, supplyGrps, linkedWo, count, text, and logisticsPaused being written only when the save had it), WorldObjectConverterTests, JsonRecordSerializerTests (camelCase, null omission, and an InvalidDataException that names the offending section index).

Planet identity PlanetHashTests checks the hash against the integers the game actually wrote, including resolving every placed world object on the multi-planet fixture.

App layer SaveFileWorkspaceTests, InventoryEditorTests (moves, full-destination refusal, grouping, the Move dialog's options), PlanetIndexTests, the codec tests, ItemCatalogTests / LogisticsGroupCatalogTests (the embedded JSON parses and every item's category exists), IdSearchTests and InventoryGroupTests (id-prefix matching, text vs id search, narrowing a card to its matching items while keeping the true fill), and ViewModel tests for Overview, Inventories, the Move dialog (SelectInventoryViewModelTests), Teleport and the logistics editor.

Localization LocalizationCatalogTests enforce key parity across all 15 catalogs, non-empty values, matching {0} placeholders, and that every LocKeys constant exists. See Localization.

Test doubles

PCEdit.App.Core.Tests/Fakes/ holds hand-written fakes for the platform interfaces (FakeNavigationService, FakeDialogService, FakeSaveFileStore, FakeSaveBackupService, FakeScreenReaderAnnouncer) — no mocking framework. The test stack is xUnit 2 with Microsoft.NET.Test.Sdk 18 and xunit.runner.visualstudio 4; there is no coverage collector. Dependabot keeps the two test projects on the same versions through one grouped update. Fixtures/WorkspaceFixtures.cs builds a loaded workspace to drive ViewModels against.

What is not covered

There are no automated UI tests. Anything that only exists in XAML — layout, the theme, the shell's navigation — is verified by running the app, and on Linux by the manual checklist and WSL distro matrix in Building & Packaging.

Clone this wiki locally