mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:27:45 +00:00
AK: Update OptionParser::m_arg_index by substracting skipped args
On argument swapping to put positional ones toward the end, m_arg_index was pointing at "last arg index" + "skipped args" + "consumed args" and thus was pointing ahead of the skipped ones. m_arg_index now points after the current parsed option arguments.
This commit is contained in:
parent
8320faf052
commit
a9df60ff1c
5 changed files with 382 additions and 2 deletions
174
Tests/AK/TestOptionParser.cpp
Normal file
174
Tests/AK/TestOptionParser.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (c) 2021, the SerenityOS developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibTest/TestCase.h>
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/OptionParser.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
TEST_CASE(string_option)
|
||||
{
|
||||
ByteString short_options = "";
|
||||
int index_of_found_long_option = -1;
|
||||
Vector<OptionParser::Option> long_options;
|
||||
long_options.append(
|
||||
{ "string_opt"sv,
|
||||
OptionParser::ArgumentRequirement::HasRequiredArgument,
|
||||
&index_of_found_long_option,
|
||||
0 });
|
||||
|
||||
Array<StringView, 3> argument_array({ "app"sv, "--string_opt"sv, "string_opt_value"sv });
|
||||
Span<StringView> arguments(argument_array);
|
||||
size_t next_argument_index = 1;
|
||||
|
||||
OptionParser parser;
|
||||
auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
|
||||
// found a long option
|
||||
EXPECT_EQ(result.result, 0);
|
||||
// found long option at index 0
|
||||
EXPECT_EQ(index_of_found_long_option, 0);
|
||||
// 2 args consumed: option name and value
|
||||
EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
|
||||
// option has a value
|
||||
EXPECT_EQ(result.optarg_value, "string_opt_value");
|
||||
|
||||
next_argument_index += result.consumed_args;
|
||||
// we are past the end
|
||||
EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
|
||||
}
|
||||
|
||||
TEST_CASE(string_option_then_positional)
|
||||
{
|
||||
ByteString short_options = "";
|
||||
int index_of_found_long_option = -1;
|
||||
Vector<OptionParser::Option> long_options;
|
||||
long_options.append(
|
||||
{ "string_opt"sv,
|
||||
OptionParser::ArgumentRequirement::HasRequiredArgument,
|
||||
&index_of_found_long_option,
|
||||
0 });
|
||||
|
||||
Array<StringView, 4> argument_array({ "app"sv, "--string_opt"sv, "string_opt_value"sv, "positional"sv });
|
||||
Span<StringView> arguments(argument_array);
|
||||
size_t next_argument_index = 1;
|
||||
|
||||
OptionParser parser;
|
||||
auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
|
||||
// found a long option
|
||||
EXPECT_EQ(result.result, 0);
|
||||
// found long option at index 0
|
||||
EXPECT_EQ(index_of_found_long_option, 0);
|
||||
// 2 args consumed: option name and value
|
||||
EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
|
||||
// option has a value
|
||||
EXPECT_EQ(result.optarg_value, "string_opt_value");
|
||||
|
||||
next_argument_index += result.consumed_args;
|
||||
// we are at "positional" index of arguments vector
|
||||
EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
|
||||
EXPECT_EQ(arguments[next_argument_index], "positional");
|
||||
|
||||
result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
// there's no more options
|
||||
EXPECT_EQ(result.result, -1);
|
||||
}
|
||||
|
||||
TEST_CASE(positional_then_string_option)
|
||||
{
|
||||
ByteString short_options = "";
|
||||
int index_of_found_long_option = -1;
|
||||
Vector<OptionParser::Option> long_options;
|
||||
long_options.append(
|
||||
{ "string_opt"sv,
|
||||
OptionParser::ArgumentRequirement::HasRequiredArgument,
|
||||
&index_of_found_long_option,
|
||||
0 });
|
||||
|
||||
Array<StringView, 4> argument_array({ "app"sv, "positional"sv, "--string_opt"sv, "string_opt_value"sv });
|
||||
Span<StringView> arguments(argument_array);
|
||||
size_t next_argument_index = 1;
|
||||
|
||||
OptionParser parser;
|
||||
auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
|
||||
// found a long option
|
||||
EXPECT_EQ(result.result, 0);
|
||||
// found long option at index 0
|
||||
EXPECT_EQ(index_of_found_long_option, 0);
|
||||
// 2 args consumed: option name and value
|
||||
EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
|
||||
// option has a value
|
||||
EXPECT_EQ(result.optarg_value, "string_opt_value");
|
||||
|
||||
next_argument_index += result.consumed_args;
|
||||
// we are at "positional" index of arguments vector
|
||||
EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
|
||||
EXPECT_EQ(arguments[next_argument_index], "positional");
|
||||
|
||||
result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
// there's no more options
|
||||
EXPECT_EQ(result.result, -1);
|
||||
}
|
||||
|
||||
TEST_CASE(positional_then_string_option_then_bool_option)
|
||||
{
|
||||
// #22759: Positional arguments were sometimes incorrectly not shifted, leading to an incorrect parse.
|
||||
|
||||
ByteString short_options = "";
|
||||
int index_of_found_long_option = -1;
|
||||
Vector<OptionParser::Option> long_options;
|
||||
long_options.append(
|
||||
{ "string_opt"sv,
|
||||
OptionParser::ArgumentRequirement::HasRequiredArgument,
|
||||
&index_of_found_long_option,
|
||||
0 });
|
||||
long_options.append(
|
||||
{ "bool_opt"sv,
|
||||
OptionParser::ArgumentRequirement::NoArgument,
|
||||
&index_of_found_long_option,
|
||||
1 });
|
||||
|
||||
Array<StringView, 5> argument_array({ "app"sv, "positional"sv, "--string_opt"sv, "string_opt_value"sv, "--bool_opt"sv });
|
||||
Span<StringView> arguments(argument_array);
|
||||
size_t next_argument_index = 1;
|
||||
|
||||
OptionParser parser;
|
||||
auto result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
// found a long option
|
||||
EXPECT_EQ(result.result, 0);
|
||||
// found long option at index 0
|
||||
EXPECT_EQ(index_of_found_long_option, 0);
|
||||
// 2 args consumed: option name and value
|
||||
EXPECT_EQ(result.consumed_args, static_cast<size_t>(2));
|
||||
// option has a value
|
||||
EXPECT_EQ(result.optarg_value, "string_opt_value");
|
||||
|
||||
next_argument_index += result.consumed_args;
|
||||
EXPECT_EQ(next_argument_index, static_cast<size_t>(3));
|
||||
// positional argument has been shifted here
|
||||
EXPECT_EQ(arguments[next_argument_index], "positional");
|
||||
|
||||
result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
// found another long option
|
||||
EXPECT_EQ(result.result, 0);
|
||||
// found long option at index 1
|
||||
EXPECT_EQ(index_of_found_long_option, 1);
|
||||
// 1 arg consumed: option name
|
||||
EXPECT_EQ(result.consumed_args, static_cast<size_t>(1));
|
||||
|
||||
next_argument_index += result.consumed_args;
|
||||
// "positional" argument has been shifted here
|
||||
EXPECT_EQ(next_argument_index, static_cast<size_t>(4));
|
||||
EXPECT_EQ(arguments[next_argument_index], "positional");
|
||||
|
||||
result = parser.getopt(arguments.slice(1), short_options, long_options, {});
|
||||
// there's no more options
|
||||
EXPECT_EQ(result.result, -1);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue