authorgravatar for techatrix@mailbox.orgTechatrix <techatrix@mailbox.org> 2026-03-10 17:28:04+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-11 02:32:29+01:00
log1a8ed9a6580453e6aa8133d716692e7bad34be14
tree6dd5263953c82b424478924386708d738147b913
parentacde20a14804dd3c6dcc7940cdf49af240c74f58

checkAllAllocationFailures: provide result type to 'extra_args'


1 files changed, 55 insertions(+), 40 deletions(-)

lib/std/testing.zig+55-40
...@@ -1112,48 +1112,16 @@ test {...@@ -1112,48 +1112,16 @@ test {
1112/// defer allocator.free(bar);1112/// defer allocator.free(bar);
1113/// }1113/// }
1114/// ```1114/// ```
1115pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime test_fn: anytype, extra_args: anytype) !void {1115pub fn checkAllAllocationFailures(
1116 switch (@typeInfo(@typeInfo(@TypeOf(test_fn)).@"fn".return_type.?)) {1116 backing_allocator: std.mem.Allocator,
1117 .error_union => |info| {1117 comptime test_fn: anytype,
1118 if (info.payload != void) {1118 extra_args: CheckAllAllocationFailuresExtraArgs(@TypeOf(test_fn)),
1119 @compileError("Return type must be !void");1119) !void {
1120 }
1121 },
1122 else => @compileError("Return type must be !void"),
1123 }
1124 if (@typeInfo(@TypeOf(extra_args)) != .@"struct") {
1125 @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(extra_args)));
1126 }
1127
1128 const ArgsTuple = std.meta.ArgsTuple(@TypeOf(test_fn));
1129 const fn_args_fields = @typeInfo(ArgsTuple).@"struct".fields;
1130 if (fn_args_fields.len == 0 or fn_args_fields[0].type != std.mem.Allocator) {
1131 @compileError("The provided function must have an " ++ @typeName(std.mem.Allocator) ++ " as its first argument");
1132 }
1133 const expected_args_tuple_len = fn_args_fields.len - 1;
1134 if (extra_args.len != expected_args_tuple_len) {
1135 @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}));
1136 }
1137
1138 // Setup the tuple that will actually be used with @call (we'll need to insert
1139 // the failing allocator in field @"0" before each @call)
1140 var args: ArgsTuple = undefined;
1141 inline for (@typeInfo(@TypeOf(extra_args)).@"struct".fields, 0..) |field, i| {
1142 const arg_i_str = comptime str: {
1143 var str_buf: [100]u8 = undefined;
1144 const args_i = i + 1;
1145 const str_len = std.fmt.printInt(&str_buf, args_i, 10, .lower, .{});
1146 break :str str_buf[0..str_len];
1147 };
1148 @field(args, arg_i_str) = @field(extra_args, field.name);
1149 }
1150
1151 // Try it once with unlimited memory, make sure it works1120 // Try it once with unlimited memory, make sure it works
1152 const needed_alloc_count = x: {1121 const needed_alloc_count = x: {
1153 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{});1122 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{});
1154 args.@"0" = failing_allocator_inst.allocator();
11551123
1156 try @call(.auto, test_fn, args);1124 try @call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args);
1157 break :x failing_allocator_inst.alloc_index;1125 break :x failing_allocator_inst.alloc_index;
1158 };1126 };
11591127
...@@ -1161,9 +1129,8 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime...@@ -1161,9 +1129,8 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
1161 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{1129 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{
1162 .fail_index = fail_index,1130 .fail_index = fail_index,
1163 });1131 });
1164 args.@"0" = failing_allocator_inst.allocator();
11651132
1166 if (@call(.auto, test_fn, args)) |_| {1133 if (@call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args)) |_| {
1167 if (failing_allocator_inst.has_induced_failure) {1134 if (failing_allocator_inst.has_induced_failure) {
1168 return error.SwallowedOutOfMemoryError;1135 return error.SwallowedOutOfMemoryError;
1169 } else {1136 } else {
...@@ -1194,6 +1161,54 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime...@@ -1194,6 +1161,54 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
1194 }1161 }
1195}1162}
11961163
1164fn CheckAllAllocationFailuresExtraArgs(comptime TestFn: type) type {
1165 switch (@typeInfo(@typeInfo(TestFn).@"fn".return_type.?)) {
1166 .error_union => |info| {
1167 if (info.payload != void) {
1168 @compileError("Return type must be !void");
1169 }
1170 },
1171 else => @compileError("Return type must be !void"),
1172 }
1173
1174 const ArgsTuple = std.meta.ArgsTuple(TestFn);
1175
1176 const fields = @typeInfo(ArgsTuple).@"struct".fields;
1177 if (fields.len == 0 or fields[0].type != std.mem.Allocator) {
1178 @compileError("The provided function must have an " ++ @typeName(std.mem.Allocator) ++ " as its first argument");
1179 }
1180
1181 var extra_args: [fields.len - 1]type = undefined;
1182 for (&extra_args, fields[1..]) |*arg, field| {
1183 arg.* = field.type;
1184 }
1185
1186 return @Tuple(&extra_args);
1187}
1188
1189test "checkAllAllocationFailures provide result type to 'extra_args' argument" {
1190 try checkAllAllocationFailures(
1191 std.testing.allocator,
1192 struct {
1193 fn f(ally: std.mem.Allocator, params: struct {
1194 foo_len: u32,
1195 bar_len: u32,
1196 }) !void {
1197 const foo = try ally.alloc(u8, params.foo_len);
1198 defer ally.free(foo);
1199 const bar = try ally.alloc(u8, params.bar_len);
1200 defer ally.free(bar);
1201 }
1202 }.f,
1203 .{
1204 .{
1205 .foo_len = 3,
1206 .bar_len = 5,
1207 },
1208 },
1209 );
1210}
1211
1197/// Given a type, references all the declarations inside, so that the semantic analyzer sees them.1212/// Given a type, references all the declarations inside, so that the semantic analyzer sees them.
1198pub fn refAllDecls(comptime T: type) void {1213pub fn refAllDecls(comptime T: type) void {
1199 if (!builtin.is_test) return;1214 if (!builtin.is_test) return;