Merge pull request #17431 from castholm/expectEqual

Update `std.testing.expectEqual` and friends to use peer type resolution
This commit is contained in:
Andrew Kelley
2024-01-03 23:55:59 -08:00
committed by GitHub
11 changed files with 108 additions and 88 deletions

View File

@@ -853,8 +853,8 @@ test "machoSearchSymbols" {
.{ .addr = 300, .strx = undefined, .size = undefined, .ofile = undefined },
};
try testing.expectEqual(@as(?*const MachoSymbol, null), machoSearchSymbols(&symbols, 0));
try testing.expectEqual(@as(?*const MachoSymbol, null), machoSearchSymbols(&symbols, 99));
try testing.expectEqual(null, machoSearchSymbols(&symbols, 0));
try testing.expectEqual(null, machoSearchSymbols(&symbols, 99));
try testing.expectEqual(&symbols[0], machoSearchSymbols(&symbols, 100).?);
try testing.expectEqual(&symbols[0], machoSearchSymbols(&symbols, 150).?);
try testing.expectEqual(&symbols[0], machoSearchSymbols(&symbols, 199).?);

View File

@@ -283,8 +283,8 @@ pub const Status = enum(u10) {
}
test {
try std.testing.expectEqual(@as(?Status.Class, Status.Class.success), Status.ok.class());
try std.testing.expectEqual(@as(?Status.Class, Status.Class.client_error), Status.not_found.class());
try std.testing.expectEqual(Status.Class.success, Status.ok.class());
try std.testing.expectEqual(Status.Class.client_error, Status.not_found.class());
}
};

View File

