ReviewOS

also looking at this

zig-utils/zig-test-framework

fix: honor discovery-mode CLI options

#17
Merged glennmichael123 wants to merge codex/issue-2-discovery-cli into main
10 files +192 -24
src/cli.zigmodified+83-1
Changes to src/cli.zig
@@ -11,7 +11,7 @@ pub const CLIOptions = struct {
1111 quiet: bool = false,
1212 no_color: bool = false,
1313 // Test discovery options
14 test_dir: ?[]const u8 = null,
14 test_dir: ?[]const u8 = ".",
1515 pattern: []const u8 = "*.test.zig",
1616 no_recursive: bool = false,
1717 // Coverage options
@@ -140,6 +140,12 @@ pub const CLI = struct {
140140 }
141141 i += 1;
142142 self.options.coverage_tool = args[i];
143 if (!std.mem.eql(u8, self.options.coverage_tool, "kcov") and
144 !std.mem.eql(u8, self.options.coverage_tool, "grindcov"))
145 {
146 std.debug.print("Error: --coverage-tool must be 'kcov' or 'grindcov'\n", .{});
147 return CLIError.InvalidArgument;
148 }
143149 } else if (std.mem.eql(u8, arg, "--parallel") or std.mem.eql(u8, arg, "-p")) {
144150 self.options.parallel = true;
145151 } else if (std.mem.eql(u8, arg, "--jobs") or std.mem.eql(u8, arg, "-j")) {
@@ -153,6 +159,10 @@ pub const CLI = struct {
153159 std.debug.print("Error: --jobs must be a valid number\n", .{});
154160 return CLIError.InvalidArgument;
155161 };
162 if (self.options.jobs.? == 0) {
163 std.debug.print("Error: --jobs must be greater than zero\n", .{});
164 return CLIError.InvalidArgument;
165 }
156166 } else if (std.mem.eql(u8, arg, "--ui")) {
157167 self.options.ui = true;
158168 } else if (std.mem.eql(u8, arg, "--ui-port")) {
@@ -245,6 +255,31 @@ pub const CLI = struct {
245255 }
246256 }
247257
258 /// Return the first option that discovery mode cannot currently honor.
259 /// Discovery must reject these options explicitly instead of silently
260 /// behaving differently from programmatic mode.
261 pub fn unsupportedDiscoveryOption(self: Self) ?[]const u8 {
262 if (!self.options.coverage and !std.mem.eql(u8, self.options.coverage_dir, "coverage")) return "--coverage-dir";
263 if (!self.options.coverage and !std.mem.eql(u8, self.options.coverage_tool, "kcov")) return "--coverage-tool";
264 if (!self.options.watch and self.options.watch_debounce != 300) return "--watch-debounce";
265 if (!self.options.ui and self.options.ui_port != 8080) return "--ui-port";
266 if (!self.options.ui and !std.mem.eql(u8, self.options.ui_host, "127.0.0.1")) return "--ui-host";
267 if (self.options.reporter != .spec) return "--reporter";
268 if (self.options.quiet) return "--quiet";
269 if (self.options.parallel) return "--parallel";
270 if (self.options.jobs != null) return "--jobs";
271 if (self.options.update_snapshots) return "--update-snapshots";
272 if (!std.mem.eql(u8, self.options.snapshot_dir, ".snapshots")) return "--snapshot-dir";
273 if (self.options.profile_memory) return "--profile-memory";
274 if (self.options.memory_threshold != 0) return "--memory-threshold";
275 if (self.options.fail_on_leak) return "--fail-on-leak";
276 if (self.options.config != null) return "--config";
277 if (self.options.junit_output != null) return "--junit-output";
278 if (self.options.timeout != null) return "--timeout";
279 if (self.options.ui) return "--ui";
280 return null;
281 }
282
248283 /// Print help message
249284 pub fn printHelp(self: Self) void {
250285 _ = self;
@@ -467,6 +502,7 @@ test "CLI default values" {
467502 try std.testing.expectEqualStrings("coverage", cli.options.coverage_dir);
468503 try std.testing.expectEqualStrings("kcov", cli.options.coverage_tool);
469504 try std.testing.expectEqualStrings("*.test.zig", cli.options.pattern);
505 try std.testing.expectEqualStrings(".", cli.options.test_dir.?);
470506 try std.testing.expect(!cli.options.ui);
471507 try std.testing.expectEqual(@as(u16, 8080), cli.options.ui_port);
472508 try std.testing.expectEqualStrings("127.0.0.1", cli.options.ui_host);
@@ -531,6 +567,52 @@ test "CLI parse timeout" {
531567 try std.testing.expectEqual(@as(?u64, 5000), cli.options.timeout);
532568}
533569
570test "CLI rejects invalid coverage tool" {
571 var cli = CLI.init(std.testing.allocator);
572 const args = [_][]const u8{ "zig-test", "--coverage-tool", "unknown" };
573 try std.testing.expectError(CLIError.InvalidArgument, cli.parse(&args));
574}
575
576test "CLI rejects zero jobs" {
577 var cli = CLI.init(std.testing.allocator);
578 const args = [_][]const u8{ "zig-test", "--jobs", "0" };
579 try std.testing.expectError(CLIError.InvalidArgument, cli.parse(&args));
580}
581
582test "discovery accepts implemented options" {
583 var cli = CLI.init(std.testing.allocator);
584 const args = [_][]const u8{
585 "zig-test",
586 "--test-dir",
587 "tests",
588 "--filter",
589 "math",
590 "--no-color",
591 "--bail",
592 "--verbose",
593 };
594 try cli.parse(&args);
595 try std.testing.expectEqual(@as(?[]const u8, null), cli.unsupportedDiscoveryOption());
596}
597
598test "discovery identifies unsupported options" {
599 var cli = CLI.init(std.testing.allocator);
600 const args = [_][]const u8{ "zig-test", "--test-dir", "tests", "--reporter", "json" };
601 try cli.parse(&args);
602 try std.testing.expectEqualStrings("--reporter", cli.unsupportedDiscoveryOption().?);
603}
604
605test "discovery reports timeout and parallel options as unsupported" {
606 var cli = CLI.init(std.testing.allocator);
607
608 cli.options.timeout = 1000;
609 try std.testing.expectEqualStrings("--timeout", cli.unsupportedDiscoveryOption().?);
610
611 cli.options.timeout = null;
612 cli.options.parallel = true;
613 try std.testing.expectEqualStrings("--parallel", cli.unsupportedDiscoveryOption().?);
614}
615
534616test "CLI parse update-snapshots short flag" {
535617 var cli = CLI.init(std.testing.allocator);
536618 const args = [_][]const u8{ "zig-test", "-u" };