authorgravatar for alexandermitterholzer@noreply.codeberg.orgAlexander Mitterholzer <alexandermitterholzer@noreply.codeberg.org> 2026-10-09 21:55:17+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-10-09 21:55:17+02:00
logf2084ab74a8279db4a4607d97d49d27b0acda4db
treef0ce56e5467da147b731f314d88890f3e0204ad9
parent06efbc37cb777907178217e8f994737736b27b74

std.testing: refuse resizes in checkAllAllocationFailures (#37142)

Fixes #37141 With resizes refused every growth goes through alloc, which is what the check counts, so the count no longer depends on the backing allocator. Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37142 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

1 files changed, 22 insertions(+), 1 deletions(-)

lib/std/testing.zig+22-1
...@@ -1149,7 +1149,9 @@ pub fn checkAllAllocationFailures(...@@ -1149,7 +1149,9 @@ pub fn checkAllAllocationFailures(
1149) !void {1149) !void {
1150 // Try it once with unlimited memory, make sure it works1150 // Try it once with unlimited memory, make sure it works
1151 const needed_alloc_count = x: {1151 const needed_alloc_count = x: {
1152 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{});1152 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{
1153 .resize_fail_index = 0,
1154 });
11531155
1154 try @call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args);1156 try @call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args);
1155 break :x failing_allocator_inst.alloc_index;1157 break :x failing_allocator_inst.alloc_index;
...@@ -1158,6 +1160,7 @@ pub fn checkAllAllocationFailures(...@@ -1158,6 +1160,7 @@ pub fn checkAllAllocationFailures(
1158 for (0..needed_alloc_count) |fail_index| {1160 for (0..needed_alloc_count) |fail_index| {
1159 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{1161 var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{
1160 .fail_index = fail_index,1162 .fail_index = fail_index,
1163 .resize_fail_index = 0,
1161 });1164 });
11621165
1163 if (@call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args)) |_| {1166 if (@call(.auto, test_fn, .{failing_allocator_inst.allocator()} ++ extra_args)) |_| {
...@@ -1239,6 +1242,24 @@ test "checkAllAllocationFailures provide result type to 'extra_args' argument" {...@@ -1239,6 +1242,24 @@ test "checkAllAllocationFailures provide result type to 'extra_args' argument" {
1239 );1242 );
1240}1243}
12411244
1245test "checkAllAllocationFailures does not depend on in-place resizes" {
1246 // `std.testing.allocator` never reuses memory, so whether a list can grow in place depends
1247 // on where in its bucket it landed, which differs from one run to the next.
1248 const S = struct {
1249 fn f(gpa: std.mem.Allocator) !void {
1250 var a: std.ArrayList(u8) = .empty;
1251 defer a.deinit(gpa);
1252 var b: std.ArrayList(u8) = .empty;
1253 defer b.deinit(gpa);
1254 for (0..2000) |i| {
1255 try a.append(gpa, @truncate(i));
1256 try b.append(gpa, @truncate(i));
1257 }
1258 }
1259 };
1260 try checkAllAllocationFailures(std.testing.allocator, S.f, .{});
1261}
1262
1242/// Given a type, references all the declarations inside, so that the semantic analyzer sees them.1263/// Given a type, references all the declarations inside, so that the semantic analyzer sees them.
1243pub fn refAllDecls(comptime T: type) void {1264pub fn refAllDecls(comptime T: type) void {
1244 if (!builtin.is_test) return;1265 if (!builtin.is_test) return;