Repository navigation
Testing
Three xUnit projects. All must be green before any commit — CI runs them on every push and pull request, on Linux, Windows and macOS.
dotnet test PCEdit.SaveFileHandler.Tests/PCEdit.SaveFileHandler.Tests.csproj
dotnet test PCEdit.App.Core.Tests/PCEdit.App.Core.Tests.csproj
dotnet test PCEdit.Desktop.Tests/PCEdit.Desktop.Tests.csproj # headless UI tests| Project | Covers |
|---|---|
PCEdit.SaveFileHandler.Tests |
Framing, JSON records, key order, number format, BOM behaviour, planet hash |
PCEdit.App.Core.Tests |
Workspace, inventory editor, codecs, catalogs, ViewModels, localization parity |
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 |
RoundTrip_PlayerTypedText_IsWrittenWithoutEscapes |
Accented, CJK, emoji and HTML-sensitive text is not turned into escapes (v1.7.1) |
Serialize_ReproducesTheGameFraming |
The \r, \r@\r, \r@ framing |
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_KeepsTheBomSave_OverAFileThatHasNoBom_KeepsItBomLess-
Save_OverARealBomLessGamePassSave_KeepsItBomLess— against the raw WGS fixture, on real bytes.
| 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
-textin.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.jsonmust never gain a BOM. It is the only proof the Game Pass path works on real bytes. -
Interplanetary-2.102.jsoncannot joinSaveThenLoad_OfAnUnchangedSave_IsByteIdenticalOnDisk— that theory saves to a path that does not exist yet, which is "Save As", and correctly emits a BOM. -
mini-save.jsonhas 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.
Format details
GameNumberFormatTests (an unedited number keeps its exact text; an edited 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, record and key; duplicate keys, a null
in a modelled key and a missing always-written key are each refused by name), ModelRequiredKeyTests
(every model property is either required or nullable).
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. Since v1.8.0
also JsonSettingsStoreTests and FileSaveBackupServiceTests (both work in a throwaway folder
under the temp directory), UnexpectedErrorReporterTests and QuietTraceListenerTests.
Atomic writes
AtomicFileTests (handler tests, v1.8.0) pin the write itself: no temp file left behind, a failed
write leaves the old bytes, a read-only file is refused, a symbolic link survives and a chain of
links ends where it really ends, permissions are kept. The link and permission cases return early
on Windows, where creating a link needs a privilege; CI runs the tests on Linux.
Localization
LocalizationCatalogTests enforce key parity across all 15 catalogs, non-empty values, matching
{0} placeholders, and that every LocKeys constant exists. See Localization.
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 test projects on the same versions through one grouped update.
PCEdit.Desktop.Tests is the exception to "xUnit 2": it is on xUnit v3, because Avalonia's headless
test package is built on it and the two cannot be mixed in one project.
Fixtures/WorkspaceFixtures.cs builds a loaded workspace to drive ViewModels against.
PCEdit.Desktop.Tests runs the real main window and dialogs on Avalonia's headless platform, with
no display. Each test builds a UiHarness, which gives it:
- fresh services and a real
MainWindow; - a private folder for settings, backups, the log and a copy of a fixture save, so a test never reads or writes your real PCEdit files;
- helpers to load the save, make an edit, press Save, and read or click the dialogs that opened.
They cover what only exists in the desktop head: the disclaimer gate, the Save button and its status colour, the "save file has changed" prompt, the "Settings not saved" and "Unexpected error" dialogs, and the Move dialog's status line being readable by assistive technology.
Two things to know when adding one:
- The app keeps running after an unexpected error, which would let a broken test pass. The harness
records any exception that reaches the UI thread and fails the test, unless the test sets
ExpectUnexpectedError. - Each test has its own localizer. Text a view model provides follows it; text bound in XAML with
{m:Loc}stays in the start-up language, so assert language changes on view-model text.
Layout, the theme and colours are not asserted: the UI tests check that the right control has the right text and style class, not how it is drawn. Those are verified by running the app, and on Linux by the manual checklist and WSL distro matrix in Building & Packaging. The Inventories, Teleport, Terra Tokens and Repair pages have view-model tests but no UI tests yet.
PCEdit · unofficial fan tool for The Planet Crafter · GPL-3.0-or-later · back up your saves (disclaimer)
Using PCEdit
- Installation
- Save File Locations
- Quick Start
- Worlds & Planets
- Overview & Terraforming
- Inventories
- Logistics Editor
- Terra Tokens
- Teleport
- Languages & Settings
- FAQ & Troubleshooting
Development
- Architecture
- Save File Format
- Save File Library
- App Core
- Desktop UI
- Localization
- Item Catalog
- Testing
Shipping