lib/coz/src/properties/replay.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const coz = @import("coz");
 3 const hypothesis = @import("hypothesis");
 4 const data = @import("data.zig");
 5 const expect = @import("expect.zig");
 6 
 7 const profile = coz.profile;
 8 const analysis = coz.analysis;
 9 
10 pub const Property = struct {
11     pub fn property(conjecture: *hypothesis.ConjectureData, property_allocator: std.mem.Allocator) !void {
12         const events = try data.drawEvents(conjecture, property_allocator);
13         defer property_allocator.free(events);
14 
15         for (events) |item| {
16             try expectJsonRoundtrip(property_allocator, item);
17         }
18 
19         var direct = try directSummary(property_allocator, events);
20         defer direct.deinit(property_allocator);
21 
22         const bytes = try writeJsonLines(property_allocator, events);
23         defer property_allocator.free(bytes);
24 
25         var replayed = try analysis.summarizeJsonLines(
26             property_allocator,
27             bytes,
28             .{ .min_delta = 1, .min_points = 1 },
29         );
30         defer replayed.deinit(property_allocator);
31 
32         try expect.summaryEqual(direct, replayed);
33         try expect.summaryInvariants(replayed);
34     }
35 };
36 
37 fn expectJsonRoundtrip(allocator: std.mem.Allocator, item: profile.Event) !void {
38     var out = std.Io.Writer.Allocating.init(allocator);
39     defer out.deinit();
40     try item.writeJsonLine(&out.writer);
41     const bytes = try out.toOwnedSlice();
42     defer allocator.free(bytes);
43 
44     var parsed = try profile.parseJsonLine(allocator, bytes);
45     defer parsed.deinit(allocator);
46 
47     try expect.eventEqual(item, parsed.event);
48 }
49 
50 fn writeJsonLines(allocator: std.mem.Allocator, events: []const profile.Event) ![]u8 {
51     var out = std.Io.Writer.Allocating.init(allocator);
52     defer out.deinit();
53     for (events) |item| {
54         try item.writeJsonLine(&out.writer);
55     }
56     return try out.toOwnedSlice();
57 }
58 
59 fn directSummary(allocator: std.mem.Allocator, events: []const profile.Event) !analysis.Summary {
60     var accumulator: analysis.Accumulator = .{};
61     defer accumulator.deinit(allocator);
62 
63     for (events) |item| {
64         try accumulator.observe(allocator, item);
65     }
66 
67     return try accumulator.summarize(allocator, .{ .min_delta = 1, .min_points = 1 });
68 }
69 
70 test "property: profile JSONL replay preserves analysis summary" {
71     try hypothesis.checkNamed(Property, "coz-profile-analysis-replay", data.settings());
72 }