Something went wrong. Try again.
A Zig library that reads git repositories without running git, with log, show and diff output byte-identical to git's.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Inputs for the fuzz tests of the format parsers.//!//! A corpus entry is a file exactly as git wrote it, with no framing of its//! own, so when the test runner replays one (`smith.in` is set) the whole of//! it is the input. Only when the fuzzer is driving is the input asked of//! the `Smith`, which reads a length before the bytes.
const std = @import("std");const builtin = @import("builtin");const Smith = std.testing.Smith;
/// `std.testing.fuzz`, but in a test run with `ZIG_GIT_TEST_NO_FUZZ` set/// the corpus is only replayed, as `std.testing.fuzz` replays it in a run/// that is not fuzzing, without the test being declared a fuzz test.////// `zig build test` runs the test binary twice, the second time with/// SHA-256 repositories, and `zig build test --fuzz` fuzzes every run that/// declared a fuzz test: both runs of the one binary at once, sharing its/// corpus and its coverage, which ends in a failure that is the tooling's/// and not a finding. The second run sets this, so that it still replays/// every corpus entry and only the first is fuzzed.pub fn fuzz( context: anytype, comptime testOne: fn (context: @TypeOf(context), smith: *Smith) anyerror!void, options: std.testing.FuzzInputOptions,) anyerror!void { if (!builtin.fuzz and replayOnly()) { for (options.corpus) |input| { var smith: Smith = .{ .in = input }; try testOne(context, &smith); } var smith: Smith = .{ .in = "" }; return testOne(context, &smith); } return std.testing.fuzz(context, testOne, options);}
fn replayOnly() bool { const gpa = std.testing.allocator; const value = std.testing.environ.getAlloc(gpa, "ZIG_GIT_TEST_NO_FUZZ") catch return false; defer gpa.free(value); return value.len != 0;}
/// The bytes to parse: the corpus entry being replayed, or up to/// `buffer.len` bytes from the fuzzer.pub fn bytes(smith: *Smith, buffer: []u8) []const u8 { if (smith.in) |in| { smith.in = in[in.len..]; return in; } return buffer[0..smith.slice(buffer)];}
/// An allocator for a fuzz test, passing everything on to `backing` but/// refusing, as out of memory, any one allocation bigger than `limit`, and/// remembering that it did. The inputs a fuzz test is given are a few/// kilobytes, so an allocation of many megabytes means the library took a/// size from the input and believed it. Under `std.testing.allocator`,/// which fills what is freed, such an allocation is not merely reserved/// but written to end to end: one input claiming fifty gigabytes did that,/// and the fuzzer was killed for the memory, taking the machine's other/// work with it. Here it is a test failure instead.pub const Bounded = struct { backing: std.mem.Allocator, limit: usize, /// The size of the first allocation refused, if any was. refused: ?usize = null,
pub fn allocator(b: *Bounded) std.mem.Allocator { return .{ .ptr = b, .vtable = &.{ .alloc = alloc, .resize = resize, .remap = remap, .free = free, } }; }
/// Fails the test if anything was refused. pub fn check(b: *const Bounded) error{TestUnexpectedResult}!void { if (b.refused) |n| { std.debug.print("an input of a few kilobytes asked for an allocation of {d} bytes\n", .{n}); return error.TestUnexpectedResult; } }
fn refuse(b: *Bounded, len: usize) bool { if (len <= b.limit) return false; if (b.refused == null) b.refused = len; return true; }
fn alloc(ptr: *anyopaque, len: usize, alignment: std.mem.Alignment, ret_addr: usize) ?[*]u8 { const b: *Bounded = @ptrCast(@alignCast(ptr)); if (b.refuse(len)) return null; return b.backing.rawAlloc(len, alignment, ret_addr); }
fn resize(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) bool { const b: *Bounded = @ptrCast(@alignCast(ptr)); if (b.refuse(new_len)) return false; return b.backing.rawResize(memory, alignment, new_len, ret_addr); }
fn remap(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, new_len: usize, ret_addr: usize) ?[*]u8 { const b: *Bounded = @ptrCast(@alignCast(ptr)); if (b.refuse(new_len)) return null; return b.backing.rawRemap(memory, alignment, new_len, ret_addr); }
fn free(ptr: *anyopaque, memory: []u8, alignment: std.mem.Alignment, ret_addr: usize) void { const b: *Bounded = @ptrCast(@alignCast(ptr)); b.backing.rawFree(memory, alignment, ret_addr); }};