summaryrefslogtreecommitdiff
path: root/test/args.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/args.cc')
-rw-r--r--test/args.cc412
1 files changed, 412 insertions, 0 deletions
diff --git a/test/args.cc b/test/args.cc
new file mode 100644
index 0000000..22159d2
--- /dev/null
+++ b/test/args.cc
@@ -0,0 +1,412 @@
+#include "args.hh"
+
+#include <cstddef>
+#include <gtest/gtest.h>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#define SETUP_OPTIONS(args) \
+ auto short_only = (args)->option('a', "", "an option"); \
+ auto short_long = (args)->option('b', "bold", "set font style to bold"); \
+ auto long_only = (args)->option("cold", "use if it is cold outside"); \
+ auto short_only_req = (args)->option_argument('d', "", "", "distance"); \
+ auto short_long_req = (args)->option_argument( \
+ 'e', "eat", "FOOD", "what to order, what to eat?"); \
+ auto long_only_req = \
+ (args)->option_argument("form", "", "circle, shape or something else?"); \
+ auto short_only_opt = (args)->option_argument('g', "", "", "", false); \
+ auto short_long_opt = (args)->option_argument('h', "hold", "", "", false); \
+ auto long_only_opt = (args)->option_argument("invert", "", "", false);
+
+namespace {
+
+class Arguments {
+ public:
+ [[nodiscard]] int c() const { return static_cast<int>(str_.size()); };
+ [[nodiscard]] char** v() const { return ptr_.get(); }
+
+ explicit Arguments(std::vector<std::string> str) : str_(std::move(str)) {
+ ptr_ = std::make_unique<char*[]>(str_.size());
+ for (size_t i = 0; i < str_.size(); ++i) {
+ ptr_[i] = const_cast<char*>(str_[i].c_str());
+ }
+ }
+
+ Arguments(Arguments const&) = delete;
+ Arguments& operator=(Arguments const&) = delete;
+
+ private:
+ std::unique_ptr<char*[]> ptr_;
+ std::vector<std::string> str_;
+};
+
+class Builder {
+ public:
+ Arguments build() { return Arguments(std::move(str_)); }
+
+ Builder& add(std::string str) {
+ str_.emplace_back(std::move(str));
+ return *this;
+ }
+
+ private:
+ std::vector<std::string> str_;
+};
+
+} // namespace
+
+TEST(args, empty) {
+ auto args = Args::create();
+ EXPECT_TRUE(args->run(0, nullptr));
+ {
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("", ss.str());
+ }
+ {
+ std::stringstream ss;
+ args->print_help(ss);
+ EXPECT_EQ("", ss.str());
+ }
+}
+
+TEST(args, options_not_set) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ EXPECT_TRUE(args->run(0, nullptr));
+ EXPECT_FALSE(short_only->is_set());
+ EXPECT_FALSE(short_long->is_set());
+ EXPECT_FALSE(long_only->is_set());
+ EXPECT_FALSE(short_only_req->is_set());
+ EXPECT_FALSE(short_long_req->is_set());
+ EXPECT_FALSE(long_only_req->is_set());
+ EXPECT_FALSE(short_only_opt->is_set());
+ EXPECT_FALSE(short_long_opt->is_set());
+ EXPECT_FALSE(long_only_opt->is_set());
+}
+
+TEST(args, options_not_set_arguments) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("bar").add("fum").build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ ASSERT_EQ(2, out.size());
+ EXPECT_EQ("bar", out[0]);
+ EXPECT_EQ("fum", out[1]);
+}
+
+TEST(args, options_set) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo")
+ .add("-a")
+ .add("-b")
+ .add("--cold")
+ .add("-d")
+ .add("10")
+ .add("-e")
+ .add("hamburger")
+ .add("--form=circle")
+ .add("-g")
+ .add("-h")
+ .add("--invert")
+ .build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ EXPECT_TRUE(out.empty());
+ EXPECT_TRUE(short_only->is_set());
+ EXPECT_TRUE(short_long->is_set());
+ EXPECT_TRUE(long_only->is_set());
+ EXPECT_TRUE(short_only_req->is_set());
+ EXPECT_TRUE(short_only_req->has_argument());
+ EXPECT_EQ("10", short_only_req->argument());
+ EXPECT_TRUE(short_long_req->is_set());
+ EXPECT_TRUE(short_long_req->has_argument());
+ EXPECT_EQ("hamburger", short_long_req->argument());
+ EXPECT_TRUE(long_only_req->is_set());
+ EXPECT_TRUE(long_only_req->has_argument());
+ EXPECT_EQ("circle", long_only_req->argument());
+ EXPECT_TRUE(short_only_opt->is_set());
+ EXPECT_FALSE(short_only_opt->has_argument());
+ EXPECT_TRUE(short_long_opt->is_set());
+ EXPECT_FALSE(short_long_opt->has_argument());
+ EXPECT_TRUE(long_only_opt->is_set());
+ EXPECT_FALSE(long_only_opt->has_argument());
+}
+
+TEST(args, options_set_variant) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo")
+ .add("-a")
+ .add("--bold")
+ .add("--cold")
+ .add("-d")
+ .add("10")
+ .add("--eat=hamburger")
+ .add("--form")
+ .add("circle")
+ .add("-g")
+ .add("--hold=foo")
+ .add("--invert=bar")
+ .build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ EXPECT_TRUE(out.empty());
+ EXPECT_TRUE(short_only->is_set());
+ EXPECT_TRUE(short_long->is_set());
+ EXPECT_TRUE(long_only->is_set());
+ EXPECT_TRUE(short_only_req->is_set());
+ EXPECT_TRUE(short_only_req->has_argument());
+ EXPECT_EQ("10", short_only_req->argument());
+ EXPECT_TRUE(short_long_req->is_set());
+ EXPECT_TRUE(short_long_req->has_argument());
+ EXPECT_EQ("hamburger", short_long_req->argument());
+ EXPECT_TRUE(long_only_req->is_set());
+ EXPECT_TRUE(long_only_req->has_argument());
+ EXPECT_EQ("circle", long_only_req->argument());
+ EXPECT_TRUE(short_only_opt->is_set());
+ EXPECT_FALSE(short_only_opt->has_argument());
+ EXPECT_TRUE(short_long_opt->is_set());
+ EXPECT_TRUE(short_long_opt->has_argument());
+ EXPECT_EQ("foo", short_long_opt->argument());
+ EXPECT_TRUE(long_only_opt->is_set());
+ EXPECT_TRUE(long_only_opt->has_argument());
+ EXPECT_EQ("bar", long_only_opt->argument());
+}
+
+TEST(args, options_short_missing_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("-d").build();
+ EXPECT_FALSE(args->run(arg.c(), arg.v()));
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("foo: option requires an argument -- 'd'\n", ss.str());
+}
+
+TEST(args, options_short_unknown_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("-X").build();
+ EXPECT_FALSE(args->run(arg.c(), arg.v()));
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("foo: invalid option -- 'X'\n", ss.str());
+}
+
+TEST(args, options_long_missing_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("--form").build();
+ EXPECT_FALSE(args->run(arg.c(), arg.v()));
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("foo: option '--form' requires an argument\n", ss.str());
+}
+
+TEST(args, options_long_unsupported_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("--cold=feet").build();
+ EXPECT_FALSE(args->run(arg.c(), arg.v()));
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("foo: option '--cold' doesn't allow an argument\n", ss.str());
+}
+
+TEST(args, options_long_unknown_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("--experience").build();
+ EXPECT_FALSE(args->run(arg.c(), arg.v()));
+ std::stringstream ss;
+ args->print_error(ss);
+ EXPECT_EQ("foo: unrecognized option '--experience'\n", ss.str());
+}
+
+TEST(args, options_short_dash_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("-d").add("-").build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ EXPECT_TRUE(out.empty());
+ EXPECT_TRUE(short_only_req->is_set());
+ EXPECT_TRUE(short_only_req->has_argument());
+ EXPECT_EQ("-", short_only_req->argument());
+}
+
+TEST(args, options_long_dash_dash_value) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("--eat").add("--").build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ EXPECT_TRUE(out.empty());
+ EXPECT_TRUE(short_long_req->is_set());
+ EXPECT_TRUE(short_long_req->has_argument());
+ EXPECT_EQ("--", short_long_req->argument());
+}
+
+TEST(args, options_dash_dash) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo")
+ .add("--")
+ .add("-d")
+ .add("10")
+ .add("--eat=hamburger")
+ .add("--form")
+ .add("circle")
+ .build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ ASSERT_EQ(5, out.size());
+ EXPECT_EQ("-d", out[0]);
+ EXPECT_EQ("10", out[1]);
+ EXPECT_EQ("--eat=hamburger", out[2]);
+ EXPECT_EQ("--form", out[3]);
+ EXPECT_EQ("circle", out[4]);
+ EXPECT_FALSE(short_only_req->is_set());
+ EXPECT_FALSE(short_long_req->is_set());
+ EXPECT_FALSE(long_only_req->is_set());
+}
+
+TEST(args, options_dash_dash_end) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ Builder builder;
+ auto arg = builder.add("foo").add("--").build();
+ std::vector<std::string_view> out;
+ EXPECT_TRUE(args->run(arg.c(), arg.v(), &out));
+ EXPECT_TRUE(out.empty());
+}
+
+TEST(args, help) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ std::stringstream ss;
+ args->print_help(ss, 50);
+ EXPECT_EQ(R"(Mandatory arguments to long options are mandatory
+ for short options too.
+ -a an option
+ -b, --bold set font style to bold
+ --cold use if it is cold outside
+ -d ARG distance
+ -e, --eat=FOOD what to order, what to eat?
+ --form=ARG circle, shape or something
+ else?
+ -g
+ -h, --hold=[ARG]
+ --invert=[ARG]
+)",
+ ss.str());
+}
+
+TEST(args, help_wide) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ std::stringstream ss;
+ args->print_help(ss, 100);
+ EXPECT_EQ(
+ R"(Mandatory arguments to long options are mandatory for short options too.
+ -a an option
+ -b, --bold set font style to bold
+ --cold use if it is cold outside
+ -d ARG distance
+ -e, --eat=FOOD what to order, what to eat?
+ --form=ARG circle, shape or something else?
+ -g
+ -h, --hold=[ARG]
+ --invert=[ARG]
+)",
+ ss.str());
+}
+
+TEST(args, help_narrow) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ std::stringstream ss;
+ args->print_help(ss, 35);
+ EXPECT_EQ(R"(Mandatory arguments to long options
+ are mandatory for short options
+ too.
+ -a an option
+ -b, --bold set font style to
+ bold
+ --cold use if it is cold
+ outside
+ -d ARG distance
+ -e, --eat=FOOD
+what to order, what to eat?
+ --form=ARG
+circle, shape or something else?
+ -g
+ -h, --hold=[ARG]
+ --invert=[ARG]
+)",
+ ss.str());
+}
+
+TEST(args, help_very_narrow) {
+ auto args = Args::create();
+ SETUP_OPTIONS(args);
+ std::stringstream ss;
+ args->print_help(ss, 20);
+ EXPECT_EQ(R"(Mandatory arguments
+ to long options are
+ mandatory for short
+ options too.
+ -a an option
+ -b, --bold
+set font style to
+ bold
+ --cold
+use if it is cold
+ outside
+ -d ARG distance
+ -e, --eat=FOOD
+what to order, what
+ to eat?
+ --form=ARG
+circle, shape or
+ something else?
+ -g
+ -h, --hold=[ARG]
+ --invert=[ARG]
+)",
+ ss.str());
+}
+
+TEST(args, help_long_word) {
+ auto args = Args::create();
+ std::stringstream ss;
+ auto opt = args->option('a', "arg", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+ args->print_help(ss, 20);
+ EXPECT_EQ(R"(Mandatory arguments
+ to long options are
+ mandatory for short
+ options too.
+ -a, --arg
+aaaaaaaaaaaaaaaaaaa-
+aaaaaaaaaaaaaaaaaaa
+)",
+ ss.str());
+}