Skip to content

std/fuzz

std/fuzz — byte-stream fuzzing harness layered on std/test. A fuzz target is a function that takes a string of bytes (the fuzz input) and returns Option[string] — same shape as a regular test: None — the input passed all the property checks Some(msg) — a counter-example: msg describes the violation, the runner records both the message + the offending input (escaped) so the failure log doubles as a reproducer. Inputs are drawn from a seed corpus the caller supplies, plus mutated variants of each seed (byte flip / byte insert / byte delete). Iteration count is fixed per call (iterations arg) — lang has no panic-recovery yet, so a fuzz target that genuinely crashes (out-of-bounds index, division by zero) aborts the whole run; that’s the trade for not needing a coroutine / fiber primitive. Real fuzzing frameworks add coverage feedback (libFuzzer / AFL) — we don’t, but the API is shaped so coverage can layer in later without a breaking change. Usage: function check_no_panic(input: string): Option[string] { // try to parse input; return Some if a property // we care about doesn’t hold. return None; } function main(): i32 { var r: TestRunner = test_new(“parser fuzz”); r = r.fuzz(“parse”, [“function main(): i32 { return 0; }”, ""], 100, check_no_panic); return r.finish(); } r.fuzz(name, seeds, iterations, target) reports as one TAP case (“ok N - ” or “not ok N - ”). The counter-example input is included in the failure diagnostics so CI logs preserve enough state to reproduce locally without re-running the whole loop.

pub function fuzz_default_iterations(): i32

fuzz_default_iterations — used by callers that want the default loop count without pulling in a config constant per call site. Tuned to land sub-second runs on the small targets the migration uses; bigger property tests can pass a larger explicit count.

pub function fuzz_run(seeds: string[], iterations: i32, target: (string) => Option[string]): Option[string]

fuzz_run(name, seeds, iterations, target) — run the target function against iterations inputs drawn from seeds + mutations. Returns: None — every input passed Some(diagnostic) — the first failing input, with the target’s message and the escaped offending input embedded

Use through r.fuzz(...) rather than calling directly; the receiver-method form folds the result into the runner’s TAP stream.

pub function (r: test.TestRunner) fuzz(name: string, seeds: string[], iterations: i32, target: (string) => Option[string]): test.TestRunner

(r) fuzz(name, seeds, iterations, target) — run a fuzz target and report its outcome as a single TAP case. Pass at most one fuzz target per it-equivalent line so the failure attribution stays clean.

pub function fuzz_run_shrink(seeds: string[], iterations: i32, target: (string) => Option[string]): Option[string]

fuzz_run_shrink — run the regular fuzz loop, and on the first failure shrink the offending input to a minimal reproducer before returning. The diagnostic embeds both the original failing input AND the minimised one when they differ, so the log shows what the harness found AND what survived shrinking.

Counts shrink-time target(...) calls under a separate budget that does NOT inflate the user-visible iterations param — iterations is the mutation count, shrinking is a post-process amortised against the first failure.

pub function (r: test.TestRunner) fuzz_shrink(name: string, seeds: string[], iterations: i32, target: (string) => Option[string]): test.TestRunner

(r) fuzz_shrink — receiver-method form. Reports as one TAP case; on failure the case message embeds the minimised counter-example so CI logs preserve a clean reproducer.

pub function fuzz_corpus_from_dir(path: string): Result[string[], IoError]

fuzz_corpus_from_dir(path) — load every regular file under path as a seed string. Returns the seeds via Result so the caller can fall back to inline seeds when the corpus directory isn’t present (typical pattern: ship a small inline corpus + grow it on disk over time).

pub function fuzz_corpus_from_dir_or(path: string, fallback: string[]): string[]

fuzz_corpus_from_dir_or(path, fallback) — convenience for the typical “use the on-disk corpus when present, else fall back to inline seeds” pattern:

var seeds: string[] = fuzz_corpus_from_dir_or( “testdata/parser_corpus”, ["", “function main(): i32 { return 0; }”]);

Concatenates the on-disk seeds AFTER the fallback so the inline ones are guaranteed to run even if the disk corpus is empty.