Skip to the content.

Testing

The test suite is split into three projects, so the default run is always the fast one:

Throughput measurements live in RVZSharp.Benchmarks (BenchmarkDotNet; see Benchmarks below).

# fast suite (default; runs per target framework of the solution)
dotnet test CSharp_RVZSharp.sln -c Release

# a single class
dotnet test CSharp_RVZSharp.sln -c Release --filter "FullyQualifiedName~RvzWriterTests"

# slow / real-file suite (explicit opt-in; not part of the solution)
dotnet test RVZSharp.Slow.Tests -c Release

# a single framework (either project)
dotnet test RVZSharp.Slow.Tests -c Release --framework net8.0

Strategy

The suite runs against synthetic discs built in memory (cross-checked against the reference implementations’ semantics) and, when a local library of real game images is mounted (F:\Nintendo GameCube, F:\Nintendo Wii), against real RVZ files validated byte-for-byte against their official No-Intro SHA-1s:

  1. Synthetic builders generate byte-exact images (RVZ/WIA, all legacy formats, and realistic Wii ISOs with tickets, partition tables and encrypted data).
  2. Round trips prove byte-exactness: build → write → read → compare.
  3. Format semantics were validated against Dolphin’s C++ (References/dolphin-master) and the Go reader (References/rvz-1.0.3) — including a Python prototype used during development to pin down the PRNG seed-recovery algorithm before the C# port.
  4. Reference-alignment regressions (2025 audit): every finding from the comparison against Dolphin/Go is pinned by a test — LZMA1 end markers, raw-table group counts, TGC/WBFS magic offsets, >2 MiB chunk exception lists, zero-fill hash trees, overlapping-window hash exceptions, overlap/ordering validation, empty-table hashes, decompressed-size probes, split WBFS, scrubbing, truncated packing headers, and the CLI option surface.
  5. Mutation/fuzz robustness (ParserRobustnessTests): every container is truncated, bit-flipped, extended and size-patched (80 mutations per format, seeded) and fed through Blob.Open + reads; only RvzException subclasses may escape. Set RVZSHARP_FUZZ_ITERATIONS=2000 for a deeper local pass (CI keeps the default).
  6. Concurrency (ConcurrentReadTests): parallel random ReadAt calls on GC/Wii containers (mixed with ReadFully) must match the reference ISO byte-for-byte.
  7. Continuous integration (.github/workflows/ci.yml): builds the solution and runs the fast suite on net8.0/net9.0/net10.0 with coverage uploaded as an artifact, packs the library (API-compat against 1.0.0 + embedded SBOM) and publishes a smoke-tested framework-dependent single-file CLI. The slow suite and differential tests stay opt-in on developer machines.
  8. 1.1.0 correctness review: a commit-by-commit audit pinned the fixes below with regression tests — retail ticket title keys are common-key decrypted (WiiVolumeTests; the synthetic Wii builders now store encrypted keys like real discs), the WBFS header declares the size relative to the stream position and carries the disc-header copy (WbfsWriterTests), extract reads TMD/cert/H3 at partition-relative offsets and skips H3 for hashless discs, convert -f iso --json keeps stdout JSON-only, convert --verify honors Ctrl+C (exit 130), writer progress is monotonic (ProgressReader), and parallel decode/encode failures surface the original exception instead of AggregateException (ParallelExecution).

Test files

