Testing a command line argument parser

I wrote a small command line argument parser and I would now like to test it. The parser takes a std.process.Args.Iterator for initialization like so:

const args: std.process.Args = .{
    .vector = &.{ "--asdf", "--qwerty", "--", "-abc", "waow", "woah", "hmmm" },
};
var iter: ArgsIterator = .init(args.iterate());

Which works great… on linux. But this test can’t run on windows and some other targets because the type of .vector is different.
Is there any platform-agnostic way to create a std.process.Args instance for testing purposes?

1 Like

I think it should be somewhat manual, I was looking at the code for std.process.Args.zig

fn testIteratorWindows(cmd_line: []const u8, expected_args: []const []const u8) !void {
    const cmd_line_w = try std.unicode.wtf8ToWtf16LeAllocZ(testing.allocator, cmd_line);
    defer testing.allocator.free(cmd_line_w);

    // next
    {
        var it = try Iterator.Windows.init(testing.allocator, cmd_line_w);
        defer it.deinit();
...

line 669

It is extremely cheap in the grand scheme of things to allocate the args into an ArrayList at initialization time and parse a []const []const u8 instead. That allows you to pass &.{"foo", "bar"} to tests (maybe with some massaging, I’m not at a computer to verify).

For a parser I worked on, I built the logic around parsing slices, then provided an additional adapter/wrapper for consuming an iterator instead.

It seems like it would be either changing the api like that or constructing it manually as @IbrahimOuhamou mentioned.
I’m probably just gonna leave the test posix-only as it is just a part of a bigger project, not an arg-parsing library. Might have to figure out a way to auto-skip that test if I add others to not have all the tests be platform specific just because that one refuses to compile.

I think it would be better to make a small wrapper function which converts []const []const u8 to utf16 on windows, since it is apparently the only thing that needs conversion (and wasi need [*:0]const u8)

since the std code is

pub const Vector = switch (native_os) {
    .windows => []const u16, // WTF-16 encoded
    .wasi => switch (builtin.link_libc) {
        false => void,
        true => []const [*:0]const u8,
    },
    .freestanding, .other => void,
    else => []const [*:0]const u8,
};