Backlog/lib/zargs/tests/test_parsing.zig

359 lines
12 KiB
Zig

const std = @import("std");
const parsing = @import("parsing");
const ArgumentRegistry = @import("ArgumentRegistry").ArgumentRegistry;
const metadata = @import("metadata");
const ArgumentType = @import("ArgumentType").ArgumentType;
// Test struct for parsing
const SimpleConfig = struct {
verbose: bool = false,
output: []const u8 = "default.txt",
count: u32 = 10,
pub const meta = .{
.verbose = .{ .short = 'v', .help = "Verbose output" },
.output = .{ .short = 'o', .help = "Output file" },
.count = .{ .short = 'c', .help = "Item count" },
};
};
test "parse long boolean flag" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--verbose" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("verbose");
try std.testing.expect(value != null);
try std.testing.expectEqual(true, value.?.bool);
}
test "parse short boolean flag" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-v" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("verbose");
try std.testing.expect(value != null);
try std.testing.expectEqual(true, value.?.bool);
}
test "parse long flag with equals value" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--output=myfile.txt" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("output");
try std.testing.expect(value != null);
try std.testing.expectEqualStrings("myfile.txt", value.?.string);
}
test "parse long flag with space-separated value" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--output", "myfile.txt" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("output");
try std.testing.expect(value != null);
try std.testing.expectEqualStrings("myfile.txt", value.?.string);
}
test "parse short flag with value" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-o", "myfile.txt" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("output");
try std.testing.expect(value != null);
try std.testing.expectEqualStrings("myfile.txt", value.?.string);
}
test "parse integer value" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--count=42" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("count");
try std.testing.expect(value != null);
try std.testing.expectEqual(@as(u32, 42), value.?.u32);
}
test "parse multiple arguments" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-v", "--output", "test.txt", "--count=99" };
try parsing.parseArgv(&registry, argv);
const verbose = registry.getParsedValue("verbose");
const output = registry.getParsedValue("output");
const count = registry.getParsedValue("count");
try std.testing.expect(verbose != null);
try std.testing.expectEqual(true, verbose.?.bool);
try std.testing.expect(output != null);
try std.testing.expectEqualStrings("test.txt", output.?.string);
try std.testing.expect(count != null);
try std.testing.expectEqual(@as(u32, 99), count.?.u32);
}
test "parse multi-flag short form" {
const MultiFlag = struct {
verbose: bool = false,
debug: bool = false,
quiet: bool = false,
pub const meta = .{
.verbose = .{ .short = 'v' },
.debug = .{ .short = 'd' },
.quiet = .{ .short = 'q' },
};
};
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(MultiFlag, "test");
const argv = &[_][:0]const u8{ "program", "-vdq" };
try parsing.parseArgv(&registry, argv);
const verbose = registry.getParsedValue("verbose");
const debug = registry.getParsedValue("debug");
const quiet = registry.getParsedValue("quiet");
try std.testing.expect(verbose != null);
try std.testing.expectEqual(true, verbose.?.bool);
try std.testing.expect(debug != null);
try std.testing.expectEqual(true, debug.?.bool);
try std.testing.expect(quiet != null);
try std.testing.expectEqual(true, quiet.?.bool);
}
test "parse help flag" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--help" };
try parsing.parseArgv(&registry, argv);
try std.testing.expect(registry.isHelpRequested());
}
test "parse short help flag" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-h" };
try parsing.parseArgv(&registry, argv);
try std.testing.expect(registry.isHelpRequested());
}
test "unknown argument error" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--unknown" };
const result = parsing.parseArgv(&registry, argv);
try std.testing.expectError(error.UnknownArgument, result);
}
test "missing value error" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "--output" };
const result = parsing.parseArgv(&registry, argv);
try std.testing.expectError(error.MissingArgumentValue, result);
}
test "populate struct with defaults" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{"program"};
try parsing.parseArgv(&registry, argv);
const config = try parsing.populateStruct(SimpleConfig, &registry, std.testing.allocator);
try std.testing.expectEqual(false, config.verbose);
try std.testing.expectEqualStrings("default.txt", config.output);
try std.testing.expectEqual(@as(u32, 10), config.count);
}
test "populate struct with parsed values" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-v", "--output=result.txt", "--count=5" };
try parsing.parseArgv(&registry, argv);
const config = try parsing.populateStruct(SimpleConfig, &registry, std.testing.allocator);
try std.testing.expectEqual(true, config.verbose);
try std.testing.expectEqualStrings("result.txt", config.output);
try std.testing.expectEqual(@as(u32, 5), config.count);
}
test "populate struct with mixed defaults and values" {
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(SimpleConfig, "test");
const argv = &[_][:0]const u8{ "program", "-v" };
try parsing.parseArgv(&registry, argv);
const config = try parsing.populateStruct(SimpleConfig, &registry, std.testing.allocator);
try std.testing.expectEqual(true, config.verbose);
try std.testing.expectEqualStrings("default.txt", config.output);
try std.testing.expectEqual(@as(u32, 10), config.count);
}
test "parse enum values" {
const Mode = enum { fast, slow, medium };
const EnumConfig = struct {
mode: Mode = .medium,
pub const meta = .{
.mode = .{ .help = "Processing mode" },
};
};
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(EnumConfig, "test");
const argv = &[_][:0]const u8{ "program", "--mode=fast" };
try parsing.parseArgv(&registry, argv);
const config = try parsing.populateStruct(EnumConfig, &registry, std.testing.allocator);
try std.testing.expectEqual(Mode.fast, config.mode);
}
test "parse optional types" {
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");
// Test with values
{
const argv = &[_][:0]const u8{ "program", "--name=Alice", "--age=30" };
try parsing.parseArgv(&registry, argv);
const config = try parsing.populateStruct(OptionalConfig, &registry, std.testing.allocator);
try std.testing.expect(config.name != null);
try std.testing.expectEqualStrings("Alice", config.name.?);
try std.testing.expect(config.age != null);
try std.testing.expectEqual(@as(u32, 30), config.age.?);
}
}
test "parse string list with comma separation" {
const ListConfig = struct {
files: []const []const u8 = &[_][]const u8{},
pub const meta = .{
.files = .{ .help = "List of files" },
};
};
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(ListConfig, "test");
const argv = &[_][:0]const u8{ "program", "--files=a.txt,b.txt,c.txt" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("files");
try std.testing.expect(value != null);
try std.testing.expectEqual(@as(usize, 3), value.?.string_list.len);
try std.testing.expectEqualStrings("a.txt", value.?.string_list[0]);
try std.testing.expectEqualStrings("b.txt", value.?.string_list[1]);
try std.testing.expectEqualStrings("c.txt", value.?.string_list[2]);
}
test "parse string list with repeated arguments" {
const ListConfig = struct {
files: []const []const u8 = &[_][]const u8{},
pub const meta = .{
.files = .{ .help = "List of files" },
};
};
var registry = ArgumentRegistry.init(std.testing.allocator);
defer registry.deinit();
try registry.registerMetadata(ListConfig, "test");
const argv = &[_][:0]const u8{ "program", "--files=a.txt", "--files=b.txt", "--files=c.txt" };
try parsing.parseArgv(&registry, argv);
const value = registry.getParsedValue("files");
try std.testing.expect(value != null);
try std.testing.expectEqual(@as(usize, 3), value.?.string_list.len);
try std.testing.expectEqualStrings("a.txt", value.?.string_list[0]);
try std.testing.expectEqualStrings("b.txt", value.?.string_list[1]);
try std.testing.expectEqualStrings("c.txt", value.?.string_list[2]);
}