Skip to content

Latest commit

 

History

History
123 lines (96 loc) · 12.8 KB

File metadata and controls

123 lines (96 loc) · 12.8 KB
title Testing
nav_order 12

11. Testing

The solution contains a single test project, BatchConvertToCHD.Tests (xUnit, net10.0-windows), with 777 tests across 44 test classes, plus the shared FakeHttpMessageHandler and IszImageBuilder helpers.

Expected result on a clean machine: 762 passed, 15 failed. The 15 failures are a fixture problem, not a regression — see §11.5. A change that leaves exactly those 15 failing has broken nothing.

11.1 Running the Tests

dotnet test CSharp_BatchConvertToCHD.sln -c Release
# or, faster, without rebuilding:
dotnet test BatchConvertToCHD.Tests/BatchConvertToCHD.Tests.csproj --no-build

Requirements: the tests are run on Windows (the app project is net10.0-windows). Some tests need the app's output directory to contain chdman.exe (it is copied by the build).

11.2 How Tests Are Structured

  • Plain xUnit [Fact] / [Theory] + [InlineData].
  • Filesystem-dependent tests create a GUID temp directory per test class (Path.GetTempPath() + $"{ClassName}_{Guid:N}") and clean it up in Dispose.
  • HTTP-dependent tests inject an HttpClient backed by FakeHttpMessageHandler (the only shared helper): a Func<HttpRequestMessage, HttpResponseMessage> or a convenience (HttpStatusCode, string content, string contentType) constructor, plus a static WithAsyncHandler helper.
  • Internals are tested because BatchConvertToCHD.csproj grants InternalsVisibleTo("BatchConvertToCHD.Tests").
  • Integration tests are tagged [Trait("Category", "Integration")] and read real sample files from fixed absolute directories (D:\Emulators\...). Most early-return when the samples are absent, so on machines without the sample folders they are effectively skipped (reported as passed). PbpFileIntegrationTests is the exception — see §11.5.
  • Committed fixtures live in BatchConvertToCHD.Tests/Fixtures/ and are copied to the output directory by the csproj. There is one today, ecm-sample.ecm; it exists so the ECM decoder can be verified against the reference implementation's own output without that tool being installed.
  • Format fixtures are built in code rather than committed where the format allows it: IszImageBuilder writes spec-conformant ISZ files, and MdsTests/RawCdImageDetectorTests/SplitImageJoinerTests synthesise their descriptors and sector data. This keeps the repository free of disc-sized binaries.

11.3 Coverage by File

Application-level tests

File Focus
AppConfigTests.cs Arm64 detection, chdman/7za exe names, API URLs/keys, app name, interval/timeout constants
AppHttpClientTests.cs Singleton behavior, Accept header, TLS 1.2+1.3, dispose semantics, thread safety
ArchiveServiceTests.cs ZIP extraction (real ZIPs built in-test), corrupt/unsupported/missing archives, bin-only archives → auto-cue, ExtractCsoAsync failure/cancellation, 7za fallback matrix, multi-part RAR / network detection, disk-full errors
BinCueGeneratorTests.cs Auto-cue marker, cue content, mode alternation, read/rewrite
BugReportApiSinkTests.cs Sink forwards Warning/Error/Fatal, ignores Debug/Info
BugReportServiceTests.cs Report formatting (inner exceptions, depth), HTTP method/header/body, success/failure mapping, the full exclusion-pattern list (incl. case-insensitivity), no-HTTP-call for excluded messages
CancellationHandlingTests.cs IsCancellationException, IsDiskSpaceException, IsCorruptionException, IsCrcErrorException and their mutual exclusivity
CueNormalizerTests.cs Encoding detection (CP949/CP1251/CP932/UTF-8/UTF-32LE BOM), canonicalization, zero-padding resolution, unresolved names, MP3 transform hook, canonical write format
CueWorkDirectoryTests.cs Work-dir creation rules, in-place BOM fast path, MP3→WAV decoding (fake + real NAudio decoders), end-to-end tests running real chdman.exe (BOM regression, cue/bin/mp3, cue/iso/mp3; skipped when chdman is absent)
FileExtensionsTests.cs All extension constants and sets via reflection, cross-consistency, no duplicates
FileItemTests.cs INotifyPropertyChanged, DisplaySize formatting (0 B … 1.5 TB)
FileWatcherServiceTests.cs Start/Stop/Dispose, GetContextForMissingFile diagnostics with a real FileSystemWatcher, history eviction at 1000 entries, buffer-overflow clearing
GameFileParserTests.cs cue/gdi/toc referenced-file extraction (quoted/unquoted/spaces/multi-file), encoding detection
GitHubReleaseTests.cs Model defaults, JSON (de)serialization
IsoSectorValidatorTests.cs Sector-size alignment warnings; descriptors/empty/missing not validated
MainWindowHelperTests.cs StripUtf8BomIfPresentAsync, SelectChdmanErrorLine (skips progress lines, picks last real error)
PathUtilsTests.cs SanitizeFileName, GetSafeTempFileName, path validation, relative paths, best-temp-directory selection, and CreateTempDirectoryOnSameVolume — including the property that actually matters: a cue written in the returned directory can reach the image by a non-rooted relative path, checked for every ready fixed volume
PbpExtractionResultTests.cs Result-model defaults/setters
RetryingFileOperationsTests.cs TryDeleteAsync/TryMoveAsync with real file locks (FileShare.None), read-only attribute clearing, retry-then-give-up, success-after-lock-release, missing-source/missing-destination semantics
StatsServiceTests.cs POST method/URL/Bearer header/body, no-throw on 429/401/400/500/network errors
UpdateServiceTests.cs Version parsing/normalization theories, new/older/minor/major comparisons, draft/prerelease skip, rate-limit and 5xx handling (no bug report), bug-report paths, invalid tags

