aboutsummaryrefslogtreecommitdiff
path: root/test.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'test.cxx')
-rw-r--r--test.cxx262
1 files changed, 233 insertions, 29 deletions
diff --git a/test.cxx b/test.cxx
index d7eb11d..c39e000 100644
--- a/test.cxx
+++ b/test.cxx
@@ -6,38 +6,242 @@
#include <args.hxx>
-int main(int argc, char **argv)
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
+
+TEST_CASE("Help flag throws Help exception", "[args]")
{
args::ArgumentParser parser("This is a test program.", "This goes after the options.");
args::HelpFlag help(parser, "help", "Display this help menu", args::Matcher({'h'}, {"help"}));
- args::ArgFlag<int> integer(parser, "integer", "The integer flag", args::Matcher({'i'}));
- args::ArgFlagList<char> characters(parser, "characters", "The character flag", args::Matcher({'c'}));
- args::PosArg<std::string> foo(parser, "foo", "The foo position");
- args::PosArgList<double> numbers(parser, "numbers", "The numbers position list");
- try
- {
- parser.ParseCLI(argc, argv);
- }
- catch (args::Help)
- {
- std::cout << parser.Help();
- return 0;
- }
- catch (args::ParseError e)
- {
- std::cerr << e.what() << std::endl;
- std::cerr << parser.Help();
- return 1;
- }
- catch (args::ValidationError e)
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--help"}), args::Help);
+}
+
+TEST_CASE("Unknown flags throw exceptions", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::HelpFlag help(parser, "help", "Display this help menu", args::Matcher({'h'}, {"help"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--Help"}), args::ParseError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-H"}), args::ParseError);
+}
+
+TEST_CASE("Boolean flags work as expected, with clustering", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::Flag foo(parser, "FOO", "test flag", args::Matcher({'f'}, {"foo"}));
+ args::Flag bar(parser, "BAR", "test flag", args::Matcher({'b'}, {"bar"}));
+ args::Flag baz(parser, "BAZ", "test flag", args::Matcher({'a'}, {"baz"}));
+ args::Flag bix(parser, "BAZ", "test flag", args::Matcher({'x'}, {"bix"}));
+ parser.ParseArgs(std::vector<std::string>{"--baz", "-fb"});
+ REQUIRE(foo);
+ REQUIRE(bar);
+ REQUIRE(baz);
+ REQUIRE_FALSE(bix);
+}
+
+TEST_CASE("Argument flags work as expected, with clustering", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::ArgFlag<std::string> foo(parser, "FOO", "test flag", args::Matcher({'f'}, {"foo"}));
+ args::Flag bar(parser, "BAR", "test flag", args::Matcher({'b'}, {"bar"}));
+ args::ArgFlag<double> baz(parser, "BAZ", "test flag", args::Matcher({'a'}, {"baz"}));
+ args::ArgFlag<char> bim(parser, "BAZ", "test flag", args::Matcher({'B'}, {"bim"}));
+ args::Flag bix(parser, "BAZ", "test flag", args::Matcher({'x'}, {"bix"}));
+ parser.ParseArgs(std::vector<std::string>{"-bftest", "--baz=7.555e2", "--bim", "c"});
+ REQUIRE(foo);
+ REQUIRE(foo.value == "test");
+ REQUIRE(bar);
+ REQUIRE(baz);
+ REQUIRE((baz.value > 755.49 && baz.value < 755.51));
+ REQUIRE(bim);
+ REQUIRE(bim.value == 'c');
+ REQUIRE_FALSE(bix);
+}
+
+TEST_CASE("Invalid argument parsing throws parsing exceptions", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::ArgFlag<int> foo(parser, "FOO", "test flag", args::Matcher({'f'}, {"foo"}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--foo=7.5"}), args::ParseError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--foo", "7a"}), args::ParseError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--foo", "7e4"}), args::ParseError);
+}
+
+TEST_CASE("Argument flag lists work as expected", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::ArgFlagList<int> foo(parser, "FOO", "test flag", args::Matcher({'f'}, {"foo"}));
+ parser.ParseArgs(std::vector<std::string>{"--foo=7", "-f2", "-f", "9", "--foo", "42"});
+ REQUIRE((foo.values == std::vector<int>{7, 2, 9, 42}));
+}
+
+TEST_CASE("Positional arguments and positional argument lists work as expected", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::PosArg<std::string> foo(parser, "FOO", "test flag");
+ args::PosArg<bool> bar(parser, "BAR", "test flag");
+ args::PosArgList<char> baz(parser, "BAZ", "test flag");
+ parser.ParseArgs(std::vector<std::string>{"this is a test flag", "0", "a", "b", "c", "x", "y", "z"});
+ REQUIRE(foo);
+ REQUIRE((foo.value == "this is a test flag"));
+ REQUIRE(bar);
+ REQUIRE(!bar.value);
+ REQUIRE(baz);
+ REQUIRE((baz.values == std::vector<char>{'a', 'b', 'c', 'x', 'y', 'z'}));
+}
+
+TEST_CASE("Positionals that are unspecified evaluate false", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::PosArg<std::string> foo(parser, "FOO", "test flag");
+ args::PosArg<bool> bar(parser, "BAR", "test flag");
+ args::PosArgList<char> baz(parser, "BAZ", "test flag");
+ parser.ParseArgs(std::vector<std::string>{"this is a test flag again"});
+ REQUIRE(foo);
+ REQUIRE((foo.value == "this is a test flag again"));
+ REQUIRE_FALSE(bar);
+ REQUIRE_FALSE(baz);
+}
+
+TEST_CASE("Additional positionals throw an exception", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::PosArg<std::string> foo(parser, "FOO", "test flag");
+ args::PosArg<bool> bar(parser, "BAR", "test flag");
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"this is a test flag again", "1", "this has no positional available"}), args::ParseError);
+}
+
+TEST_CASE("Argument groups should throw when validation fails", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::Group xorgroup(parser, "this group provides xor validation", args::Group::Validators::Xor);
+ args::Flag a(xorgroup, "a", "test flag", args::Matcher({'a'}));
+ args::Flag b(xorgroup, "b", "test flag", args::Matcher({'b'}));
+ args::Flag c(xorgroup, "c", "test flag", args::Matcher({'c'}));
+ args::Group nxor(parser, "this group provides all-or-none (nxor) validation", args::Group::Validators::AllOrNone);
+ args::Flag d(nxor, "d", "test flag", args::Matcher({'d'}));
+ args::Flag e(nxor, "e", "test flag", args::Matcher({'e'}));
+ args::Flag f(nxor, "f", "test flag", args::Matcher({'f'}));
+ args::Group atleastone(parser, "this group provides at-least-one validation", args::Group::Validators::AtLeastOne);
+ args::Flag g(atleastone, "g", "test flag", args::Matcher({'g'}));
+ args::Flag h(atleastone, "h", "test flag", args::Matcher({'h'}));
+ // Needs g or h
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-a"}), args::ValidationError);
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-g", "-a"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-h", "-a"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-gh", "-a"}));
+ // Xor stuff
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g"}), args::ValidationError);
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-h", "-b"}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-ab"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-ac"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-abc"}), args::ValidationError);
+ // Nxor stuff
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-h", "-a"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-h", "-adef"}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-ad"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-adf"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g", "-aef"}), args::ValidationError);
+}
+
+TEST_CASE("Argument groups should nest", "[args]")
+{
+ args::ArgumentParser parser("This is a test program.", "This goes after the options.");
+ args::Group xorgroup(parser, "this group provides xor validation", args::Group::Validators::Xor);
+ args::Flag a(xorgroup, "a", "test flag", args::Matcher({'a'}));
+ args::Flag b(xorgroup, "b", "test flag", args::Matcher({'b'}));
+ args::Flag c(xorgroup, "c", "test flag", args::Matcher({'c'}));
+ args::Group nxor(xorgroup, "this group provides all-or-none (nxor) validation", args::Group::Validators::AllOrNone);
+ args::Flag d(nxor, "d", "test flag", args::Matcher({'d'}));
+ args::Flag e(nxor, "e", "test flag", args::Matcher({'e'}));
+ args::Flag f(nxor, "f", "test flag", args::Matcher({'f'}));
+ args::Group atleastone(xorgroup, "this group provides at-least-one validation", args::Group::Validators::AtLeastOne);
+ args::Flag g(atleastone, "g", "test flag", args::Matcher({'g'}));
+ args::Flag h(atleastone, "h", "test flag", args::Matcher({'h'}));
+ // Nothing actually matches, because nxor validates properly when it's empty,
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-a", "-d"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-c", "-f"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-de", "-f"}));
+ REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"-gh", "-f"}));
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-g"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-a"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-b"}), args::ValidationError);
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-a", "-dg"}), args::ValidationError);
+}
+
+#include <tuple>
+
+std::istream& operator>>(std::istream& is, std::tuple<int, int>& ints)
+{
+ is >> std::get<0>(ints);
+ is.get();
+ is >> std::get<1>(ints);
+ return is;
+}
+
+void DoublesReader(const std::string &name, const std::string &value, std::tuple<double, double> &destination)
+{
+ size_t commapos = 0;
+ std::get<0>(destination) = std::stod(value, &commapos);
+ std::get<1>(destination) = std::stod(std::string(value, commapos + 1));
+}
+
+TEST_CASE("Custom types work", "[args]")
+{
{
- std::cerr << e.what() << std::endl;
- std::cerr << parser.Help();
- return 1;
+ args::ArgumentParser parser("This is a test program.");
+ args::PosArg<std::tuple<int, int>> ints(parser, "INTS", "This takes a pair of integers.");
+ args::PosArg<std::tuple<double, double>, DoublesReader> doubles(parser, "DOUBLES", "This takes a pair of doubles.");
+ REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"1.2,2", "3.8,4"}), args::ParseError);
}
- if (integer) { std::cout << "i: " << integer.value << std::endl; }
- if (characters) { for (const auto ch: characters.values) { std::cout << "c: " << ch << std::endl; } }
- if (foo) { std::cout << "f: " << foo.value << std::endl; }
- if (numbers) { for (const auto nm: numbers.values) { std::cout << "n: " << nm << std::endl; } }
- return 0;
+ args::ArgumentParser parser("This is a test program.");
+ args::PosArg<std::tuple<int, int>> ints(parser, "INTS", "This takes a pair of integers.");
+ args::PosArg<std::tuple<double, double>, DoublesReader> doubles(parser, "DOUBLES", "This takes a pair of doubles.");
+ parser.ParseArgs(std::vector<std::string>{"1,2", "3.8,4"});
+ REQUIRE(std::get<0>(ints.value) == 1);
+ REQUIRE(std::get<1>(ints.value) == 2);
+ REQUIRE((std::get<0>(doubles.value) > 3.79 && std::get<0>(doubles.value) < 3.81));
+ REQUIRE((std::get<1>(doubles.value) > 3.99 && std::get<1>(doubles.value) < 4.01));
+}
+
+TEST_CASE("Custom parser prefixes (dd-style)", "[args]")
+{
+ args::ArgumentParser parser("This command likes to break your disks");
+ parser.LongPrefix("");
+ parser.LongSeparator("=");
+ args::HelpFlag help(parser, "HELP", "Show this help menu.", args::Matcher({"help"}));
+ args::ArgFlag<long> bs(parser, "BYTES", "Block size", args::Matcher({"bs"}), 512);
+ args::ArgFlag<long> skip(parser, "BYTES", "Bytes to skip", args::Matcher({"skip"}), 0);
+ args::ArgFlag<std::string> input(parser, "BLOCK SIZE", "Block size", args::Matcher({"if"}));
+ args::ArgFlag<std::string> output(parser, "BLOCK SIZE", "Block size", args::Matcher({"of"}));
+ parser.ParseArgs(std::vector<std::string>{"skip=8", "if=/dev/null"});
+ REQUIRE_FALSE(bs);
+ REQUIRE(bs.value == 512);
+ REQUIRE(skip);
+ REQUIRE(skip.value == 8);
+ REQUIRE(input);
+ REQUIRE(input.value == "/dev/null");
+ REQUIRE_FALSE(output);
+}
+
+TEST_CASE("Custom parser prefixes (Some Windows styles)", "[args]")
+{
+ args::ArgumentParser parser("This command likes to break your disks");
+ parser.LongPrefix("/");
+ parser.LongSeparator(":");
+ args::HelpFlag help(parser, "HELP", "Show this help menu.", args::Matcher({"help"}));
+ args::ArgFlag<long> bs(parser, "BYTES", "Block size", args::Matcher({"bs"}), 512);
+ args::ArgFlag<long> skip(parser, "BYTES", "Bytes to skip", args::Matcher({"skip"}), 0);
+ args::ArgFlag<std::string> input(parser, "BLOCK SIZE", "Block size", args::Matcher({"if"}));
+ args::ArgFlag<std::string> output(parser, "BLOCK SIZE", "Block size", args::Matcher({"of"}));
+ parser.ParseArgs(std::vector<std::string>{"/skip:8", "/if:/dev/null"});
+ REQUIRE_FALSE(bs);
+ REQUIRE(bs.value == 512);
+ REQUIRE(skip);
+ REQUIRE(skip.value == 8);
+ REQUIRE(input);
+ REQUIRE(input.value == "/dev/null");
+ REQUIRE_FALSE(output);
}