master
   1const std = @import("std.zig");
   2const builtin = @import("builtin");
   3const assert = std.debug.assert;
   4const math = std.math;
   5
   6/// Provides deterministic randomness in unit tests.
   7/// Initialized on startup. Read-only after that.
   8pub var random_seed: u32 = 0;
   9
  10pub const FailingAllocator = @import("testing/FailingAllocator.zig");
  11pub const failing_allocator = failing_allocator_instance.allocator();
  12var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.allocator(), .{
  13    .fail_index = 0,
  14});
  15var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
  16
  17/// This should only be used in temporary test programs.
  18pub const allocator = allocator_instance.allocator();
  19pub var allocator_instance: std.heap.GeneralPurposeAllocator(.{
  20    .stack_trace_frames = if (std.debug.sys_can_stack_trace) 10 else 0,
  21    .resize_stack_traces = true,
  22    // A unique value so that when a default-constructed
  23    // GeneralPurposeAllocator is incorrectly passed to testing allocator, or
  24    // vice versa, panic occurs.
  25    .canary = @truncate(0x2731e675c3a701ba),
  26}) = b: {
  27    if (!builtin.is_test) @compileError("testing allocator used when not testing");
  28    break :b .init;
  29};
  30
  31pub var io_instance: std.Io.Threaded = undefined;
  32pub const io = io_instance.io();
  33
  34/// TODO https://github.com/ziglang/zig/issues/5738
  35pub var log_level = std.log.Level.warn;
  36
  37// Disable printing in tests for simple backends.
  38pub const backend_can_print = switch (builtin.zig_backend) {
  39    .stage2_aarch64,
  40    .stage2_powerpc,
  41    .stage2_riscv64,
  42    .stage2_spirv,
  43    => false,
  44    else => true,
  45};
  46
  47fn print(comptime fmt: []const u8, args: anytype) void {
  48    if (@inComptime()) {
  49        @compileError(std.fmt.comptimePrint(fmt, args));
  50    } else if (backend_can_print) {
  51        std.debug.print(fmt, args);
  52    }
  53}
  54
  55/// This function is intended to be used only in tests. It prints diagnostics to stderr
  56/// and then returns a test failure error when actual_error_union is not expected_error.
  57pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void {
  58    if (actual_error_union) |actual_payload| {
  59        print("expected error.{s}, found {any}\n", .{ @errorName(expected_error), actual_payload });
  60        return error.TestExpectedError;
  61    } else |actual_error| {
  62        if (expected_error != actual_error) {
  63            print("expected error.{s}, found error.{s}\n", .{
  64                @errorName(expected_error),
  65                @errorName(actual_error),
  66            });
  67            return error.TestUnexpectedError;
  68        }
  69    }
  70}
  71
  72/// This function is intended to be used only in tests. When the two values are not
  73/// equal, prints diagnostics to stderr to show exactly how they are not equal,
  74/// then returns a test failure error.
  75/// `actual` and `expected` are coerced to a common type using peer type resolution.
  76pub inline fn expectEqual(expected: anytype, actual: anytype) !void {
  77    const T = @TypeOf(expected, actual);
  78    return expectEqualInner(T, expected, actual);
  79}
  80
  81fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
  82    switch (@typeInfo(@TypeOf(actual))) {
  83        .noreturn,
  84        .@"opaque",
  85        .frame,
  86        .@"anyframe",
  87        => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
  88
  89        .undefined,
  90        .null,
  91        .void,
  92        => return,
  93
  94        .type => {
  95            if (actual != expected) {
  96                print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
  97                return error.TestExpectedEqual;
  98            }
  99        },
 100
 101        .bool,
 102        .int,
 103        .float,
 104        .comptime_float,
 105        .comptime_int,
 106        .enum_literal,
 107        .@"enum",
 108        .@"fn",
 109        .error_set,
 110        => {
 111            if (actual != expected) {
 112                print("expected {any}, found {any}\n", .{ expected, actual });
 113                return error.TestExpectedEqual;
 114            }
 115        },
 116
 117        .pointer => |pointer| {
 118            switch (pointer.size) {
 119                .one, .many, .c => {
 120                    if (actual != expected) {
 121                        print("expected {*}, found {*}\n", .{ expected, actual });
 122                        return error.TestExpectedEqual;
 123                    }
 124                },
 125                .slice => {
 126                    if (actual.ptr != expected.ptr) {
 127                        print("expected slice ptr {*}, found {*}\n", .{ expected.ptr, actual.ptr });
 128                        return error.TestExpectedEqual;
 129                    }
 130                    if (actual.len != expected.len) {
 131                        print("expected slice len {}, found {}\n", .{ expected.len, actual.len });
 132                        return error.TestExpectedEqual;
 133                    }
 134                },
 135            }
 136        },
 137
 138        .array => |array| try expectEqualSlices(array.child, &expected, &actual),
 139
 140        .vector => |info| {
 141            const expect_array: [info.len]info.child = expected;
 142            const actual_array: [info.len]info.child = actual;
 143            try expectEqualSlices(info.child, &expect_array, &actual_array);
 144        },
 145
 146        .@"struct" => |structType| {
 147            inline for (structType.fields) |field| {
 148                try expectEqual(@field(expected, field.name), @field(actual, field.name));
 149            }
 150        },
 151
 152        .@"union" => |union_info| {
 153            if (union_info.tag_type == null) {
 154                const first_size = @bitSizeOf(union_info.fields[0].type);
 155                inline for (union_info.fields) |field| {
 156                    if (@bitSizeOf(field.type) != first_size) {
 157                        @compileError("Unable to compare untagged unions with varying field sizes for type " ++ @typeName(@TypeOf(actual)));
 158                    }
 159                }
 160
 161                const BackingInt = std.meta.Int(.unsigned, @bitSizeOf(T));
 162                return expectEqual(
 163                    @as(BackingInt, @bitCast(expected)),
 164                    @as(BackingInt, @bitCast(actual)),
 165                );
 166            }
 167
 168            const Tag = std.meta.Tag(@TypeOf(expected));
 169
 170            const expectedTag = @as(Tag, expected);
 171            const actualTag = @as(Tag, actual);
 172
 173            try expectEqual(expectedTag, actualTag);
 174
 175            // we only reach this switch if the tags are equal
 176            switch (expected) {
 177                inline else => |val, tag| try expectEqual(val, @field(actual, @tagName(tag))),
 178            }
 179        },
 180
 181        .optional => {
 182            if (expected) |expected_payload| {
 183                if (actual) |actual_payload| {
 184                    try expectEqual(expected_payload, actual_payload);
 185                } else {
 186                    print("expected {any}, found null\n", .{expected_payload});
 187                    return error.TestExpectedEqual;
 188                }
 189            } else {
 190                if (actual) |actual_payload| {
 191                    print("expected null, found {any}\n", .{actual_payload});
 192                    return error.TestExpectedEqual;
 193                }
 194            }
 195        },
 196
 197        .error_union => {
 198            if (expected) |expected_payload| {
 199                if (actual) |actual_payload| {
 200                    try expectEqual(expected_payload, actual_payload);
 201                } else |actual_err| {
 202                    print("expected {any}, found {}\n", .{ expected_payload, actual_err });
 203                    return error.TestExpectedEqual;
 204                }
 205            } else |expected_err| {
 206                if (actual) |actual_payload| {
 207                    print("expected {}, found {any}\n", .{ expected_err, actual_payload });
 208                    return error.TestExpectedEqual;
 209                } else |actual_err| {
 210                    try expectEqual(expected_err, actual_err);
 211                }
 212            }
 213        },
 214    }
 215}
 216
 217test "expectEqual.union(enum)" {
 218    const T = union(enum) {
 219        a: i32,
 220        b: f32,
 221    };
 222
 223    const a10 = T{ .a = 10 };
 224
 225    try expectEqual(a10, a10);
 226}
 227
 228test "expectEqual union with comptime-only field" {
 229    const U = union(enum) {
 230        a: void,
 231        b: void,
 232        c: comptime_int,
 233    };
 234
 235    try expectEqual(U{ .a = {} }, .a);
 236}
 237
 238test "expectEqual nested array" {
 239    const a = [2][2]f32{
 240        [_]f32{ 1.0, 0.0 },
 241        [_]f32{ 0.0, 1.0 },
 242    };
 243
 244    const b = [2][2]f32{
 245        [_]f32{ 1.0, 0.0 },
 246        [_]f32{ 0.0, 1.0 },
 247    };
 248
 249    try expectEqual(a, b);
 250}
 251
 252test "expectEqual vector" {
 253    const a: @Vector(4, u32) = @splat(4);
 254    const b: @Vector(4, u32) = @splat(4);
 255
 256    try expectEqual(a, b);
 257}
 258
 259test "expectEqual null" {
 260    const a = .{null};
 261    const b = @Vector(1, ?*u8){null};
 262
 263    try expectEqual(a, b);
 264}
 265
 266/// This function is intended to be used only in tests. When the formatted result of the template
 267/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how
 268/// they are not equal, then returns an error. It depends on `expectEqualStrings` for printing
 269/// diagnostics.
 270pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void {
 271    if (@inComptime()) {
 272        var buffer: [std.fmt.count(template, args)]u8 = undefined;
 273        return expectEqualStrings(expected, try std.fmt.bufPrint(&buffer, template, args));
 274    }
 275    const actual = try std.fmt.allocPrint(allocator, template, args);
 276    defer allocator.free(actual);
 277    return expectEqualStrings(expected, actual);
 278}
 279
 280/// This function is intended to be used only in tests. When the actual value is
 281/// not approximately equal to the expected value, prints diagnostics to stderr
 282/// to show exactly how they are not equal, then returns a test failure error.
 283/// See `math.approxEqAbs` for more information on the tolerance parameter.
 284/// The types must be floating-point.
 285/// `actual` and `expected` are coerced to a common type using peer type resolution.
 286pub inline fn expectApproxEqAbs(expected: anytype, actual: anytype, tolerance: anytype) !void {
 287    const T = @TypeOf(expected, actual, tolerance);
 288    return expectApproxEqAbsInner(T, expected, actual, tolerance);
 289}
 290
 291fn expectApproxEqAbsInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
 292    switch (@typeInfo(T)) {
 293        .float => if (!math.approxEqAbs(T, expected, actual, tolerance)) {
 294            print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });
 295            return error.TestExpectedApproxEqAbs;
 296        },
 297
 298        .comptime_float => @compileError("Cannot approximately compare two comptime_float values"),
 299
 300        else => @compileError("Unable to compare non floating point values"),
 301    }
 302}
 303
 304test expectApproxEqAbs {
 305    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
 306        const pos_x: T = 12.0;
 307        const pos_y: T = 12.06;
 308        const neg_x: T = -12.0;
 309        const neg_y: T = -12.06;
 310
 311        try expectApproxEqAbs(pos_x, pos_y, 0.1);
 312        try expectApproxEqAbs(neg_x, neg_y, 0.1);
 313    }
 314}
 315
 316/// This function is intended to be used only in tests. When the actual value is
 317/// not approximately equal to the expected value, prints diagnostics to stderr
 318/// to show exactly how they are not equal, then returns a test failure error.
 319/// See `math.approxEqRel` for more information on the tolerance parameter.
 320/// The types must be floating-point.
 321/// `actual` and `expected` are coerced to a common type using peer type resolution.
 322pub inline fn expectApproxEqRel(expected: anytype, actual: anytype, tolerance: anytype) !void {
 323    const T = @TypeOf(expected, actual, tolerance);
 324    return expectApproxEqRelInner(T, expected, actual, tolerance);
 325}
 326
 327fn expectApproxEqRelInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
 328    switch (@typeInfo(T)) {
 329        .float => if (!math.approxEqRel(T, expected, actual, tolerance)) {
 330            print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });
 331            return error.TestExpectedApproxEqRel;
 332        },
 333
 334        .comptime_float => @compileError("Cannot approximately compare two comptime_float values"),
 335
 336        else => @compileError("Unable to compare non floating point values"),
 337    }
 338}
 339
 340test expectApproxEqRel {
 341    inline for ([_]type{ f16, f32, f64, f128 }) |T| {
 342        const eps_value = comptime math.floatEps(T);
 343        const sqrt_eps_value = comptime @sqrt(eps_value);
 344
 345        const pos_x: T = 12.0;
 346        const pos_y: T = pos_x + 2 * eps_value;
 347        const neg_x: T = -12.0;
 348        const neg_y: T = neg_x - 2 * eps_value;
 349
 350        try expectApproxEqRel(pos_x, pos_y, sqrt_eps_value);
 351        try expectApproxEqRel(neg_x, neg_y, sqrt_eps_value);
 352    }
 353}
 354
 355/// This function is intended to be used only in tests. When the two slices are not
 356/// equal, prints diagnostics to stderr to show exactly how they are not equal (with
 357/// the differences highlighted in red), then returns a test failure error.
 358/// The colorized output is optional and controlled by the return of `std.Io.tty.Config.detect`.
 359/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
 360pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {
 361    const diff_index: usize = diff_index: {
 362        const shortest = @min(expected.len, actual.len);
 363        var index: usize = 0;
 364        while (index < shortest) : (index += 1) {
 365            if (!std.meta.eql(actual[index], expected[index])) break :diff_index index;
 366        }
 367        break :diff_index if (expected.len == actual.len) return else shortest;
 368    };
 369    if (!backend_can_print) return error.TestExpectedEqual;
 370    const stderr_w, const ttyconf = std.debug.lockStderrWriter(&.{});
 371    defer std.debug.unlockStderrWriter();
 372    failEqualSlices(T, expected, actual, diff_index, stderr_w, ttyconf) catch {};
 373    return error.TestExpectedEqual;
 374}
 375
 376fn failEqualSlices(
 377    comptime T: type,
 378    expected: []const T,
 379    actual: []const T,
 380    diff_index: usize,
 381    w: *std.Io.Writer,
 382    ttyconf: std.Io.tty.Config,
 383) !void {
 384    try w.print("slices differ. first difference occurs at index {d} (0x{X})\n", .{ diff_index, diff_index });
 385
 386    // TODO: Should this be configurable by the caller?
 387    const max_lines: usize = 16;
 388    const max_window_size: usize = if (T == u8) max_lines * 16 else max_lines;
 389
 390    // Print a maximum of max_window_size items of each input, starting just before the
 391    // first difference to give a bit of context.
 392    var window_start: usize = 0;
 393    if (@max(actual.len, expected.len) > max_window_size) {
 394        const alignment = if (T == u8) 16 else 2;
 395        window_start = std.mem.alignBackward(usize, diff_index - @min(diff_index, alignment), alignment);
 396    }
 397    const expected_window = expected[window_start..@min(expected.len, window_start + max_window_size)];
 398    const expected_truncated = window_start + expected_window.len < expected.len;
 399    const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
 400    const actual_truncated = window_start + actual_window.len < actual.len;
 401
 402    var differ = if (T == u8) BytesDiffer{
 403        .expected = expected_window,
 404        .actual = actual_window,
 405        .ttyconf = ttyconf,
 406    } else SliceDiffer(T){
 407        .start_index = window_start,
 408        .expected = expected_window,
 409        .actual = actual_window,
 410        .ttyconf = ttyconf,
 411    };
 412
 413    // Print indexes as hex for slices of u8 since it's more likely to be binary data where
 414    // that is usually useful.
 415    const index_fmt = if (T == u8) "0x{X}" else "{}";
 416
 417    try w.print("\n============ expected this output: =============  len: {} (0x{X})\n\n", .{ expected.len, expected.len });
 418    if (window_start > 0) {
 419        if (T == u8) {
 420            try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
 421        } else {
 422            try w.print("... truncated ...\n", .{});
 423        }
 424    }
 425    differ.write(w) catch {};
 426    if (expected_truncated) {
 427        const end_offset = window_start + expected_window.len;
 428        const num_missing_items = expected.len - (window_start + expected_window.len);
 429        if (T == u8) {
 430            try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
 431        } else {
 432            try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
 433        }
 434    }
 435
 436    // now reverse expected/actual and print again
 437    differ.expected = actual_window;
 438    differ.actual = expected_window;
 439    try w.print("\n============= instead found this: ==============  len: {} (0x{X})\n\n", .{ actual.len, actual.len });
 440    if (window_start > 0) {
 441        if (T == u8) {
 442            try w.print("... truncated, start index: " ++ index_fmt ++ " ...\n", .{window_start});
 443        } else {
 444            try w.print("... truncated ...\n", .{});
 445        }
 446    }
 447    differ.write(w) catch {};
 448    if (actual_truncated) {
 449        const end_offset = window_start + actual_window.len;
 450        const num_missing_items = actual.len - (window_start + actual_window.len);
 451        if (T == u8) {
 452            try w.print("... truncated, indexes [" ++ index_fmt ++ "..] not shown, remaining bytes: " ++ index_fmt ++ " ...\n", .{ end_offset, num_missing_items });
 453        } else {
 454            try w.print("... truncated, remaining items: " ++ index_fmt ++ " ...\n", .{num_missing_items});
 455        }
 456    }
 457    try w.print("\n================================================\n\n", .{});
 458
 459    return error.TestExpectedEqual;
 460}
 461
 462fn SliceDiffer(comptime T: type) type {
 463    return struct {
 464        start_index: usize,
 465        expected: []const T,
 466        actual: []const T,
 467        ttyconf: std.Io.tty.Config,
 468
 469        const Self = @This();
 470
 471        pub fn write(self: Self, writer: *std.Io.Writer) !void {
 472            for (self.expected, 0..) |value, i| {
 473                const full_index = self.start_index + i;
 474                const diff = if (i < self.actual.len) !std.meta.eql(self.actual[i], value) else true;
 475                if (diff) try self.ttyconf.setColor(writer, .red);
 476                if (@typeInfo(T) == .pointer) {
 477                    try writer.print("[{}]{*}: {any}\n", .{ full_index, value, value });
 478                } else {
 479                    try writer.print("[{}]: {any}\n", .{ full_index, value });
 480                }
 481                if (diff) try self.ttyconf.setColor(writer, .reset);
 482            }
 483        }
 484    };
 485}
 486
 487const BytesDiffer = struct {
 488    expected: []const u8,
 489    actual: []const u8,
 490    ttyconf: std.Io.tty.Config,
 491
 492    pub fn write(self: BytesDiffer, writer: *std.Io.Writer) !void {
 493        var expected_iterator = std.mem.window(u8, self.expected, 16, 16);
 494        var row: usize = 0;
 495        while (expected_iterator.next()) |chunk| {
 496            // to avoid having to calculate diffs twice per chunk
 497            var diffs: std.bit_set.IntegerBitSet(16) = .{ .mask = 0 };
 498            for (chunk, 0..) |byte, col| {
 499                const absolute_byte_index = col + row * 16;
 500                const diff = if (absolute_byte_index < self.actual.len) self.actual[absolute_byte_index] != byte else true;
 501                if (diff) diffs.set(col);
 502                try self.writeDiff(writer, "{X:0>2} ", .{byte}, diff);
 503                if (col == 7) try writer.writeByte(' ');
 504            }
 505            try writer.writeByte(' ');
 506            if (chunk.len < 16) {
 507                var missing_columns = (16 - chunk.len) * 3;
 508                if (chunk.len < 8) missing_columns += 1;
 509                try writer.splatByteAll(' ', missing_columns);
 510            }
 511            for (chunk, 0..) |byte, col| {
 512                const diff = diffs.isSet(col);
 513                if (std.ascii.isPrint(byte)) {
 514                    try self.writeDiff(writer, "{c}", .{byte}, diff);
 515                } else {
 516                    // TODO: remove this `if` when https://github.com/ziglang/zig/issues/7600 is fixed
 517                    if (self.ttyconf == .windows_api) {
 518                        try self.writeDiff(writer, ".", .{}, diff);
 519                        continue;
 520                    }
 521
 522                    // Let's print some common control codes as graphical Unicode symbols.
 523                    // We don't want to do this for all control codes because most control codes apart from
 524                    // the ones that Zig has escape sequences for are likely not very useful to print as symbols.
 525                    switch (byte) {
 526                        '\n' => try self.writeDiff(writer, "", .{}, diff),
 527                        '\r' => try self.writeDiff(writer, "", .{}, diff),
 528                        '\t' => try self.writeDiff(writer, "", .{}, diff),
 529                        else => try self.writeDiff(writer, ".", .{}, diff),
 530                    }
 531                }
 532            }
 533            try writer.writeByte('\n');
 534            row += 1;
 535        }
 536    }
 537
 538    fn writeDiff(self: BytesDiffer, writer: *std.Io.Writer, comptime fmt: []const u8, args: anytype, diff: bool) !void {
 539        if (diff) try self.ttyconf.setColor(writer, .red);
 540        try writer.print(fmt, args);
 541        if (diff) try self.ttyconf.setColor(writer, .reset);
 542    }
 543};
 544
 545test {
 546    try expectEqualSlices(u8, "foo\x00", "foo\x00");
 547    try expectEqualSlices(u16, &[_]u16{ 100, 200, 300, 400 }, &[_]u16{ 100, 200, 300, 400 });
 548    const E = enum { foo, bar };
 549    const S = struct {
 550        v: E,
 551    };
 552    try expectEqualSlices(
 553        S,
 554        &[_]S{ .{ .v = .foo }, .{ .v = .bar }, .{ .v = .foo }, .{ .v = .bar } },
 555        &[_]S{ .{ .v = .foo }, .{ .v = .bar }, .{ .v = .foo }, .{ .v = .bar } },
 556    );
 557}
 558
 559/// This function is intended to be used only in tests. Checks that two slices or two arrays are equal,
 560/// including that their sentinel (if any) are the same. Will error if given another type.
 561pub fn expectEqualSentinel(comptime T: type, comptime sentinel: T, expected: [:sentinel]const T, actual: [:sentinel]const T) !void {
 562    try expectEqualSlices(T, expected, actual);
 563
 564    const expected_value_sentinel = blk: {
 565        switch (@typeInfo(@TypeOf(expected))) {
 566            .pointer => {
 567                break :blk expected[expected.len];
 568            },
 569            .array => |array_info| {
 570                const indexable_outside_of_bounds = @as([]const array_info.child, &expected);
 571                break :blk indexable_outside_of_bounds[indexable_outside_of_bounds.len];
 572            },
 573            else => {},
 574        }
 575    };
 576
 577    const actual_value_sentinel = blk: {
 578        switch (@typeInfo(@TypeOf(actual))) {
 579            .pointer => {
 580                break :blk actual[actual.len];
 581            },
 582            .array => |array_info| {
 583                const indexable_outside_of_bounds = @as([]const array_info.child, &actual);
 584                break :blk indexable_outside_of_bounds[indexable_outside_of_bounds.len];
 585            },
 586            else => {},
 587        }
 588    };
 589
 590    if (!std.meta.eql(sentinel, expected_value_sentinel)) {
 591        print("expectEqualSentinel: 'expected' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, expected_value_sentinel });
 592        return error.TestExpectedEqual;
 593    }
 594
 595    if (!std.meta.eql(sentinel, actual_value_sentinel)) {
 596        print("expectEqualSentinel: 'actual' sentinel in memory is different from its type sentinel. type sentinel {}, in memory sentinel {}\n", .{ sentinel, actual_value_sentinel });
 597        return error.TestExpectedEqual;
 598    }
 599}
 600
 601/// This function is intended to be used only in tests.
 602/// When `ok` is false, returns a test failure error.
 603pub fn expect(ok: bool) !void {
 604    if (!ok) return error.TestUnexpectedResult;
 605}
 606
 607pub const TmpDir = struct {
 608    dir: std.fs.Dir,
 609    parent_dir: std.fs.Dir,
 610    sub_path: [sub_path_len]u8,
 611
 612    const random_bytes_count = 12;
 613    const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
 614
 615    pub fn cleanup(self: *TmpDir) void {
 616        self.dir.close();
 617        self.parent_dir.deleteTree(&self.sub_path) catch {};
 618        self.parent_dir.close();
 619        self.* = undefined;
 620    }
 621};
 622
 623pub fn tmpDir(opts: std.fs.Dir.OpenOptions) TmpDir {
 624    var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
 625    std.crypto.random.bytes(&random_bytes);
 626    var sub_path: [TmpDir.sub_path_len]u8 = undefined;
 627    _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
 628
 629    const cwd = std.fs.cwd();
 630    var cache_dir = cwd.makeOpenPath(".zig-cache", .{}) catch
 631        @panic("unable to make tmp dir for testing: unable to make and open .zig-cache dir");
 632    defer cache_dir.close();
 633    const parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
 634        @panic("unable to make tmp dir for testing: unable to make and open .zig-cache/tmp dir");
 635    const dir = parent_dir.makeOpenPath(&sub_path, opts) catch
 636        @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
 637
 638    return .{
 639        .dir = dir,
 640        .parent_dir = parent_dir,
 641        .sub_path = sub_path,
 642    };
 643}
 644
 645pub fn expectEqualStrings(expected: []const u8, actual: []const u8) !void {
 646    if (std.mem.indexOfDiff(u8, actual, expected)) |diff_index| {
 647        if (@inComptime()) {
 648            @compileError(std.fmt.comptimePrint("\nexpected:\n{s}\nfound:\n{s}\ndifference starts at index {d}", .{
 649                expected, actual, diff_index,
 650            }));
 651        }
 652        print("\n====== expected this output: =========\n", .{});
 653        printWithVisibleNewlines(expected);
 654        print("\n======== instead found this: =========\n", .{});
 655        printWithVisibleNewlines(actual);
 656        print("\n======================================\n", .{});
 657
 658        var diff_line_number: usize = 1;
 659        for (expected[0..diff_index]) |value| {
 660            if (value == '\n') diff_line_number += 1;
 661        }
 662        print("First difference occurs on line {d}:\n", .{diff_line_number});
 663
 664        print("expected:\n", .{});
 665        printIndicatorLine(expected, diff_index);
 666
 667        print("found:\n", .{});
 668        printIndicatorLine(actual, diff_index);
 669
 670        return error.TestExpectedEqual;
 671    }
 672}
 673
 674pub fn expectStringStartsWith(actual: []const u8, expected_starts_with: []const u8) !void {
 675    if (std.mem.startsWith(u8, actual, expected_starts_with))
 676        return;
 677
 678    const shortened_actual = if (actual.len >= expected_starts_with.len)
 679        actual[0..expected_starts_with.len]
 680    else
 681        actual;
 682
 683    print("\n====== expected to start with: =========\n", .{});
 684    printWithVisibleNewlines(expected_starts_with);
 685    print("\n====== instead started with: ===========\n", .{});
 686    printWithVisibleNewlines(shortened_actual);
 687    print("\n========= full output: ==============\n", .{});
 688    printWithVisibleNewlines(actual);
 689    print("\n======================================\n", .{});
 690
 691    return error.TestExpectedStartsWith;
 692}
 693
 694pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) !void {
 695    if (std.mem.endsWith(u8, actual, expected_ends_with))
 696        return;
 697
 698    const shortened_actual = if (actual.len >= expected_ends_with.len)
 699        actual[(actual.len - expected_ends_with.len)..]
 700    else
 701        actual;
 702
 703    print("\n====== expected to end with: =========\n", .{});
 704    printWithVisibleNewlines(expected_ends_with);
 705    print("\n====== instead ended with: ===========\n", .{});
 706    printWithVisibleNewlines(shortened_actual);
 707    print("\n========= full output: ==============\n", .{});
 708    printWithVisibleNewlines(actual);
 709    print("\n======================================\n", .{});
 710
 711    return error.TestExpectedEndsWith;
 712}
 713
 714/// This function is intended to be used only in tests. When the two values are not
 715/// deeply equal, prints diagnostics to stderr to show exactly how they are not equal,
 716/// then returns a test failure error.
 717/// `actual` and `expected` are coerced to a common type using peer type resolution.
 718///
 719/// Deeply equal is defined as follows:
 720/// Primitive types are deeply equal if they are equal using `==` operator.
 721/// Struct values are deeply equal if their corresponding fields are deeply equal.
 722/// Container types(like Array/Slice/Vector) deeply equal when their corresponding elements are deeply equal.
 723/// Pointer values are deeply equal if values they point to are deeply equal.
 724///
 725/// Note: Self-referential structs are supported (e.g. things like std.SinglyLinkedList)
 726/// but may cause infinite recursion or stack overflow when a container has a pointer to itself.
 727pub inline fn expectEqualDeep(expected: anytype, actual: anytype) error{TestExpectedEqual}!void {
 728    const T = @TypeOf(expected, actual);
 729    return expectEqualDeepInner(T, expected, actual);
 730}
 731
 732fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpectedEqual}!void {
 733    switch (@typeInfo(@TypeOf(actual))) {
 734        .noreturn,
 735        .@"opaque",
 736        .frame,
 737        .@"anyframe",
 738        => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
 739
 740        .undefined,
 741        .null,
 742        .void,
 743        => return,
 744
 745        .type => {
 746            if (actual != expected) {
 747                print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
 748                return error.TestExpectedEqual;
 749            }
 750        },
 751
 752        .bool,
 753        .int,
 754        .float,
 755        .comptime_float,
 756        .comptime_int,
 757        .enum_literal,
 758        .@"enum",
 759        .@"fn",
 760        .error_set,
 761        => {
 762            if (actual != expected) {
 763                print("expected {any}, found {any}\n", .{ expected, actual });
 764                return error.TestExpectedEqual;
 765            }
 766        },
 767
 768        .pointer => |pointer| {
 769            switch (pointer.size) {
 770                // We have no idea what is behind those pointers, so the best we can do is `==` check.
 771                .c, .many => {
 772                    if (actual != expected) {
 773                        print("expected {*}, found {*}\n", .{ expected, actual });
 774                        return error.TestExpectedEqual;
 775                    }
 776                },
 777                .one => {
 778                    // Length of those pointers are runtime value, so the best we can do is `==` check.
 779                    switch (@typeInfo(pointer.child)) {
 780                        .@"fn", .@"opaque" => {
 781                            if (actual != expected) {
 782                                print("expected {*}, found {*}\n", .{ expected, actual });
 783                                return error.TestExpectedEqual;
 784                            }
 785                        },
 786                        else => try expectEqualDeep(expected.*, actual.*),
 787                    }
 788                },
 789                .slice => {
 790                    if (expected.len != actual.len) {
 791                        print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
 792                        return error.TestExpectedEqual;
 793                    }
 794                    var i: usize = 0;
 795                    while (i < expected.len) : (i += 1) {
 796                        expectEqualDeep(expected[i], actual[i]) catch |e| {
 797                            print("index {d} incorrect. expected {any}, found {any}\n", .{
 798                                i, expected[i], actual[i],
 799                            });
 800                            return e;
 801                        };
 802                    }
 803                },
 804            }
 805        },
 806
 807        .array => |_| {
 808            if (expected.len != actual.len) {
 809                print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
 810                return error.TestExpectedEqual;
 811            }
 812            var i: usize = 0;
 813            while (i < expected.len) : (i += 1) {
 814                expectEqualDeep(expected[i], actual[i]) catch |e| {
 815                    print("index {d} incorrect. expected {any}, found {any}\n", .{
 816                        i, expected[i], actual[i],
 817                    });
 818                    return e;
 819                };
 820            }
 821        },
 822
 823        .vector => |info| {
 824            if (info.len != @typeInfo(@TypeOf(actual)).vector.len) {
 825                print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len });
 826                return error.TestExpectedEqual;
 827            }
 828            inline for (0..info.len) |i| {
 829                expectEqualDeep(expected[i], actual[i]) catch |e| {
 830                    print("index {d} incorrect. expected {any}, found {any}\n", .{
 831                        i, expected[i], actual[i],
 832                    });
 833                    return e;
 834                };
 835            }
 836        },
 837
 838        .@"struct" => |structType| {
 839            inline for (structType.fields) |field| {
 840                expectEqualDeep(@field(expected, field.name), @field(actual, field.name)) catch |e| {
 841                    print("Field {s} incorrect. expected {any}, found {any}\n", .{ field.name, @field(expected, field.name), @field(actual, field.name) });
 842                    return e;
 843                };
 844            }
 845        },
 846
 847        .@"union" => |union_info| {
 848            if (union_info.tag_type == null) {
 849                @compileError("Unable to compare untagged union values for type " ++ @typeName(@TypeOf(actual)));
 850            }
 851
 852            const Tag = std.meta.Tag(@TypeOf(expected));
 853
 854            const expectedTag = @as(Tag, expected);
 855            const actualTag = @as(Tag, actual);
 856
 857            try expectEqual(expectedTag, actualTag);
 858
 859            // we only reach this switch if the tags are equal
 860            switch (expected) {
 861                inline else => |val, tag| {
 862                    try expectEqualDeep(val, @field(actual, @tagName(tag)));
 863                },
 864            }
 865        },
 866
 867        .optional => {
 868            if (expected) |expected_payload| {
 869                if (actual) |actual_payload| {
 870                    try expectEqualDeep(expected_payload, actual_payload);
 871                } else {
 872                    print("expected {any}, found null\n", .{expected_payload});
 873                    return error.TestExpectedEqual;
 874                }
 875            } else {
 876                if (actual) |actual_payload| {
 877                    print("expected null, found {any}\n", .{actual_payload});
 878                    return error.TestExpectedEqual;
 879                }
 880            }
 881        },
 882
 883        .error_union => {
 884            if (expected) |expected_payload| {
 885                if (actual) |actual_payload| {
 886                    try expectEqualDeep(expected_payload, actual_payload);
 887                } else |actual_err| {
 888                    print("expected {any}, found {any}\n", .{ expected_payload, actual_err });
 889                    return error.TestExpectedEqual;
 890                }
 891            } else |expected_err| {
 892                if (actual) |actual_payload| {
 893                    print("expected {any}, found {any}\n", .{ expected_err, actual_payload });
 894                    return error.TestExpectedEqual;
 895                } else |actual_err| {
 896                    try expectEqualDeep(expected_err, actual_err);
 897                }
 898            }
 899        },
 900    }
 901}
 902
 903test "expectEqualDeep primitive type" {
 904    try expectEqualDeep(1, 1);
 905    try expectEqualDeep(true, true);
 906    try expectEqualDeep(1.5, 1.5);
 907    try expectEqualDeep(u8, u8);
 908    try expectEqualDeep(error.Bad, error.Bad);
 909
 910    // optional
 911    {
 912        const foo: ?u32 = 1;
 913        const bar: ?u32 = 1;
 914        try expectEqualDeep(foo, bar);
 915        try expectEqualDeep(?u32, ?u32);
 916    }
 917    // function type
 918    {
 919        const fnType = struct {
 920            fn foo() void {
 921                unreachable;
 922            }
 923        }.foo;
 924        try expectEqualDeep(fnType, fnType);
 925    }
 926    // enum with formatter
 927    {
 928        const TestEnum = enum {
 929            a,
 930            b,
 931
 932            pub fn format(self: @This(), writer: *std.Io.Writer) !void {
 933                try writer.writeAll(@tagName(self));
 934            }
 935        };
 936        try expectEqualDeep(TestEnum.b, TestEnum.b);
 937    }
 938}
 939
 940test "expectEqualDeep pointer" {
 941    const a = 1;
 942    const b = 1;
 943    try expectEqualDeep(&a, &b);
 944}
 945
 946test "expectEqualDeep composite type" {
 947    try expectEqualDeep("abc", "abc");
 948    const s1: []const u8 = "abc";
 949    const s2 = "abcd";
 950    const s3: []const u8 = s2[0..3];
 951    try expectEqualDeep(s1, s3);
 952
 953    const TestStruct = struct { s: []const u8 };
 954    try expectEqualDeep(TestStruct{ .s = "abc" }, TestStruct{ .s = "abc" });
 955    try expectEqualDeep([_][]const u8{ "a", "b", "c" }, [_][]const u8{ "a", "b", "c" });
 956
 957    // vector
 958    try expectEqualDeep(@as(@Vector(4, u32), @splat(4)), @as(@Vector(4, u32), @splat(4)));
 959
 960    // nested array
 961    {
 962        const a = [2][2]f32{
 963            [_]f32{ 1.0, 0.0 },
 964            [_]f32{ 0.0, 1.0 },
 965        };
 966
 967        const b = [2][2]f32{
 968            [_]f32{ 1.0, 0.0 },
 969            [_]f32{ 0.0, 1.0 },
 970        };
 971
 972        try expectEqualDeep(a, b);
 973        try expectEqualDeep(&a, &b);
 974    }
 975
 976    // inferred union
 977    const TestStruct2 = struct {
 978        const A = union(enum) { b: B, c: C };
 979        const B = struct {};
 980        const C = struct { a: *const A };
 981    };
 982
 983    const union1 = TestStruct2.A{ .b = .{} };
 984    try expectEqualDeep(
 985        TestStruct2.A{ .c = .{ .a = &union1 } },
 986        TestStruct2.A{ .c = .{ .a = &union1 } },
 987    );
 988}
 989
 990fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
 991    const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
 992        line_begin + 1
 993    else
 994        0;
 995    const line_end_index = if (std.mem.indexOfScalar(u8, source[indicator_index..], '\n')) |line_end|
 996        (indicator_index + line_end)
 997    else
 998        source.len;
 999
