From a021c7b1b2428ecda85e79e281d43fa1c92f8c94 Mon Sep 17 00:00:00 2001 From: Jay Petacat Date: Mon, 11 Jan 2021 20:30:43 -0500 Subject: [PATCH] Move fmt.testFmt to testing.expectFmt --- lib/std/SemanticVersion.zig | 20 +-- lib/std/fmt.zig | 344 +++++++++++++++++------------------- lib/std/testing.zig | 16 ++ lib/std/zig/fmt.zig | 13 +- 4 files changed, 193 insertions(+), 200 deletions(-) diff --git a/lib/std/SemanticVersion.zig b/lib/std/SemanticVersion.zig index 76767de8b5..52c3ae59df 100644 --- a/lib/std/SemanticVersion.zig +++ b/lib/std/SemanticVersion.zig @@ -205,7 +205,7 @@ test "SemanticVersion format" { "1.2.3----R-S.12.9.1--.12+meta", "1.2.3----RC-SNAPSHOT.12.9.1--.12", "1.0.0+0.build.1-rc.10000aaa-kk-0.1", - }) |valid| try testFmt(valid, "{}", .{try parse(valid)}); + }) |valid| try std.testing.expectFmt(valid, "{}", .{try parse(valid)}); // Invalid version strings should be rejected. for ([_][]const u8{ @@ -253,7 +253,9 @@ test "SemanticVersion format" { // Valid version string that may overflow. const big_valid = "99999999999999999999999.999999999999999999.99999999999999999"; - if (parse(big_valid)) |ver| try testFmt(big_valid, "{}", .{ver}) else |err| expect(err == error.Overflow); + if (parse(big_valid)) |ver| { + try std.testing.expectFmt(big_valid, "{}", .{ver}); + } else |err| expect(err == error.Overflow); // Invalid version string that may overflow. const big_invalid = "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12"; @@ -280,20 +282,6 @@ test "SemanticVersion precedence" { expect(order(try parse("1.0.0-rc.1"), try parse("1.0.0")) == .lt); } -// This is copy-pasted from fmt.zig since it is not public. -fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void { - var buf: [100]u8 = undefined; - const result = try std.fmt.bufPrint(buf[0..], template, args); - if (std.mem.eql(u8, result, expected)) return; - - std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{s}", .{expected}); - std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{s}", .{result}); - std.debug.warn("\n======================================\n", .{}); - return error.TestFailed; -} - test "zig_version" { // An approximate Zig build that predates this test. const older_version = .{ .major = 0, .minor = 8, .patch = 0, .pre = "dev.874" }; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 8d18044964..196e95f838 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -12,6 +12,7 @@ const meta = std.meta; const builtin = @import("builtin"); const errol = @import("fmt/errol.zig"); const lossyCast = std.math.lossyCast; +const expectFmt = std.testing.expectFmt; pub const default_max_depth = 3; @@ -1531,91 +1532,91 @@ test "parse unsigned comptime" { } test "escaped braces" { - try testFmt("escaped: {{foo}}\n", "escaped: {{{{foo}}}}\n", .{}); - try testFmt("escaped: {foo}\n", "escaped: {{foo}}\n", .{}); + try expectFmt("escaped: {{foo}}\n", "escaped: {{{{foo}}}}\n", .{}); + try expectFmt("escaped: {foo}\n", "escaped: {{foo}}\n", .{}); } test "optional" { { const value: ?i32 = 1234; - try testFmt("optional: 1234\n", "optional: {}\n", .{value}); + try expectFmt("optional: 1234\n", "optional: {}\n", .{value}); } { const value: ?i32 = null; - try testFmt("optional: null\n", "optional: {}\n", .{value}); + try expectFmt("optional: null\n", "optional: {}\n", .{value}); } { const value = @intToPtr(?*i32, 0xf000d000); - try testFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value}); + try expectFmt("optional: *i32@f000d000\n", "optional: {*}\n", .{value}); } } test "error" { { const value: anyerror!i32 = 1234; - try testFmt("error union: 1234\n", "error union: {}\n", .{value}); + try expectFmt("error union: 1234\n", "error union: {}\n", .{value}); } { const value: anyerror!i32 = error.InvalidChar; - try testFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); + try expectFmt("error union: error.InvalidChar\n", "error union: {}\n", .{value}); } } test "int.small" { { const value: u3 = 0b101; - try testFmt("u3: 5\n", "u3: {}\n", .{value}); + try expectFmt("u3: 5\n", "u3: {}\n", .{value}); } } test "int.specifier" { { const value: u8 = 'a'; - try testFmt("u8: a\n", "u8: {c}\n", .{value}); + try expectFmt("u8: a\n", "u8: {c}\n", .{value}); } { const value: u8 = 0b1100; - try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); + try expectFmt("u8: 0b1100\n", "u8: 0b{b}\n", .{value}); } { const value: u16 = 0o1234; - try testFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value}); + try expectFmt("u16: 0o1234\n", "u16: 0o{o}\n", .{value}); } { const value: u8 = 'a'; - try testFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value}); + try expectFmt("UTF-8: a\n", "UTF-8: {u}\n", .{value}); } { const value: u21 = 0x1F310; - try testFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value}); + try expectFmt("UTF-8: 🌐\n", "UTF-8: {u}\n", .{value}); } { const value: u21 = 0xD800; - try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); + try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); } { const value: u21 = 0x110001; - try testFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); + try expectFmt("UTF-8: �\n", "UTF-8: {u}\n", .{value}); } } test "int.padded" { - try testFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); - try testFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)}); - try testFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)}); - try testFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)}); - try testFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)}); - try testFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)}); - try testFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)}); - try testFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)}); - try testFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)}); - try testFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)}); - try testFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)}); - try testFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)}); + try expectFmt("u8: ' 1'", "u8: '{:4}'", .{@as(u8, 1)}); + try expectFmt("u8: '1000'", "u8: '{:0<4}'", .{@as(u8, 1)}); + try expectFmt("u8: '0001'", "u8: '{:0>4}'", .{@as(u8, 1)}); + try expectFmt("u8: '0100'", "u8: '{:0^4}'", .{@as(u8, 1)}); + try expectFmt("i8: '-1 '", "i8: '{:<4}'", .{@as(i8, -1)}); + try expectFmt("i8: ' -1'", "i8: '{:>4}'", .{@as(i8, -1)}); + try expectFmt("i8: ' -1 '", "i8: '{:^4}'", .{@as(i8, -1)}); + try expectFmt("i16: '-1234'", "i16: '{:4}'", .{@as(i16, -1234)}); + try expectFmt("i16: '+1234'", "i16: '{:4}'", .{@as(i16, 1234)}); + try expectFmt("i16: '-12345'", "i16: '{:4}'", .{@as(i16, -12345)}); + try expectFmt("i16: '+12345'", "i16: '{:4}'", .{@as(i16, 12345)}); + try expectFmt("u16: '12345'", "u16: '{:4}'", .{@as(u16, 12345)}); - try testFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'}); - try testFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'}); - try testFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'}); + try expectFmt("UTF-8: 'ü '", "UTF-8: '{u:<4}'", .{'ü'}); + try expectFmt("UTF-8: ' ü'", "UTF-8: '{u:>4}'", .{'ü'}); + try expectFmt("UTF-8: ' ü '", "UTF-8: '{u:^4}'", .{'ü'}); } test "buffer" { @@ -1638,12 +1639,12 @@ test "buffer" { test "array" { { const value: [3]u8 = "abc".*; - try testFmt("array: abc\n", "array: {s}\n", .{value}); - try testFmt("array: abc\n", "array: {s}\n", .{&value}); - try testFmt("array: { 97, 98, 99 }\n", "array: {d}\n", .{value}); + try expectFmt("array: abc\n", "array: {s}\n", .{value}); + try expectFmt("array: abc\n", "array: {s}\n", .{&value}); + try expectFmt("array: { 97, 98, 99 }\n", "array: {d}\n", .{value}); var buf: [100]u8 = undefined; - try testFmt( + try expectFmt( try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@ptrToInt(&value)}), "array: {*}\n", .{&value}, @@ -1654,60 +1655,60 @@ test "array" { test "slice" { { const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {s}\n", .{value}); + try expectFmt("slice: abc\n", "slice: {s}\n", .{value}); } { var runtime_zero: usize = 0; const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero]; - try testFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value}); + try expectFmt("slice: []const u8@deadbeef\n", "slice: {*}\n", .{value}); } { const null_term_slice: [:0]const u8 = "\x00hello\x00"; - try testFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); + try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); } - try testFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); + try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); + try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; var runtime_zero: usize = 0; - try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); - try testFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); - try testFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); - try testFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); + try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); } } test "escape non-printable" { - try testFmt("abc", "{e}", .{"abc"}); - try testFmt("ab\\xffc", "{e}", .{"ab\xffc"}); - try testFmt("ab\\xFFc", "{E}", .{"ab\xffc"}); + try expectFmt("abc", "{e}", .{"abc"}); + try expectFmt("ab\\xffc", "{e}", .{"ab\xffc"}); + try expectFmt("ab\\xFFc", "{E}", .{"ab\xffc"}); } test "pointer" { { const value = @intToPtr(*align(1) i32, 0xdeadbeef); - try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); - try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); + try expectFmt("pointer: i32@deadbeef\n", "pointer: {}\n", .{value}); + try expectFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", .{value}); } { const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } { const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); + try expectFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", .{value}); } } test "cstr" { - try testFmt( + try expectFmt( "cstr: Test C\n", "cstr: {s}\n", .{@ptrCast([*c]const u8, "Test C")}, ); - try testFmt( + try expectFmt( "cstr: Test C\n", "cstr: {s:10}\n", .{@ptrCast([*c]const u8, "Test C")}, @@ -1715,8 +1716,8 @@ test "cstr" { } test "filesize" { - try testFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); + try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); + try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); } test "struct" { @@ -1725,8 +1726,8 @@ test "struct" { field: u8, }; const value = Struct{ .field = 42 }; - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); + try expectFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{value}); + try expectFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", .{&value}); } { const Struct = struct { @@ -1734,7 +1735,7 @@ test "struct" { b: u1, }; const value = Struct{ .a = 0, .b = 1 }; - try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); + try expectFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", .{value}); } } @@ -1744,13 +1745,13 @@ test "enum" { Two, }; const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); - try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One}); - try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); + try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{value}); + try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{&value}); + try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One}); + try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); // test very large enum to verify ct branch quota is large enough - try testFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); + try expectFmt("enum: Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION}); } test "non-exhaustive enum" { @@ -1759,78 +1760,78 @@ test "non-exhaustive enum" { Two = 0xbeef, _, }; - try testFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One}); - try testFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two}); - try testFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); - try testFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One}); - try testFmt("enum: Enum.Two\n", "enum: {x}\n", .{Enum.Two}); - try testFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); - try testFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); + try expectFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One}); + try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two}); + try expectFmt("enum: Enum(4660)\n", "enum: {}\n", .{@intToEnum(Enum, 0x1234)}); + try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One}); + try expectFmt("enum: Enum.Two\n", "enum: {x}\n", .{Enum.Two}); + try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two}); + try expectFmt("enum: Enum(1234)\n", "enum: {x}\n", .{@intToEnum(Enum, 0x1234)}); } test "float.scientific" { - try testFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); - try testFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); - try testFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); - try testFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); + try expectFmt("f32: 1.34000003e+00", "f32: {e}", .{@as(f32, 1.34)}); + try expectFmt("f32: 1.23400001e+01", "f32: {e}", .{@as(f32, 12.34)}); + try expectFmt("f64: -1.234e+11", "f64: {e}", .{@as(f64, -12.34e10)}); + try expectFmt("f64: 9.99996e-40", "f64: {e}", .{@as(f64, 9.999960e-40)}); } test "float.scientific.precision" { - try testFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); - try testFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); - try testFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); + try expectFmt("f64: 1.40971e-42", "f64: {e:.5}", .{@as(f64, 1.409706e-42)}); + try expectFmt("f64: 1.00000e-09", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 814313563)))}); + try expectFmt("f64: 7.81250e-03", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1006632960)))}); // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - try testFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); + try expectFmt("f64: 1.00001e+05", "f64: {e:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1203982400)))}); } test "float.special" { - try testFmt("f64: nan", "f64: {}", .{math.nan_f64}); + try expectFmt("f64: nan", "f64: {}", .{math.nan_f64}); // negative nan is not defined by IEE 754, // and ARM thus normalizes it to positive nan if (builtin.arch != builtin.Arch.arm) { - try testFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); + try expectFmt("f64: -nan", "f64: {}", .{-math.nan_f64}); } - try testFmt("f64: inf", "f64: {}", .{math.inf_f64}); - try testFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); + try expectFmt("f64: inf", "f64: {}", .{math.inf_f64}); + try expectFmt("f64: -inf", "f64: {}", .{-math.inf_f64}); } test "float.decimal" { - try testFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); - try testFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); - try testFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); - try testFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); + try expectFmt("f64: 152314000000000000000000000000", "f64: {d}", .{@as(f64, 1.52314e+29)}); + try expectFmt("f32: 0", "f32: {d}", .{@as(f32, 0.0)}); + try expectFmt("f32: 1.1", "f32: {d:.1}", .{@as(f32, 1.1234)}); + try expectFmt("f32: 1234.57", "f32: {d:.2}", .{@as(f32, 1234.567)}); // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). // -11.12339... is rounded back up to -11.1234 - try testFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); - try testFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); - try testFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); - try testFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); - try testFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); - try testFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); - try testFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); - try testFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); + try expectFmt("f32: -11.1234", "f32: {d:.4}", .{@as(f32, -11.1234)}); + try expectFmt("f32: 91.12345", "f32: {d:.5}", .{@as(f32, 91.12345)}); + try expectFmt("f64: 91.1234567890", "f64: {d:.10}", .{@as(f64, 91.12345678901235)}); + try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 0.0)}); + try expectFmt("f64: 6", "f64: {d:.0}", .{@as(f64, 5.700)}); + try expectFmt("f64: 10.0", "f64: {d:.1}", .{@as(f64, 9.999)}); + try expectFmt("f64: 1.000", "f64: {d:.3}", .{@as(f64, 1.0)}); + try expectFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)}); + try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)}); + try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)}); } test "float.libc.sanity" { - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); - try testFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); - try testFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); - try testFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); - try testFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); + try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 916964781)))}); + try expectFmt("f64: 0.00001", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 925353389)))}); + try expectFmt("f64: 0.10000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1036831278)))}); + try expectFmt("f64: 1.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1065353133)))}); + try expectFmt("f64: 10.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1092616192)))}); // libc differences // // This is 0.015625 exactly according to gdb. We thus round down, // however glibc rounds up for some reason. This occurs for all // floats of the form x.yyyy25 on a precision point. - try testFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); + try expectFmt("f64: 0.01563", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1015021568)))}); // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 // also rounds to 630 so I'm inclined to believe libc is not // optimal here. - try testFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); + try expectFmt("f64: 18014400656965630.00000", "f64: {d:.5}", .{@as(f64, @bitCast(f32, @as(u32, 1518338049)))}); } test "custom" { @@ -1860,12 +1861,12 @@ test "custom" { .x = 10.2, .y = 2.22, }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); + try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{&value}); + try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{&value}); // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); + try expectFmt("point: (10.200,2.220)\n", "point: {}\n", .{value}); + try expectFmt("dim: 10.200x2.220\n", "dim: {d}\n", .{value}); } test "struct" { @@ -1879,7 +1880,7 @@ test "struct" { .b = error.Unused, }; - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); + try expectFmt("S{ .a = 456, .b = error.Unused }", "{}", .{inst}); } test "union" { @@ -1902,7 +1903,7 @@ test "union" { const uu_inst = UU{ .int = 456 }; const eu_inst = EU{ .float = 321.123 }; - try testFmt("TU{ .int = 123 }", "{}", .{tu_inst}); + try expectFmt("TU{ .int = 123 }", "{}", .{tu_inst}); var buf: [100]u8 = undefined; const uu_result = try bufPrint(buf[0..], "{}", .{uu_inst}); @@ -1921,7 +1922,7 @@ test "enum" { const inst = E.Two; - try testFmt("E.Two", "{}", .{inst}); + try expectFmt("E.Two", "{}", .{inst}); } test "struct.self-referential" { @@ -1935,7 +1936,7 @@ test "struct.self-referential" { }; inst.a = &inst; - try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); + try expectFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", .{inst}); } test "struct.zero-size" { @@ -1950,31 +1951,18 @@ test "struct.zero-size" { const a = A{}; const b = B{ .a = a, .c = 0 }; - try testFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); + try expectFmt("B{ .a = A{ }, .c = 0 }", "{}", .{b}); } test "bytes.hex" { const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); + try expectFmt("lowercase: cafebabe\n", "lowercase: {x}\n", .{some_bytes}); + try expectFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", .{some_bytes}); //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); + try expectFmt("uppercase: CAFE\n", "uppercase: {X}\n", .{some_bytes[0..2]}); + try expectFmt("lowercase: babe\n", "lowercase: {x}\n", .{some_bytes[2..]}); const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); -} - -pub fn testFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void { - var buf: [100]u8 = undefined; - const result = try bufPrint(buf[0..], template, args); - if (mem.eql(u8, result, expected)) return; - - std.debug.warn("\n====== expected this output: =========\n", .{}); - std.debug.warn("{s}", .{expected}); - std.debug.warn("\n======== instead found this: =========\n", .{}); - std.debug.warn("{s}", .{result}); - std.debug.warn("\n======================================\n", .{}); - return error.TestFailed; + try expectFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", .{bytes_with_zeros}); } pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead"); @@ -1996,7 +1984,7 @@ test "hexToBytes" { const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; var pb: [32]u8 = undefined; try hexToBytes(pb[0..], test_hex_str); - try testFmt(test_hex_str, "{X}", .{pb}); + try expectFmt(test_hex_str, "{X}", .{pb}); } test "formatIntValue with comptime_int" { @@ -2016,8 +2004,8 @@ test "formatFloatValue with comptime_float" { try formatFloatValue(value, "", FormatOptions{}, fbs.writer()); std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00")); - try testFmt("1.0e+00", "{}", .{value}); - try testFmt("1.0e+00", "{}", .{1.0}); + try expectFmt("1.0e+00", "{}", .{value}); + try expectFmt("1.0e+00", "{}", .{1.0}); } test "formatType max_depth" { @@ -2086,19 +2074,19 @@ test "formatType max_depth" { } test "positional" { - try testFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); - try testFmt("0 0", "{0} {0}", .{@as(usize, 0)}); - try testFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); - try testFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); + try expectFmt("2 1 0", "{2} {1} {0}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try expectFmt("2 1 0", "{2} {1} {}", .{ @as(usize, 0), @as(usize, 1), @as(usize, 2) }); + try expectFmt("0 0", "{0} {0}", .{@as(usize, 0)}); + try expectFmt("0 1", "{} {1}", .{ @as(usize, 0), @as(usize, 1) }); + try expectFmt("1 0 0 1", "{1} {} {0} {}", .{ @as(usize, 0), @as(usize, 1) }); } test "positional with specifier" { - try testFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); + try expectFmt("10.0", "{0d:.1}", .{@as(f64, 9.999)}); } test "positional/alignment/width/precision" { - try testFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); + try expectFmt("10.0", "{0d: >3.1}", .{@as(f64, 9.999)}); } test "vector" { @@ -2119,76 +2107,76 @@ test "vector" { const vi64: std.meta.Vector(4, i64) = [_]i64{ -2, -1, 0, 1 }; const vu64: std.meta.Vector(4, u64) = [_]u64{ 1000, 2000, 3000, 4000 }; - try testFmt("{ true, false, true, false }", "{}", .{vbool}); - try testFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); - try testFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); - try testFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); - try testFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try testFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try testFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); + try expectFmt("{ true, false, true, false }", "{}", .{vbool}); + try expectFmt("{ -2, -1, 0, 1 }", "{}", .{vi64}); + try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); + try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); + try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); + try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); + try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); } test "enum-literal" { - try testFmt(".hello_world", "{s}", .{.hello_world}); + try expectFmt(".hello_world", "{s}", .{.hello_world}); } test "padding" { - try testFmt("Simple", "{s}", .{"Simple"}); - try testFmt(" true", "{:10}", .{true}); - try testFmt(" true", "{:>10}", .{true}); - try testFmt("======true", "{:=>10}", .{true}); - try testFmt("true======", "{:=<10}", .{true}); - try testFmt(" true ", "{:^10}", .{true}); - try testFmt("===true===", "{:=^10}", .{true}); - try testFmt(" Minimum width", "{s:18} width", .{"Minimum"}); - try testFmt("==================Filled", "{s:=>24}", .{"Filled"}); - try testFmt(" Centered ", "{s:^24}", .{"Centered"}); - try testFmt("-", "{s:-^1}", .{""}); - try testFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); - try testFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); - try testFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); + try expectFmt("Simple", "{s}", .{"Simple"}); + try expectFmt(" true", "{:10}", .{true}); + try expectFmt(" true", "{:>10}", .{true}); + try expectFmt("======true", "{:=>10}", .{true}); + try expectFmt("true======", "{:=<10}", .{true}); + try expectFmt(" true ", "{:^10}", .{true}); + try expectFmt("===true===", "{:=^10}", .{true}); + try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"}); + try expectFmt("==================Filled", "{s:=>24}", .{"Filled"}); + try expectFmt(" Centered ", "{s:^24}", .{"Centered"}); + try expectFmt("-", "{s:-^1}", .{""}); + try expectFmt("==crêpe===", "{s:=^10}", .{"crêpe"}); + try expectFmt("=====crêpe", "{s:=>10}", .{"crêpe"}); + try expectFmt("crêpe=====", "{s:=<10}", .{"crêpe"}); } test "decimal float padding" { var number: f32 = 3.1415; - try testFmt("left-pad: **3.141\n", "left-pad: {d:*>7.3}\n", .{number}); - try testFmt("center-pad: *3.141*\n", "center-pad: {d:*^7.3}\n", .{number}); - try testFmt("right-pad: 3.141**\n", "right-pad: {d:*<7.3}\n", .{number}); + try expectFmt("left-pad: **3.141\n", "left-pad: {d:*>7.3}\n", .{number}); + try expectFmt("center-pad: *3.141*\n", "center-pad: {d:*^7.3}\n", .{number}); + try expectFmt("right-pad: 3.141**\n", "right-pad: {d:*<7.3}\n", .{number}); } test "sci float padding" { var number: f32 = 3.1415; - try testFmt("left-pad: **3.141e+00\n", "left-pad: {e:*>11.3}\n", .{number}); - try testFmt("center-pad: *3.141e+00*\n", "center-pad: {e:*^11.3}\n", .{number}); - try testFmt("right-pad: 3.141e+00**\n", "right-pad: {e:*<11.3}\n", .{number}); + try expectFmt("left-pad: **3.141e+00\n", "left-pad: {e:*>11.3}\n", .{number}); + try expectFmt("center-pad: *3.141e+00*\n", "center-pad: {e:*^11.3}\n", .{number}); + try expectFmt("right-pad: 3.141e+00**\n", "right-pad: {e:*<11.3}\n", .{number}); } test "null" { const inst = null; - try testFmt("null", "{}", .{inst}); + try expectFmt("null", "{}", .{inst}); } test "type" { - try testFmt("u8", "{}", .{u8}); - try testFmt("?f32", "{}", .{?f32}); - try testFmt("[]const u8", "{}", .{[]const u8}); + try expectFmt("u8", "{}", .{u8}); + try expectFmt("?f32", "{}", .{?f32}); + try expectFmt("[]const u8", "{}", .{[]const u8}); } test "named arguments" { - try testFmt("hello world!", "{s} world{c}", .{ "hello", '!' }); - try testFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); - try testFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" }); + try expectFmt("hello world!", "{s} world{c}", .{ "hello", '!' }); + try expectFmt("hello world!", "{[greeting]s} world{[punctuation]c}", .{ .punctuation = '!', .greeting = "hello" }); + try expectFmt("hello world!", "{[1]s} world{[0]c}", .{ '!', "hello" }); } test "runtime width specifier" { var width: usize = 9; - try testFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); - try testFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); + try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); + try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); } test "runtime precision specifier" { var number: f32 = 3.1415; var precision: usize = 2; - try testFmt("3.14e+00", "{:1.[1]}", .{ number, precision }); - try testFmt("3.14e+00", "{:1.[precision]}", .{ .number = number, .precision = precision }); + try expectFmt("3.14e+00", "{:1.[1]}", .{ number, precision }); + try expectFmt("3.14e+00", "{:1.[precision]}", .{ .number = number, .precision = precision }); } diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 4f4a46dbf7..288eb5b662 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -184,6 +184,22 @@ test "expectEqual.union(enum)" { expectEqual(a10, a10); } +/// This function is intended to be used only in tests. When the formatted result of the template +/// and its arguments does not equal the expected text, it prints diagnostics to stderr to show how +/// they are not equal, then returns an error. +pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anytype) !void { + const result = try std.fmt.allocPrint(allocator, template, args); + defer allocator.free(result); + if (std.mem.eql(u8, result, expected)) return; + + print("\n====== expected this output: =========\n", .{}); + print("{s}", .{expected}); + print("\n======== instead found this: =========\n", .{}); + print("{s}", .{result}); + print("\n======================================\n", .{}); + return error.TestFailed; +} + /// This function is intended to be used only in tests. When the actual value is not /// within the margin of the expected value, /// prints diagnostics to stderr to show exactly how they are not equal, then aborts. diff --git a/lib/std/zig/fmt.zig b/lib/std/zig/fmt.zig index d852f2b49d..2ca5279263 100644 --- a/lib/std/zig/fmt.zig +++ b/lib/std/zig/fmt.zig @@ -60,12 +60,13 @@ pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(formatEscapes) { } test "escape invalid identifiers" { - try std.fmt.testFmt("@\"while\"", "{}", .{fmtId("while")}); - try std.fmt.testFmt("hello", "{}", .{fmtId("hello")}); - try std.fmt.testFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); - try std.fmt.testFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); - try std.fmt.testFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); - try std.fmt.testFmt( + const expectFmt = std.testing.expectFmt; + try expectFmt("@\"while\"", "{}", .{fmtId("while")}); + try expectFmt("hello", "{}", .{fmtId("hello")}); + try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")}); + try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")}); + try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")}); + try expectFmt( \\" \\ hi \x07 \x11 \" derp \'" , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")}); }