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 }