File Covers
WiaFileHeadTests, WiaDiscTests, TableParserTests container structs, tables, hash validation
CompressionCodecTests, CompressionLzmaTests every codec round trip, props, LZMA1/LZMA2 framing
ExceptionListParserTests exception-list parsing: multiple lists, 4-byte alignment of the last list, truncation errors
ChunkDecoderTests, ChunkDecoderPackingTests group decoding, exception lists, packed chunks, every codec
PackingTests segment streams, mixed literal/junk, skip semantics
LaggedFibonacciGeneratorTests GetSeed at 11 offsets (incl. unaligned), random-data rejection, PRNG equivalence
RvzPackingEncoderTests pack → decode round trips, literal shortcut, zero-junk header
PartitionRegionBuilderTests hash tree, encryption, exceptions
WiiHashCalculatorTests, WiiPartitionExtractorTests h0/h1/h2 layout, exception application, AES region round trips and corruption detection
ScrubbedBlobTests, PlainBlobTests scrubbing of non-game partitions, plain-ISO reads and ownership
BlobDetectionTests magic-byte auto-detection
Adler32Tests, SpanReader/SectionStreamTests, NonDisposingStreamTests checksums, big-endian reads, section bounds, stream ownership
GczBlobTests, CisoBlobTests, WbfsBlobTests, TgcBlobTests, NfsBlobTests legacy decoders
WiaReaderTests, RvzReaderTests, RvzReaderMatrixTests full-container decoding across codecs/chunk sizes
RvzWriterTests writer round trips: GC + Wii (FST split, corrupted hashes, small chunks), legacy → RVZ → ISO, zero-image, all-zero Wii partition (zero groups), junk-only image, >2 MiB chunks, overlapping/odd partitions, scrubbing, raw-table group counts, MaxThreads determinism
WiaWriterTests WIA round trips across all five codecs (GC + Wii with hash exceptions), 4/6 MiB chunks, magic/version, PURGE, option validation, MaxThreads determinism
GczWriterTests GCZ round trips (GC + Wii), last-block zero padding, header fields, raw/compressed block storage, MaxThreads determinism, option validation
CisoWriterTests CISO round trips (GC + Wii), presence map, absent all-zero blocks, scrub shrinking, header fields, option validation
WbfsWriterTests WBFS round trips (Wii), shared zero cluster, header fields, disc-header copy, non-zero stream position, scrub, cluster-size/map limits, option validation
WiiVolumeTests ticket title-key decryption (common key, IV = title ID) and partition discovery returning plaintext keys
TgcWriterTests TGC round trips (GC), DOL/FST header fields and relocation, random access, GameCube-only validation
DiscVerifierTests Wii hash-tree verification: valid unencrypted + encrypted partitions, corrupt data/hash areas, H3/TMD mismatches, truncation, GameCube/non-disc reports
ParallelDecodeTests parallel CopyTo equals sequential for GC/Wii/WIA (multi-batch, multi-region chunks, exceptions), progress, cancellation, non-RVZ fallback
ConcurrentReadTests thread-safe ReadAt: parallel random reads on GC/Wii match the reference ISO; concurrent ReadFully + ReadAt
LruCacheTests decoded-unit LRU: hits, eviction, recency refresh, oversized values, concurrent misses
ParserRobustnessTests mutation/fuzz: corrupt containers and magic-prefixed garbage fail only with RvzException; hostile table counts are capped
DiscInfoTests, DiscInfoEdgeCaseTests disc metadata: game/maker ID, revision, name, region, country, title ID, fallbacks, the full country-code table, region words, CP1252/Shift-JIS names
ScrubOptionTests RvzWriteOptions.Scrub zeroes non-game partitions while keeping the game partition byte-exact
AsyncApiTests ReadFullyAsync/CopyToAsync/WriteAsync equal their synchronous forms, cancellation
DiscFileSystemTests FST parsing (GC + decrypted Wii partitions), case-insensitive lookup, file reads, PartitionReader decryption, every invalid-FST rejection path
WiiVolumeEdgeCaseTests offset-shift detection, boot DOL/FST header reads, disc-header flags, hostile partition tables, retail/Korean/RVT title keys, partition offset math
BlobFactoryTests magic sniffing, split plain ISOs, stream ownership, the NFS key overload, blob-type names
IoUtilityTests SpanReader bounds, CRC-32 check values, ParallelExecution exception preservation, CodecErrorStream mapping, MultiPartStream concatenation
NfsBlobEdgeCaseTests NFS header validation, continuation-file lookup, out-of-range/block-boundary reads, disposal ownership
RVZSharp.Cli.Tests option parsing, extract path guards, shell completions (bash/zsh/fish/PowerShell) and update-check tag parsing
RVZSharp.Slow.Tests/RealRvzFileTests.cs 276 real-file tests (see below)
RVZSharp.Slow.Tests/RealFileDecodeTests.cs env-var-driven real-file decode (RVZ_REAL_FILE/RVZ_REAL_SHA1)
RVZSharp.Slow.Tests/RealLegacyFileTests.cs real GCZ/CISO/WBFS/TGC/WIA/NFS decode to their expected SHA-1 (RVZ_REAL_GCZ … RVZ_REAL_NFS/RVZ_REAL_NFS_KEY)
RVZSharp.Slow.Tests/DifferentialToolTests.cs optional cross-checks against dolphin-tool/wit/wwt (RVZ_DOLPHIN_TOOL/RVZ_WIT/RVZ_WWT + RVZ_DIFF_ISO)

