Serenity Operating System
at master 394 lines 15 kB view raw
1/* 2 * Copyright (c) 2021, the SerenityOS developers. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Function.h> 8#include <AK/Vector.h> 9#include <LibCore/ArgsParser.h> 10#include <LibTest/TestCase.h> 11#include <string.h> 12 13class ParserResult { 14public: 15 ParserResult(Vector<StringView> arguments) 16 : arguments(move(arguments)) 17 { 18 } 19 20 ParserResult(ParserResult&& other) 21 { 22 arguments = move(other.arguments); 23 result = other.result; 24 } 25 26 ParserResult& operator=(ParserResult&& other) 27 { 28 if (this != &other) { 29 arguments = move(other.arguments); 30 result = other.result; 31 } 32 return *this; 33 } 34 35 Vector<StringView> arguments; 36 bool result { false }; 37}; 38 39static ParserResult run_parser(Vector<StringView> arguments, Function<void(Core::ArgsParser&)> parser_initialization = {}) 40{ 41 Core::ArgsParser parser; 42 if (parser_initialization) 43 parser_initialization(parser); 44 45 auto parse_result = ParserResult { move(arguments) }; 46 parse_result.result = parser.parse(parse_result.arguments, Core::ArgsParser::FailureBehavior::Ignore); 47 return parse_result; 48} 49 50TEST_CASE(no_arguments) 51{ 52 auto parser_result = run_parser({ "app"sv }); 53 EXPECT_EQ(parser_result.result, true); 54} 55 56TEST_CASE(bool_option) 57{ 58 // Short option 59 bool force = false; 60 auto parser_result = run_parser({ "app"sv, "-f"sv }, [&](auto& parser) { 61 parser.add_option(force, "force", nullptr, 'f'); 62 }); 63 EXPECT_EQ(parser_result.result, true); 64 EXPECT_EQ(force, true); 65 66 // Short option, not given 67 force = false; 68 parser_result = run_parser({ "app"sv }, [&](auto& parser) { 69 parser.add_option(force, "force", nullptr, 'f'); 70 }); 71 EXPECT_EQ(parser_result.result, true); 72 EXPECT_EQ(force, false); 73 74 // Long option 75 force = false; 76 parser_result = run_parser({ "app"sv, "--force"sv }, [&](auto& parser) { 77 parser.add_option(force, "force", "force", '\0'); 78 }); 79 EXPECT_EQ(parser_result.result, true); 80 EXPECT_EQ(force, true); 81 82 // Long option, not given 83 force = false; 84 parser_result = run_parser({ "app"sv }, [&](auto& parser) { 85 parser.add_option(force, "force", "force", '\0'); 86 }); 87 EXPECT_EQ(parser_result.result, true); 88 EXPECT_EQ(force, false); 89 90 // Allow both short and long option, provide short 91 force = false; 92 parser_result = run_parser({ "app"sv, "-f"sv }, [&](auto& parser) { 93 parser.add_option(force, "force", "force", 'f'); 94 }); 95 EXPECT_EQ(parser_result.result, true); 96 EXPECT_EQ(force, true); 97 98 // Allow both short and long option, provide long 99 force = false; 100 parser_result = run_parser({ "app"sv, "--force"sv }, [&](auto& parser) { 101 parser.add_option(force, "force", "force", 'f'); 102 }); 103 EXPECT_EQ(parser_result.result, true); 104 EXPECT_EQ(force, true); 105 106 // Allow both short and long option, provide both 107 force = false; 108 parser_result = run_parser({ "app"sv, "--force"sv, "-f"sv }, [&](auto& parser) { 109 parser.add_option(force, "force", "force", 'f'); 110 }); 111 EXPECT_EQ(parser_result.result, true); 112 EXPECT_EQ(force, true); 113} 114 115TEST_CASE(positional_string_argument) 116{ 117 // Single required string argument 118 DeprecatedString name = ""; 119 auto parser_result = run_parser({ "app"sv, "buggie"sv }, [&](auto& parser) { 120 parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes); 121 }); 122 EXPECT_EQ(parser_result.result, true); 123 EXPECT_EQ(name, "buggie"); 124 125 // Single required string argument, not given 126 name = ""; 127 parser_result = run_parser({ "app"sv }, [&](auto& parser) { 128 parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::Yes); 129 }); 130 EXPECT_EQ(parser_result.result, false); 131 EXPECT_EQ(name, ""); 132 133 // Single optional string argument 134 name = ""; 135 parser_result = run_parser({ "app"sv, "buggie"sv }, [&](auto& parser) { 136 parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No); 137 }); 138 EXPECT_EQ(parser_result.result, true); 139 EXPECT_EQ(name, "buggie"); 140 141 // Single optional string argument, not given 142 name = ""; 143 parser_result = run_parser({ "app"sv }, [&](auto& parser) { 144 parser.add_positional_argument(name, "name", "name", Core::ArgsParser::Required::No); 145 }); 146 EXPECT_EQ(parser_result.result, true); 147 EXPECT_EQ(name, ""); 148} 149 150TEST_CASE(positional_vector_string_argument) 151{ 152 Vector<StringView> values; 153 154 // Zero or more positional arguments, zero given 155 auto parser_result = run_parser({ "app"sv }, [&](auto& parser) { 156 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); 157 }); 158 EXPECT_EQ(parser_result.result, true); 159 EXPECT_EQ(values.size(), 0u); 160 161 // Zero or more positional arguments, one given 162 values = {}; 163 parser_result = run_parser({ "app"sv, "one"sv }, [&](auto& parser) { 164 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); 165 }); 166 EXPECT_EQ(parser_result.result, true); 167 EXPECT_EQ(values.size(), 1u); 168 if (values.size() == 1u) 169 EXPECT_EQ(values[0], "one"); 170 171 // Zero or more positional arguments, two given 172 values = {}; 173 parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) { 174 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::No); 175 }); 176 EXPECT_EQ(parser_result.result, true); 177 EXPECT_EQ(values.size(), 2u); 178 if (values.size() == 2u) { 179 EXPECT_EQ(values[0], "one"); 180 EXPECT_EQ(values[1], "two"); 181 } 182 183 // One or more positional arguments, zero given 184 values = {}; 185 parser_result = run_parser({ "app"sv }, [&](auto& parser) { 186 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); 187 }); 188 EXPECT_EQ(parser_result.result, false); 189 EXPECT_EQ(values.size(), 0u); 190 191 // One or more positional arguments, one given 192 values = {}; 193 parser_result = run_parser({ "app"sv, "one"sv }, [&](auto& parser) { 194 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); 195 }); 196 EXPECT_EQ(parser_result.result, true); 197 EXPECT_EQ(values.size(), 1u); 198 if (values.size() == 1u) 199 EXPECT_EQ(values[0], "one"); 200 201 // One or more positional arguments, two given 202 values = {}; 203 parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) { 204 parser.add_positional_argument(values, "values", "values", Core::ArgsParser::Required::Yes); 205 }); 206 EXPECT_EQ(parser_result.result, true); 207 EXPECT_EQ(values.size(), 2u); 208 if (values.size() == 2u) { 209 EXPECT_EQ(values[0], "one"); 210 EXPECT_EQ(values[1], "two"); 211 } 212} 213 214TEST_CASE(combination_of_bool_options_with_positional_vector_string) 215{ 216 Vector<StringView> positionals; 217 // Bool options (given) and positional arguments (given) 218 // Expected: all arguments fill as given 219 bool bool_opt1 = false; 220 bool bool_opt2 = false; 221 auto parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv, "two"sv }, [&](auto& parser) { 222 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 223 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 224 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 225 }); 226 EXPECT_EQ(parser_result.result, true); 227 EXPECT_EQ(bool_opt1, true); 228 EXPECT_EQ(bool_opt2, true); 229 EXPECT_EQ(positionals.size(), 2u); 230 if (positionals.size() == 2u) { 231 EXPECT_EQ(positionals[0], "one"); 232 EXPECT_EQ(positionals[1], "two"); 233 } 234 235 // Bool options (missing) and positional arguments (given) 236 // Expected: only the positional arguments are filled 237 bool_opt1 = false; 238 bool_opt2 = false; 239 positionals = {}; 240 parser_result = run_parser({ "app"sv, "one"sv, "two"sv }, [&](auto& parser) { 241 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 242 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 243 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 244 }); 245 EXPECT_EQ(parser_result.result, true); 246 EXPECT_EQ(bool_opt1, false); 247 EXPECT_EQ(bool_opt2, false); 248 EXPECT_EQ(positionals.size(), 2u); 249 if (positionals.size() == 2u) { 250 EXPECT_EQ(positionals[0], "one"); 251 EXPECT_EQ(positionals[1], "two"); 252 } 253 254 // Bool options (given) and positional arguments (missing) 255 // Expected: only the bool options are filled 256 bool_opt1 = false; 257 bool_opt2 = false; 258 positionals = {}; 259 parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv }, [&](auto& parser) { 260 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 261 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 262 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 263 }); 264 EXPECT_EQ(parser_result.result, true); 265 EXPECT_EQ(bool_opt1, true); 266 EXPECT_EQ(bool_opt2, true); 267 EXPECT_EQ(positionals.size(), 0u); 268 269 // Bool options (missing) and positional arguments (given) using double dash 270 // Expected: the bool options are interpreted as positional arguments 271 bool_opt1 = false; 272 bool_opt2 = false; 273 positionals = {}; 274 parser_result = run_parser({ "app"sv, "--"sv, "-b"sv, "-c"sv }, [&](auto& parser) { 275 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 276 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 277 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 278 }); 279 EXPECT_EQ(parser_result.result, true); 280 EXPECT_EQ(bool_opt1, false); 281 EXPECT_EQ(bool_opt2, false); 282 EXPECT_EQ(positionals.size(), 2u); 283 if (positionals.size() == 2u) { 284 EXPECT_EQ(positionals[0], "-b"); 285 EXPECT_EQ(positionals[1], "-c"); 286 } 287 288 // Bool options (one given) and positional arguments (one given) using double dash 289 // Expected: bool_opt1 is set, one positional is added 290 bool_opt1 = false; 291 bool_opt2 = false; 292 positionals = {}; 293 parser_result = run_parser({ "app"sv, "-b"sv, "--"sv, "-c"sv }, [&](auto& parser) { 294 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 295 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 296 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 297 }); 298 EXPECT_EQ(parser_result.result, true); 299 EXPECT_EQ(bool_opt1, true); 300 EXPECT_EQ(bool_opt2, false); 301 EXPECT_EQ(positionals.size(), 1u); 302 if (positionals.size() == 1u) { 303 EXPECT_EQ(positionals[0], "-c"); 304 } 305 306 // Bool options (three given, one incorrect) and positional arguments (missing) 307 // Expected: parser fails 308 bool_opt1 = false; 309 bool_opt2 = false; 310 positionals = {}; 311 parser_result = run_parser({ "app"sv, "-b"sv, "-d"sv, "-c"sv }, [&](auto& parser) { 312 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 313 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 314 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::No); 315 }); 316 EXPECT_EQ(parser_result.result, false); 317}; 318 319TEST_CASE(stop_on_first_non_option) 320{ 321 // Do not stop on first non-option; arguments in correct order 322 // Expected: bool options are set and one positional argument is filled 323 bool bool_opt1 = false; 324 bool bool_opt2 = false; 325 Vector<StringView> positionals; 326 auto parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv }, [&](auto& parser) { 327 parser.set_stop_on_first_non_option(false); 328 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 329 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 330 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes); 331 }); 332 EXPECT_EQ(parser_result.result, true); 333 EXPECT_EQ(bool_opt1, true); 334 EXPECT_EQ(bool_opt2, true); 335 EXPECT_EQ(positionals.size(), 1u); 336 if (positionals.size() == 1u) 337 EXPECT_EQ(positionals[0], "one"); 338 339 // Do not stop on first non-option; arguments in wrong order 340 // Expected: bool options are set and one positional argument is filled 341 bool_opt1 = false; 342 bool_opt2 = false; 343 positionals = {}; 344 parser_result = run_parser({ "app"sv, "-b"sv, "one"sv, "-c"sv }, [&](auto& parser) { 345 parser.set_stop_on_first_non_option(false); 346 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 347 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 348 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes); 349 }); 350 EXPECT_EQ(parser_result.result, true); 351 EXPECT_EQ(bool_opt1, true); 352 EXPECT_EQ(bool_opt2, true); 353 EXPECT_EQ(positionals.size(), 1u); 354 if (positionals.size() == 1u) 355 EXPECT_EQ(positionals[0], "one"); 356 357 // Stop on first non-option; arguments in correct order 358 // Expected: bool options are set and one positional argument is filled 359 bool_opt1 = false; 360 bool_opt2 = false; 361 positionals = {}; 362 parser_result = run_parser({ "app"sv, "-b"sv, "-c"sv, "one"sv }, [&](auto& parser) { 363 parser.set_stop_on_first_non_option(true); 364 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 365 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 366 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes); 367 }); 368 EXPECT_EQ(parser_result.result, true); 369 EXPECT_EQ(bool_opt1, true); 370 EXPECT_EQ(bool_opt2, true); 371 EXPECT_EQ(positionals.size(), 1u); 372 if (positionals.size() == 1u) 373 EXPECT_EQ(positionals[0], "one"); 374 375 // Stop on first non-option; arguments in wrong order 376 // Expected: bool_opt1 is set, other arguments are filled as positional arguments 377 bool_opt1 = false; 378 bool_opt2 = false; 379 positionals = {}; 380 parser_result = run_parser({ "app"sv, "-b"sv, "one"sv, "-c"sv }, [&](auto& parser) { 381 parser.set_stop_on_first_non_option(true); 382 parser.add_option(bool_opt1, "bool_opt1", nullptr, 'b'); 383 parser.add_option(bool_opt2, "bool_opt2", nullptr, 'c'); 384 parser.add_positional_argument(positionals, "pos", "pos", Core::ArgsParser::Required::Yes); 385 }); 386 EXPECT_EQ(parser_result.result, true); 387 EXPECT_EQ(bool_opt1, true); 388 EXPECT_EQ(bool_opt2, false); 389 EXPECT_EQ(positionals.size(), 2u); 390 if (positionals.size() == 2u) { 391 EXPECT_EQ(positionals[0], "one"); 392 EXPECT_EQ(positionals[1], "-c"); 393 } 394}