From c87f9627efc8b612eb9b000acfcc6731cad15765 Mon Sep 17 00:00:00 2001 From: Joel Klinghed Date: Tue, 7 Oct 2025 09:12:22 +0200 Subject: Initial commit --- test/args.cc | 412 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 412 insertions(+) create mode 100644 test/args.cc (limited to 'test/args.cc') 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 +#include +#include +#include +#include +#include +#include +#include + +#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(str_.size()); }; + [[nodiscard]] char** v() const { return ptr_.get(); } + + explicit Arguments(std::vector str) : str_(std::move(str)) { + ptr_ = std::make_unique(str_.size()); + for (size_t i = 0; i < str_.size(); ++i) { + ptr_[i] = const_cast(str_[i].c_str()); + } + } + + Arguments(Arguments const&) = delete; + Arguments& operator=(Arguments const&) = delete; + + private: + std::unique_ptr ptr_; + std::vector 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 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 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 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 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 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 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 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 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()); +} -- cgit v1.2.3-70-g09d2