Format detection and recovery tests

File Focus
DiscImageSignatureTests.cs Magic-byte identification of every DiscImageKind, IsArchive grouping, Describe phrasing, unknown/short/missing files
RawCdImageDetectorTests.cs Sync-mark and mode-byte sniffing (MODE1/MODE2), rejection of cooked 2048-byte images and non-sector-aligned files, candidate extensions, generated cue content, and the cross-volume refusal that returns null
InputFileFilterTests.cs A raw image is dropped when a sibling descriptor covers it (by base name and by cue text), kept when nothing covers it, and matching is directory-scoped and case-insensitive
SplitImageJoinerTests.cs .001/.002 and .i00/.i01 set discovery and ordering, gaps, single-file non-sets, byte totals, and join output equality
TrackBinCueBuilderTests.cs (Track N) set recognition and ordering, multi-FILE cue content, data track mode vs. AUDIO tracks, non-track-set rejection
MdsTests.cs .mds header/session/track parsing, mode-to-cue-track mapping, sector-size classification (2352 / 2448 / 2368 / 2048), implausible session counts, .mdf lookup, subchannel stripping, MSF formatting, and the three MdsInputPreparer shapes
IszHeaderTests.cs Every header field read at its documented offset (the test that catches an offset mistake), 64-bit image-size arithmetic for dual-layer sizes, signature and short-input rejection, and each refusal in GetUnusableReason including all four encryption modes
IszDecoderTests.cs Chunk-entry bit-packing for 2/3/4-byte pointers, segment naming, round trips for zlib / bzip2 / stored / all-zero and mixed chunk types, trailing partial chunks, two-segment images with a chunk straddling the boundary, and the refusals: not-an-ISZ, encrypted, truncated file, truncated chunk table, corrupt compressed data, missing segment, and a segment from a different image
CdSectorEccEdcTests.cs Sync/mode layout, EDC accumulation equivalence whole vs. in pieces, and the parity distinction that matters: Mode 1 parity covers the address, Mode 2 Form 1 parity does not and restores it afterwards; Form 2 gets an EDC and no parity
EcmImageDecoderTests.cs Decoding the committed reference fixture to an expected SHA1, a guard asserting the fixture still contains all four block kinds, repeatability, output naming, and the refusals: no signature, truncated, wrong trailing checksum, corrupt data, missing file
CueNormalizerFallbackTests.cs FILE-line fallbacks: bare name beside the cue, extension swap, single-FILE match by elimination — and that audio tracks and multi-FILE cues are deliberately not guessed

