| ... | ... | @@ -1,13 +1,5 @@ |
| 1 | 1 | //! Allocator that fails after N allocations, useful for making sure out of |
| 2 | 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 | 3 | const std = @import("../std.zig"); |
| 12 | 4 | const mem = std.mem; |
| 13 | 5 | const FailingAllocator = @This(); |
| ... | ... | @@ -28,12 +20,7 @@ const num_stack_frames = if (std.debug.sys_can_stack_trace) 16 else 0; |
| 28 | 20 | |
| 29 | 21 | pub const Config = struct { |
| 30 | 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 |
| 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)); |
| 23 | /// The next allocation will fail. |
| 37 | 24 | fail_index: usize = std.math.maxInt(usize), |
| 38 | 25 | |
| 39 | 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 | 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 | } |