Commit cab9da35bd
Changed files (7)
lib/std/heap/general_purpose_allocator.zig
@@ -1305,7 +1305,7 @@ test "realloc large object to larger alignment" {
}
test "large object shrinks to small but allocation fails during shrink" {
- var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, 3);
+ var failing_allocator = std.testing.FailingAllocator.init(std.heap.page_allocator, .{ .fail_index = 3 });
var gpa = GeneralPurposeAllocator(.{}){ .backing_allocator = failing_allocator.allocator() };
defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
const allocator = gpa.allocator();
lib/std/heap/memory_pool.zig
@@ -164,8 +164,8 @@ test "memory pool: preheating (success)" {
}
test "memory pool: preheating (failure)" {
- var failer = std.testing.FailingAllocator.init(std.testing.allocator, 0);
- try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initPreheated(failer.allocator(), 5));
+ var failer = std.testing.failing_allocator;
+ try std.testing.expectError(error.OutOfMemory, MemoryPool(u32).initPreheated(failer, 5));
}
test "memory pool: growable" {
lib/std/json/scanner_test.zig
@@ -397,7 +397,7 @@ test "skipValue" {
}
fn testEnsureStackCapacity(do_ensure: bool) !void {
- var fail_alloc = std.testing.FailingAllocator.init(std.testing.allocator, 1);
+ var fail_alloc = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 1 });
const failing_allocator = fail_alloc.allocator();
const nestings = 999; // intentionally not a power of 2.
lib/std/json/static_test.zig
@@ -461,8 +461,7 @@ test "parse into tagged union errors" {
try testing.expectError(error.UnexpectedToken, parseFromSliceLeaky(T, arena.allocator(), "{\"nothing\":{\"no\":0}}", .{}));
// Allocator failure
- var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0);
- try testing.expectError(error.OutOfMemory, parseFromSlice(T, fail_alloc.allocator(), "{\"string\"\"foo\"}", .{}));
+ try testing.expectError(error.OutOfMemory, parseFromSlice(T, testing.failing_allocator, "{\"string\"\"foo\"}", .{}));
}
test "parse into struct with no fields" {
lib/std/testing/failing_allocator.zig
@@ -1,19 +1,33 @@
const std = @import("../std.zig");
const mem = std.mem;
+pub const Config = struct {
+ /// The number of successful allocations you can expect from this allocator.
+ /// The next allocation will fail. For example, with `fail_index` equal to
+ /// 2, the following test will pass:
+ ///
+ /// var a = try failing_alloc.create(i32);
+ /// var b = try failing_alloc.create(i32);
+ /// testing.expectError(error.OutOfMemory, failing_alloc.create(i32));
+ fail_index: usize = std.math.maxInt(usize),
+
+ /// Number of successful resizes to expect from this allocator. The next resize will fail.
+ resize_fail_index: usize = std.math.maxInt(usize),
+};
+
/// Allocator that fails after N allocations, useful for making sure out of
/// memory conditions are handled correctly.
///
/// To use this, first initialize it and get an allocator with
///
/// `const failing_allocator = &FailingAllocator.init(<allocator>,
-/// <fail_index>).allocator;`
+/// <config>).allocator;`
///
/// Then use `failing_allocator` anywhere you would have used a
/// different allocator.
pub const FailingAllocator = struct {
- index: usize,
- fail_index: usize,
+ alloc_index: usize,
+ resize_index: usize,
internal_allocator: mem.Allocator,
allocated_bytes: usize,
freed_bytes: usize,
@@ -21,28 +35,24 @@ pub const FailingAllocator = struct {
deallocations: usize,
stack_addresses: [num_stack_frames]usize,
has_induced_failure: bool,
+ fail_index: usize,
+ resize_fail_index: usize,
const num_stack_frames = if (std.debug.sys_can_stack_trace) 16 else 0;
- /// `fail_index` is the number of successful allocations you can
- /// expect from this allocator. The next allocation will fail.
- /// For example, if this is called with `fail_index` equal to 2,
- /// the following test will pass:
- ///
- /// var a = try failing_alloc.create(i32);
- /// var b = try failing_alloc.create(i32);
- /// testing.expectError(error.OutOfMemory, failing_alloc.create(i32));
- pub fn init(internal_allocator: mem.Allocator, fail_index: usize) FailingAllocator {
+ pub fn init(internal_allocator: mem.Allocator, config: Config) FailingAllocator {
return FailingAllocator{
.internal_allocator = internal_allocator,
- .fail_index = fail_index,
- .index = 0,
+ .alloc_index = 0,
+ .resize_index = 0,
.allocated_bytes = 0,
.freed_bytes = 0,
.allocations = 0,
.deallocations = 0,
.stack_addresses = undefined,
.has_induced_failure = false,
+ .fail_index = config.fail_index,
+ .resize_fail_index = config.resize_fail_index,
};
}
@@ -64,7 +74,7 @@ pub const FailingAllocator = struct {
return_address: usize,
) ?[*]u8 {
const self: *FailingAllocator = @ptrCast(@alignCast(ctx));
- if (self.index == self.fail_index) {
+ if (self.alloc_index == self.fail_index) {
if (!self.has_induced_failure) {
@memset(&self.stack_addresses, 0);
var stack_trace = std.builtin.StackTrace{
@@ -80,7 +90,7 @@ pub const FailingAllocator = struct {
return null;
self.allocated_bytes += len;
self.allocations += 1;
- self.index += 1;
+ self.alloc_index += 1;
return result;
}
@@ -92,6 +102,8 @@ pub const FailingAllocator = struct {
ra: usize,
) bool {
const self: *FailingAllocator = @ptrCast(@alignCast(ctx));
+ if (self.resize_index == self.resize_fail_index)
+ return false;
if (!self.internal_allocator.rawResize(old_mem, log2_old_align, new_len, ra))
return false;
if (new_len < old_mem.len) {
@@ -99,6 +111,7 @@ pub const FailingAllocator = struct {
} else {
self.allocated_bytes += new_len - old_mem.len;
}
+ self.resize_index += 1;
return true;
}
lib/std/array_list.zig
@@ -1587,13 +1587,8 @@ test "std.ArrayListUnmanaged(u8) implements writer" {
}
test "shrink still sets length when resizing is disabled" {
- // Use the testing allocator but with resize disabled.
- var a = testing.allocator;
- a.vtable = &.{
- .alloc = a.vtable.alloc,
- .resize = Allocator.noResize,
- .free = a.vtable.free,
- };
+ var failing_allocator = testing.FailingAllocator.init(testing.allocator, .{ .resize_fail_index = 0 });
+ const a = failing_allocator.allocator();
{
var list = ArrayList(i32).init(a);
@@ -1620,13 +1615,9 @@ test "shrink still sets length when resizing is disabled" {
}
test "shrinkAndFree with a copy" {
- // Use the testing allocator but with resize disabled.
- var a = testing.allocator;
- a.vtable = &.{
- .alloc = a.vtable.alloc,
- .resize = Allocator.noResize,
- .free = a.vtable.free,
- };
+ var failing_allocator = testing.FailingAllocator.init(testing.allocator, .{ .resize_fail_index = 0 });
+ const a = failing_allocator.allocator();
+
var list = ArrayList(i32).init(a);
defer list.deinit();
@@ -1748,8 +1739,7 @@ test "ArrayListAligned/ArrayListAlignedUnmanaged accepts unaligned slices" {
test "std.ArrayList(u0)" {
// An ArrayList on zero-sized types should not need to allocate
- var failing_allocator = testing.FailingAllocator.init(testing.allocator, 0);
- const a = failing_allocator.allocator();
+ const a = testing.failing_allocator;
var list = ArrayList(u0).init(a);
defer list.deinit();
lib/std/testing.zig
@@ -14,7 +14,7 @@ pub var allocator_instance = b: {
};
pub const failing_allocator = failing_allocator_instance.allocator();
-pub var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.allocator(), 0);
+pub var failing_allocator_instance = FailingAllocator.init(base_allocator_instance.allocator(), .{ .fail_index = 0 });
pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
@@ -1081,16 +1081,16 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
// Try it once with unlimited memory, make sure it works
const needed_alloc_count = x: {
- var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, std.math.maxInt(usize));
+ var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{});
args.@"0" = failing_allocator_inst.allocator();
try @call(.auto, test_fn, args);
- break :x failing_allocator_inst.index;
+ break :x failing_allocator_inst.alloc_index;
};
var fail_index: usize = 0;
while (fail_index < needed_alloc_count) : (fail_index += 1) {
- var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, fail_index);
+ var failing_allocator_inst = std.testing.FailingAllocator.init(backing_allocator, .{ .fail_index = fail_index });
args.@"0" = failing_allocator_inst.allocator();
if (@call(.auto, test_fn, args)) |_| {