lib/hypothesis/src/coverage.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const Bucket = enum {
4 valid,
5 invalid,
6 boundary,
7 large_state,
8 };
9
10 pub const Counts = struct {
11 valid: u64 = 0,
12 invalid: u64 = 0,
13 boundary: u64 = 0,
14 large_state: u64 = 0,
15
16 pub fn get(self: Counts, bucket: Bucket) u64 {
17 return switch (bucket) {
18 .valid => self.valid,
19 .invalid => self.invalid,
20 .boundary => self.boundary,
21 .large_state => self.large_state,
22 };
23 }
24 };
25
26 pub const Counters = struct {
27 counts: Counts = .{},
28
29 pub fn record(self: *Counters, bucket: Bucket) void {
30 const counter = switch (bucket) {
31 .valid => &self.counts.valid,
32 .invalid => &self.counts.invalid,
33 .boundary => &self.counts.boundary,
34 .large_state => &self.counts.large_state,
35 };
36 std.debug.assert(counter.* < std.math.maxInt(u64));
37 counter.* += 1;
38 }
39
40 pub fn snapshot(self: *const Counters) Counts {
41 return self.counts;
42 }
43
44 pub fn meets(self: *const Counters, minimum: Counts) bool {
45 for (std.meta.tags(Bucket)) |bucket| {
46 if (self.counts.get(bucket) < minimum.get(bucket)) return false;
47 }
48 return true;
49 }
50
51 pub fn require(self: *const Counters, minimum: Counts) error{InsufficientCoverage}!void {
52 if (!self.meets(minimum)) return error.InsufficientCoverage;
53 }
54 };
55
56 test "coverage: independent generators retain reachability buckets" {
57 var plans = Counters{};
58 var values = Counters{};
59 plans.record(.valid);
60 plans.record(.boundary);
61 values.record(.invalid);
62 values.record(.large_state);
63
64 try std.testing.expect(plans.meets(.{ .valid = 1, .boundary = 1 }));
65 try std.testing.expect(values.meets(.{ .invalid = 1, .large_state = 1 }));
66 try std.testing.expect(!plans.meets(.{ .large_state = 1 }));
67 try std.testing.expectError(
68 error.InsufficientCoverage,
69 plans.require(.{ .large_state = 1 }),
70 );
71 try values.require(.{ .invalid = 1, .large_state = 1 });
72 try std.testing.expectEqual(@as(u64, 0), values.snapshot().valid);
73 }