TestOptionParser.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. /*
  2. * Copyright (c) 2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibTest/TestCase.h>
  7. #include <AK/Array.h>
  8. #include <AK/OptionParser.h>
  9. #include <AK/String.h>
  10. #include <AK/Vector.h>
  11. TEST_CASE(string_option)
  12. {
  13. ByteString short_options = "";
  14. int index_of_found_long_option = -1;
  15. Vector<OptionParser::Option> long_options;
  16. long_options.append(
  17. { "string_opt"sv,
  18. OptionParser::ArgumentRequirement::HasRequiredArgument,
  19. &index_of_found_long_option,
  20. 0 });
  21. Array<StringView, 3> argument_array({ "app"sv, "--string_opt"sv, "string_opt_value"sv });
  22. Span<StringView> arguments(argument_array);
  23. size_t next_argument_index = 1;
  24. OptionParser parser;
  25. auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  26. // found a long option
  27. EXPECT_EQ(result.result, 0);
  28. // found long option at index 0
  29. EXPECT_EQ(index_of_found_long_option, 0);
  30. // 2 args consumed: option name and value
  31. EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
  32. // option has a value
  33. EXPECT_EQ(result.optarg_value, "string_opt_value");
  34. next_argument_index += result.consumed_args;
  35. // we are past the end
  36. EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
  37. }
  38. TEST_CASE(string_option_then_positional)
  39. {
  40. ByteString short_options = "";
  41. int index_of_found_long_option = -1;
  42. Vector<OptionParser::Option> long_options;
  43. long_options.append(
  44. { "string_opt"sv,
  45. OptionParser::ArgumentRequirement::HasRequiredArgument,
  46. &index_of_found_long_option,
  47. 0 });
  48. Array<StringView, 4> argument_array({ "app"sv, "--string_opt"sv, "string_opt_value"sv, "positional"sv });
  49. Span<StringView> arguments(argument_array);
  50. size_t next_argument_index = 1;
  51. OptionParser parser;
  52. auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  53. // found a long option
  54. EXPECT_EQ(result.result, 0);
  55. // found long option at index 0
  56. EXPECT_EQ(index_of_found_long_option, 0);
  57. // 2 args consumed: option name and value
  58. EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
  59. // option has a value
  60. EXPECT_EQ(result.optarg_value, "string_opt_value");
  61. next_argument_index += result.consumed_args;
  62. // we are at "positional" index of arguments vector
  63. EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
  64. EXPECT_EQ(arguments[next_argument_index], "positional");
  65. result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  66. // there's no more options
  67. EXPECT_EQ(result.result, -1);
  68. }
  69. TEST_CASE(positional_then_string_option)
  70. {
  71. ByteString short_options = "";
  72. int index_of_found_long_option = -1;
  73. Vector<OptionParser::Option> long_options;
  74. long_options.append(
  75. { "string_opt"sv,
  76. OptionParser::ArgumentRequirement::HasRequiredArgument,
  77. &index_of_found_long_option,
  78. 0 });
  79. Array<StringView, 4> argument_array({ "app"sv, "positional"sv, "--string_opt"sv, "string_opt_value"sv });
  80. Span<StringView> arguments(argument_array);
  81. size_t next_argument_index = 1;
  82. OptionParser parser;
  83. auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  84. // found a long option
  85. EXPECT_EQ(result.result, 0);
  86. // found long option at index 0
  87. EXPECT_EQ(index_of_found_long_option, 0);
  88. // 2 args consumed: option name and value
  89. EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
  90. // option has a value
  91. EXPECT_EQ(result.optarg_value, "string_opt_value");
  92. next_argument_index += result.consumed_args;
  93. // we are at "positional" index of arguments vector
  94. EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
  95. EXPECT_EQ(arguments[next_argument_index], "positional");
  96. result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  97. // there's no more options
  98. EXPECT_EQ(result.result, -1);
  99. }
  100. TEST_CASE(positional_then_string_option_then_bool_option)
  101. {
  102. // #22759: Positional arguments were sometimes incorrectly not shifted, leading to an incorrect parse.
  103. ByteString short_options = "";
  104. int index_of_found_long_option = -1;
  105. Vector<OptionParser::Option> long_options;
  106. long_options.append(
  107. { "string_opt"sv,
  108. OptionParser::ArgumentRequirement::HasRequiredArgument,
  109. &index_of_found_long_option,
  110. 0 });
  111. long_options.append(
  112. { "bool_opt"sv,
  113. OptionParser::ArgumentRequirement::NoArgument,
  114. &index_of_found_long_option,
  115. 1 });
  116. Array<StringView, 5> argument_array({ "app"sv, "positional"sv, "--string_opt"sv, "string_opt_value"sv, "--bool_opt"sv });
  117. Span<StringView> arguments(argument_array);
  118. size_t next_argument_index = 1;
  119. OptionParser parser;
  120. auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  121. // found a long option
  122. EXPECT_EQ(result.result, 0);
  123. // found long option at index 0
  124. EXPECT_EQ(index_of_found_long_option, 0);
  125. // 2 args consumed: option name and value
  126. EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
  127. // option has a value
  128. EXPECT_EQ(result.optarg_value, "string_opt_value");
  129. next_argument_index += result.consumed_args;
  130. EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
  131. // positional argument has been shifted here
  132. EXPECT_EQ(arguments[next_argument_index], "positional");
  133. result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  134. // found another long option
  135. EXPECT_EQ(result.result, 0);
  136. // found long option at index 1
  137. EXPECT_EQ(index_of_found_long_option, 1);
  138. // 1 arg consumed: option name
  139. EXPECT_EQ(result.consumed_args, static_cast<size_t>(1));
  140. next_argument_index += result.consumed_args;
  141. // "positional" argument has been shifted here
  142. EXPECT_EQ(next_argument_index, static_cast<size_t>(4));
  143. EXPECT_EQ(arguments[next_argument_index], "positional");
  144. result = parser.getopt(arguments.slice(1), short_options, long_options, {});
  145. // there's no more options
  146. EXPECT_EQ(result.result, -1);
  147. }