123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- /*
- * Copyright (c) 2021, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Function.h>
- #include <AK/Vector.h>
- #include <LibCore/ArgsParser.h>
- #include <LibTest/TestCase.h>
- #include <string.h>
- class ParserResult {
- public:
- ParserResult(Vector<StringView> arguments)
- : arguments(move(arguments))
- {
- }
- ParserResult(ParserResult&& other)
- {
- arguments = move(other.arguments);
- result = other.result;
- }
- ParserResult& operator=(ParserResult&& other)
- {
- if (this != &other) {
- arguments = move(other.arguments);
- result = other.result;
- }
- return *this;
- }
- Vector<StringView> arguments;
- bool result { false };
- };
- static ParserResult run_parser(Vector<StringView> arguments, Function<void(Core::ArgsParser&)> parser_initialization = {})
- {
- Core::ArgsParser parser;
- if (parser_initialization)
- parser_initialization(parser);
- auto parse_result = ParserResult { move(arguments) };
- parse_result.result = parser.parse(parse_result.arguments, Core::ArgsParser::FailureBehavior::Ignore);
- return parse_result;
- }
- TEST_CASE(no_arguments)
- {
- auto parser_result = run_parser({ "app"sv });
- EXPECT_EQ(parser_result.result, true);
- }
- TEST_CASE(bool_option)
- {
- // Short option
- bool force = false;
- auto parser_result = run_parser({ "app"sv, "-f"sv }, [&](auto& parser) {
- parser.add_option(force, "force", nullptr, 'f');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, true);
- // Short option, not given
- force = false;
- parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_option(force, "force", nullptr, 'f');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, false);
- // Long option
- force = false;
- parser_result = run_parser({ "app"sv, "--force"sv }, [&](auto& parser) {
- parser.add_option(force, "force", "force", '\0');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, true);
- // Long option, not given
- force = false;
- parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_option(force, "force", "force", '\0');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, false);
- // Allow both short and long option, provide short
- force = false;
- parser_result = run_parser({ "app"sv, "-f"sv }, [&](auto& parser) {
- parser.add_option(force, "force", "force", 'f');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, true);
- // Allow both short and long option, provide long
- force = false;
- parser_result = run_parser({ "app"sv, "--force"sv }, [&](auto& parser) {
- parser.add_option(force, "force", "force", 'f');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, true);
- // Allow both short and long option, provide both
- force = false;
- parser_result = run_parser({ "app"sv, "--force"sv, "-f"sv }, [&](auto& parser) {
- parser.add_option(force, "force", "force", 'f');
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(force, true);
- }
- TEST_CASE(positional_string_argument)
- {
- // Single required string argument
- DeprecatedString name = "";
- auto parser_result = run_parser({ "app"sv, "buggie"sv }, [&](auto& parser) {
- parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(name, "buggie");
- // Single required string argument, not given
- name = "";
- parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, false);
- EXPECT_EQ(name, "");
- // Single optional string argument
- name = "";
- parser_result = run_parser({ "app"sv, "buggie"sv }, [&](auto& parser) {
- parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(name, "buggie");
- // Single optional string argument, not given
- name = "";
- parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(name, "");
- }
- TEST_CASE(positional_vector_string_argument)
- {
- Vector<StringView> values;
- // Zero or more positional arguments, zero given
- auto parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(values.size(), 0u);
- // Zero or more positional arguments, one given
- values = {};
- parser_result = run_parser({ "app"sv, "one"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(values.size(), 1u);
- if (values.size() == 1u)
- EXPECT_EQ(values[0], "one");
- // Zero or more positional arguments, two given
- values = {};
- parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(values.size(), 2u);
- if (values.size() == 2u) {
- EXPECT_EQ(values[0], "one");
- EXPECT_EQ(values[1], "two");
- }
- // One or more positional arguments, zero given
- values = {};
- parser_result = run_parser({ "app"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, false);
- EXPECT_EQ(values.size(), 0u);
- // One or more positional arguments, one given
- values = {};
- parser_result = run_parser({ "app"sv, "one"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(values.size(), 1u);
- if (values.size() == 1u)
- EXPECT_EQ(values[0], "one");
- // One or more positional arguments, two given
- values = {};
- parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) {
- parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(values.size(), 2u);
- if (values.size() == 2u) {
- EXPECT_EQ(values[0], "one");
- EXPECT_EQ(values[1], "two");
- }
- }
- TEST_CASE(combination_of_bool_options_with_positional_vector_string)
- {
- Vector<StringView> positionals;
- // Bool options (given) and positional arguments (given)
- // Expected: all arguments fill as given
- bool bool_opt1 = false;
- bool bool_opt2 = false;
- auto parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv, "two"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, true);
- EXPECT_EQ(positionals.size(), 2u);
- if (positionals.size() == 2u) {
- EXPECT_EQ(positionals[0], "one");
- EXPECT_EQ(positionals[1], "two");
- }
- // Bool options (missing) and positional arguments (given)
- // Expected: only the positional arguments are filled
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, false);
- EXPECT_EQ(bool_opt2, false);
- EXPECT_EQ(positionals.size(), 2u);
- if (positionals.size() == 2u) {
- EXPECT_EQ(positionals[0], "one");
- EXPECT_EQ(positionals[1], "two");
- }
- // Bool options (given) and positional arguments (missing)
- // Expected: only the bool options are filled
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, true);
- EXPECT_EQ(positionals.size(), 0u);
- // Bool options (missing) and positional arguments (given) using double dash
- // Expected: the bool options are interpreted as positional arguments
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "--"sv, "-b"sv, "-c"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, false);
- EXPECT_EQ(bool_opt2, false);
- EXPECT_EQ(positionals.size(), 2u);
- if (positionals.size() == 2u) {
- EXPECT_EQ(positionals[0], "-b");
- EXPECT_EQ(positionals[1], "-c");
- }
- // Bool options (one given) and positional arguments (one given) using double dash
- // Expected: bool_opt1 is set, one positional is added
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "--"sv, "-c"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, false);
- EXPECT_EQ(positionals.size(), 1u);
- if (positionals.size() == 1u) {
- EXPECT_EQ(positionals[0], "-c");
- }
- // Bool options (three given, one incorrect) and positional arguments (missing)
- // Expected: parser fails
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "-d"sv, "-c"sv }, [&](auto& parser) {
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No);
- });
- EXPECT_EQ(parser_result.result, false);
- }
- TEST_CASE(stop_on_first_non_option)
- {
- // Do not stop on first non-option; arguments in correct order
- // Expected: bool options are set and one positional argument is filled
- bool bool_opt1 = false;
- bool bool_opt2 = false;
- Vector<StringView> positionals;
- auto parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv }, [&](auto& parser) {
- parser.set_stop_on_first_non_option(false);
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, true);
- EXPECT_EQ(positionals.size(), 1u);
- if (positionals.size() == 1u)
- EXPECT_EQ(positionals[0], "one");
- // Do not stop on first non-option; arguments in wrong order
- // Expected: bool options are set and one positional argument is filled
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "one"sv, "-c"sv }, [&](auto& parser) {
- parser.set_stop_on_first_non_option(false);
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, true);
- EXPECT_EQ(positionals.size(), 1u);
- if (positionals.size() == 1u)
- EXPECT_EQ(positionals[0], "one");
- // Stop on first non-option; arguments in correct order
- // Expected: bool options are set and one positional argument is filled
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv }, [&](auto& parser) {
- parser.set_stop_on_first_non_option(true);
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, true);
- EXPECT_EQ(positionals.size(), 1u);
- if (positionals.size() == 1u)
- EXPECT_EQ(positionals[0], "one");
- // Stop on first non-option; arguments in wrong order
- // Expected: bool_opt1 is set, other arguments are filled as positional arguments
- bool_opt1 = false;
- bool_opt2 = false;
- positionals = {};
- parser_result = run_parser({ "app"sv, "-b"sv, "one"sv, "-c"sv }, [&](auto& parser) {
- parser.set_stop_on_first_non_option(true);
- parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b');
- parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c');
- parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes);
- });
- EXPECT_EQ(parser_result.result, true);
- EXPECT_EQ(bool_opt1, true);
- EXPECT_EQ(bool_opt2, false);
- EXPECT_EQ(positionals.size(), 2u);
- if (positionals.size() == 2u) {
- EXPECT_EQ(positionals[0], "one");
- EXPECT_EQ(positionals[1], "-c");
- }
- }
|