From 5c7016f2bc10c0f964fce5c4d9e7210db5786da7 Mon Sep 17 00:00:00 2001 From: Jimmi HC Date: Fri, 1 Jun 2018 13:37:40 +0200 Subject: Reworked extended.zig again! --- tests/extended.zig | 494 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 305 insertions(+), 189 deletions(-) (limited to 'tests') diff --git a/tests/extended.zig b/tests/extended.zig index 8f722e4..140c822 100644 --- a/tests/extended.zig +++ b/tests/extended.zig @@ -9,226 +9,342 @@ const extended = clap.extended; const assert = debug.assert; const ArgSliceIterator = core.ArgSliceIterator; -const Command = extended.Command; +const Names = core.Names; +const Clap = extended.Clap; const Param = extended.Param; const Parser = extended.Parser; -const Options = struct { - str: []const u8, - int: i64, - uint: u64, - a: bool, - b: bool, - cc: bool, - sub: &const SubOptions, - - pub fn with(op: &const Options, comptime field: []const u8, value: var) Options { - var res = op.*; - @field(res, field) = value; - return res; - } -}; - -const SubOptions = struct { - a: bool, - b: u64, - qq: bool, +fn success(comptime parser: var, expect: var, args: []const []const u8) void { + var iter = ArgSliceIterator.init(args); + const actual = parser.parse(ArgSliceIterator.Error, &iter.iter) catch unreachable; - pub fn with(op: &const SubOptions, comptime field: []const u8, value: var) SubOptions { - var res = op.*; - @field(res, field) = value; - return res; + const T = @typeOf(expect).Child; + inline for (@typeInfo(T).Struct.fields) |field| { + assert(@field(expect, field.name) == @field(actual, field.name)); } -}; - -const default = Options { - .str = "", - .int = 0, - .uint = 0, - .a = false, - .b = false, - .cc = false, - .sub = SubOptions{ - .a = false, - .b = 0, - .qq = false, - }, -}; - -fn testNoErr(comptime command: &const Command, args: []const []const u8, expected: &const command.Result) void { - var arg_iter = ArgSliceIterator.init(args); - const actual = command.parse(debug.global_allocator, &arg_iter.iter) catch unreachable; - assert(mem.eql(u8, expected.str, actual.str)); - assert(expected.int == actual.int); - assert(expected.uint == actual.uint); - assert(expected.a == actual.a); - assert(expected.b == actual.b); - assert(expected.cc == actual.cc); - assert(expected.sub.a == actual.sub.a); - assert(expected.sub.b == actual.sub.b); } -fn testErr(comptime command: &const Command, args: []const []const u8, expected: error) void { - var arg_iter = ArgSliceIterator.init(args); - if (command.parse(debug.global_allocator, &arg_iter.iter)) |actual| { +fn fail(comptime parser: var, expect: error, args: []const []const u8) void { + var iter = ArgSliceIterator.init(args); + if (parser.parse(ArgSliceIterator.Error, &iter.iter)) |_| { unreachable; - } else |err| { - assert(err == expected); + } else |actual| { + assert(expect == actual); } } -test "clap.extended: short" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("a"), - Param.smart("b"), - Param.smart("int") - .with("short", 'i') - .with("takes_value", Parser.int(i64, 10)), - }, - []Command{}, - ); - - testNoErr(command, [][]const u8 { "-a" }, default.with("a", true)); - testNoErr(command, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testNoErr(command, [][]const u8 { "-i=100" }, default.with("int", 100)); - testNoErr(command, [][]const u8 { "-i100" }, default.with("int", 100)); - testNoErr(command, [][]const u8 { "-i", "100" }, default.with("int", 100)); - testNoErr(command, [][]const u8 { "-ab" }, default.with("a", true).with("b", true)); - testNoErr(command, [][]const u8 { "-abi", "100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(command, [][]const u8 { "-abi=100" }, default.with("a", true).with("b", true).with("int", 100)); - testNoErr(command, [][]const u8 { "-abi100" }, default.with("a", true).with("b", true).with("int", 100)); -} +pub fn Test(comptime Expect: type) type { + return struct { + const Self = this; -test "clap.extended: long" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("cc"), - Param.smart("int").with("takes_value", Parser.int(i64, 10)), - Param.smart("uint").with("takes_value", Parser.int(u64, 10)), - Param.smart("str").with("takes_value", Parser.string), - }, - []Command{}, - ); + args: []const []const u8, + kind: Kind, - testNoErr(command, [][]const u8 { "--cc" }, default.with("cc", true)); - testNoErr(command, [][]const u8 { "--int", "100" }, default.with("int", 100)); -} + const Kind = union(enum) { + Success: Expect, + Fail: error, + }; -test "clap.extended: value bool" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("a"), - }, - []Command{}, - ); + pub fn success(args: []const []const u8, expected: &const Expect) Self { + return Self{ + .args = args, + .kind = Kind{ + .Success = expected.*, + }, + }; + } - testNoErr(command, [][]const u8 { "-a" }, default.with("a", true)); -} + pub fn fail(args: []const []const u8, err: error) Self { + return Self{ + .args = args, + .kind = Kind{ + .Fail = err, + }, + }; + } -test "clap.extended: value str" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("str").with("takes_value", Parser.string), - }, - []Command{}, - ); + pub fn run(t: &const Self, comptime parser: var) void { + var iter = ArgSliceIterator.init(t.args); + const actual = parser.parse(ArgSliceIterator.Error, &iter.iter); - testNoErr(command, [][]const u8 { "--str", "Hello World!" }, default.with("str", "Hello World!")); + switch (t.kind) { + Kind.Success => |expected| { + const actual_value = actual catch unreachable; + inline for (@typeInfo(Expect).Struct.fields) |field| { + assert(@field(expected, field.name) == @field(actual_value, field.name)); + } + }, + Kind.Fail => |expected| { + if (actual) |_| { + unreachable; + } else |actual_err| { + assert(actual_err == expected); + } + }, + } + } + }; } -test "clap.extended: value int" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("int").with("takes_value", Parser.int(i64, 10)), - }, - []Command{}, - ); - - testNoErr(command, [][]const u8 { "--int", "100" }, default.with("int", 100)); -} +test "clap.extended: short" { + const S = struct { + a: bool, + b: u8, + }; -test "clap.extended: position" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("a").with("position", 0), - Param.smart("b").with("position", 1), + const parser = comptime Clap(S){ + .default = S{ + .a = false, + .b = 0, }, - []Command{}, - ); + .params = []Param{ + Param{ + .field = "a", + .names = Names.short('a'), + .kind = Param.Kind.Flag, + .required = true, + .position = 0, + }, + Param{ + .field = "b", + .names = Names.short('b'), + .kind = Param.Kind{ .Option = Parser.int(u8, 10) }, + .required = false, + .position = null, + }, + } + }; + + const T = Test(S); + const tests = []T{ + T.success( + [][]const u8 { "-a" }, + S{ + .a = true, + .b = 0, + }, + ), + T.success( + [][]const u8 { "-a", "-b", "100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "-a", "-b=100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "-a", "-b100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "-ab", "100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "-ab=100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "-ab100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.fail( + [][]const u8 { "-q" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "--a" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "-b=100" }, + error.ParamNotHandled, + ), + T.fail( + [][]const u8 { "-b=100", "-a" }, + error.InvalidArgument, + ), + }; - testNoErr(command, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); - testErr(command, [][]const u8 { "-b", "-a" }, error.InvalidArgument); + for (tests) |t| { + t.run(parser); + } } -test "clap.extended: sub fields" { - const B = struct { +test "clap.extended: long" { + const S = struct { a: bool, - }; - const A = struct { - b: B, + b: u8, }; - const command = comptime Command.init( - "", - A, - A { .b = B { .a = false } }, - []Param { - Param.short('a') - .with("field", "b.a"), + const parser = comptime Clap(S){ + .default = S{ + .a = false, + .b = 0, }, - []Command{}, - ); + .params = []Param{ + Param{ + .field = "a", + .names = Names.long("a"), + .kind = Param.Kind.Flag, + .required = true, + .position = 0, + }, + Param{ + .field = "b", + .names = Names.long("b"), + .kind = Param.Kind{ .Option = Parser.int(u8, 10) }, + .required = false, + .position = null, + }, + } + }; - var arg_iter = ArgSliceIterator.init([][]const u8{ "-a" }); - const res = command.parse(debug.global_allocator, &arg_iter.iter) catch unreachable; - debug.assert(res.b.a == true); + const T = Test(S); + const tests = []T{ + T.success( + [][]const u8 { "--a" }, + S{ + .a = true, + .b = 0, + }, + ), + T.success( + [][]const u8 { "--a", "--b", "100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "--a", "--b=100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.fail( + [][]const u8 { "--a=100" }, + error.DoesntTakeValue, + ), + T.fail( + [][]const u8 { "--q" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "-a" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "--b=100" }, + error.ParamNotHandled, + ), + T.fail( + [][]const u8 { "--b=100", "--a" }, + error.InvalidArgument, + ), + }; + + for (tests) |t| { + t.run(parser); + } } -test "clap.extended: sub commands" { - const command = comptime Command.init( - "", - Options, - default, - []Param { - Param.smart("a"), - Param.smart("b"), - }, - []Command{ - Command.init( - "sub", - SubOptions, - default.sub, - []Param { - Param.smart("a"), - Param.smart("b") - .with("takes_value", Parser.int(u64, 10)), - }, - []Command{}, - ), +test "clap.extended: bare" { + const S = struct { + a: bool, + b: u8, + }; + + const parser = comptime Clap(S){ + .default = S{ + .a = false, + .b = 0, }, - ); + .params = []Param{ + Param{ + .field = "a", + .names = Names.bare("a"), + .kind = Param.Kind.Flag, + .required = true, + .position = 0, + }, + Param{ + .field = "b", + .names = Names.bare("b"), + .kind = Param.Kind{ .Option = Parser.int(u8, 10) }, + .required = false, + .position = null, + }, + } + }; + + const T = Test(S); + const tests = []T{ + T.success( + [][]const u8 { "a" }, + S{ + .a = true, + .b = 0, + }, + ), + T.success( + [][]const u8 { "a", "b", "100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.success( + [][]const u8 { "a", "b=100" }, + S{ + .a = true, + .b = 100, + }, + ), + T.fail( + [][]const u8 { "a=100" }, + error.DoesntTakeValue, + ), + T.fail( + [][]const u8 { "--a" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "-a" }, + error.InvalidArgument, + ), + T.fail( + [][]const u8 { "b=100" }, + error.ParamNotHandled, + ), + T.fail( + [][]const u8 { "b=100", "--a" }, + error.InvalidArgument, + ), + }; - testNoErr(command, [][]const u8 { "sub", "-a" }, default.with("sub", default.sub.with("a", true))); - testNoErr(command, [][]const u8 { "sub", "-b", "100" }, default.with("sub", default.sub.with("b", 100))); - testNoErr(command, [][]const u8 { "-a", "sub", "-a" }, default.with("a", true).with("sub", default.sub.with("a", true))); - testErr(command, [][]const u8 { "-qq", "sub" }, error.InvalidArgument); + for (tests) |t| { + t.run(parser); + } } + +// TODO: Test sub commands and sub field access -- cgit v1.2.3