lib/tldr/src/profiling/collect/parse/suite/relink.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const collect = @import("../../root.zig");
2 const collect_base = collect.base;
3 const std = collect_base.std;
4 const check = collect_base.check;
5 const discover = collect_base.discover;
6 const relink = collect_base.relink;
7
8 const model = collect.model;
9 const parse = @import("../root.zig");
10
11 pub fn parseRelinkConfig(allocator: model.Allocator, args: []const []const u8) !model.RelinkConfig {
12 if (args.len == 0) return error.InvalidArguments;
13 if (std.mem.startsWith(u8, args[0], "-")) return error.InvalidArguments;
14 var platforms: std.ArrayList(discover.Platform) = .empty;
15 var patterns: std.ArrayList([]const u8) = .empty;
16 var config = model.RelinkConfig{
17 .discovery = .{
18 .root = args[0],
19 .platforms = &.{},
20 .patterns = &.{},
21 },
22 };
23 var index: usize = 1;
24 while (index < args.len) {
25 const arg = args[index];
26 if (std.mem.eql(u8, arg, "--format")) {
27 index += 1;
28 if (index >= args.len) return error.InvalidArguments;
29 config.discovery.format = discover.SuiteFormat.parse(args[index]) orelse return error.InvalidArguments;
30 index += 1;
31 } else if (std.mem.eql(u8, arg, "--platform")) {
32 index += 1;
33 if (index >= args.len) return error.InvalidArguments;
34 try platforms.append(allocator, discover.Platform.parse(args[index]) orelse return error.InvalidArguments);
35 index += 1;
36 } else if (std.mem.eql(u8, arg, "--case")) {
37 index += 1;
38 if (index >= args.len) return error.InvalidArguments;
39 try patterns.append(allocator, args[index]);
40 index += 1;
41 } else if (std.mem.eql(u8, arg, "--tldr-command")) {
42 index += 1;
43 if (index >= args.len) return error.InvalidArguments;
44 config.tldr_command = try check.parseCommand(allocator, args[index]);
45 index += 1;
46 } else if (std.mem.eql(u8, arg, "--tmp-dir")) {
47 index += 1;
48 if (index >= args.len) return error.InvalidArguments;
49 config.tmp_dir = args[index];
50 index += 1;
51 } else if (std.mem.eql(u8, arg, "--warmup")) {
52 index += 1;
53 if (index >= args.len) return error.InvalidArguments;
54 config.warmup = try parse.parseNonNegativeUsize(args[index]);
55 index += 1;
56 } else if (std.mem.eql(u8, arg, "--min-runs")) {
57 index += 1;
58 if (index >= args.len) return error.InvalidArguments;
59 config.min_runs = try parse.parsePositiveUsize(args[index]);
60 index += 1;
61 } else if (std.mem.eql(u8, arg, "--max-runs")) {
62 index += 1;
63 if (index >= args.len) return error.InvalidArguments;
64 config.max_runs = try parse.parsePositiveUsize(args[index]);
65 index += 1;
66 } else if (std.mem.eql(u8, arg, "--min-time")) {
67 index += 1;
68 if (index >= args.len) return error.InvalidArguments;
69 config.min_time_s = try parse.parseNonNegativeFloat(args[index]);
70 index += 1;
71 } else if (std.mem.eql(u8, arg, "--timeout")) {
72 index += 1;
73 if (index >= args.len) return error.InvalidArguments;
74 config.timeout_s = try parse.parsePositiveFloat(args[index]);
75 index += 1;
76 } else if (std.mem.eql(u8, arg, "--no-fork")) {
77 config.no_fork = true;
78 index += 1;
79 } else if (std.mem.eql(u8, arg, "--output-csv")) {
80 index += 1;
81 if (index >= args.len) return error.InvalidArguments;
82 config.output_csv = args[index];
83 index += 1;
84 } else if (std.mem.eql(u8, arg, "--output-jsonl")) {
85 index += 1;
86 if (index >= args.len) return error.InvalidArguments;
87 config.output_jsonl = args[index];
88 index += 1;
89 } else if (std.mem.eql(u8, arg, "--output-report-markdown")) {
90 index += 1;
91 if (index >= args.len) return error.InvalidArguments;
92 config.output_report_markdown = args[index];
93 index += 1;
94 } else {
95 return error.InvalidArguments;
96 }
97 }
98 if (config.max_runs < config.min_runs) return error.InvalidArguments;
99 config.discovery.platforms = try platforms.toOwnedSlice(allocator);
100 config.discovery.patterns = try patterns.toOwnedSlice(allocator);
101 return config;
102 }
103
104 test "relink config defaults" {
105 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
106 defer arena_state.deinit();
107 const arena = arena_state.allocator();
108 const config = try parseRelinkConfig(arena, &.{"zig-out/linker-suites"});
109 try std.testing.expectEqualStrings("zig-out/linker-suites", config.discovery.root);
110 try std.testing.expectEqual(discover.SuiteFormat.auto, config.discovery.format);
111 try std.testing.expectEqual(@as(usize, 0), config.discovery.platforms.len);
112 try std.testing.expectEqual(@as(usize, 0), config.discovery.patterns.len);
113 try std.testing.expectEqualStrings("tldr-link", config.tldr_command[0]);
114 try std.testing.expectEqualStrings(check.default_tmp_dir, config.tmp_dir);
115 try std.testing.expectEqual(@as(usize, relink.default_warmup_runs), config.warmup);
116 try std.testing.expectEqual(@as(usize, relink.default_min_runs), config.min_runs);
117 try std.testing.expectEqual(@as(usize, relink.default_max_runs), config.max_runs);
118 try std.testing.expectEqual(@as(f64, relink.default_min_time_s), config.min_time_s);
119 try std.testing.expect(config.timeout_s == null);
120 try std.testing.expect(!config.no_fork);
121 }
122
123 test "relink config parses outputs filters and timing" {
124 var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
125 defer arena_state.deinit();
126 const arena = arena_state.allocator();
127 const config = try parseRelinkConfig(arena, &.{ "suite", "--format", "lld", "--platform", "elf", "--case", "tldr-*", "--tldr-command", "/tmp/tldr-link --flag", "--tmp-dir", ".zig-cache/tmp", "--warmup", "0", "--min-runs", "2", "--max-runs", "5", "--min-time", "0", "--timeout", "1.25", "--no-fork", "--output-csv", "relink.csv", "--output-jsonl", "relink.jsonl", "--output-report-markdown", "relink.md" });
128 try std.testing.expectEqual(discover.SuiteFormat.lld, config.discovery.format);
129 try std.testing.expectEqualSlices(discover.Platform, &.{.elf}, config.discovery.platforms);
130 try std.testing.expectEqualStrings("tldr-*", config.discovery.patterns[0]);
131 try std.testing.expectEqualStrings("/tmp/tldr-link", config.tldr_command[0]);
132 try std.testing.expectEqualStrings("--flag", config.tldr_command[1]);
133 try std.testing.expectEqualStrings(".zig-cache/tmp", config.tmp_dir);
134 try std.testing.expectEqual(@as(usize, 0), config.warmup);
135 try std.testing.expectEqual(@as(usize, 2), config.min_runs);
136 try std.testing.expectEqual(@as(usize, 5), config.max_runs);
137 try std.testing.expectEqual(@as(f64, 0), config.min_time_s);
138 try std.testing.expectEqual(@as(f64, 1.25), config.timeout_s.?);
139 try std.testing.expect(config.no_fork);
140 try std.testing.expectEqualStrings("relink.csv", config.output_csv.?);
141 try std.testing.expectEqualStrings("relink.jsonl", config.output_jsonl.?);
142 try std.testing.expectEqualStrings("relink.md", config.output_report_markdown.?);
143 }
144
145 test "relink config rejects invalid timing" {
146 try std.testing.expectError(error.InvalidArguments, parseRelinkConfig(std.testing.allocator, &.{ "suite", "--min-time", "-1" }));
147 try std.testing.expectError(error.InvalidArguments, parseRelinkConfig(std.testing.allocator, &.{ "suite", "--min-runs", "3", "--max-runs", "2" }));
148 }