const std = @import("std"); const help = @import("help"); const ArgumentRegistry = @import("ArgumentRegistry").ArgumentRegistry; const metadata = @import("metadata"); const ArgumentType = @import("ArgumentType").ArgumentType; const SimpleConfig = struct { verbose: bool = false, output: []const u8 = "output.txt", count: u32 = 10, pub const meta = .{ .verbose = .{ .short = 'v', .help = "Enable verbose output" }, .output = .{ .short = 'o', .help = "Output file path" }, .count = .{ .short = 'c', .help = "Number of items to process" }, }; }; test "generate help text basic" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should contain usage line try std.testing.expect(std.mem.indexOf(u8, help_text, "Usage:") != null); // Should contain Options header try std.testing.expect(std.mem.indexOf(u8, help_text, "Options:") != null); // Should contain help flag try std.testing.expect(std.mem.indexOf(u8, help_text, "--help") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "-h") != null); } test "generate help text with all arguments" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should contain all argument names try std.testing.expect(std.mem.indexOf(u8, help_text, "--verbose") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "--output") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "--count") != null); // Should contain short flags try std.testing.expect(std.mem.indexOf(u8, help_text, "-v") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "-o") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "-c") != null); } test "generate help text with help descriptions" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should contain help text for each argument try std.testing.expect(std.mem.indexOf(u8, help_text, "Enable verbose output") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "Output file path") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "Number of items to process") != null); } test "generate help text with default values" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should show default values try std.testing.expect(std.mem.indexOf(u8, help_text, "[default: false]") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "[default: output.txt]") != null); // Note: integer defaults are disabled, so count won't show default } test "generate help text with value placeholders" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Boolean should not have placeholder const verbose_line_start = std.mem.indexOf(u8, help_text, "-v, --verbose").?; const verbose_line_end = std.mem.indexOfPos(u8, help_text, verbose_line_start, "\n").?; const verbose_line = help_text[verbose_line_start..verbose_line_end]; try std.testing.expect(std.mem.indexOf(u8, verbose_line, "<") == null); // String should have placeholder try std.testing.expect(std.mem.indexOf(u8, help_text, "--output ") != null); // Number should have placeholder try std.testing.expect(std.mem.indexOf(u8, help_text, "--count ") != null); } test "generate help text with program name" { var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(SimpleConfig, "test"); const help_text = try help.generateHelpText(®istry, std.testing.allocator, "myprogram"); defer std.testing.allocator.free(help_text); // Should contain program name in usage line try std.testing.expect(std.mem.indexOf(u8, help_text, "Usage: myprogram") != null); } test "generate help text with enum choices" { const Mode = enum { fast, slow, balanced }; const EnumConfig = struct { mode: Mode = .balanced, pub const meta = .{ .mode = .{ .short = 'm', .help = "Processing mode" }, }; }; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(EnumConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should contain enum choices (if implemented) // Note: enum values extraction is currently disabled due to comptime limitations // This test documents the expected behavior } test "generate help text alphabetical order" { const UnorderedConfig = struct { zebra: bool = false, apple: bool = false, middle: bool = false, pub const meta = .{ .zebra = .{ .help = "Last" }, .apple = .{ .help = "First" }, .middle = .{ .help = "Middle" }, }; }; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(UnorderedConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Find positions of each argument const apple_pos = std.mem.indexOf(u8, help_text, "--apple").?; const middle_pos = std.mem.indexOf(u8, help_text, "--middle").?; const zebra_pos = std.mem.indexOf(u8, help_text, "--zebra").?; // Should be in alphabetical order try std.testing.expect(apple_pos < middle_pos); try std.testing.expect(middle_pos < zebra_pos); } test "generate help text with optional fields" { const OptionalConfig = struct { name: ?[]const u8 = null, age: ?u32 = null, pub const meta = .{ .name = .{ .help = "Optional name" }, .age = .{ .help = "Optional age" }, }; }; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(OptionalConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should contain optional fields try std.testing.expect(std.mem.indexOf(u8, help_text, "--name") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "--age") != null); // Optional fields should not be marked as required try std.testing.expect(std.mem.indexOf(u8, help_text, "(required)") == null); } test "generate help text with string list" { const ListConfig = struct { files: []const []const u8 = &[_][]const u8{}, pub const meta = .{ .files = .{ .short = 'f', .help = "Input files" }, }; }; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(ListConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should have placeholder for string list try std.testing.expect(std.mem.indexOf(u8, help_text, "--files ") != null); } test "generate help text alignment" { const VaryingLengthConfig = struct { a: bool = false, very_long_argument_name: bool = false, mid: bool = false, pub const meta = .{ .a = .{ .help = "Short name" }, .very_long_argument_name = .{ .help = "Long name" }, .mid = .{ .help = "Medium name" }, }; }; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(VaryingLengthConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Parse lines and check that help text starts at a consistent column var lines = std.mem.splitSequence(u8, help_text, "\n"); var help_text_columns = std.ArrayListUnmanaged(usize){}; defer help_text_columns.deinit(std.testing.allocator); while (lines.next()) |line| { // Skip header lines if (std.mem.indexOf(u8, line, "--") == null) continue; // Find where the help text starts (after the argument name) if (std.mem.indexOf(u8, line, "Short name")) |pos| { try help_text_columns.append(std.testing.allocator, pos); } else if (std.mem.indexOf(u8, line, "Long name")) |pos| { try help_text_columns.append(std.testing.allocator, pos); } else if (std.mem.indexOf(u8, line, "Medium name")) |pos| { try help_text_columns.append(std.testing.allocator, pos); } } // All help text should start at the same column (within reason) if (help_text_columns.items.len >= 2) { const first_col = help_text_columns.items[0]; for (help_text_columns.items[1..]) |col| { // Allow some variation due to spacing, but should be close const diff = if (col > first_col) col - first_col else first_col - col; try std.testing.expect(diff < 5); } } } test "generate help with no arguments" { const EmptyConfig = struct {}; var registry = ArgumentRegistry.init(std.testing.allocator); defer registry.deinit(); try registry.registerMetadata(EmptyConfig, "test"); const help_text = try help.generateSimpleHelp(®istry, std.testing.allocator); defer std.testing.allocator.free(help_text); // Should still have basic structure try std.testing.expect(std.mem.indexOf(u8, help_text, "Usage:") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "Options:") != null); try std.testing.expect(std.mem.indexOf(u8, help_text, "--help") != null); }