2016-08-25 01:49:56 +03:00
|
|
|
#include "catch.hpp"
|
|
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
#include <initializer_list>
|
|
|
|
|
|
2016-12-02 23:44:46 +03:00
|
|
|
#include "cxxopts.hpp"
|
2016-08-26 01:22:04 +03:00
|
|
|
|
|
|
|
|
class Argv {
|
|
|
|
|
public:
|
|
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
Argv(std::initializer_list<const char*> argv)
|
|
|
|
|
: m_argv(new char*[argv.size()])
|
|
|
|
|
, m_argc(argv.size())
|
2016-08-26 01:22:04 +03:00
|
|
|
{
|
2016-08-26 12:20:55 +03:00
|
|
|
int i = 0;
|
|
|
|
|
auto iter = argv.begin();
|
|
|
|
|
while (iter != argv.end()) {
|
|
|
|
|
auto len = strlen(*iter) + 1;
|
2016-08-26 01:22:04 +03:00
|
|
|
auto ptr = std::unique_ptr<char[]>(new char[len]);
|
|
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
strcpy(ptr.get(), *iter);
|
2016-08-26 01:22:04 +03:00
|
|
|
m_args.push_back(std::move(ptr));
|
|
|
|
|
m_argv.get()[i] = m_args.back().get();
|
2016-08-26 12:20:55 +03:00
|
|
|
|
|
|
|
|
++iter;
|
|
|
|
|
++i;
|
2016-08-26 01:22:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char** argv() const {
|
|
|
|
|
return m_argv.get();
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
int argc() const {
|
|
|
|
|
return m_argc;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-26 01:22:04 +03:00
|
|
|
private:
|
|
|
|
|
|
|
|
|
|
std::vector<std::unique_ptr<char[]>> m_args;
|
|
|
|
|
std::unique_ptr<char*[]> m_argv;
|
2016-08-26 12:20:55 +03:00
|
|
|
int m_argc;
|
2016-08-26 01:22:04 +03:00
|
|
|
};
|
|
|
|
|
|
2016-08-25 01:49:56 +03:00
|
|
|
TEST_CASE("Basic options", "[options]")
|
|
|
|
|
{
|
2016-08-26 01:22:04 +03:00
|
|
|
|
|
|
|
|
cxxopts::Options options("tester", " - test basic options");
|
|
|
|
|
|
|
|
|
|
options.add_options()
|
|
|
|
|
("long", "a long option")
|
|
|
|
|
("s,short", "a short option")
|
|
|
|
|
("value", "an option with a value", cxxopts::value<std::string>())
|
2016-11-16 10:03:35 +03:00
|
|
|
("a,av", "a short option with a value", cxxopts::value<std::string>())
|
|
|
|
|
("6,six", "a short number option")
|
|
|
|
|
;
|
2016-08-26 01:22:04 +03:00
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
Argv argv({
|
2016-08-26 01:22:04 +03:00
|
|
|
"tester",
|
|
|
|
|
"--long",
|
|
|
|
|
"-s",
|
|
|
|
|
"--value",
|
|
|
|
|
"value",
|
|
|
|
|
"-a",
|
2016-11-16 10:03:35 +03:00
|
|
|
"b",
|
|
|
|
|
"-6"
|
2016-08-26 12:20:55 +03:00
|
|
|
});
|
2016-08-26 01:22:04 +03:00
|
|
|
|
|
|
|
|
char** actual_argv = argv.argv();
|
2016-08-26 12:20:55 +03:00
|
|
|
auto argc = argv.argc();
|
2016-08-26 01:22:04 +03:00
|
|
|
|
|
|
|
|
options.parse(argc, actual_argv);
|
|
|
|
|
|
|
|
|
|
CHECK(options.count("long") == 1);
|
2016-08-26 12:09:40 +03:00
|
|
|
CHECK(options.count("s") == 1);
|
|
|
|
|
CHECK(options.count("value") == 1);
|
|
|
|
|
CHECK(options.count("a") == 1);
|
|
|
|
|
CHECK(options["value"].as<std::string>() == "value");
|
|
|
|
|
CHECK(options["a"].as<std::string>() == "b");
|
2016-11-16 10:03:35 +03:00
|
|
|
CHECK(options.count("6") == 1);
|
2016-08-25 01:49:56 +03:00
|
|
|
}
|
2016-08-26 12:20:55 +03:00
|
|
|
|
2017-05-31 10:40:14 +03:00
|
|
|
TEST_CASE("Short options", "[options]")
|
|
|
|
|
{
|
|
|
|
|
cxxopts::Options options("test_short", " - test short options");
|
|
|
|
|
|
|
|
|
|
options.add_options()
|
|
|
|
|
("a", "a short option", cxxopts::value<std::string>());
|
|
|
|
|
|
|
|
|
|
Argv argv({"test_short", "-a", "value"});
|
|
|
|
|
|
|
|
|
|
auto actual_argv = argv.argv();
|
|
|
|
|
auto argc = argv.argc();
|
|
|
|
|
|
|
|
|
|
options.parse(argc, actual_argv);
|
|
|
|
|
|
|
|
|
|
CHECK(options.count("a") == 1);
|
|
|
|
|
CHECK(options["a"].as<std::string>() == "value");
|
|
|
|
|
|
|
|
|
|
REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
|
|
|
|
|
cxxopts::invalid_option_format_error);
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-26 12:20:55 +03:00
|
|
|
TEST_CASE("No positional", "[positional]")
|
|
|
|
|
{
|
2016-08-29 11:11:18 +03:00
|
|
|
cxxopts::Options options("test_no_positional",
|
|
|
|
|
" - test no positional options");
|
2016-08-26 12:20:55 +03:00
|
|
|
|
2016-08-29 11:11:18 +03:00
|
|
|
Argv av({"tester", "a", "b", "def"});
|
2016-08-26 12:20:55 +03:00
|
|
|
|
2016-08-29 11:11:18 +03:00
|
|
|
char** argv = av.argv();
|
|
|
|
|
auto argc = av.argc();
|
|
|
|
|
options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
REQUIRE(argc == 4);
|
|
|
|
|
CHECK(strcmp(argv[1], "a") == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-26 23:54:42 +03:00
|
|
|
TEST_CASE("All positional", "[positional]")
|
|
|
|
|
{
|
|
|
|
|
std::vector<std::string> positional;
|
|
|
|
|
|
|
|
|
|
cxxopts::Options options("test_all_positional", " - test all positional");
|
|
|
|
|
options.add_options()
|
|
|
|
|
("positional", "Positional parameters",
|
|
|
|
|
cxxopts::value<std::vector<std::string>>(positional))
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
Argv av({"tester", "a", "b", "c"});
|
|
|
|
|
|
|
|
|
|
auto argc = av.argc();
|
|
|
|
|
auto argv = av.argv();
|
|
|
|
|
|
|
|
|
|
options.parse_positional("positional");
|
|
|
|
|
|
|
|
|
|
options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
REQUIRE(argc == 1);
|
|
|
|
|
REQUIRE(positional.size() == 3);
|
|
|
|
|
|
|
|
|
|
CHECK(positional[0] == "a");
|
|
|
|
|
CHECK(positional[1] == "b");
|
|
|
|
|
CHECK(positional[2] == "c");
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-29 11:11:18 +03:00
|
|
|
TEST_CASE("Some positional explicit", "[positional]")
|
|
|
|
|
{
|
|
|
|
|
cxxopts::Options options("positional_explicit", " - test positional");
|
|
|
|
|
|
|
|
|
|
options.add_options()
|
|
|
|
|
("input", "Input file", cxxopts::value<std::string>())
|
|
|
|
|
("output", "Output file", cxxopts::value<std::string>())
|
|
|
|
|
("positional", "Positional parameters",
|
|
|
|
|
cxxopts::value<std::vector<std::string>>())
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
options.parse_positional({"input", "output", "positional"});
|
|
|
|
|
|
|
|
|
|
Argv av({"tester", "--output", "a", "b", "c", "d"});
|
|
|
|
|
|
|
|
|
|
char** argv = av.argv();
|
|
|
|
|
auto argc = av.argc();
|
|
|
|
|
|
|
|
|
|
options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
CHECK(argc == 1);
|
|
|
|
|
CHECK(options.count("output"));
|
|
|
|
|
CHECK(options["input"].as<std::string>() == "b");
|
|
|
|
|
CHECK(options["output"].as<std::string>() == "a");
|
|
|
|
|
|
|
|
|
|
auto& positional = options["positional"].as<std::vector<std::string>>();
|
|
|
|
|
|
|
|
|
|
REQUIRE(positional.size() == 2);
|
|
|
|
|
CHECK(positional[0] == "c");
|
|
|
|
|
CHECK(positional[1] == "d");
|
2016-08-26 12:20:55 +03:00
|
|
|
}
|
2017-05-06 07:16:00 +03:00
|
|
|
|
|
|
|
|
TEST_CASE("No positional with extras", "[positional]")
|
|
|
|
|
{
|
|
|
|
|
cxxopts::Options options("posargmaster", "shows incorrect handling");
|
|
|
|
|
options.add_options()
|
|
|
|
|
("dummy", "oh no", cxxopts::value<std::string>())
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
Argv av({"extras", "--", "a", "b", "c", "d"});
|
|
|
|
|
|
|
|
|
|
char** argv = av.argv();
|
|
|
|
|
auto argc = av.argc();
|
|
|
|
|
|
|
|
|
|
auto old_argv = argv;
|
|
|
|
|
auto old_argc = argc;
|
|
|
|
|
|
|
|
|
|
options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
REQUIRE(argc == old_argc - 1);
|
|
|
|
|
CHECK(argv[0] == std::string("extras"));
|
|
|
|
|
CHECK(argv[1] == std::string("a"));
|
|
|
|
|
}
|
2017-05-17 00:38:30 +03:00
|
|
|
|
|
|
|
|
TEST_CASE("Empty with implicit value", "[implicit]")
|
|
|
|
|
{
|
|
|
|
|
cxxopts::Options options("empty_implicit", "doesn't handle empty");
|
|
|
|
|
options.add_options()
|
|
|
|
|
("implicit", "Has implicit", cxxopts::value<std::string>()
|
|
|
|
|
->implicit_value("foo"));
|
|
|
|
|
|
|
|
|
|
Argv av({"implicit", "--implicit", ""});
|
|
|
|
|
|
|
|
|
|
char** argv = av.argv();
|
|
|
|
|
auto argc = av.argc();
|
|
|
|
|
|
|
|
|
|
options.parse(argc, argv);
|
|
|
|
|
|
|
|
|
|
REQUIRE(options.count("implicit") == 1);
|
|
|
|
|
REQUIRE(options["implicit"].as<std::string>() == "");
|
|
|
|
|
}
|