lib/alloc/src/test.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const alloc = @import("root.zig");
  3 const alloc_phase = @import("alloc_phase");
  4 const diagnostics = @import("diagnostics.zig");
  5 const policy = @import("policy.zig");
  6 
  7 const test_allocator_mode = alloc.test_allocator_mode;
  8 const allocator_diagnostics_mode = alloc.allocator_diagnostics_mode;
  9 const process_phase_mode = alloc.process_phase_mode;
 10 const longLivedProcessAllocator = alloc.longLivedProcessAllocator;
 11 const interactiveSessionAllocator = alloc.interactiveSessionAllocator;
 12 const standaloneProcessAllocator = alloc.standaloneProcessAllocator;
 13 const workerAllocator = alloc.workerAllocator;
 14 const threadedIoAllocator = alloc.threadedIoAllocator;
 15 const temporaryScratchAllocator = alloc.temporaryScratchAllocator;
 16 const testIntegrationAllocator = alloc.testIntegrationAllocator;
 17 const diagnosticAllocatorConfig = alloc.diagnosticAllocatorConfig;
 18 const diagnosticAllocatorReport = alloc.diagnosticAllocatorReport;
 19 const sealProcessAllocatorPhase = alloc.sealProcessAllocatorPhase;
 20 const beginProcessAllocatorTeardown = alloc.beginProcessAllocatorTeardown;
 21 const processAllocatorPhase = alloc.processAllocatorPhase;
 22 const processAllocatorPhaseViolations = alloc.processAllocatorPhaseViolations;
 23 const processAllocatorPhaseViolationSites = alloc.processAllocatorPhaseViolationSites;
 24 const ProcessAllocatorOwner = alloc.ProcessAllocatorOwner;
 25 const ProcessAllocatorFinish = alloc.ProcessAllocatorFinish;
 26 
 27 test "alloc package namespace" {
 28     std.testing.refAllDecls(alloc);
 29 }
 30 
 31 test "page namespace" {
 32     std.testing.refAllDecls(@import("page/root.zig"));
 33 }
 34 
 35 test "public phase allocator interoperates with internal phase consumers" {
 36     var phase: alloc_phase.SealedPhaseAllocator =
 37         try alloc.phase.SealedPhaseAllocator.init(std.testing.allocator);
 38     const expected = [_]u8{ 3, 1, 4, 1 };
 39     const storage = phase.initializationAllocator().alloc(u8, expected.len) catch |err| {
 40         phase.abortInitialization();
 41         phase.deinit();
 42         return err;
 43     };
 44     @memcpy(storage, &expected);
 45 
 46     phase.seal();
 47     const state: alloc.phase.capacity.Phase = phase.phase();
 48     const violations: alloc.phase.PhaseViolations = phase.violations();
 49     phase.beginTeardown();
 50     defer phase.deinit();
 51     defer phase.teardownAllocator().free(storage);
 52 
 53     try std.testing.expectEqual(.steady, state);
 54     try std.testing.expectEqual(@as(u64, 0), violations.total());
 55     try std.testing.expectEqualSlices(u8, &expected, storage);
 56 }
 57 
 58 fn expectSameAllocator(expected: std.mem.Allocator, actual: std.mem.Allocator) !void {
 59     try std.testing.expectEqual(expected.ptr, actual.ptr);
 60     try std.testing.expectEqual(expected.vtable, actual.vtable);
 61 }
 62 
 63 test "allocator policy separates fast integration tests from leak-checking tests" {
 64     const diagnostic_allocator = std.testing.allocator;
 65     const policy_allocator = policy.platformProcessAllocator();
 66     const expected_test_allocator = switch (test_allocator_mode) {
 67         .fast => policy_allocator,
 68         .leak_check => diagnostic_allocator,
 69     };
 70 
 71     try expectSameAllocator(policy_allocator, longLivedProcessAllocator());
 72     try expectSameAllocator(expected_test_allocator, interactiveSessionAllocator(diagnostic_allocator));
 73     try expectSameAllocator(expected_test_allocator, standaloneProcessAllocator());
 74     try expectSameAllocator(expected_test_allocator, testIntegrationAllocator());
 75     try expectSameAllocator(expected_test_allocator, workerAllocator());
 76     try expectSameAllocator(expected_test_allocator, threadedIoAllocator());
 77     try expectSameAllocator(expected_test_allocator, temporaryScratchAllocator());
 78 }
 79 
 80 test "allocator diagnostics mode is exposed as deadalloc config" {
 81     const config = diagnosticAllocatorConfig() orelse {
 82         try std.testing.expect(allocator_diagnostics_mode == null);
 83         return;
 84     };
 85     try std.testing.expect(config.diagnostics.enabled);
 86     try std.testing.expectEqual(allocator_diagnostics_mode.?, config.mode);
 87 }
 88 
 89 test "allocator diagnostics mode routes process allocations through deadalloc" {
 90     const config = diagnosticAllocatorConfig() orelse {
 91         try std.testing.expect(diagnosticAllocatorReport() == null);
 92         return;
 93     };
 94 
 95     const allocator = longLivedProcessAllocator();
 96     try std.testing.expect(allocator.ptr != diagnostics.baseProcessAllocator().ptr or allocator.vtable != diagnostics.baseProcessAllocator().vtable);
 97 
 98     const before = diagnosticAllocatorReport().?;
 99     const leaked = try allocator.alloc(u8, 32);
100     @memset(leaked, 0x5a);
101     const during = diagnosticAllocatorReport().?;
102     try std.testing.expect(during.live_allocations >= before.live_allocations + 1);
103     try std.testing.expect(during.live_bytes >= before.live_bytes + 32);
104     allocator.free(leaked);
105 
106     const after = diagnosticAllocatorReport().?;
107     try std.testing.expect(after.live_allocations <= during.live_allocations);
108     try std.testing.expectEqual(config.mode, allocator_diagnostics_mode.?);
109 }
110 
111 test "process allocator owner follows runtime diagnostics policy" {
112     var process_owner = ProcessAllocatorOwner.init();
113     const allocator = process_owner.allocator();
114     const runtime_allocator = standaloneProcessAllocator();
115 
116     if (diagnosticAllocatorConfig() != null or process_phase_mode != .off) {
117         try expectSameAllocator(runtime_allocator, allocator);
118     } else {
119         try std.testing.expect(allocator.ptr != runtime_allocator.ptr or allocator.vtable != runtime_allocator.vtable);
120         try std.testing.expectEqual(ProcessAllocatorFinish.clean, process_owner.finish());
121     }
122 }
123 
124 test "process allocator owner returns deadalloc diagnostics" {
125     const config = diagnosticAllocatorConfig() orelse return;
126     var process_owner = ProcessAllocatorOwner.init();
127     const allocator = process_owner.allocator();
128 
129     const before = diagnosticAllocatorReport().?;
130     const leaked = try allocator.alloc(u8, 19);
131     defer allocator.free(leaked);
132     @memset(leaked, 0x71);
133 
134     const finish = process_owner.finish();
135     const report = finish.diagnosticReport() orelse return error.ExpectedAllocatorDiagnostics;
136     try std.testing.expectEqual(@as(u8, 1), finish.exitCode());
137     try std.testing.expect(report.live_allocations >= before.live_allocations + 1);
138     try std.testing.expect(report.live_bytes >= before.live_bytes + 19);
139     try std.testing.expectEqual(config.mode, allocator_diagnostics_mode.?);
140 }
141 
142 test "process allocator owner returns invalid free diagnostics" {
143     const config = diagnosticAllocatorConfig() orelse return;
144     var process_owner = ProcessAllocatorOwner.init();
145     const allocator = process_owner.allocator();
146 
147     const before = diagnosticAllocatorReport().?;
148     var stack_byte: u8 = 0;
149     allocator.rawFree((&stack_byte)[0..1], .@"1", @returnAddress());
150 
151     const finish = process_owner.finish();
152     const report = finish.diagnosticReport() orelse return error.ExpectedAllocatorDiagnostics;
153     const issue = report.last_issue orelse return error.ExpectedAllocatorDiagnostics;
154     try std.testing.expectEqual(@as(u8, 1), finish.exitCode());
155     try std.testing.expect(report.counters.invalid_free >= before.counters.invalid_free + 1);
156     try std.testing.expectEqual(.invalid_free, issue.kind);
157     try std.testing.expectEqual(@intFromPtr(&stack_byte), issue.address);
158     try std.testing.expectEqual(config.mode, allocator_diagnostics_mode.?);
159 }
160 
161 test "process allocator owner returns overflow diagnostics" {
162     const config = diagnosticAllocatorConfig() orelse return;
163     var process_owner = ProcessAllocatorOwner.init();
164     const allocator = process_owner.allocator();
165 
166     const before = diagnosticAllocatorReport().?;
167     const ptr = allocator.rawAlloc(8, .@"1", @returnAddress()) orelse return error.OutOfMemory;
168     ptr[8] = 0xee;
169 
170     const finish = process_owner.finish();
171     const report = finish.diagnosticReport() orelse return error.ExpectedAllocatorDiagnostics;
172     const issue = report.last_issue orelse return error.ExpectedAllocatorDiagnostics;
173     try std.testing.expectEqual(@as(u8, 1), finish.exitCode());
174     try std.testing.expect(report.counters.buffer_overflow >= before.counters.buffer_overflow + 1);
175     try std.testing.expect(report.live_allocations >= before.live_allocations + 1);
176     try std.testing.expectEqual(.buffer_overflow, issue.kind);
177     try std.testing.expectEqual(@intFromPtr(ptr), issue.address);
178     try std.testing.expectEqual(@as(usize, 8), issue.offset);
179     try std.testing.expectEqual(@as(usize, 8), issue.requested_len);
180     try std.testing.expect(issue.allocation_id != 0);
181     try std.testing.expectEqual(config.mode, allocator_diagnostics_mode.?);
182 
183     allocator.rawFree(ptr[0..8], .@"1", @returnAddress());
184 }
185 
186 test "process allocator phase lifecycle" {
187     switch (process_phase_mode) {
188         .off => {
189             try std.testing.expect(processAllocatorPhaseViolations() == null);
190             try std.testing.expect(processAllocatorPhaseViolationSites() == null);
191             try std.testing.expect(processAllocatorPhase() == null);
192             sealProcessAllocatorPhase();
193             beginProcessAllocatorTeardown();
194         },
195         .observe => {
196             const allocator = policy.platformProcessAllocator();
197             const startup = try allocator.alloc(u8, 24);
198             @memset(startup, 0x11);
199             const before = processAllocatorPhaseViolations().?;
200 
201             sealProcessAllocatorPhase();
202             try std.testing.expectEqual(.steady, processAllocatorPhase().?);
203             const steady = try allocator.alloc(u8, 24);
204             @memset(steady, 0x22);
205             allocator.free(steady);
206             const during = processAllocatorPhaseViolations().?;
207             try std.testing.expect(during.allocations >= before.allocations + 1);
208             try std.testing.expect(during.frees >= before.frees + 1);
209             try std.testing.expect(processAllocatorPhaseViolationSites().?.count >= 1);
210 
211             beginProcessAllocatorTeardown();
212             try std.testing.expectEqual(.teardown, processAllocatorPhase().?);
213             allocator.free(startup);
214             const after = processAllocatorPhaseViolations().?;
215             try std.testing.expectEqual(during.frees, after.frees);
216         },
217         .seal => {
218             const allocator = policy.platformProcessAllocator();
219             const startup = try allocator.alloc(u8, 24);
220             @memset(startup, 0x33);
221             try std.testing.expectEqual(.initialization, processAllocatorPhase().?);
222             try std.testing.expect(processAllocatorPhaseViolationSites() == null);
223 
224             sealProcessAllocatorPhase();
225             try std.testing.expectEqual(.steady, processAllocatorPhase().?);
226             try std.testing.expectEqual(@as(u64, 0), processAllocatorPhaseViolations().?.total());
227 
228             beginProcessAllocatorTeardown();
229             try std.testing.expectEqual(.teardown, processAllocatorPhase().?);
230             allocator.free(startup);
231             try std.testing.expectEqual(@as(u64, 0), processAllocatorPhaseViolations().?.total());
232         },
233     }
234 }
235 
236 test "process allocator finish maps reports to exit codes" {
237     const clean: ProcessAllocatorFinish = .clean;
238     const leak: ProcessAllocatorFinish = .leak;
239     try std.testing.expectEqual(@as(u8, 0), clean.exitCode());
240     try std.testing.expectEqual(@as(u8, 1), leak.exitCode());
241     try std.testing.expectEqual(@as(u8, 1), (ProcessAllocatorFinish{ .diagnostics = .{ .counters = .{ .invalid_free = 1 } } }).exitCode());
242     try std.testing.expect((ProcessAllocatorFinish{ .diagnostics = .{} }).diagnosticReport() != null);
243     try std.testing.expect(clean.diagnosticReport() == null);
244 }