294 lines
11 KiB
Zig
294 lines
11 KiB
Zig
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 <VALUE> placeholder
|
|
try std.testing.expect(std.mem.indexOf(u8, help_text, "--output <VALUE>") != null);
|
|
|
|
// Number should have <NUM> placeholder
|
|
try std.testing.expect(std.mem.indexOf(u8, help_text, "--count <NUM>") != 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 <LIST> placeholder for string list
|
|
try std.testing.expect(std.mem.indexOf(u8, help_text, "--files <LIST>") != 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);
|
|
}
|