#include #include #include #include /////////////////////////////////////////////////////////////////////////////// static void test_combinations (cruft::TAP::logger &tap) { static const struct { std::vector args; int foo; std::optional bar; std::string qux; bool verbose; } TESTS[] = { { .args = { "cmd", "-f", "1", "--bar", "2", "val" }, .foo = 1, .bar = 2, .qux = "val", .verbose = false, }, { .args = { "cmd", "--bar", "2", "-f", "1", "val" }, .foo = 1, .bar = 2, .qux = "val", .verbose = false, }, { .args = { "cmd", "--bar", "2", "val", "-f", "1", }, .foo = 1, .bar = 2, .qux = "val", .verbose = false, }, { .args = { "cmd", "-v", "--bar", "2", "val", "-f", "1", }, .foo = 1, .bar = 2, .qux = "val", .verbose = true, }, { .args = { "cmd", "--bar", "2", "val", "-f", "1", }, .foo = 1, .bar = 2, .qux = "val", .verbose = false, }, { .args = { "cmd", "val", "--bar", "2", "-f", "1", }, .foo = 1, .bar = 2, .qux = "val", .verbose = false, }, { .args = { "cmd", "val", "-v", "-f", "1", }, .foo = 1, .bar = {}, .qux = "val", .verbose = true, }, { .args = { "cmd", "val", "-f", "1", }, .foo = 1, .bar = {}, .qux = "val", .verbose = true, }, }; int foo; std::optional bar; std::string qux; bool verbose; using namespace cruft::cmdopt2; parser p ("test suite"); p.add (keyword ("foo").flag ().flag ('f').bind (foo)); p.add (keyword ("bar").bind (bar)); p.add (positional ("qux").bind (qux)); p.add (keyword ("verbose").flag ().flag ('v').present (verbose)); for (auto const &t: TESTS) { foo = decltype(foo) {}; bar = decltype(bar) {}; qux = decltype(qux) {}; verbose = false; auto const consumed = p.parse (int (t.args.size ()), t.args.data ()); tap.expect ( consumed == int (t.args.size ()) and foo == t.foo and bar == t.bar and qux == t.qux, "{}", fmt::join (t.args.begin (), t.args.end (), " ") ); } } /////////////////////////////////////////////////////////////////////////////// static void test_presence (cruft::TAP::logger &tap) { static const struct { std::vector args; int count; bool present; } TESTS[] = { { .args = { "cmd", "-v", }, .count = 1, .present = false, }, { .args = { "cmd", "-vvv", }, .count = 3, .present = false, }, { .args = { "cmd", "-v", "-vv"}, .count = 3, .present = false, }, { .args = { "cmd", }, .count = 0, .present = false, }, { .args = { "cmd", "-p"}, .count = 0, .present = true, }, { .args = { "cmd", "-ppp"}, .count = 0, .present = true, }, { .args = { "cmd", "-p", "-p"}, .count = 0, .present = true, }, { .args = { "cmd", "-p", "-v", "-p"}, .count = 1, .present = true, }, { .args = { "cmd", "-p", "-v", "-p", "-v"}, .count = 2, .present = true, }, }; int count; bool present; using namespace cruft::cmdopt2; parser p ("test suite"); p.add (keyword ("verbose").flag ().flag ('v').count (count)); p.add (keyword ("present").flag ().flag ('p').present (present)); for (auto const &t: TESTS) { count = 0; present = false; auto const consumed = p.parse (int (t.args.size ()), t.args.data ()); tap.expect ( consumed == int (t.args.size ()) and count == t.count and present == t.present, "{}", fmt::join (t.args.begin (), t.args.end (), " ") ); } } /////////////////////////////////////////////////////////////////////////////// static void test_required (cruft::TAP::logger &tap) { static const struct { std::vector args; bool success; } TESTS[] = { { .args = { "cmd", }, .success = false }, { .args = { "cmd", "-y" }, .success = true }, { .args = { "cmd", "-n" }, .success = false }, }; using namespace cruft::cmdopt2; parser p ("test suite"); p.add (keyword ("y").flag ('y').required (true ).ignore ()); p.add (keyword ("n").flag ('n').required (false).ignore ()); for (auto const &t: TESTS) { bool success; try { auto const consumed = p.parse (int (t.args.size ()), t.args.data ()); bool const completed = consumed == int (t.args.size ()); success = completed == t.success; } catch (...) { success = not t.success; } tap.expect ( success, "{}", fmt::join (t.args.begin (), t.args.end (), " ") ); } } /////////////////////////////////////////////////////////////////////////////// static void test_repeated (cruft::TAP::logger &tap) { static const struct { std::vector args; std::vector values; } TESTS[] = { { .args = { "cmd" }, .values = { } }, { .args = { "cmd", "1", }, .values = { 1, } }, { .args = { "cmd", "1", "1", }, .values = { 1, 1, } }, { .args = { "cmd", "-v", "1", }, .values = { 1, } }, { .args = { "cmd", "-v", "1", "-v", "1"}, .values = { 1, 1 } }, { .args = { "cmd", "--foo", "1", }, .values = { 1, } }, { .args = { "cmd", "--foo", "1", "--foo", "1"}, .values = { 1, 1, } }, }; std::vector values; using namespace cruft::cmdopt2; parser p ("test suite"); p.add (keyword ("value").flag ('v').repeat (true).bind (values)); p.add (keyword ("foo").repeat (true).bind (values)); p.add (positional ("var").repeat (true).bind (values)); for (auto const &t: TESTS) { values.clear (); auto const consumed = p.parse (int (t.args.size ()), t.args.data ()); tap.expect ( consumed == int (t.args.size ()) and values == t.values, "{}", fmt::join (t.args.begin (), t.args.end (), " ") ); } } /////////////////////////////////////////////////////////////////////////////// static void test_bind (cruft::TAP::logger &tap) { static constexpr char const* VALUES[] = { "one", "two", "three" }; std::vector values; using namespace cruft::cmdopt2; parser p ("test suite"); p.add ( keyword ("value") .flag ('v') .repeat (true) .bind ([&] (char const *str) { values.push_back (str); }) ); char const *argv[] = { "./cmd", "--value", VALUES[0], "--value", VALUES[1], "--value", VALUES[2] }; auto const consumed = p.parse (std::ssize (argv), argv); tap.expect_eq (consumed, std::ssize (argv), "bound function: consumed"); tap.expect ( std::equal ( std::begin (values), std::end (values), std::begin (VALUES), std::end (VALUES) ), "bound function: values" ); } /////////////////////////////////////////////////////////////////////////////// int main () { cruft::TAP::logger tap; test_combinations (tap); test_presence (tap); test_required (tap); test_repeated (tap); test_bind (tap); return tap.status (); }