| ... | @@ -1,13 +1,5 @@ | ... | @@ -1,13 +1,5 @@ |
| 1 | //! Allocator that fails after N allocations, useful for making sure out of | 1 | //! Allocator that fails after N allocations, useful for making sure out of |
| 2 | //! memory conditions are handled correctly. | 2 | //! memory conditions are handled correctly. |
| 3 | //! | | |
| 4 | //! To use this, first initialize it and get an allocator with | | |
| 5 | //! | | |
| 6 | //! `const failing_allocator = &FailingAllocator.init(<allocator>, | | |
| 7 | //! <config>).allocator;` | | |
| 8 | //! | | |
| 9 | //! Then use `failing_allocator` anywhere you would have used a | | |
| 10 | //! different allocator. | | |
| 11 | const std = @import("../std.zig"); | 3 | const std = @import("../std.zig"); |
| 12 | const mem = std.mem; | 4 | const mem = std.mem; |
| 13 | const FailingAllocator = @This(); | 5 | const FailingAllocator = @This(); |
| ... | @@ -28,12 +20,7 @@ const num_stack_frames = if (std.debug.sys_can_stack_trace) 16 else 0; | ... | @@ -28,12 +20,7 @@ const num_stack_frames = if (std.debug.sys_can_stack_trace) 16 else 0; |
| 28 | | 20 | |
| 29 | pub const Config = struct { | 21 | pub const Config = struct { |
| 30 | /// The number of successful allocations you can expect from this allocator. | 22 | /// The number of successful allocations you can expect from this allocator. |
| 31 | /// The next allocation will fail. For example, with `fail_index` equal to | 23 | /// The next allocation will fail. |
| 32 | /// 2, the following test will pass: | | |
| 33 | /// | | |
| 34 | /// var a = try failing_alloc.create(i32); | | |
| 35 | /// var b = try failing_alloc.create(i32); | | |
| 36 | /// testing.expectError(error.OutOfMemory, failing_alloc.create(i32)); | | |
| 37 | fail_index: usize = std.math.maxInt(usize), | 24 | fail_index: usize = std.math.maxInt(usize), |
| 38 | | 25 | |
| 39 | /// Number of successful resizes to expect from this allocator. The next resize will fail. | 26 | /// Number of successful resizes to expect from this allocator. The next resize will fail. |
| ... | @@ -159,3 +146,40 @@ pub fn getStackTrace(self: *FailingAllocator) std.builtin.StackTrace { | ... | @@ -159,3 +146,40 @@ pub fn getStackTrace(self: *FailingAllocator) std.builtin.StackTrace { |
| 159 | .index = len, | 146 | .index = len, |
| 160 | }; | 147 | }; |
| 161 | } | 148 | } |
| | 149 | |
| | 150 | test FailingAllocator { |
| | 151 | // Fail on allocation |
| | 152 | { |
| | 153 | var failing_allocator_state = FailingAllocator.init(std.testing.allocator, .{ |
| | 154 | .fail_index = 2, |
| | 155 | }); |
| | 156 | const failing_alloc = failing_allocator_state.allocator(); |
| | 157 | |
| | 158 | const a = try failing_alloc.create(i32); |
| | 159 | defer failing_alloc.destroy(a); |
| | 160 | const b = try failing_alloc.create(i32); |
| | 161 | defer failing_alloc.destroy(b); |
| | 162 | try std.testing.expectError(error.OutOfMemory, failing_alloc.create(i32)); |
| | 163 | } |
| | 164 | // Fail on resize |
| | 165 | { |
| | 166 | var failing_allocator_state = FailingAllocator.init(std.testing.allocator, .{ |
| | 167 | .resize_fail_index = 1, |
| | 168 | }); |
| | 169 | const failing_alloc = failing_allocator_state.allocator(); |
| | 170 | |
| | 171 | const resized_slice = blk: { |
| | 172 | const slice = try failing_alloc.alloc(u8, 8); |
| | 173 | errdefer failing_alloc.free(slice); |
| | 174 | |
| | 175 | break :blk failing_alloc.remap(slice, 6) orelse return error.UnexpectedRemapFailure; |
| | 176 | }; |
| | 177 | defer failing_alloc.free(resized_slice); |
| | 178 | |
| | 179 | // Remap and resize should fail from here on out |
| | 180 | try std.testing.expectEqual(null, failing_alloc.remap(resized_slice, 4)); |
| | 181 | try std.testing.expectEqual(false, failing_alloc.resize(resized_slice, 4)); |
| | 182 | |
| | 183 | // Note: realloc could succeed because it falls back to free+alloc |
| | 184 | } |
| | 185 | } |