std::test
Test Assertions
Native testing helpers for @test functions.
The std::test module provides the standard assertion and snapshot surface
used by ignis test.
It is intentionally small and stable:
Test::assert(condition)Test::assertEq<T>(left, right)Test::assertNe<T>(left, right)Test::fail()Test::assertSnapshot(name, actual)Test::assertFileSnapshot(name, filePath)
Design Goals
- keep language-level tests deterministic
- make failures explicit and easy to spot in runner output
- route generic equality through the canonical
std::hash::Eqcontract - store snapshots next to the source module under test
- avoid compiler-only test APIs leaking into user code
Assertions
assert, assertEq, and assertNe all fail the current test by calling
@panic(...) with a fixed message.
assertEq<T> and assertNe<T> rely on builtin @eq<T>. Supported T
includes:
- integer primitives
booleancharstr- records and enums implementing canonical
std::hash::Eq
Unsupported equality must be rejected during analysis before code generation or harness build.
Snapshots
Snapshot helpers compare UTF-8 text against deterministic files under a
sibling __snapshots__/ directory.
File naming is derived from:
- the fully-qualified test name
- the user-supplied snapshot label
Both parts are escaped so snapshots from different modules can safely reuse the same logical label.
Update behavior is driven by the native runner through environment variables:
IGNIS_TEST_NAMEIGNIS_TEST_SNAPSHOT_DIRIGNIS_TEST_UPDATE_SNAPSHOTS
With update mode enabled, missing or mismatched snapshots are written back to disk. Without update mode, the helpers fail the current test.
Failure Output
Snapshot mismatches print small, bounded diagnostics to stderr before panicking:
- failure reason
- snapshot path
- expected byte count
- actual byte count
The native runner is responsible for truncating long stderr/stdout blocks in the final report.
Example
import Test from "std::test";
@test
function smoke(): void {
Test::assert(true);
Test::assertEq<i32>(2 + 2, 4);
}
Generic Equality Example
import Eq from "std::hash";
import Test from "std::test";
@implements(Eq)
record UserId {
public value: i32;
equals(&self, other: &UserId): boolean {
return self.value == other.value;
}
}
@test
function compareUserIds(): void {
let left: UserId = UserId { value: 7 };
let right: UserId = UserId { value: 7 };
let other: UserId = UserId { value: 8 };
Test::assertEq<UserId>(left, right);
Test::assertNe<UserId>(left, other);
}
Snapshot Example
import Test from "std::test";
@test
function snapshotRenderedOutput(): void {
Test::assertSnapshot("rendered", "hello snapshot\n");
}
File Snapshot Example
import Test from "std::test";
@test
function snapshotGeneratedFile(): void {
Test::assertFileSnapshot("artifact", "./actual-output.txt");
}
namespace TestAssertion and snapshot helpers for @test bodies.
Test is a pure namespace surface used by the native ignis test runner.
It does not hold state; instead, snapshot helpers read runner-provided
environment variables to discover the active test name, snapshot directory,
and update mode.
Equality assertions route through canonical builtin @eq<T> dispatch.
Snapshot assertions compare or update UTF-8 text files in a sibling
__snapshots__/ directory.
Functions
function assert(condition: boolean): voidFails the current test when condition is false.
Arguments
condition- Boolean condition that must evaluate totrue.
Panics
Panics with "assertion failed" when condition is false.
Example
import Test from "std::test";
@test
function smoke(): void {
Test::assert(2 + 2 == 4);
}
function assertEq<T>(left: T, right: T): voidFails the current test when two values are not equal under canonical Eq.
T must be supported by builtin @eq<T>. For user-defined records and
enums this means implementing canonical std::hash::Eq with a valid
equals(&self, other: &T): boolean method.
Type Parameters
T- The type being compared.
Arguments
left- Left-hand value.right- Right-hand value.
Panics
Panics with "assertion failed: values are not equal" when the values do
not compare equal.
Example
import Test from "std::test";
@test
function compareNumbers(): void {
Test::assertEq<i32>(40 + 2, 42);
Test::assertEq<str>("abc", "abc");
}
function assertFileSnapshot(name: str, filePath: str): voidReads filePath as UTF-8 text before applying snapshot comparison.
This is useful when the code under test writes an artifact to disk and the test wants the snapshot baseline to track the file contents.
Arguments
name- Logical snapshot label within the current test.filePath- Path to a UTF-8 text file to snapshot.
Panics
Panics with "snapshot assertion failed" if the input file cannot be
read, if the snapshot mismatches without update mode, or if snapshot I/O
fails.
Example
import Test from "std::test";
@test
function snapshotFile(): void {
Test::assertFileSnapshot("artifact", "./actual-output.txt");
}
function assertNe<T>(left: T, right: T): voidFails the current test when two values are equal under canonical Eq.
This is the inverse of assertEq<T>.
Type Parameters
T- The type being compared.
Arguments
left- Left-hand value.right- Right-hand value.
Panics
Panics with "assertion failed: values are equal" when the values compare
equal.
Example
import Test from "std::test";
@test
function compareDifferentValues(): void {
Test::assertNe<i32>(1, 2);
}
function assertSnapshot(name: str, actual: str): voidFails the current test when the named text snapshot does not match.
The snapshot file is resolved from the current test name and the snapshot
label, then stored in a sibling __snapshots__/ directory next to the
module under test.
Arguments
name- Logical snapshot label within the current test.actual- UTF-8 text to compare or write.
Update Mode
When ignis test --update-snapshots is active, missing or mismatched
snapshots are written to disk instead of failing the test.
Example
import Test from "std::test";
@test
function snapshotText(): void {
Test::assertSnapshot("rendered", "hello snapshot\n");
}
function fail(): voidFails the current test unconditionally.
Use this when a branch should be treated as an immediate test failure.
Panics
Always panics with "test failed".
Example
import Test from "std::test";
@test
function forcedFailure(): void {
Test::fail();
}
Functions
function assertSnapshotText(name: str, actual: &String): voidInternal text snapshot engine shared by Test::assertSnapshot and
Test::assertFileSnapshot.
This resolves runner context, computes the snapshot file path, performs compare-or-update behavior, and emits bounded mismatch diagnostics before panicking on failure.
function escapeSnapshotComponent(value: &String): StringEscapes a file-name component into a deterministic ASCII-safe string.
Safe bytes are kept as-is. All other bytes are rewritten as
_<hex><hex>.
Arguments
value- Raw component to normalize for use in a file name.
function failSnapshotIo(reason: str, snapshotPath: str, detail: str): voidEmits a standard snapshot I/O diagnostic and panics.
Arguments
reason- High-level failure category.snapshotPath- Path associated with the failed I/O operation.detail- Small detail string included in stderr output.
function failSnapshotMismatch(snapshotPath: &String, expectedBytes: u64, actualBytes: u64): voidEmits a standard mismatch diagnostic and panics.
The byte counts are reported to keep diagnostics stable and compact even for large snapshot bodies.
Arguments
snapshotPath- Snapshot file path.expectedBytes- Byte length of the baseline snapshot.actualBytes- Byte length of the actual snapshot text.
function failSnapshotMissing(snapshotPath: &String): voidEmits a standard snapshot missing failure and panics.
Arguments
snapshotPath- Missing snapshot file path.
function hexDigit(value: u8): charConverts a nibble (0..=15) to its lowercase hexadecimal digit.
function isSafeSnapshotByte(byte: u8): booleanReturns whether a byte may appear unchanged in a snapshot file name.
Safe bytes are limited to ASCII letters, digits, -, ., and _.
function printSnapshotPath(snapshotPath: &String): voidPrints the normalized snapshot: ... line shared by all snapshot failure
diagnostics.
Arguments
snapshotPath- Snapshot path to print in user-facing diagnostics.
function requiredSnapshotEnv(name: str): StringReads a required test-runner environment variable as an owned String.
Arguments
name- Name of the environment variable to read.
Panics
Panics with "snapshot assertion failed" if the runner did not provide
the required snapshot context variable.
function snapshotFileName(testName: &String, snapshotName: str): StringProduces the deterministic on-disk file name for a snapshot.
The file name layout is:
<escaped-test-name>__<escaped-snapshot-name>.snap.txt
Escaping prevents collisions with path separators, ::, spaces, and
other punctuation while keeping ASCII-safe names for the filesystem.
Arguments
testName- Fully-qualified test name.snapshotName- Logical snapshot label.
function snapshotPath(snapshotDir: &String, testName: &String, snapshotName: str): PathBufBuilds the final snapshot path for the current test and snapshot label.
The resulting file lives in the test’s snapshot directory and uses the
escaped file name returned by snapshotFileName(...).
Arguments
snapshotDir- Snapshot directory provided by the runner.testName- Fully-qualified test name.snapshotName- Logical snapshot label within the test.
function snapshotUpdateModeEnabled(): booleanReturns whether snapshot update mode is enabled for the current test run.
The native runner encodes this as IGNIS_TEST_UPDATE_SNAPSHOTS=1.
function writeSnapshot(snapshotDir: &String, snapshotPath: &PathBuf, actual: &String): voidWrites snapshot text to disk, creating the snapshot directory first.
Arguments
snapshotDir- Parent snapshot directory.snapshotPath- Full path to the snapshot file.actual- Snapshot contents to write.
Panics
Panics with "snapshot assertion failed" if directory creation or file
writing fails, or if the write reports a short write.