805 lines
27 KiB
C
805 lines
27 KiB
C
#include "malunal/microtest.h"
|
|||
|
|
#include "malunal/argparse.h"
|
||
|
|
|
||
|
|
#define PARSE(parser, result, ...) \
|
||
|
|
argparser_parse( \
|
||
|
|
(parser), \
|
||
|
|
(malunal_int32_t)( \
|
||
|
|
sizeof((malunal_cstr_t[]){ __VA_ARGS__ }) / sizeof(malunal_str_t) \
|
||
|
|
), \
|
||
|
|
(malunal_cstr_t[]){ __VA_ARGS__ }, \
|
||
|
|
(result) \
|
||
|
|
)
|
||
|
|
|
||
|
|
static
|
||
|
|
malunal_bool_t
|
||
|
|
view_is(
|
||
|
|
strview_t view,
|
||
|
|
malunal_cstr_t text
|
||
|
|
) {
|
||
|
|
strview_t other = strview_from_cstr(text);
|
||
|
|
return strview_equals(&view, &other);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Parser construction.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, accepts_flag_with_short_and_long) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'v',
|
||
|
|
.long_name = "verbose"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_option_without_names) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_long_name_with_dashes) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "--verbose"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_duplicate_short_name) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'v',
|
||
|
|
.long_name = "verbose"
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'v',
|
||
|
|
.long_name = "version"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_DUPLICATE_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_duplicate_long_name) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'v',
|
||
|
|
.long_name = "verbose"
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "verbose"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_DUPLICATE_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_conflict_with_builtin_help) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'h',
|
||
|
|
.long_name = "host"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_DUPLICATE_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_positional_named_like_option) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "input",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "input",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_DUPLICATE_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_required_flag) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'v',
|
||
|
|
.required = true
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_flag_typed_positional) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "input",
|
||
|
|
.value_type = ARGOPTION_TYPE_FLAG
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_positional_after_multiple) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "files",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING,
|
||
|
|
.multiple = true
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "extra",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_required_positional_after_optional) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "first",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "second",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING,
|
||
|
|
.required = true
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_unconvertible_default) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "jobs",
|
||
|
|
.value_type = ARGOPTION_TYPE_NUMBER,
|
||
|
|
.default_value = "many"
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_option, rejects_positional_when_subcommands_exist) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t sub;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &sub);
|
||
|
|
|
||
|
|
error_t err = argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "x",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_subcommand, rejects_when_positionals_exist) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t sub;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "x",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = argparser_add_subcommand(&parser, "sub", null, &sub);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_add_subcommand, rejects_duplicate_name) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t first;
|
||
|
|
argparser_mptr_t second;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "build", null, &first);
|
||
|
|
|
||
|
|
error_t err = argparser_add_subcommand(&parser, "build", null, &second);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_DUPLICATE_DEFINITION);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Parsing: flags.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, short_and_long_flags_are_recognized) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'a', .long_name = "all"));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'b', .long_name = "bold"));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'c', .long_name = "color"));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-a", "--bold");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "all"));
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "-b"));
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "--bold"));
|
||
|
|
MICROTEST_EXPECT_FALSE(argresult_has(&result, "color"));
|
||
|
|
|
||
|
|
malunal_bool_t color = true;
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_boolean(&result, "color", &color).domain);
|
||
|
|
MICROTEST_EXPECT_FALSE(color);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, flags_count_occurrences_and_cluster) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v', .long_name = "verbose"));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'q'));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-vvq", "--verbose");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "verbose"), (malunal_size_t)3);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "q"), (malunal_size_t)1);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, flag_with_inline_value_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v', .long_name = "verbose"));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "--verbose=yes");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNEXPECTED_VALUE);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, unknown_options_fail) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "--bogus");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNKNOWN_OPTION);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "-z");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNKNOWN_OPTION);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, help_flag_is_reported) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-h");
|
||
|
|
MICROTEST_EXPECT_NOT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_HELP_REQUESTED);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "--help");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_HELP_REQUESTED);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Parsing: value-bearing options.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, string_option_accepts_every_spelling) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'o',
|
||
|
|
.long_name = "output",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
|
||
|
|
malunal_cstr_t spellings[][3] = {
|
||
|
|
{ "-o", "out.txt", null },
|
||
|
|
{ "-oout.txt", null, null },
|
||
|
|
{ "-o=out.txt", null, null },
|
||
|
|
{ "--output", "out.txt", null },
|
||
|
|
{ "--output=out.txt", null, null },
|
||
|
|
};
|
||
|
|
|
||
|
|
malunal_size_t index;
|
||
|
|
for (index = 0; index < sizeof(spellings) / sizeof(spellings[0]); index++) {
|
||
|
|
malunal_cstr_t argv[3] = { "prog", spellings[index][0], spellings[index][1] };
|
||
|
|
malunal_int32_t argc = spellings[index][1] != null ? 3 : 2;
|
||
|
|
|
||
|
|
error_t err = argparser_parse(&parser, argc, argv, &result);
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_string(&result, "output", &value).domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "out.txt"));
|
||
|
|
argresult_free(&result);
|
||
|
|
}
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, flag_cluster_ends_with_value_option) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t commit;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t message;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &commit);
|
||
|
|
argparser_add_option(commit, &ARGOPTION_FLAG(.short_name = 'a', .long_name = "all"));
|
||
|
|
argparser_add_option(commit, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'm',
|
||
|
|
.long_name = "message",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING,
|
||
|
|
.required = true
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "git", "commit", "-am", "fix bug");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
|
||
|
|
argresult_iptr_t sub = argresult_subresult(&result);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(sub, "all"));
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_string(sub, "message", &message).domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(message, "fix bug"));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, single_option_keeps_last_value) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'o',
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-o", "first", "-o", "second");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "o"), (malunal_size_t)1);
|
||
|
|
argresult_get_string(&result, "o", &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "second"));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, multiple_option_appends_values) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.short_name = 'I',
|
||
|
|
.long_name = "include",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING,
|
||
|
|
.multiple = true
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-Ifoo", "--include", "bar", "--include=baz");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "include"), (malunal_size_t)3);
|
||
|
|
|
||
|
|
argresult_get_string_at(&result, "include", 0, &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "foo"));
|
||
|
|
argresult_get_string_at(&result, "include", 1, &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "bar"));
|
||
|
|
argresult_get_string_at(&result, "include", 2, &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "baz"));
|
||
|
|
|
||
|
|
err = argresult_get_string_at(&result, "include", 3, &value);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_OUT_OF_RANGE);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, typed_options_convert_values) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
malunal_int64_t number = 0;
|
||
|
|
malunal_double_t floating = 0.0;
|
||
|
|
malunal_bool_t boolean = false;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "jobs", .value_type = ARGOPTION_TYPE_NUMBER));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "scale", .value_type = ARGOPTION_TYPE_FLOAT));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "color", .value_type = ARGOPTION_TYPE_BOOLEAN));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "--jobs", "-4", "--scale=2.5", "--color", "YES");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_number(&result, "jobs", &number).domain);
|
||
|
|
MICROTEST_EXPECT_EQ(number, (malunal_int64_t)-4);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_float(&result, "scale", &floating).domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(floating > 2.49 && floating < 2.51);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_boolean(&result, "color", &boolean).domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(boolean);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, invalid_values_fail) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "jobs", .value_type = ARGOPTION_TYPE_NUMBER));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "color", .value_type = ARGOPTION_TYPE_BOOLEAN));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "--jobs=abc");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_VALUE);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "--color=maybe");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_INVALID_VALUE);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, option_missing_value_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'o', .value_type = ARGOPTION_TYPE_STRING));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v'));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-o");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_MISSING_VALUE);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "-o", "-v");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_MISSING_VALUE);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, long_options_match_unique_prefixes) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "verbose"));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "version"));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.long_name = "quiet"));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "--q", "--verb");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "quiet"));
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "verbose"));
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "--ver");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_AMBIGUOUS_OPTION);
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, required_option_missing_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "jobs",
|
||
|
|
.value_type = ARGOPTION_TYPE_NUMBER,
|
||
|
|
.required = true
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_MISSING_REQUIRED_OPTION);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, defaults_apply_when_absent) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
malunal_int64_t jobs = 0;
|
||
|
|
strview_t mode;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(
|
||
|
|
.long_name = "jobs",
|
||
|
|
.value_type = ARGOPTION_TYPE_NUMBER,
|
||
|
|
.default_value = "8"
|
||
|
|
));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(
|
||
|
|
.long_name = "mode",
|
||
|
|
.value_type = ARGOPTION_TYPE_STRING,
|
||
|
|
.default_value = "debug"
|
||
|
|
));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_FALSE(argresult_has(&result, "jobs"));
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "jobs"), (malunal_size_t)1);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_number(&result, "jobs", &jobs).domain);
|
||
|
|
MICROTEST_EXPECT_EQ(jobs, (malunal_int64_t)8);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_get_string(&result, "mode", &mode).domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(mode, "debug"));
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
err = PARSE(&parser, &result, "prog", "--jobs", "2", "release");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
argresult_get_number(&result, "jobs", &jobs);
|
||
|
|
MICROTEST_EXPECT_EQ(jobs, (malunal_int64_t)2);
|
||
|
|
argresult_get_string(&result, "mode", &mode);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(mode, "release"));
|
||
|
|
argresult_free(&result);
|
||
|
|
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Parsing: positionals.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, positionals_fill_in_order_around_options) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t src;
|
||
|
|
strview_t dst;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v'));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "src", .value_type = ARGOPTION_TYPE_STRING, .required = true));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "dst", .value_type = ARGOPTION_TYPE_STRING, .required = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "a.txt", "-v", "b.txt");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "v"));
|
||
|
|
argresult_get_string(&result, "src", &src);
|
||
|
|
argresult_get_string(&result, "dst", &dst);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(src, "a.txt"));
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(dst, "b.txt"));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, multiple_positional_collects_remaining) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "cmd", .value_type = ARGOPTION_TYPE_STRING, .required = true));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "files", .value_type = ARGOPTION_TYPE_STRING, .multiple = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "cat", "a", "b", "c");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "files"), (malunal_size_t)3);
|
||
|
|
argresult_get_string_at(&result, "files", 2, &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "c"));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, required_positional_missing_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "files", .value_type = ARGOPTION_TYPE_STRING, .required = true, .multiple = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_MISSING_REQUIRED_POSITIONAL);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, extra_positional_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "only", .value_type = ARGOPTION_TYPE_STRING));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "one", "two");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_TOO_MANY_POSITIONALS);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, negative_numbers_are_values) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
malunal_double_t value = 0.0;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "n", .value_type = ARGOPTION_TYPE_FLOAT, .required = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-1.5");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
argresult_get_float(&result, "n", &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(value < -1.49 && value > -1.51);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, double_dash_ends_options) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v'));
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_POSITIONAL(.long_name = "file", .value_type = ARGOPTION_TYPE_STRING, .required = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog", "-v", "--", "-v");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(&result, "v"), (malunal_size_t)1);
|
||
|
|
argresult_get_string(&result, "file", &value);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(value, "-v"));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Parsing: subcommands.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, subcommand_is_dispatched_after_parent_options) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t push;
|
||
|
|
argparser_mptr_t commit;
|
||
|
|
argresult_t result;
|
||
|
|
strview_t remote;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'v'));
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &commit);
|
||
|
|
argparser_add_subcommand(&parser, "push", null, &push);
|
||
|
|
argparser_add_option(push, &ARGOPTION_FLAG(.short_name = 'f', .long_name = "force"));
|
||
|
|
argparser_add_option(push, &ARGOPTION_POSITIONAL(.long_name = "remote", .value_type = ARGOPTION_TYPE_STRING));
|
||
|
|
argparser_add_option(push, &ARGOPTION_POSITIONAL(.long_name = "refs", .value_type = ARGOPTION_TYPE_STRING, .multiple = true));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "git", "-v", "push", "--force", "origin", "main", "dev");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(&result, "v"));
|
||
|
|
MICROTEST_EXPECT_TRUE(strcmp(argresult_subcommand(&result), "push") == 0);
|
||
|
|
|
||
|
|
argresult_iptr_t sub = argresult_subresult(&result);
|
||
|
|
MICROTEST_EXPECT_NOT_NULL(sub);
|
||
|
|
MICROTEST_EXPECT_TRUE(argresult_has(sub, "force"));
|
||
|
|
argresult_get_string(sub, "remote", &remote);
|
||
|
|
MICROTEST_EXPECT_TRUE(view_is(remote, "origin"));
|
||
|
|
MICROTEST_EXPECT_EQ(argresult_count(sub, "refs"), (malunal_size_t)2);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, no_subcommand_leaves_subresult_null) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t commit;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &commit);
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "git");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_subcommand(&result));
|
||
|
|
MICROTEST_EXPECT_NULL(argresult_subresult(&result));
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, unknown_subcommand_fails) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t commit;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &commit);
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "git", "bogus");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNKNOWN_SUBCOMMAND);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST(argparser_parse, sibling_subcommands_do_not_share_options) {
|
||
|
|
argparser_t parser;
|
||
|
|
argparser_mptr_t commit;
|
||
|
|
argparser_mptr_t push;
|
||
|
|
argresult_t result;
|
||
|
|
argparser_init("git", null, null, &parser);
|
||
|
|
argparser_add_subcommand(&parser, "commit", null, &commit);
|
||
|
|
argparser_add_subcommand(&parser, "push", null, &push);
|
||
|
|
argparser_add_option(commit, &ARGOPTION_FLAG(.short_name = 'm', .value_type = ARGOPTION_TYPE_STRING));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "git", "push", "-m", "nope");
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNKNOWN_OPTION);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/* -------------------------------------------------------------------------
|
||
|
|
* Reading results.
|
||
|
|
* ---------------------------------------------------------------------- */
|
||
|
|
|
||
|
|
MICROTEST(argresult_get, reports_lookup_errors) {
|
||
|
|
argparser_t parser;
|
||
|
|
argresult_t result;
|
||
|
|
malunal_int64_t number = 0;
|
||
|
|
strview_t value;
|
||
|
|
argparser_init("prog", null, null, &parser);
|
||
|
|
argparser_add_option(&parser, &ARGOPTION_FLAG(.short_name = 'o', .value_type = ARGOPTION_TYPE_STRING));
|
||
|
|
|
||
|
|
error_t err = PARSE(&parser, &result, "prog");
|
||
|
|
MICROTEST_EXPECT_NULL(err.domain);
|
||
|
|
|
||
|
|
err = argresult_get_string(&result, "missing", &value);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_UNKNOWN_NAME);
|
||
|
|
err = argresult_get_number(&result, "o", &number);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_TYPE_MISMATCH);
|
||
|
|
err = argresult_get_string(&result, "o", &value);
|
||
|
|
MICROTEST_EXPECT_EQ(err.code, ARGPARSE_ERROR_NOT_PRESENT);
|
||
|
|
|
||
|
|
argresult_free(&result);
|
||
|
|
argparser_free(&parser);
|
||
|
|
}
|
||
|
|
|
||
|
|
MICROTEST_MAIN()
|