lib/alloc/phase/src/capacity/test.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const capacity = @import("capacity");
3 const phase = @import("../root.zig");
4
5 const Allocator = std.mem.Allocator;
6 const OwnerShapeViolation = capacity.OwnerShapeViolation;
7 const Phase = capacity.Phase;
8 const ProvisionedExactFixture = capacity.ProvisionedExactFixture;
9 const ProvisionedRejectingFixture = capacity.ProvisionedRejectingFixture;
10
11 const TestLimits = struct {
12 slots: u32,
13 };
14
15 const TestCapacity = struct {
16 slots: usize,
17
18 pub fn derive(limits: TestLimits) error{InvalidLimit}!TestCapacity {
19 if (limits.slots == 0) return error.InvalidLimit;
20 return .{ .slots = limits.slots };
21 }
22 };
23
24 const TestExhaustion = error{Full};
25
26 const TestOwner = struct {
27 pub const Limits = TestLimits;
28 pub const Capacity = TestCapacity;
29 pub const Exhaustion = TestExhaustion;
30
31 pub const claim: capacity.Declaration = .{
32 .source = .{
33 .id = "alloc.shape_fixture",
34 .kind = .phase_static,
35 .limit_source = .caller,
36 .storage = .{
37 .covered = &.{.{
38 .id = "slot_storage",
39 .lifetime = .steady,
40 .detail = "slot storage",
41 }},
42 .excluded = &.{"caller-owned payloads"},
43 },
44 .capacity = .{
45 .inputs = &.{capacity.bindInput(Limits, "slots", "slots")},
46 .nodes = &.{.{ .input = 0 }},
47 .assertions = &.{.{
48 .scope = .closure_total,
49 .measure = .retained,
50 .relation = .exact,
51 .expression = 0,
52 }},
53 },
54 .overload = .{
55 .kind = .reject_before_mutation,
56 .detail = "take returns Full before any slot mutates",
57 },
58 .risks = .{
59 .transitive = .{ .status = .open, .detail = "fixture has no callees" },
60 .foreign = .{ .status = .open, .detail = "fixture crosses no foreign resources" },
61 },
62 .obligations = &.{
63 .{ .key = "capacity", .role = .capacity_model },
64 .{ .key = "overload", .role = .overload },
65 },
66 },
67 .bindings = .{
68 .owner = @This(),
69 .seal = .{
70 .family = capacity.selector(@This().activate),
71 .premise = .{
72 .class = .checked_semantic_fact,
73 .authority = .checker,
74 },
75 },
76 .teardown = .{
77 .family = capacity.selector(@This().deinit),
78 .premise = .{
79 .class = .checked_semantic_fact,
80 .authority = .checker,
81 },
82 },
83 },
84 };
85
86 phase: Phase,
87 capacity: TestCapacity,
88 storage: []u8,
89
90 pub fn init(allocator: Allocator, limits: TestLimits) !TestOwner {
91 const derived = try TestCapacity.derive(limits);
92 return .{
93 .phase = .initialization,
94 .capacity = derived,
95 .storage = try allocator.alloc(u8, derived.slots),
96 };
97 }
98
99 pub fn activate(self: *TestOwner) void {
100 self.phase = .steady;
101 }
102
103 pub fn available(self: *const TestOwner) usize {
104 std.debug.assert(self.phase == .steady);
105 return self.storage.len;
106 }
107
108 pub fn take(self: *TestOwner) Exhaustion!void {
109 if (self.storage.len == 0) return error.Full;
110 }
111
112 pub fn deinit(self: *TestOwner, allocator: Allocator) void {
113 self.phase = .teardown;
114 allocator.free(self.storage);
115 }
116 };
117
118 const TypedOwner = struct {
119 pub const Limits = struct {
120 shape: struct { slots: u16 },
121 slot_bytes: u32,
122 };
123 pub const Capacity = struct {
124 storage_bytes: usize,
125
126 pub fn derive(limits: Limits) error{ InvalidLimit, CapacityOverflow }!Capacity {
127 if (limits.shape.slots == 0 or limits.slot_bytes == 0) {
128 return error.InvalidLimit;
129 }
130 return .{
131 .storage_bytes = try capacity.mul(
132 usize,
133 limits.shape.slots,
134 limits.slot_bytes,
135 ),
136 };
137 }
138 };
139 pub const claim: capacity.Declaration = .{
140 .source = .{
141 .id = "alloc.typed_shape_fixture",
142 .kind = .phase_static,
143 .limit_source = .application_default,
144 .storage = .{
145 .covered = &.{
146 .{
147 .id = "scratch",
148 .lifetime = .initialization,
149 .detail = "capacity derivation scratch",
150 },
151 .{
152 .id = "slots",
153 .lifetime = .steady,
154 .detail = "retained slot storage",
155 },
156 .{
157 .id = "handoff",
158 .lifetime = .transferred,
159 .detail = "returned payload storage",
160 },
161 },
162 .excluded = &.{"caller-owned payloads"},
163 },
164 .capacity = .{
165 .inputs = &.{
166 capacity.bindInput(Limits, "slots", "shape.slots"),
167 capacity.bindInput(Limits, "slot_bytes", "slot_bytes"),
168 },
169 .nodes = &.{
170 .{ .constant = 1 },
171 .{ .input = 0 },
172 .{ .input = 1 },
173 .{ .add = .{ .left = 1, .right = 2 } },
174 .{ .maximum = .{ .left = 0, .right = 3 } },
175 .{ .scale = .{ .node = 4, .coefficient = .{ .literal = 2 } } },
176 .{ .scale = .{
177 .node = 5,
178 .coefficient = .{ .unsigned_comptime_parameter = 0 },
179 } },
180 .{ .scale = .{
181 .node = 6,
182 .coefficient = .{ .size_of_type_parameter = 0 },
183 } },
184 },
185 .assertions = &.{
186 .{
187 .scope = .closure_total,
188 .measure = .retained,
189 .relation = .upper_bound,
190 .expression = 7,
191 },
192 .{
193 .scope = .closure_total,
194 .measure = .retained,
195 .relation = .exact,
196 .expression = 3,
197 },
198 },
199 },
200 .overload = .{
201 .kind = .reject_before_seal,
202 .detail = "capacity admission rejects before owner construction",
203 },
204 .risks = .{
205 .transitive = .{
206 .status = .witnessed,
207 .detail = "the fixture has no transitive owners",
208 },
209 .foreign = .{
210 .status = .excluded,
211 .detail = "the fixture crosses no foreign boundary",
212 },
213 },
214 .work = .{ .equation = "transition steps are at most one" },
215 .dependencies = &.{"alloc.fixture_support"},
216 .obligations = &.{
217 .{ .key = "capacity", .role = .capacity_model },
218 .{ .key = "acquire", .role = .acquisition },
219 .{ .key = "init_failure", .role = .initialization_failure },
220 .{ .key = "overload", .role = .overload },
221 .{ .key = "seal", .role = .seal },
222 .{ .key = "teardown", .role = .teardown },
223 .{ .key = "work", .role = .work_bound },
224 .{ .key = "transitive", .role = .transitive_risk },
225 .{ .key = "foreign", .role = .foreign_risk },
226 .{ .key = "integration", .role = .integration },
227 .{ .key = "extension", .role = .custom },
228 },
229 },
230 .bindings = .{
231 .owner = @This(),
232 .default_limits = capacity.selector(defaultLimits),
233 .seal = .{
234 .family = capacity.selector(@This().activate),
235 .premise = .{
236 .class = .checked_semantic_fact,
237 .authority = .checker,
238 },
239 },
240 .teardown = .{
241 .family = capacity.selector(@This().deinit),
242 .premise = .{
243 .class = .theorem_domain,
244 .authority = .{ .theorem = .{ .key = "teardown" } },
245 },
246 },
247 },
248 };
249
250 phase: Phase,
251 capacity: Capacity,
252 storage: []u8,
253
254 fn defaultLimits() Limits {
255 return .{ .shape = .{ .slots = 4 }, .slot_bytes = 1 };
256 }
257
258 pub fn init(allocator: Allocator, limits: Limits) !TypedOwner {
259 const derived = try Capacity.derive(limits);
260 return .{
261 .phase = .initialization,
262 .capacity = derived,
263 .storage = try allocator.alloc(u8, derived.storage_bytes),
264 };
265 }
266
267 pub fn activate(self: *TypedOwner) void {
268 std.debug.assert(self.phase == .initialization);
269 self.phase = .steady;
270 }
271
272 pub fn deinit(self: *TypedOwner, allocator: Allocator) void {
273 std.debug.assert(self.phase == .steady);
274 self.phase = .teardown;
275 allocator.free(self.storage);
276 }
277 };
278
279 const ExactOwnerFixture = struct {
280 pub const Limits = TestLimits;
281 pub const Capacity = TestCapacity;
282
283 phase: Phase,
284 capacity: TestCapacity,
285
286 pub fn init(_: Allocator, limits: TestLimits) !@This() {
287 return .{
288 .phase = .initialization,
289 .capacity = try TestCapacity.derive(limits),
290 };
291 }
292
293 pub fn activate(self: *@This()) void {
294 self.phase = .steady;
295 }
296
297 pub fn run(self: *const @This()) usize {
298 return self.capacity.slots;
299 }
300
301 pub fn deinit(self: *@This(), _: Allocator) void {
302 self.phase = .teardown;
303 }
304 };
305
306 const StoredAllocatorOwnerFixture = struct {
307 pub const Limits = TestLimits;
308 pub const Capacity = TestCapacity;
309 pub const Exhaustion = TestExhaustion;
310
311 phase: Phase,
312 capacity: TestCapacity,
313 allocator: Allocator,
314
315 pub fn init(allocator: Allocator, limits: TestLimits) !@This() {
316 return .{
317 .phase = .initialization,
318 .capacity = try TestCapacity.derive(limits),
319 .allocator = allocator,
320 };
321 }
322
323 pub fn activate(self: *@This()) void {
324 self.phase = .steady;
325 }
326
327 pub fn deinit(self: *@This(), allocator: Allocator) void {
328 _ = self;
329 _ = allocator;
330 }
331 };
332
333 const SteadyAllocatorOwnerFixture = struct {
334 pub const Limits = TestLimits;
335 pub const Capacity = TestCapacity;
336 pub const Exhaustion = TestExhaustion;
337
338 phase: Phase,
339 capacity: TestCapacity,
340
341 pub fn init(_: Allocator, limits: TestLimits) !@This() {
342 return .{ .phase = .initialization, .capacity = try TestCapacity.derive(limits) };
343 }
344
345 pub fn activate(self: *@This()) void {
346 self.phase = .steady;
347 }
348
349 pub fn grow(_: *@This(), _: Allocator) void {}
350
351 pub fn deinit(self: *@This(), allocator: Allocator) void {
352 _ = self;
353 _ = allocator;
354 }
355 };
356
357 const InvalidCapacityFixture = struct {
358 pub fn derive(_: usize) @This() {
359 return .{};
360 }
361 };
362
363 const CapacityDerivationOwnerFixture = struct {
364 pub const Limits = TestLimits;
365 pub const Capacity = InvalidCapacityFixture;
366 pub const Exhaustion = TestExhaustion;
367
368 phase: Phase,
369 capacity: InvalidCapacityFixture,
370
371 pub fn init(_: Allocator, _: TestLimits) @This() {
372 return .{ .phase = .initialization, .capacity = .{} };
373 }
374
375 pub fn activate(self: *@This()) void {
376 self.phase = .steady;
377 }
378
379 pub fn deinit(self: *@This(), allocator: Allocator) void {
380 _ = self;
381 _ = allocator;
382 }
383 };
384
385 const WrongExhaustionOwnerFixture = struct {
386 pub const Limits = TestLimits;
387 pub const Capacity = TestCapacity;
388 pub const Exhaustion = usize;
389
390 phase: Phase,
391 capacity: TestCapacity,
392
393 pub fn init(_: Allocator, limits: TestLimits) !@This() {
394 return .{ .phase = .initialization, .capacity = try TestCapacity.derive(limits) };
395 }
396
397 pub fn activate(self: *@This()) void {
398 self.phase = .steady;
399 }
400
401 pub fn deinit(self: *@This(), allocator: Allocator) void {
402 _ = self;
403 _ = allocator;
404 }
405 };
406
407 const PrimitiveLimitsOwnerFixture = struct {
408 pub const Limits = usize;
409 pub const Capacity = TestCapacity;
410 pub const Exhaustion = TestExhaustion;
411 };
412
413 const PrimitiveCapacityOwnerFixture = struct {
414 pub const Limits = TestLimits;
415 pub const Capacity = usize;
416 pub const Exhaustion = TestExhaustion;
417
418 phase: Phase,
419 capacity: usize,
420 };
421
422 const SteadyAllocatorResultOwnerFixture = struct {
423 pub const Limits = TestLimits;
424 pub const Capacity = TestCapacity;
425 pub const Exhaustion = TestExhaustion;
426
427 phase: Phase,
428 capacity: TestCapacity,
429
430 pub fn init(_: Allocator, limits: TestLimits) !@This() {
431 return .{ .phase = .initialization, .capacity = try TestCapacity.derive(limits) };
432 }
433
434 pub fn activate(self: *@This()) void {
435 self.phase = .steady;
436 }
437
438 pub fn currentAllocator(_: *const @This()) Allocator {
439 return undefined;
440 }
441
442 pub fn deinit(self: *@This(), allocator: Allocator) void {
443 _ = self;
444 _ = allocator;
445 }
446 };
447
448 const ByValueAllocatorResultOwnerFixture = struct {
449 pub const Limits = TestLimits;
450 pub const Capacity = TestCapacity;
451 pub const Exhaustion = TestExhaustion;
452
453 phase: Phase,
454 capacity: TestCapacity,
455
456 pub fn init(_: Allocator, limits: TestLimits) !@This() {
457 return .{ .phase = .initialization, .capacity = try TestCapacity.derive(limits) };
458 }
459
460 pub fn activate(self: *@This()) void {
461 self.phase = .steady;
462 }
463
464 pub fn currentAllocator(_: @This()) Allocator {
465 return undefined;
466 }
467
468 pub fn deinit(self: *@This(), allocator: Allocator) void {
469 _ = self;
470 _ = allocator;
471 }
472 };
473
474 const UnobservableExhaustionOwnerFixture = struct {
475 pub const Limits = TestLimits;
476 pub const Capacity = TestCapacity;
477 pub const Exhaustion = TestExhaustion;
478
479 phase: Phase,
480 capacity: TestCapacity,
481
482 pub fn init(_: Allocator, limits: TestLimits) !@This() {
483 return .{ .phase = .initialization, .capacity = try TestCapacity.derive(limits) };
484 }
485
486 pub fn activate(self: *@This()) void {
487 self.phase = .steady;
488 }
489
490 pub fn unrelated() Exhaustion!void {}
491
492 pub fn available(_: *const @This()) usize {
493 return 0;
494 }
495
496 pub fn deinit(self: *@This(), allocator: Allocator) void {
497 _ = self;
498 _ = allocator;
499 }
500 };
501
502 test "phase owner shape accepts an allocator-free steady surface" {
503 comptime capacity.requireAllocatorRejectingOwnerShape(TestOwner);
504 try std.testing.expect(
505 capacity.validateAllocatorRejectingOwnerShape(TestOwner) == null,
506 );
507
508 var owner = try TestOwner.init(std.testing.allocator, .{ .slots = 4 });
509 defer owner.deinit(std.testing.allocator);
510 owner.activate();
511 try std.testing.expectEqual(@as(usize, 4), owner.available());
512 }
513
514 test "typed claim declaration covers the complete authoring surface" {
515 comptime capacity.requireAllocatorExactOwnerShape(TypedOwner);
516 try std.testing.expect(
517 capacity.validateAllocatorExactOwnerShape(TypedOwner) == null,
518 );
519 var owner = try TypedOwner.init(std.testing.allocator, .{
520 .shape = .{ .slots = 4 },
521 .slot_bytes = 2,
522 });
523 owner.activate();
524 try std.testing.expectEqual(@as(usize, 8), owner.storage.len);
525 owner.deinit(std.testing.allocator);
526 }
527
528 test "phase owner shape accepts exact work without steady exhaustion" {
529 comptime capacity.requireAllocatorExactOwnerShape(ExactOwnerFixture);
530 try std.testing.expect(
531 capacity.validateAllocatorExactOwnerShape(ExactOwnerFixture) == null,
532 );
533 try std.testing.expectEqual(
534 OwnerShapeViolation.missing_exhaustion_declaration,
535 capacity.validateAllocatorRejectingOwnerShape(ExactOwnerFixture).?,
536 );
537 }
538
539 test "phase owner shape classifies incomplete declarations" {
540 const MissingLimits = struct {};
541 const WrongLimits = struct {
542 pub const Limits = 1;
543 };
544 const MissingCapacity = struct {
545 pub const Limits = TestLimits;
546 };
547 try std.testing.expectEqual(
548 OwnerShapeViolation.owner_not_struct,
549 capacity.validateAllocatorExactOwnerShape(usize).?,
550 );
551 try std.testing.expectEqual(
552 OwnerShapeViolation.missing_limits_declaration,
553 capacity.validateAllocatorExactOwnerShape(MissingLimits).?,
554 );
555 try std.testing.expectEqual(
556 OwnerShapeViolation.wrong_limits_declaration,
557 capacity.validateAllocatorExactOwnerShape(WrongLimits).?,
558 );
559 try std.testing.expectEqual(
560 OwnerShapeViolation.missing_capacity_declaration,
561 capacity.validateAllocatorExactOwnerShape(MissingCapacity).?,
562 );
563 }
564
565 test "phase owner shape rejects a stored allocator" {
566 try std.testing.expectEqual(
567 OwnerShapeViolation.stored_allocator,
568 capacity.validateAllocatorExactOwnerShape(StoredAllocatorOwnerFixture).?,
569 );
570 }
571
572 test "phase owner shape rejects allocator-bearing steady methods" {
573 try std.testing.expectEqual(
574 OwnerShapeViolation.steady_allocator_parameter,
575 capacity.validateAllocatorExactOwnerShape(SteadyAllocatorOwnerFixture).?,
576 );
577 }
578
579 test "phase owner shape checks capacity derivation" {
580 try std.testing.expectEqual(
581 OwnerShapeViolation.wrong_capacity_derive,
582 capacity.validateAllocatorExactOwnerShape(CapacityDerivationOwnerFixture).?,
583 );
584 }
585
586 test "phase owner shape requires a domain exhaustion error" {
587 try std.testing.expectEqual(
588 OwnerShapeViolation.wrong_exhaustion_declaration,
589 capacity.validateAllocatorRejectingOwnerShape(WrongExhaustionOwnerFixture).?,
590 );
591 }
592
593 test "phase owner shape requires domain limit and capacity containers" {
594 try std.testing.expectEqual(
595 OwnerShapeViolation.wrong_limits_declaration,
596 capacity.validateAllocatorExactOwnerShape(PrimitiveLimitsOwnerFixture).?,
597 );
598 try std.testing.expectEqual(
599 OwnerShapeViolation.wrong_capacity_declaration,
600 capacity.validateAllocatorExactOwnerShape(PrimitiveCapacityOwnerFixture).?,
601 );
602 }
603
604 test "phase owner shape rejects allocator-bearing steady results" {
605 try std.testing.expectEqual(
606 OwnerShapeViolation.steady_allocator_result,
607 capacity.validateAllocatorExactOwnerShape(SteadyAllocatorResultOwnerFixture).?,
608 );
609 try std.testing.expectEqual(
610 OwnerShapeViolation.steady_allocator_result,
611 capacity.validateAllocatorExactOwnerShape(ByValueAllocatorResultOwnerFixture).?,
612 );
613 }
614
615 test "phase owner shape requires exhaustion on an owner operation" {
616 try std.testing.expectEqual(
617 OwnerShapeViolation.exhaustion_not_observable,
618 capacity.validateAllocatorRejectingOwnerShape(UnobservableExhaustionOwnerFixture).?,
619 );
620 }
621
622 test "provisioned exact owner returns its exact caller storage" {
623 comptime {
624 @stardustClaim(
625 capacity.witness(ProvisionedExactFixture, "alloc_provisioned_exact_capacity_capacity_model"),
626 null,
627 null,
628 null,
629 null,
630 null,
631 null,
632 );
633 }
634 comptime {
635 @stardustClaim(
636 capacity.witness(ProvisionedExactFixture, "alloc_provisioned_exact_capacity_overload"),
637 null,
638 null,
639 null,
640 null,
641 null,
642 null,
643 );
644 }
645 comptime {
646 @stardustClaim(
647 capacity.witness(ProvisionedExactFixture, "alloc_provisioned_exact_capacity_work_bound"),
648 null,
649 null,
650 null,
651 null,
652 null,
653 null,
654 );
655 }
656
657 const Owner = ProvisionedExactFixture;
658 comptime capacity.requireProvisionedExactOwnerShape(Owner);
659 try std.testing.expect(capacity.validateProvisionedExactOwnerShape(Owner) == null);
660
661 var bytes: [32]u8 align(Owner.storage_alignment) = @splat(0);
662 const storage: Owner.Storage = bytes[0..];
663 var owner = try Owner.init(storage, .{ .bytes = 24 });
664 owner.activate();
665 try std.testing.expectEqual(@as(usize, 24), owner.bytes());
666
667 const returned = owner.deinit();
668 try std.testing.expectEqual(@intFromPtr(storage.ptr), @intFromPtr(returned.ptr));
669 try std.testing.expectEqual(storage.len, returned.len);
670 }
671
672 test "provisioned owner rejects one-byte-short storage without mutation" {
673 const Owner = ProvisionedRejectingFixture;
674 var bytes: [16]u8 align(Owner.storage_alignment) = @splat(0xa5);
675 const before = bytes;
676 const short: Owner.Storage = bytes[0 .. bytes.len - 1];
677
678 try std.testing.expectError(
679 error.StorageTooShort,
680 Owner.init(short, .{ .slots = Owner.slots_max, .slot_bytes = 2 }),
681 );
682 try std.testing.expectEqualSlices(u8, &before, &bytes);
683 }
684
685 test "provisioned capacity rejects overflow and semantic max plus one" {
686 const Owner = ProvisionedRejectingFixture;
687 try std.testing.expectError(
688 error.CapacityOverflow,
689 Owner.Capacity.derive(.{
690 .slots = std.math.maxInt(usize),
691 .slot_bytes = 2,
692 }),
693 );
694 try std.testing.expectError(
695 error.CapacityExceeded,
696 Owner.Capacity.derive(.{
697 .slots = Owner.slots_max + 1,
698 .slot_bytes = 1,
699 }),
700 );
701 }
702
703 test "provisioned rejecting owner preserves state at capacity plus one" {
704 comptime {
705 @stardustClaim(
706 capacity.witness(ProvisionedRejectingFixture, "alloc_provisioned_rejecting_capacity_capacity_model"),
707 null,
708 null,
709 null,
710 null,
711 null,
712 null,
713 );
714 }
715 comptime {
716 @stardustClaim(
717 capacity.witness(ProvisionedRejectingFixture, "alloc_provisioned_rejecting_capacity_overload"),
718 null,
719 null,
720 null,
721 null,
722 null,
723 null,
724 );
725 }
726 comptime {
727 @stardustClaim(
728 capacity.witness(ProvisionedRejectingFixture, "alloc_provisioned_rejecting_capacity_work_bound"),
729 null,
730 null,
731 null,
732 null,
733 null,
734 null,
735 );
736 }
737
738 const Owner = ProvisionedRejectingFixture;
739 comptime capacity.requireProvisionedRejectingOwnerShape(Owner);
740 try std.testing.expect(
741 capacity.validateProvisionedRejectingOwnerShape(Owner) == null,
742 );
743 var bytes: [Owner.slots_max * 2]u8 align(Owner.storage_alignment) = @splat(0);
744 var owner = try Owner.init(bytes[0..], .{
745 .slots = Owner.slots_max,
746 .slot_bytes = 2,
747 });
748 owner.activate();
749 for (0..Owner.slots_max) |index| try owner.submit(@intCast(index + 1));
750
751 const before_bytes = bytes;
752 var expected_owner = owner;
753 const storage_pointer = @intFromPtr(owner.storage.ptr);
754 const storage_length = owner.storage.len;
755 expected_owner.usage.submitted += 1;
756 expected_owner.usage.rejected += 1;
757 try std.testing.expectError(error.Full, owner.submit(0xff));
758
759 try std.testing.expectEqualSlices(u8, &before_bytes, &bytes);
760 try std.testing.expectEqualDeep(expected_owner, owner);
761 try std.testing.expectEqual(storage_pointer, @intFromPtr(owner.storage.ptr));
762 try std.testing.expectEqual(storage_length, owner.storage.len);
763 _ = owner.deinit();
764 }
765
766 test "provisioned accounting rejects max plus one before mutation" {
767 const Owner = ProvisionedRejectingFixture;
768 var bytes: [Owner.slots_max]u8 align(Owner.storage_alignment) = @splat(0);
769 var owner = try Owner.init(bytes[0..], .{
770 .slots = Owner.slots_max,
771 .slot_bytes = 1,
772 });
773 owner.activate();
774 owner.usage.submitted = std.math.maxInt(usize);
775 const empty_bytes = bytes;
776 const empty_owner = owner;
777 const empty_storage_pointer = @intFromPtr(owner.storage.ptr);
778 const empty_storage_length = owner.storage.len;
779 try std.testing.expectError(error.AccountingOverflow, owner.submit(1));
780 try std.testing.expectEqualDeep(empty_owner, owner);
781 try std.testing.expectEqual(
782 empty_storage_pointer,
783 @intFromPtr(owner.storage.ptr),
784 );
785 try std.testing.expectEqual(empty_storage_length, owner.storage.len);
786 try std.testing.expectEqualSlices(u8, &empty_bytes, &bytes);
787
788 owner.usage = .{};
789 for (0..Owner.slots_max) |index| try owner.submit(@intCast(index + 1));
790 owner.usage.submitted = std.math.maxInt(usize) - 1;
791 owner.usage.rejected = std.math.maxInt(usize);
792 const full_bytes = bytes;
793 const full_owner = owner;
794 const full_storage_pointer = @intFromPtr(owner.storage.ptr);
795 const full_storage_length = owner.storage.len;
796 try std.testing.expectError(error.AccountingOverflow, owner.submit(0xff));
797 try std.testing.expectEqualDeep(full_owner, owner);
798 try std.testing.expectEqual(
799 full_storage_pointer,
800 @intFromPtr(owner.storage.ptr),
801 );
802 try std.testing.expectEqual(full_storage_length, owner.storage.len);
803 try std.testing.expectEqualSlices(u8, &full_bytes, &bytes);
804 _ = owner.deinit();
805 }
806
807 test "provisioned cleanup completes within literal work limits" {
808 const Owner = ProvisionedRejectingFixture;
809 var bytes: [Owner.slots_max]u8 align(Owner.storage_alignment) = @splat(0);
810 var owner = try Owner.init(bytes[0..], .{
811 .slots = Owner.slots_max,
812 .slot_bytes = 1,
813 });
814 owner.activate();
815 for (0..Owner.slots_max) |index| try owner.submit(@intCast(index + 1));
816
817 var cleanup_calls: usize = 0;
818 var cleanup_steps: usize = 0;
819 while (owner.cleanupOne()) {
820 cleanup_calls += 1;
821 cleanup_steps += 1;
822 try std.testing.expect(cleanup_steps <= cleanup_calls);
823 try std.testing.expectEqual(Owner.slots_max - cleanup_calls, owner.used);
824 }
825 try std.testing.expectEqual(
826 Owner.work_limits.cleanup_calls_at_capacity_max,
827 cleanup_calls,
828 );
829 try std.testing.expect(
830 cleanup_steps <= cleanup_calls * Owner.work_limits.cleanup_steps_per_call_max,
831 );
832 try std.testing.expectEqualSlices(u8, &@as([Owner.slots_max]u8, @splat(0)), &bytes);
833 _ = owner.deinit();
834 }
835
836 test "provisioned owner surface carries no allocator capability" {
837 try std.testing.expect(
838 !capacity.typeHasAllocatorCapability(ProvisionedExactFixture),
839 );
840 try std.testing.expect(
841 !capacity.typeHasAllocatorCapability(ProvisionedRejectingFixture),
842 );
843 }
844
845 test "provisioned owner lifecycle performs zero observed allocations" {
846 const Owner = ProvisionedRejectingFixture;
847 var observer = try phase.ObservingPhaseAllocator.init(std.testing.allocator);
848 const initialization_allocator = observer.initializationAllocator();
849 const bytes = try initialization_allocator.alignedAlloc(
850 u8,
851 .fromByteUnits(Owner.storage_alignment),
852 Owner.slots_max,
853 );
854 @memset(bytes, 0);
855 defer {
856 if (observer.phase() == .initialization) {
857 observer.abortInitialization();
858 } else if (observer.phase() == .steady) {
859 observer.beginTeardown();
860 }
861 observer.teardownAllocator().free(bytes);
862 observer.deinit();
863 }
864 var owner = try Owner.init(bytes[0..], .{
865 .slots = Owner.slots_max,
866 .slot_bytes = 1,
867 });
868
869 observer.seal();
870 owner.activate();
871 for (0..Owner.slots_max) |index| {
872 try owner.submit(@intCast(index + 1));
873 }
874 try std.testing.expectError(error.Full, owner.submit(0xff));
875 while (owner.cleanupOne()) {}
876 _ = owner.deinit();
877
878 try std.testing.expectEqual(@as(u64, 0), observer.violations().total());
879 }
880
881 test "capacity package namespace" {
882 std.testing.refAllDecls(capacity);
883 }