Squinch tests
The correctness test suite is run with make test. The target first builds
both command-line programs in .build/, then runs every executable
tests/stage*/test-*.sh script in shell glob order. A failure stops the
suite. Each script prints its own PASSED or FAILED status; colours are
used only when standard output is a terminal whose TERM is not dumb.
The shell tests use SQUINCH_BINARY, UNSQUINCH_BINARY, and
SQUINCH_OBJECT_DIR when they are set by make test. If they are not set,
the tests use ./.build/squinch, ./.build/unsquinch, and ./.build/obj.
Most temporary data is removed when each test exits. Stage 3 keeps its
generated outputs in a temporary directory and removes them when the test
exits.
Running the tests
Run the complete correctness suite from the project directory:
make test
The stages are arranged from build and command-line checks to API and format
checks, then to larger external fixtures. By default, make test discovers
and runs every stage. Set TEST_STAGES to a space-separated list of stage
numbers to discover only those stages:
make TEST_STAGES='1 2' test
Stage 1: build and command line
tests/stage1/test-build.sh
This test checks the build result rather than compression behavior. It
verifies that the squinch and unsquinch executables exist at the selected
paths and are executable. It also verifies that every C source file in
src/ has a corresponding object file in the selected object directory.
Finally, it rejects object files written directly into src/.
Its purpose is to catch missing build outputs, incomplete compilation, and source-tree pollution.
tests/stage1/test-cli.sh
This test checks the public command-line interface of both programs. It verifies that -h, -help, and --help succeed without standard-error output and include USAGE, OPTIONS, and NOTES. It checks compressor help for the match-limit and look-ahead options, the compression syntax, the valid window range, and case-insensitive size parsing.
It checks that --version reports the Makefile version. It checks missing arguments and misplaced or unknown options, including --match-limit before the operation. It verifies that the implicit default match limit and an explicit limit of 128 produce identical output, and exercises a match limit of 1. It also verifies that the implicit 50% look-ahead size matches an explicit size, accepts byte, kiB, and MiB sizes, and rejects invalid look-ahead sizes. Compression with a fixed look-ahead size must produce identical bytes across caller input-buffer sizes.
The test also verifies that -- permits an input file named --help, that
window suffix parsing is case insensitive, and that both d and unsquinch
decode the result back to the original bytes.
Its purpose is to protect command syntax, diagnostics, help and version output, option placement, path handling, and the CLI round-trip contract.
Stage 2: API and format behavior
tests/stage2/test-decoder-behavior.sh
This test compiles and runs tests/stage2/lib/decoder-behavior.c with the
decompressor implementation. The C test checks decoding with a zero-sized
window, deferred window allocation, RLE output that crosses the ring
position, recent and large copies, and a copy that wraps around the ring.
It also supplies malformed streams for an invalid escape, invalid RLE length, invalid recent copy, invalid medium copy, invalid large copy, and a copy longer than its unchanged source range. Both decoder APIs must return InvalidInput_SquinchResult for these streams.
It also checks invalid maximum window sizes, a stream exceeding a maximum window, an undersized decompression output buffer, and input after end of data.
The shell wrapper also verifies that unsquinch rejects an invalid escape
value and a reserved RLE length.
It also exercises valid binary, pseudo-random, and repetitive inputs through
both decompressor CLIs, including stdin and stdout streaming with -.
Its purpose is to test decoder edge cases and reject reserved or impossible instructions before they can cause incorrect output or invalid ring access.
tests/stage2/test-encoder-behavior.sh
This test compiles and runs tests/stage2/lib/encoder-behavior.c with the compressor implementation. The C test checks compressor API errors, look-ahead acceptance under output back pressure, end-of-output behavior, and immediate instruction selection. Its exact-output cases cover recent-copy instruction lengths, no-saving literals, escaped-literal savings, medium and full copies, full-copy length boundaries, category tie-breaking, and the match-limit search boundary.
Its purpose is to test encoder error results and terminal states independently of the buffer API round-trip coverage.
The shell wrapper also checks exact compressed streams for literals and copies, including a copy that wraps the ring.
Stage 3: fixture round-trip behavior
tests/stage3/test-roundtrip-bs.sh
This test compiles and runs tests/stage2/lib/stream-roundtrip.c for every
generated *.input fixture and every external fixture except SOURCES.md,
using buffer sizes from the minimum to the maximum configured size. That
program reads one fixture, feeds the compressor in chunks, drains compressed
output in chunks, then feeds that stream to the decompressor in the same
range of chunk sizes and compares the decoded bytes with the original file.
Its purpose is to test round trips across a broad range of streaming buffer sizes.
tests/stage3/test-roundtrip-ws.sh
This test uses every generated *.input fixture and every file in
tests/fixtures/external/ except SOURCES.md directly. For each fixture, it
compresses and decompresses the original file at every configured window size
from the minimum to the maximum. Each decoded file must compare byte-for-byte
with the original input.
Its purpose is to exercise the complete CLI round trip on varied real-world and binary inputs across the entire supported window-size range. It does not assert a particular compressed size or instruction sequence.
The fixture sources and their licensing information are documented in tests/fixtures/external/SOURCES.md. The generated fixtures used by the format-consistency test contain focused cases for literals, escaped literals, fixed and variable copies, recent copies, large copies, RLE lengths and splitting, and a short recent match that must lose to an older longer match.
Stage 4: format consistency
tests/stage4/test-format-consistency.sh
This test compresses every *.input file in
tests/fixtures/generated/ with an 8 kiB window. It converts each resulting
stream to a continuous lowercase hexadecimal representation and compares it
with the matching .squinch.hex file.
Its purpose is to make the encoded byte stream a golden-file contract. A changed instruction choice, header, length, offset, or byte order fails the test even when decompression still produces the original input.
Stage 5: shell decompressor
tests/stage5/test-unsquinch-shell.sh
This test checks src/unsquinch.sh. It verifies that its help output is
consistent across all help options, that it reports the same version as the
compiled decoder, and that it decompresses generated and external fixtures.
It also checks standard-input and standard-output operation and the --
option delimiter.
Its purpose is to verify the portable shell decoder independently of a particular compiled output target.
Test support files
tests/lib/lib-output.sh
This shared shell library provides the status output used by all shell test
runners. testSuccess prints the calling script name and PASSED.
testFail prints the calling script name and FAILED, formats the supplied
diagnostic, and exits with status 1. The helper also prevents duplicate
inclusion and enables status colours only for an appropriate terminal.
Its purpose is to keep test failure status, diagnostics, exit codes, and terminal output consistent across stages.
tests/stage2/lib/stream-roundtrip.c
This helper program is invoked by test-roundtrip-bs.sh with an input path and a positive chunk size. It reads the complete input into memory, compresses it by repeatedly supplying input and output buffers no larger than the requested size, drains the compressor with zero input, then decompresses the resulting stream with buffers of the same maximum size. It handles partial input consumption and compares the complete decoded output to the original input.
Its purpose is to test the incremental API with deliberately small caller buffers while keeping the shell runner focused on fixture selection.
tests/stage2/lib/decoder-behavior.c and
tests/stage2/lib/encoder-behavior.c
These are standalone C assertion programs. They return zero only when all checks pass and otherwise return one. The shell wrappers compile them with the project warning and language settings, execute them, and remove the temporary binaries afterward.
Benchmarks
The benchmark runners are not included in make test. Run all of them with
make bench. They build optimized binaries in .benchmark/, use temporary
working data, verify every compressed result by decompressing it, and write
CSV data to standard output. Build and dependency diagnostics go to standard
error.
All benchmark runners use the external fixtures selected by the common file
patterns in util/benchmark/, and compare Squinch with LZ4 and gzip where
shown. The timing runners repeat each command five times and report total
elapsed milliseconds, so their results are measurements rather than pass or
fail performance thresholds.
bench-compare-compression-size.sh
Reports uncompressed size and compressed size for Squinch 8K/32 and 64K/128,
LZ4 -1 and --best, and gzip -1 and -9.
bench-compare-compression-time.sh
Reports five-run compression time for Squinch 8K/32 and 64K/128, LZ4 -1
and --best, and gzip -1 and -9.
bench-compare-decompression-time.sh
Creates valid compressed data for all compared compressors, then reports
five-run decompression time for Squinch 8K/32 and 64K/128, LZ4 -1 and
--best, and gzip -1 and -9.
bench-match_limits-size.sh
Reports Squinch compressed size for match limits 1, 16, 32, 64, 128, 256, and 512 using a 64 kiB window.
bench-match_limits-time.sh
Reports five-run Squinch compression time for match limits 1, 16, 32, 64, 128, 256, and 512 using a 64 kiB window.
bench-window_size-size.sh
Reports Squinch compressed size for 256-byte, 4 kiB, and 64 kiB windows.
bench-window_size-time.sh
Reports five-run Squinch compression time for 256-byte, 4 kiB, and 64 kiB windows.