@@ -373,19 +373,19 @@ test "test all types" {
test "parse" {
try testing.expectEqual(false, try parseFromSliceLeaky(bool, testing.allocator, "false", .{}));
try testing.expectEqual(true, try parseFromSliceLeaky(bool, testing.allocator, "true", .{}));
try testing.expectEqual(@as(u1, 1), try parseFromSliceLeaky(u1, testing.allocator, "1", .{}));
try testing.expectEqual(1, try parseFromSliceLeaky(u1, testing.allocator, "1", .{}));
try testing.expectError(error.Overflow, parseFromSliceLeaky(u1, testing.allocator, "50", .{}));
try testing.expectEqual(@as(u64, 42), try parseFromSliceLeaky(u64, testing.allocator, "42", .{}));
try testing.expectEqual(@as(f64, 42), try parseFromSliceLeaky(f64, testing.allocator, "42.0", .{}));
try testing.expectEqual(@as(?bool, null), try parseFromSliceLeaky(?bool, testing.allocator, "null", .{}));
try testing.expectEqual(@as(?bool, true), try parseFromSliceLeaky(?bool, testing.allocator, "true", .{}));
try testing.expectEqual(42, try parseFromSliceLeaky(u64, testing.allocator, "42", .{}));
try testing.expectEqual(42, try parseFromSliceLeaky(f64, testing.allocator, "42.0", .{}));
try testing.expectEqual(null, try parseFromSliceLeaky(?bool, testing.allocator, "null", .{}));
try testing.expectEqual(true, try parseFromSliceLeaky(?bool, testing.allocator, "true", .{}));
try testing.expectEqual(@as([3]u8, "foo".*), try parseFromSliceLeaky([3]u8, testing.allocator, "\"foo\"", .{}));
try testing.expectEqual(@as([3]u8, "foo".*), try parseFromSliceLeaky([3]u8, testing.allocator, "[102, 111, 111]", .{}));
try testing.expectEqual(@as([0]u8, undefined), try parseFromSliceLeaky([0]u8, testing.allocator, "[]", .{}));
try testing.expectEqual("foo".*, try parseFromSliceLeaky([3]u8, testing.allocator, "\"foo\"", .{}));
try testing.expectEqual("foo".*, try parseFromSliceLeaky([3]u8, testing.allocator, "[102, 111, 111]", .{}));
try testing.expectEqual(undefined, try parseFromSliceLeaky([0]u8, testing.allocator, "[]", .{}));
try testing.expectEqual(@as(u64, 12345678901234567890), try parseFromSliceLeaky(u64, testing.allocator, "\"12345678901234567890\"", .{}));
try testing.expectEqual(@as(f64, 123.456), try parseFromSliceLeaky(f64, testing.allocator, "\"123.456\"", .{}));
try testing.expectEqual(12345678901234567890, try parseFromSliceLeaky(u64, testing.allocator, "\"12345678901234567890\"", .{}));
try testing.expectEqual(123.456, try parseFromSliceLeaky(f64, testing.allocator, "\"123.456\"", .{}));
}
test "parse into enum" {
@@ -394,9 +394,9 @@ test "parse into enum" {
Bar,
@"with\\escape",
};
try testing.expectEqual(@as(T, .Foo), try parseFromSliceLeaky(T, testing.allocator, "\"Foo\"", .{}));
try testing.expectEqual(@as(T, .Foo), try parseFromSliceLeaky(T, testing.allocator, "42", .{}));
try testing.expectEqual(@as(T, .@"with\\escape"), try parseFromSliceLeaky(T, testing.allocator, "\"with\\\\escape\"", .{}));
try testing.expectEqual(.Foo, try parseFromSliceLeaky(T, testing.allocator, "\"Foo\"", .{}));
try testing.expectEqual(.Foo, try parseFromSliceLeaky(T, testing.allocator, "42", .{}));
try testing.expectEqual(.@"with\\escape", try parseFromSliceLeaky(T, testing.allocator, "\"with\\\\escape\"", .{}));
try testing.expectError(error.InvalidEnumTag, parseFromSliceLeaky(T, testing.allocator, "5", .{}));
try testing.expectError(error.InvalidEnumTag, parseFromSliceLeaky(T, testing.allocator, "\"Qux\"", .{}));
}

View File

@@ -138,11 +138,11 @@ test "math.inf" {
const inf_u64: u64 = 0x7FF0000000000000;
const inf_u80: u80 = 0x7FFF8000000000000000;
const inf_u128: u128 = 0x7FFF0000000000000000000000000000;
try expectEqual(inf_u16, @bitCast(inf(f16)));
try expectEqual(inf_u32, @bitCast(inf(f32)));
try expectEqual(inf_u64, @bitCast(inf(f64)));
try expectEqual(inf_u80, @bitCast(inf(f80)));
try expectEqual(inf_u128, @bitCast(inf(f128)));
try expectEqual(inf_u16, @as(u16, @bitCast(inf(f16))));
try expectEqual(inf_u32, @as(u32, @bitCast(inf(f32))));
try expectEqual(inf_u64, @as(u64, @bitCast(inf(f64))));
try expectEqual(inf_u80, @as(u80, @bitCast(inf(f80))));
try expectEqual(inf_u128, @as(u128, @bitCast(inf(f128))));
}
test "math.nan" {
@@ -151,11 +151,11 @@ test "math.nan" {
const qnan_u64: u64 = 0x7FF8000000000000;
const qnan_u80: u80 = 0x7FFFC000000000000000;
const qnan_u128: u128 = 0x7FFF8000000000000000000000000000;
try expectEqual(qnan_u16, @bitCast(nan(f16)));
try expectEqual(qnan_u32, @bitCast(nan(f32)));
try expectEqual(qnan_u64, @bitCast(nan(f64)));
try expectEqual(qnan_u80, @bitCast(nan(f80)));
try expectEqual(qnan_u128, @bitCast(nan(f128)));
try expectEqual(qnan_u16, @as(u16, @bitCast(nan(f16))));
try expectEqual(qnan_u32, @as(u32, @bitCast(nan(f32))));
try expectEqual(qnan_u64, @as(u64, @bitCast(nan(f64))));
try expectEqual(qnan_u80, @as(u80, @bitCast(nan(f80))));
try expectEqual(qnan_u128, @as(u128, @bitCast(nan(f128))));
}
test "math.snan" {
@@ -167,9 +167,9 @@ test "math.snan" {
const snan_u64: u64 = 0x7FF4000000000000;
const snan_u80: u80 = 0x7FFFA000000000000000;
const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
try expectEqual(snan_u16, @bitCast(snan(f16)));
try expectEqual(snan_u32, @bitCast(snan(f32)));
try expectEqual(snan_u64, @bitCast(snan(f64)));
try expectEqual(snan_u80, @bitCast(snan(f80)));
try expectEqual(snan_u128, @bitCast(snan(f128)));
try expectEqual(snan_u16, @as(u16, @bitCast(snan(f16))));
try expectEqual(snan_u32, @as(u32, @bitCast(snan(f32))));
try expectEqual(snan_u64, @as(u64, @bitCast(snan(f64))));
try expectEqual(snan_u80, @as(u80, @bitCast(snan(f80))));
try expectEqual(snan_u128, @as(u128, @bitCast(snan(f128))));
}

View File

@@ -3262,7 +3262,7 @@ test "indexOfMax" {
/// Finds the indices of the smallest and largest number in a slice. O(n).
/// Returns an anonymous struct with the fields `index_min` and `index_max`.
/// `slice` must not be empty.
pub fn indexOfMinMax(comptime T: type, slice: []const T) struct { index_min: usize, index_max: usize } {
pub fn indexOfMinMax(comptime T: type, slice: []const T) IndexOfMinMaxResult {
assert(slice.len > 0);
var minVal = slice[0];
var maxVal = slice[0];
@@ -3281,10 +3281,12 @@ pub fn indexOfMinMax(comptime T: type, slice: []const T) struct { index_min: usi
return .{ .index_min = minIdx, .index_max = maxIdx };
}
pub const IndexOfMinMaxResult = struct { index_min: usize, index_max: usize };
test "indexOfMinMax" {
try testing.expectEqual(indexOfMinMax(u8, "abcdefg"), .{ .index_min = 0, .index_max = 6 });
try testing.expectEqual(indexOfMinMax(u8, "gabcdef"), .{ .index_min = 1, .index_max = 0 });
try testing.expectEqual(indexOfMinMax(u8, "a"), .{ .index_min = 0, .index_max = 0 });
try testing.expectEqual(IndexOfMinMaxResult{ .index_min = 0, .index_max = 6 }, indexOfMinMax(u8, "abcdefg"));
try testing.expectEqual(IndexOfMinMaxResult{ .index_min = 1, .index_max = 0 }, indexOfMinMax(u8, "gabcdef"));
try testing.expectEqual(IndexOfMinMaxResult{ .index_min = 0, .index_max = 0 }, indexOfMinMax(u8, "a"));
}
pub fn swap(comptime T: type, a: *T, b: *T) void {

View File

@@ -842,24 +842,24 @@ test "union" {
&.{ .a, .b, .b, .a, .a, .a, .a, .a, .a },
list.items(.tags),
);
try testing.expectEqual(list.get(0), .{ .a = 1 });
try testing.expectEqual(list.get(1), .{ .b = "zigzag" });
try testing.expectEqual(list.get(2), .{ .b = "foobar" });
try testing.expectEqual(list.get(3), .{ .a = 4 });
try testing.expectEqual(list.get(4), .{ .a = 5 });
try testing.expectEqual(list.get(5), .{ .a = 6 });
try testing.expectEqual(list.get(6), .{ .a = 7 });
try testing.expectEqual(list.get(7), .{ .a = 8 });
try testing.expectEqual(list.get(8), .{ .a = 9 });
try testing.expectEqual(Foo{ .a = 1 }, list.get(0));
try testing.expectEqual(Foo{ .b = "zigzag" }, list.get(1));
try testing.expectEqual(Foo{ .b = "foobar" }, list.get(2));
try testing.expectEqual(Foo{ .a = 4 }, list.get(3));
try testing.expectEqual(Foo{ .a = 5 }, list.get(4));
try testing.expectEqual(Foo{ .a = 6 }, list.get(5));
try testing.expectEqual(Foo{ .a = 7 }, list.get(6));
try testing.expectEqual(Foo{ .a = 8 }, list.get(7));
try testing.expectEqual(Foo{ .a = 9 }, list.get(8));
list.shrinkAndFree(ally, 3);
try testing.expectEqual(@as(usize, 3), list.items(.tags).len);
try testing.expectEqualSlices(meta.Tag(Foo), list.items(.tags), &.{ .a, .b, .b });
try testing.expectEqual(list.get(0), .{ .a = 1 });
try testing.expectEqual(list.get(1), .{ .b = "zigzag" });
try testing.expectEqual(list.get(2), .{ .b = "foobar" });
try testing.expectEqual(Foo{ .a = 1 }, list.get(0));
try testing.expectEqual(Foo{ .b = "zigzag" }, list.get(1));
try testing.expectEqual(Foo{ .b = "foobar" }, list.get(2));
}
test "sorting a span" {

View File

@@ -52,8 +52,13 @@ pub fn expectError(expected_error: anyerror, actual_error_union: anytype) !void
/// This function is intended to be used only in tests. When the two values are not
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then returns a test failure error.
/// `actual` is casted to the type of `expected`.
pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) !void {
/// `actual` and `expected` are coerced to a common type using peer type resolution.
pub inline fn expectEqual(expected: anytype, actual: anytype) !void {
const T = @TypeOf(expected, actual);
return expectEqualInner(T, expected, actual);
}
fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
switch (@typeInfo(@TypeOf(actual))) {
.NoReturn,
.Opaque,
@@ -224,9 +229,13 @@ pub fn expectFmt(expected: []const u8, comptime template: []const u8, args: anyt
/// to show exactly how they are not equal, then returns a test failure error.
/// See `math.approxEqAbs` for more information on the tolerance parameter.
/// The types must be floating-point.
pub fn expectApproxEqAbs(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) !void {
const T = @TypeOf(expected);
/// `actual` and `expected` are coerced to a common type using peer type resolution.
pub inline fn expectApproxEqAbs(expected: anytype, actual: anytype, tolerance: anytype) !void {
const T = @TypeOf(expected, actual, tolerance);
return expectApproxEqAbsInner(T, expected, actual, tolerance);
}
fn expectApproxEqAbsInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
switch (@typeInfo(T)) {
.Float => if (!math.approxEqAbs(T, expected, actual, tolerance)) {
print("actual {}, not within absolute tolerance {} of expected {}\n", .{ actual, tolerance, expected });
@@ -256,9 +265,13 @@ test "expectApproxEqAbs" {
/// to show exactly how they are not equal, then returns a test failure error.
/// See `math.approxEqRel` for more information on the tolerance parameter.
/// The types must be floating-point.
pub fn expectApproxEqRel(expected: anytype, actual: @TypeOf(expected), tolerance: @TypeOf(expected)) !void {
const T = @TypeOf(expected);
/// `actual` and `expected` are coerced to a common type using peer type resolution.
pub inline fn expectApproxEqRel(expected: anytype, actual: anytype, tolerance: anytype) !void {
const T = @TypeOf(expected, actual, tolerance);
return expectApproxEqRelInner(T, expected, actual, tolerance);
}
fn expectApproxEqRelInner(comptime T: type, expected: T, actual: T, tolerance: T) !void {
switch (@typeInfo(T)) {
.Float => if (!math.approxEqRel(T, expected, actual, tolerance)) {
print("actual {}, not within relative tolerance {} of expected {}\n", .{ actual, tolerance, expected });
@@ -653,17 +666,22 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)
/// This function is intended to be used only in tests. When the two values are not
/// deeply equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then returns a test failure error.
/// `actual` is casted to the type of `expected`.
/// `actual` and `expected` are coerced to a common type using peer type resolution.
///
/// Deeply equal is defined as follows:
/// Primitive types are deeply equal if they are equal using `==` operator.
/// Primitive types are deeply equal if they are equal using `==` operator.
/// Struct values are deeply equal if their corresponding fields are deeply equal.
/// Container types(like Array/Slice/Vector) deeply equal when their corresponding elements are deeply equal.
/// Pointer values are deeply equal if values they point to are deeply equal.
///
/// Note: Self-referential structs are supported (e.g. things like std.SinglyLinkedList)
/// but may cause infinite recursion or stack overflow when a container has a pointer to itself.
pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) error{TestExpectedEqual}!void {
pub inline fn expectEqualDeep(expected: anytype, actual: anytype) error{TestExpectedEqual}!void {
const T = @TypeOf(expected, actual);
return expectEqualDeepInner(T, expected, actual);
}
fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpectedEqual}!void {
switch (@typeInfo(@TypeOf(actual))) {
.NoReturn,
.Opaque,

View File

@@ -1156,7 +1156,7 @@ test "cast function with an opaque parameter" {
.func = @ptrCast(&Foo.funcImpl),
};
c.func(c.ctx);
try std.testing.expectEqual(foo, .{ .x = 101, .y = 201 });
try std.testing.expectEqual(Foo{ .x = 101, .y = 201 }, foo);
}
test "implicit ptr to *anyopaque" {

View File

@@ -173,17 +173,17 @@ test "correct sizeOf and offsets in packed structs" {
try expectEqual(true, s1.bool_d);
try expectEqual(true, s1.bool_e);
try expectEqual(true, s1.bool_f);
try expectEqual(@as(u1, 1), s1.u1_a);
try expectEqual(1, s1.u1_a);
try expectEqual(false, s1.bool_g);
try expectEqual(@as(u1, 0), s1.u1_b);
try expectEqual(@as(u3, 3), s1.u3_a);
try expectEqual(@as(u10, 0b1101000101), s1.u10_a);
try expectEqual(@as(u10, 0b0001001000), s1.u10_b);
try expectEqual(0, s1.u1_b);
try expectEqual(3, s1.u3_a);
try expectEqual(0b1101000101, s1.u10_a);
try expectEqual(0b0001001000, s1.u10_b);
const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
try expectEqual(@as(u1, 0), s2.x);
try expectEqual(@as(u7, 0b1111010), s2.y);
try expectEqual(@as(u24, 0xd5c71f), s2.z);
try expectEqual(0, s2.x);
try expectEqual(0b1111010, s2.y);
try expectEqual(0xd5c71f, s2.z);
}
}
@@ -208,12 +208,12 @@ test "nested packed structs" {
if (native_endian == .little) {
const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
try expectEqual(@as(u8, 0xf4), s3.x.a);
try expectEqual(@as(u8, 0x1f), s3.x.b);
try expectEqual(@as(u8, 0xc7), s3.x.c);
try expectEqual(@as(u8, 0xd5), s3.y.d);
try expectEqual(@as(u8, 0x52), s3.y.e);
try expectEqual(@as(u8, 0xe9), s3.y.f);
try expectEqual(0xf4, s3.x.a);
try expectEqual(0x1f, s3.x.b);
try expectEqual(0xc7, s3.x.c);
try expectEqual(0xd5, s3.y.d);
try expectEqual(0x52, s3.y.e);
try expectEqual(0xe9, s3.y.f);
}
const S4 = packed struct { a: i32, b: i8 };
@@ -249,8 +249,8 @@ test "regular in irregular packed struct" {
foo.bar.a = 235;
foo.bar.b = 42;
try expectEqual(@as(u16, 235), foo.bar.a);
try expectEqual(@as(u8, 42), foo.bar.b);
try expectEqual(235, foo.bar.a);
try expectEqual(42, foo.bar.b);
}
test "nested packed struct unaligned" {
@@ -456,12 +456,12 @@ test "nested packed struct field pointers" {
const ptr_p0_c = &S2.s.p0.c;
const ptr_p1_a = &S2.s.p1.a;
const ptr_p1_b = &S2.s.p1.b;
try expectEqual(@as(u8, 1), ptr_base.*);
try expectEqual(@as(u4, 2), ptr_p0_a.*);
try expectEqual(@as(u4, 3), ptr_p0_b.*);
try expectEqual(@as(u8, 4), ptr_p0_c.*);
try expectEqual(@as(u7, 5), ptr_p1_a.*);
try expectEqual(@as(u8, 6), ptr_p1_b.*);
try expectEqual(1, ptr_base.*);
try expectEqual(2, ptr_p0_a.*);
try expectEqual(3, ptr_p0_b.*);
try expectEqual(4, ptr_p0_c.*);
try expectEqual(5, ptr_p1_a.*);
try expectEqual(6, ptr_p1_b.*);
}
test "load pointer from packed struct" {
@@ -1033,12 +1033,12 @@ test "modify nested packed struct aligned field" {
var opts = Options{};
opts.pretty_print.indent += 1;
try std.testing.expectEqual(@as(u17, 0b00000000100100000), @bitCast(opts));
try std.testing.expectEqual(0b00000000100100000, @as(u17, @bitCast(opts)));
try std.testing.expect(!opts.foo);
try std.testing.expect(!opts.bar);
try std.testing.expect(!opts.pretty_print.enabled);
try std.testing.expectEqual(@as(u4, 4), opts.pretty_print.num_spaces);
try std.testing.expectEqual(@as(u8, 1), opts.pretty_print.indent);
try std.testing.expectEqual(4, opts.pretty_print.num_spaces);
try std.testing.expectEqual(1, opts.pretty_print.indent);
try std.testing.expect(!opts.baz);
}

View File

@@ -438,9 +438,9 @@ test "Type.Union" {
},
});
var tagged = Tagged{ .signed = -1 };
try testing.expectEqual(Tag.signed, tagged);
try testing.expectEqual(Tag.signed, @as(Tag, tagged));
tagged = .{ .unsigned = 1 };
try testing.expectEqual(Tag.unsigned, tagged);
try testing.expectEqual(Tag.unsigned, @as(Tag, tagged));
}
test "Type.Union from Type.Enum" {

View File

@@ -946,7 +946,7 @@ test "DC: C returns to Zig" {
if (comptime builtin.cpu.arch.isRISCV()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_DC(), .{ .v1 = -0.25, .v2 = 15 });
try expectEqual(DC{ .v1 = -0.25, .v2 = 15 }, c_ret_DC());
}
pub extern fn c_assert_DC(lv: DC) c_int;
@@ -998,7 +998,7 @@ test "CFF: C returns to Zig" {
if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_CFF(), .{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 });
try expectEqual(CFF{ .v1 = 39, .v2 = 0.875, .v3 = 1.0 }, c_ret_CFF());
}
pub extern fn c_assert_CFF(lv: CFF) c_int;
pub extern fn c_assert_ret_CFF() c_int;
@@ -1045,7 +1045,7 @@ test "PD: C returns to Zig" {
if (comptime builtin.cpu.arch.isMIPS() and builtin.mode != .Debug) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest;
if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest;
try expectEqual(c_ret_PD(), .{ .v1 = null, .v2 = 0.5 });
try expectEqual(PD{ .v1 = null, .v2 = 0.5 }, c_ret_PD());
}
pub extern fn c_assert_PD(lv: PD) c_int;
pub extern fn c_assert_ret_PD() c_int;