Real-file suite

RVZSharp.Slow.Tests/RealRvzFileTests.cs validates the library against actual game images on a local drive (F:\Nintendo GameCube / F:\Nintendo Wii). The expected ISO SHA-1s come from the official No-Intro DAT files in References/rvz-1.0.3/testdata/, so a passing test proves the decoder reproduces the original disc image byte-for-byte:

Every test no-ops (early-returns) when its file is absent, so the suite stays green on machines without the games. Running the real Wii round-trip against genuine images exposed and pinned a production writer bug (default 2 MiB chunks used the ISO ticket key instead of the RVZ partition-table key on re-signed No-Intro tickets); RvzWriter now prefers the container key and falls back to the ticket key for plain ISO inputs.

The 1.1.0 review also fixed the plain-ISO side of that story: the ticket’s title key is AES-CBC encrypted with the Wii common key on retail discs, and WiiVolume.GetPartitions now decrypts it (Dolphin: TicketReader::GetTitleKey) instead of using the raw ciphertext, so DiscVerifier/verify --partitions, DiscFileSystem/extract and RVZ writing work on real encrypted images. The synthetic builders (TestWiiIsoBuilder.WriteTicketKey) store common-key-encrypted keys so the fast suite exercises the same path.

Legacy-format real files are covered by RealLegacyFileTests through environment variables (RVZ_REAL_GCZ, RVZ_REAL_CISO, RVZ_REAL_WBFS, RVZ_REAL_TGC, RVZ_REAL_WIA, and RVZ_REAL_NFS + RVZ_REAL_NFS_KEY; each with an optional <VAR>_SHA1 expectation), and DifferentialToolTests optionally cross-checks our writers/reader against dolphin-tool (RVZ, both directions) and wit/wwt (our CISO/TGC output) when those tools are installed and RVZ_DIFF_ISO points at a real ISO. All of them no-op when unset.

Synthetic builders (RVZSharp.Tests/Helpers/)

Helper Purpose
TestRvzBuilder RVZ/WIA file builder (chunks, codecs, packing, partitions, exceptions)
TestLegacyBuilders GCZ, CISO, WBFS, TGC, NFS builders
TestWiiIsoBuilder realistic Wii ISO: disc header, partition table, RSA2048 ticket (title key common-key encrypted, like retail discs), encrypted partition data
ReferencePrng the junk PRNG used to generate padding in tests (matches the reader’s semantics)
TestCompressor reference encoders (deflate, bzip2, LZMA1/LZMA2, Zstd, Purge)

Key round-trip matrix

RvzWriterTests converts synthetic discs to RVZ and decodes them back, byte-for-byte:

Gotchas encoded in tests

Benchmarks

RVZSharp.Benchmarks (BenchmarkDotNet, net10.0) measures encode and decode throughput on a 16 MiB synthetic GameCube image (half random data, a quarter zeroes):

Class What it measures
EncodeBenchmarks RVZ per codec (Zstd/LZMA2/Bzip2/None, packing on/off), WIA LZMA2, GCZ deflate
DecodeBenchmarks full-image decode (one DiscHasher pass) for RVZ Zstd/LZMA2, WIA LZMA2 and GCZ
ThreadScalingBenchmarks RVZ Zstd at MaxThreads 1/2/4/8/0 (processor count)
# everything (takes a while — BenchmarkDotNet runs many iterations)
dotnet run -c Release --project RVZSharp.Benchmarks

# one class or method
dotnet run -c Release --project RVZSharp.Benchmarks -- --filter '*Encode*'

# list without running
dotnet run -c Release --project RVZSharp.Benchmarks -- --list flat

Results land in BenchmarkDotNet.Artifacts/ (git-ignored). Always use -c Release: Debug numbers are meaningless for codecs. The benchmark project is in the solution (so it keeps compiling) but is not a test project — dotnet test ignores it.