ReviewOS

also looking at this

zig-utils/zig-test-framework

feat: load strict JSON configuration files

#19
Merged glennmichael123 wants to merge codex/issue-12-config into main
10 files +249 -286
src/cli.zigmodified+39-14
Changes to src/cli.zig
@@ -1,4 +1,5 @@
11const std = ;
2const config_mod = ;
23const test_runner = ;
34
45pub const CLIOptions = struct {
@@ -61,6 +62,32 @@ pub const CLI = struct {
6162 };
6263 }
6364
65 /// Apply configuration defaults before parsing command-line arguments.
66 /// A subsequent `parse` call gives explicitly supplied CLI flags priority.
67 pub fn applyConfig(self: *Self, config: config_mod.TestConfig) !void {
68 self.options.test_dir = config.test_options.test_dir;
69 self.options.pattern = config.test_options.pattern;
70 self.options.no_recursive = !config.test_options.recursive;
71 self.options.filter = config.test_options.filter;
72 self.options.timeout = config.test_options.timeout;
73 self.options.parallel = config.parallel.enabled;
74 self.options.jobs = config.parallel.jobs;
75 self.options.reporter = try reporterFromName(config.reporter.reporter);
76 self.options.junit_output = config.reporter.junit_output;
77 self.options.verbose = config.reporter.verbose;
78 self.options.update_snapshots = config.snapshot.update;
79 self.options.snapshot_dir = config.snapshot.snapshot_dir;
80 self.options.watch = config.watch.enabled;
81 self.options.watch_debounce = config.watch.debounce_ms;
82 self.options.profile_memory = config.memory.enabled;
83 self.options.memory_threshold = config.memory.report_threshold;
84 self.options.fail_on_leak = config.memory.fail_on_leak;
85 self.options.ui = config.ui.enabled;
86 self.options.ui_port = config.ui.port;
87 self.options.coverage = config.coverage.enabled;
88 self.options.coverage_dir = config.coverage.output_dir;
89 }
90
6491 /// Parse command-line arguments
6592 pub fn parse(self: *Self, args: []const []const u8) !void {
6693 var i: usize = 1; // Skip program name
@@ -87,20 +114,10 @@ pub const CLI = struct {
87114 i += 1;
88115 const reporter_name = args[i];
89116
90 if (std.mem.eql(u8, reporter_name, "spec")) {
91 self.options.reporter = .spec;
92 } else if (std.mem.eql(u8, reporter_name, "dot")) {
93 self.options.reporter = .dot;
94 } else if (std.mem.eql(u8, reporter_name, "json")) {
95 self.options.reporter = .json;
96 } else if (std.mem.eql(u8, reporter_name, "tap")) {
97 self.options.reporter = .tap;
98 } else if (std.mem.eql(u8, reporter_name, "junit")) {
99 self.options.reporter = .junit;
100 } else {
117 self.options.reporter = reporterFromName(reporter_name) catch {
101118 std.debug.print("Error: Unknown reporter '{s}'. Available: spec, dot, json, tap, junit\n", .{reporter_name});
102119 return CLIError.InvalidArgument;
103 }
120 };
104121 } else if (std.mem.eql(u8, arg, "--filter") or std.mem.eql(u8, arg, "--grep")) {
105122 if (i + 1 >= args.len) {
106123 std.debug.print("Error: --filter/--grep requires a value\n", .{});
@@ -273,7 +290,6 @@ pub const CLI = struct {
273290 if (self.options.profile_memory) return "--profile-memory";
274291 if (self.options.memory_threshold != 0) return "--memory-threshold";
275292 if (self.options.fail_on_leak) return "--fail-on-leak";
276 if (self.options.config != null) return "--config";
277293 if (self.options.junit_output != null) return "--junit-output";
278294 if (self.options.timeout != null) return "--timeout";
279295 if (self.options.ui) return "--ui";
@@ -300,7 +316,7 @@ pub const CLI = struct {
300316 \\ -q, --quiet Minimal output
301317 \\ --no-color Disable colored output
302318 \\ --timeout <ms> Global timeout for all tests in milliseconds
303 \\ -c, --config <file> Load configuration from file
319 \\ -c, --config <file> Load strict JSON configuration
304320 \\
305321 \\TEST DISCOVERY:
306322 \\ --test-dir <dir> Directory to search for tests (default: .)
@@ -384,6 +400,15 @@ pub const CLI = struct {
384400 }
385401};
386402
403fn reporterFromName(name: []const u8) !test_runner.ReporterType {
404 if (std.mem.eql(u8, name, "spec")) return .spec;
405 if (std.mem.eql(u8, name, "dot")) return .dot;
406 if (std.mem.eql(u8, name, "json")) return .json;
407 if (std.mem.eql(u8, name, "tap")) return .tap;
408 if (std.mem.eql(u8, name, "junit")) return .junit;
409 return CLIError.InvalidArgument;
410}
411
387412test "CLI parse help" {
388413 var cli = CLI.init(std.testing.allocator);
389414 const args = [_][]const u8{ "zig-test", "--help" };