Artifact bdf782378f868f89f2657b923830b1a20a27498e5bcaf7c8e0f27e06b7d5a739:
- File
src/delta_fuzz.zig
— part of check-in
[c4c4ad80d1]
at
2026-07-09 19:55:09
on branch trunk
— Wire up a seeded fuzz build step and give apply() and targetSize() fuzz coverage
Add zig build fuzz, dispatching to named seeded targets in fuzz_tests.zig via a tuple->FieldEnum table, plus bench/smoke stubs that fail until those milestones land. delta_fuzz.zig parameterizes the existing apply/create/ decodeInt property checks by seed and round count for an out-of-band runner. delta.zig gains an allocation-failure test for apply() mirroring create()'s, and a targetSize() garbage-never-crashes block. (user: dmestas, size: 5171) [annotate] [blame] [check-ins using]
//! Seeded, parameterized fuzz targets for the delta format //! (draft-fossil-delta-format-00). Each target mirrors one of the fixed-seed //! blocks in delta.zig's own test suite, but takes its seed and round count //! from the caller (`zig build fuzz -- <target> [--seed N] [--rounds N]`) //! instead of a hardcoded constant, so an out-of-band runner can sweep many //! seeds at much higher round counts. Registered in fuzz_tests.zig. const std = @import("std"); const delta = @import("zit").delta; /// `apply` on arbitrary source+delta bytes never crashes: only a declared /// `Error`/`Allocator.Error` or a freed value comes back (delta §4 /// robustness; mirrors delta.zig's "apply on arbitrary source+delta never /// crashes" block). pub fn applyGarbage(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void { var prng = std.Random.DefaultPrng.init(seed); const rand = prng.random(); var round: usize = 0; while (round < rounds) : (round += 1) { var sbuf: [128]u8 = undefined; var dbuf: [160]u8 = undefined; const slen = rand.uintLessThan(usize, sbuf.len + 1); const dlen = rand.uintLessThan(usize, dbuf.len + 1); for (sbuf[0..slen]) |*b| b.* = rand.int(u8); // Bias delta bytes toward DIGIT64/opcodes so inputs reach deep into // the decoder rather than being rejected at byte 0. for (dbuf[0..dlen]) |*b| b.* = if (rand.boolean()) rand.int(u8) else delta.digit64[rand.uintLessThan(usize, delta.digit64.len)]; if (delta.apply(gpa, sbuf[0..slen], dbuf[0..dlen])) |out| gpa.free(out) else |_| {} } } /// `apply(source, create(source, target)) == target` for arbitrary inputs /// (delta §5 round-trip; mirrors delta.zig's round-trip block). pub fn roundTrip(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void { var prng = std.Random.DefaultPrng.init(seed); const rand = prng.random(); var round: usize = 0; while (round < rounds) : (round += 1) { var sbuf: [256]u8 = undefined; const slen = rand.uintLessThan(usize, sbuf.len + 1); for (sbuf[0..slen]) |*b| b.* = rand.int(u8); const source = sbuf[0..slen]; // Build target from a mix of source runs (exercises copies) and // fresh random bytes (exercises literals). var tbuf: [384]u8 = undefined; const tlen = rand.uintLessThan(usize, tbuf.len + 1); var t: usize = 0; while (t < tlen) { if (slen >= 8 and rand.boolean()) { const start = rand.uintLessThan(usize, slen); const run = 1 + rand.uintLessThan(usize, @min(slen - start, tlen - t)); @memcpy(tbuf[t..][0..run], source[start..][0..run]); t += run; } else { tbuf[t] = rand.int(u8); t += 1; } } const target = tbuf[0..tlen]; const d = try delta.create(gpa, source, target); defer gpa.free(d); const out = try delta.apply(gpa, source, d); defer gpa.free(out); try std.testing.expectEqualSlices(u8, target, out); } } /// `decodeInt` on arbitrary digit strings stays bounded: consumes no more /// than the input, and the only error it ever returns is `MalformedDelta` /// (delta §3.1; mirrors delta.zig's "decodeInt on arbitrary digit strings /// stays bounded" block). pub fn decodeIntBounded(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void { _ = gpa; var prng = std.Random.DefaultPrng.init(seed); const rand = prng.random(); var round: usize = 0; while (round < rounds) : (round += 1) { var buf: [40]u8 = undefined; const len = rand.uintLessThan(usize, buf.len + 1); // Bias toward DIGIT64 so long all-digit runs (overflow candidates) occur. for (buf[0..len]) |*b| b.* = if (rand.uintLessThan(u8, 4) == 0) rand.int(u8) else delta.digit64[rand.uintLessThan(usize, delta.digit64.len)]; if (delta.decodeInt(buf[0..len])) |dec| { try std.testing.expect(dec.len <= len); // consumed no more than the input } else |err| { try std.testing.expect(err == error.MalformedDelta); // rejection is the only error } } } /// `targetSize` on arbitrary bytes never crashes: only `MalformedDelta` or a /// value within the u32 cap `decodeInt` enforces comes back (delta §4.1). pub fn targetSizeGarbage(gpa: std.mem.Allocator, seed: u64, rounds: usize) !void { _ = gpa; var prng = std.Random.DefaultPrng.init(seed); const rand = prng.random(); var round: usize = 0; while (round < rounds) : (round += 1) { var buf: [64]u8 = undefined; const len = rand.uintLessThan(usize, buf.len + 1); for (buf[0..len]) |*b| b.* = if (rand.boolean()) rand.int(u8) else delta.digit64[rand.uintLessThan(usize, delta.digit64.len)]; if (delta.targetSize(buf[0..len])) |size| { try std.testing.expect(size <= std.math.maxInt(u32)); } else |err| { try std.testing.expect(err == error.MalformedDelta); } } }