lib/machine/src/properties/generator.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const explore = @import("../explore/root.zig");
2 const hypothesis = @import("hypothesis");
3 const profile = @import("../profile/root.zig");
4 const std = @import("std");
5
6 const Input = explore.InputGenerator(.{ .steps = 8, .alternatives = 5 });
7
8 pub const Replay = struct {
9 pub fn property(
10 data: *hypothesis.ConjectureData,
11 _: std.mem.Allocator,
12 ) !void {
13 const raw_seed = try data.drawInteger(0, std.math.maxInt(u64), 0);
14 const step_count: u16 = @intCast(try data.drawInteger(1, 8, 1));
15 const seed = explore.Seed.fromU64(raw_seed);
16 const plan = explore.TemporalPlan{
17 .steps = step_count,
18 .start_tick = 0,
19 .end_tick = 1_000,
20 };
21 var first = try ready(try Input.init(
22 profile.interpretedContinuationTestV1(),
23 seed,
24 plan,
25 ));
26 var replay = try ready(try Input.init(
27 profile.interpretedContinuationTestV1(),
28 seed,
29 plan,
30 ));
31 for (0..step_count) |_| {
32 const first_site = try site(first.next());
33 const replay_site = try site(replay.next());
34 try expectSite(first_site, replay_site);
35 const index: u8 = @intCast(try data.drawInteger(
36 0,
37 first_site.count - 1,
38 first_site.suggested,
39 ));
40 const first_value = try first.choose(first_site, index);
41 const replay_value = try replay.choose(replay_site, index);
42 try std.testing.expectEqualDeep(first_value, replay_value);
43 const declared = profile.determinism.entry(first_value.origin.source);
44 try std.testing.expectEqual(declared.version, first_value.origin.version);
45 }
46 }
47 };
48
49 test "pbt: explore generators replay explicit choices per seed" {
50 const settings = hypothesis.Settings.quick()
51 .withSeed(0xb10c_7eed)
52 .withDatabase(null);
53 try hypothesis.checkNamed(Replay, "machine-explore-replay", settings);
54 }
55
56 fn ready(result: Input.InitResult) !Input {
57 return switch (result) {
58 .item => |value| value,
59 .exhausted => error.TestUnexpectedExhaustion,
60 .incomplete => error.TestUnexpectedIncomplete,
61 };
62 }
63
64 fn site(result: Input.NextResult) !Input.Site {
65 return switch (result) {
66 .item => |value| value,
67 .exhausted => error.TestUnexpectedExhaustion,
68 .incomplete => error.TestUnexpectedIncomplete,
69 };
70 }
71
72 fn expectSite(first: Input.Site, second: Input.Site) !void {
73 try std.testing.expectEqualDeep(first.id, second.id);
74 try std.testing.expectEqual(first.count, second.count);
75 try std.testing.expectEqual(first.suggested, second.suggested);
76 try std.testing.expectEqualDeep(first.values(), second.values());
77 }