How the ECM decoder is verified. EcmImageDecoderTests.BuildReferenceImage() rebuilds, in code, the 12-sector image (4 Mode 1, 4 Mode 2 Form 1, 4 Mode 2 Form 2) that Fixtures/ecm-sample.ecm was encoded from by Neill Corlett's own encoder. Decoding the fixture and comparing proves both the block parsing and the regenerated EDC/parity match the reference implementation. The fixture was produced in a run that also confirmed the reverse direction: the real encoder reported stripping the parity from all twelve sectors — which it only does for sectors whose parity it agrees with — and the real decoder produced the identical SHA1. Regenerating the fixture requires that tool and is documented in the repository's working notes; the guard test exists so a regeneration from a simpler image cannot quietly stop exercising the Mode 2 branches.

Library tests (CSOSharp / PBPSharp)

File Focus
CsoFileTests.cs Open-error mapping, v1/v2 open, dispose behavior, block reads
CsoStreamTests.cs Full stream contract: seek/read/zero-length, cross-block reads, throw semantics
CsoHeaderTests.cs Header constants, v1/v2 validity, total blocks, index offset shift
CsoFileIntegrationTests.cs Real .cso files: byte-for-byte block comparison vs. paired .iso, full extraction equality, stream parity
PbpFileTests.cs Open errors, header/SFO/disc parsing, PbpError enum ordinal assertions, synthetic PBP+SFO builders
PbpHeaderTests.cs Magic, size (0x28), defaults, validity
SfoDataTests.cs / SfoEntryTests.cs / TocEntryTests.cs SFO lookups (incl. type mismatch), entry formats, TOC/track types
CueSheetWriterTests.cs Generated CUE content: data/audio tracks, INDEX 00 with 150-frame lead-in, zero-clamp, padding
PbpFileIntegrationTests.cs Real .pbp files: header/SFO/TOC, ExtractToBinCue byte-equality vs. original BIN, normalized CUE equality

Gap: there are currently no CCDSharp unit tests — the test project does not reference CCDSharp (BatchConvertToCHD.Tests.csproj:36–38); the only touch-point is the "CCDSharp: Conversion error" exclusion pattern. CCDSharp behavior is exercised indirectly only if a real .ccd file flows through the app.

11.4 Writing New Tests — Quick Conventions

  1. File-scoped namespace BatchConvertToCHD.Tests; using Xunit is global.
  2. For filesystem tests, mirror the GUID-temp-dir + IDisposable pattern.
  3. For HTTP tests, use FakeHttpMessageHandler and pass the HttpClient to the internal constructor overloads (StatsService, BugReportService, UpdateService, AppHttpClient).
  4. For chdman-dependent tests, early-return when chdman.exe is absent from AppContext.BaseDirectory.
  5. Prefer building binary fixtures in code (see IszImageBuilder) over committing them. Commit one only when the format cannot be generated trustworthily in-repo, as with ecm-sample.ecm.
  6. When a fixture asserts agreement with an outside implementation, add a guard test that the fixture still covers the cases it is meant to. A fixture can be regenerated more simply and silently stop testing anything.
  7. Run the full suite before pushing. A good run is 762 passed / 15 failed, with the failures being exactly the ones in §11.5.

Analyzer constraints worth knowing

The test project runs Meziantou.Analyzer too, and a few rules bite:

  • One top-level type per file (nested types are fine).
  • An internal type cannot appear in a public xUnit [Theory] signature (CS0051) — use a [Fact].
  • StringBuilder.AppendLine($"...") trips MA0011; pass an IFormatProvider or build the string first.
  • Assert.SkipUnless is xUnit v3; this project is on 2.9.3, so conditional tests early-return instead.

11.5 The 15 Expected Failures

On a machine without the integration sample folders, these 15 fail rather than skip. They are pre-existing and unrelated to the conversion pipeline:

Count Tests Why
15 PbpFileIntegrationTests.* They need a real .pbp sample that is not in the repository. Unlike the CSO integration tests, they assert on the discovered-sample collection before checking whether it is empty, so an absent sample surfaces as Assert.NotEmpty() Failure: Collection was empty instead of an early return.

The PBP group is worth fixing — by adopting the CSO tests' early-return pattern — but it does not indicate a defect in the application.