1000    printLine(source[line_begin_index..line_end_index]);
1001    for (line_begin_index..indicator_index) |_|
1002        print(" ", .{});
1003    if (indicator_index >= source.len)
1004        print("^ (end of string)\n", .{})
1005    else
1006        print("^ ('\\x{x:0>2}')\n", .{source[indicator_index]});
1007}
1008
1009fn printWithVisibleNewlines(source: []const u8) void {
1010    var i: usize = 0;
1011    while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {
1012        printLine(source[i..][0..nl]);
1013    }
1014    print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
1015}
1016
1017fn printLine(line: []const u8) void {
1018    if (line.len != 0) switch (line[line.len - 1]) {
1019        ' ', '\t' => return print("{s}⏎\n", .{line}), // Return symbol
1020        else => {},
1021    };
1022    print("{s}\n", .{line});
1023}
1024
1025test {
1026    try expectEqualStrings("foo", "foo");
1027}
1028
1029/// Exhaustively check that allocation failures within `test_fn` are handled without
1030/// introducing memory leaks. If used with the `testing.allocator` as the `backing_allocator`,
1031/// it will also be able to detect double frees, etc (when runtime safety is enabled).
1032///
1033/// The provided `test_fn` must have a `std.mem.Allocator` as its first argument,
1034/// and must have a return type of `!void`. Any extra arguments of `test_fn` can
1035/// be provided via the `extra_args` tuple.
1036///
1037/// Any relevant state shared between runs of `test_fn` *must* be reset within `test_fn`.
1038///
1039/// The strategy employed is to:
1040/// - Run the test function once to get the total number of allocations.
1041/// - Then, iterate and run the function X more times, incrementing
1042///   the failing index each iteration (where X is the total number of
1043///   allocations determined previously)
1044///
1045/// Expects that `test_fn` has a deterministic number of memory allocations:
1046/// - If an allocation was made to fail during a run of `test_fn`, but `test_fn`
1047///   didn't return `error.OutOfMemory`, then `error.SwallowedOutOfMemoryError`
1048///   is returned from `checkAllAllocationFailures`. You may want to ignore this
1049///   depending on whether or not the code you're testing includes some strategies
1050///   for recovering from `error.OutOfMemory`.
1051/// - If a run of `test_fn` with an expected allocation failure executes without
1052///   an allocation failure being induced, then `error.NondeterministicMemoryUsage`
1053///   is returned. This error means that there are allocation points that won't be
1054///   tested by the strategy this function employs (that is, there are sometimes more
1055///   points of allocation than the initial run of `test_fn` detects).
1056///
1057/// ---
1058///
1059/// Here's an example using a simple test case that will cause a leak when the
1060/// allocation of `bar` fails (but will pass normally):
1061///
1062/// ```zig
1063/// test {
1064///     const length: usize = 10;
1065///     const allocator = std.testing.allocator;
1066///     var foo = try allocator.alloc(u8, length);
1067///     var bar = try allocator.alloc(u8, length);
1068///
1069///     allocator.free(foo);
1070///     allocator.free(bar);
1071/// }
1072/// ```
1073///
1074/// The test case can be converted to something that this function can use by
1075/// doing:
1076///
1077/// ```zig
1078/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void {
1079///     var foo = try allocator.alloc(u8, length);
1080///     var bar = try allocator.alloc(u8, length);
1081///
1082///     allocator.free(foo);
1083///     allocator.free(bar);
1084/// }
1085///
1086/// test {
1087///     const length: usize = 10;
1088///     const allocator = std.testing.allocator;
1089///     try std.testing.checkAllAllocationFailures(allocator, testImpl, .{length});
1090/// }
1091/// ```
1092///
1093/// Running this test will show that `foo` is leaked when the allocation of
1094/// `bar` fails. The simplest fix, in this case, would be to use defer like so:
1095///
1096/// ```zig
1097/// fn testImpl(allocator: std.mem.Allocator, length: usize) !void {
1098///     var foo = try allocator.alloc(u8, length);
1099///     defer allocator.free(foo);
1100///     var bar = try allocator.alloc(u8, length);
1101///     defer allocator.free(bar);
1102/// }
1103/// ```
1104pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime test_fn: anytype, extra_args: anytype) !void {
1105    switch (@typeInfo(@typeInfo(@TypeOf(test_fn)).@"fn".return_type.?)) {
1106        .error_union => |info| {
1107            if (info.payload != void) {
1108                @compileError("Return type must be !void");
1109            }
1110        },
1111        else => @compileError("Return type must be !void"),
1112    }
1113    if (@typeInfo(@TypeOf(extra_args)) != .@"struct") {
1114        @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(extra_args)));
1115    }
1116
1117    const ArgsTuple = std.meta.ArgsTuple(@TypeOf(test_fn));
1118    const fn_args_fields = @typeInfo(ArgsTuple).@"struct".fields;
1119    if (fn_args_fields.len == 0 or fn_args_fields[0].type != std.mem.Allocator) {
1120        @compileError("The provided function must have an " ++ @typeName(std.mem.Allocator) ++ " as its first argument");
1121    }
1122    const expected_args_tuple_len = fn_args_fields.len - 1;
1123    if (extra_args.len != expected_args_tuple_len) {
1124        @compileError("The provided function expects " ++ std.fmt.comptimePrint("{d}", .{expected_args_tuple_len}) ++ " extra arguments, but the provided tuple contains " ++ std.fmt.comptimePrint("{d}", .{extra_args.len}));
1125    }
1126
1127    // Setup the tuple that will actually be used with @call (we'll need to insert
1128    // the failing allocator in field @"0" before each @call)
1129    var args: ArgsTuple = undefined;
1130    inline for (@typeInfo(@TypeOf(extra_args)).@"struct".fields, 0..) |field, i| {
1131        const arg_i_str = comptime str: {
1132            var str_buf: [100]u8 = undefined;
1133            const args_i = i + 1;
1134            const str_len = std.fmt.printInt(&str_buf, args_i, 10, .lower, .{});
1135            break :str str_buf[0..str_len];
1136        };
1137        @field(args, arg_i_str) = @field(extra_args, field.name);
1138    }
1139
1140    // Try it once with unlimited memory, make sure it works
1141    const needed_alloc_count = x: {
1142        var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{});
1143        args.@"0" = failing_allocator_inst.allocator();
1144
1145        try @call(.auto, test_fn, args);
1146        break :x failing_allocator_inst.alloc_index;
1147    };
1148
1149    var fail_index: usize = 0;
1150    while (fail_index < needed_alloc_count) : (fail_index += 1) {
1151        var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{ .fail_index = fail_index });
1152        args.@"0" = failing_allocator_inst.allocator();
1153
1154        if (@call(.auto, test_fn, args)) |_| {
1155            if (failing_allocator_inst.has_induced_failure) {
1156                return error.SwallowedOutOfMemoryError;
1157            } else {
1158                return error.NondeterministicMemoryUsage;
1159            }
1160        } else |err| switch (err) {
1161            error.OutOfMemory => {
1162                if (failing_allocator_inst.allocated_bytes != failing_allocator_inst.freed_bytes) {
1163                    const tty_config = std.Io.tty.detectConfig(.stderr());
1164                    print(
1165                        "\nfail_index: {d}/{d}\nallocated bytes: {d}\nfreed bytes: {d}\nallocations: {d}\ndeallocations: {d}\nallocation that was made to fail: {f}",
1166                        .{
1167                            fail_index,
1168                            needed_alloc_count,
1169                            failing_allocator_inst.allocated_bytes,
1170                            failing_allocator_inst.freed_bytes,
1171                            failing_allocator_inst.allocations,
1172                            failing_allocator_inst.deallocations,
1173                            std.debug.FormatStackTrace{
1174                                .stack_trace = failing_allocator_inst.getStackTrace(),
1175                                .tty_config = tty_config,
1176                            },
1177                        },
1178                    );
1179                    return error.MemoryLeakDetected;
1180                }
1181            },
1182            else => return err,
1183        }
1184    }
1185}
1186
1187/// Given a type, references all the declarations inside, so that the semantic analyzer sees them.
1188pub fn refAllDecls(comptime T: type) void {
1189    if (!builtin.is_test) return;
1190    inline for (comptime std.meta.declarations(T)) |decl| {
1191        _ = &@field(T, decl.name);
1192    }
1193}
1194
1195/// Given a type, recursively references all the declarations inside, so that the semantic analyzer sees them.
1196/// For deep types, you may use `@setEvalBranchQuota`.
1197pub fn refAllDeclsRecursive(comptime T: type) void {
1198    if (!builtin.is_test) return;
1199    inline for (comptime std.meta.declarations(T)) |decl| {
1200        if (@TypeOf(@field(T, decl.name)) == type) {
1201            switch (@typeInfo(@field(T, decl.name))) {
1202                .@"struct", .@"enum", .@"union", .@"opaque" => refAllDeclsRecursive(@field(T, decl.name)),
1203                else => {},
1204            }
1205        }
1206        _ = &@field(T, decl.name);
1207    }
1208}
1209
1210pub const FuzzInputOptions = struct {
1211    corpus: []const []const u8 = &.{},
1212};
1213
1214/// Inline to avoid coverage instrumentation.
1215pub inline fn fuzz(
1216    context: anytype,
1217    comptime testOne: fn (context: @TypeOf(context), input: []const u8) anyerror!void,
1218    options: FuzzInputOptions,
1219) anyerror!void {
1220    return @import("root").fuzz(context, testOne, options);
1221}
1222
1223/// A `std.Io.Reader` that writes a predetermined list of buffers during `stream`.
1224pub const Reader = struct {
1225    calls: []const Call,
1226    interface: std.Io.Reader,
1227    next_call_index: usize,
1228    next_offset: usize,
1229    /// Further reduces how many bytes are written in each `stream` call.
1230    artificial_limit: std.Io.Limit = .unlimited,
1231
1232    pub const Call = struct {
1233        buffer: []const u8,
1234    };
1235
1236    pub fn init(buffer: []u8, calls: []const Call) Reader {
1237        return .{
1238            .next_call_index = 0,
1239            .next_offset = 0,
1240            .interface = .{
1241                .vtable = &.{ .stream = stream },
1242                .buffer = buffer,
1243                .seek = 0,
1244                .end = 0,
1245            },
1246            .calls = calls,
1247        };
1248    }
1249
1250    fn stream(io_r: *std.Io.Reader, w: *std.Io.Writer, limit: std.Io.Limit) std.Io.Reader.StreamError!usize {
1251        const r: *Reader = @alignCast(@fieldParentPtr("interface", io_r));
1252        if (r.calls.len - r.next_call_index == 0) return error.EndOfStream;
1253        const call = r.calls[r.next_call_index];
1254        const buffer = r.artificial_limit.sliceConst(limit.sliceConst(call.buffer[r.next_offset..]));
1255        const n = try w.write(buffer);
1256        r.next_offset += n;
1257        if (call.buffer.len - r.next_offset == 0) {
1258            r.next_call_index += 1;
1259            r.next_offset = 0;
1260        }
1261        return n;
1262    }
1263};
1264
1265/// A `std.Io.Reader` that gets its data from another `std.Io.Reader`, and always
1266/// writes to its own buffer (and returns 0) during `stream` and `readVec`.
1267pub const ReaderIndirect = struct {
1268    in: *std.Io.Reader,
1269    interface: std.Io.Reader,
1270
1271    pub fn init(in: *std.Io.Reader, buffer: []u8) ReaderIndirect {
1272        return .{
1273            .in = in,
1274            .interface = .{
1275                .vtable = &.{
1276                    .stream = stream,
1277                    .readVec = readVec,
1278                },
1279                .buffer = buffer,
1280                .seek = 0,
1281                .end = 0,
1282            },
1283        };
1284    }
1285
1286    fn readVec(r: *std.Io.Reader, _: [][]u8) std.Io.Reader.Error!usize {
1287        try streamInner(r);
1288        return 0;
1289    }
1290
1291    fn stream(r: *std.Io.Reader, _: *std.Io.Writer, _: std.Io.Limit) std.Io.Reader.StreamError!usize {
1292        try streamInner(r);
1293        return 0;
1294    }
1295
1296    fn streamInner(r: *std.Io.Reader) std.Io.Reader.Error!void {
1297        const r_indirect: *ReaderIndirect = @alignCast(@fieldParentPtr("interface", r));
1298
1299        // If there's no room remaining in the buffer at all, make room.
1300        if (r.buffer.len == r.end) {
1301            try r.rebase(r.buffer.len);
1302        }
1303
1304        var writer: std.Io.Writer = .{
1305            .buffer = r.buffer,
1306            .end = r.end,
1307            .vtable = &.{
1308                .drain = std.Io.Writer.unreachableDrain,
1309                .rebase = std.Io.Writer.unreachableRebase,
1310            },
1311        };
1312        defer r.end = writer.end;
1313
1314        r_indirect.in.streamExact(&writer, r.buffer.len - r.end) catch |err| switch (err) {
1315            // Only forward EndOfStream if no new bytes were written to the buffer
1316            error.EndOfStream => |e| if (r.end == writer.end) {
1317                return e;
1318            },
1319            error.WriteFailed => unreachable,
1320            else => |e| return e,
1321        };
1322    }
1323};