#include "cli.h" #include "../utest/utest.h" #include "../utils.h" /** * MUTSTR - hack to "force" string literals to be mutable. * @literal: a C string literal. */ #define MUTSTR(literal) \ ({ \ static char _s[] = literal; \ _s; \ }) CLI_OPT_LONG(sopt, 's', "signed", NULL); CLI_OPT_ULONG(uopt, 'u', "unsigned", NULL); CLI_OPT_STRING(stropt, 'S', "string", NULL); CLI_OPT_FLAG(aopt, 'a', "flag-a", NULL); CLI_OPT_FLAG(bopt, 'b', "flag-b", NULL); CLI_OPT_FLAG(copt, 'c', "flag-c", NULL); struct expected { long i; unsigned long u; const char *s; bool a; bool b; bool c; }; struct cli_opt *options[] = { &sopt.opt, &uopt.opt, &stropt.opt, &aopt.opt, &bopt.opt, &copt.opt, NULL, }; static void reset_opts(void) { sopt.value = 0; uopt.value = 0; stropt.value = NULL; aopt.value = 0; bopt.value = 0; copt.value = 0; } TEST_BEGIN(test_parse_long) { struct tcase { int argc; char *argv[2]; struct expected expected_vars; enum cli_rc expected_rc; } cases[] = { { .argc = 2, .argv = { MUTSTR("--signed"), MUTSTR("-2"), }, .expected_vars = { .i = -2, .u = 0, .s = NULL, .a = false, 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("--flag-a"), MUTSTR("--unsigned=42"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .a = true, 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("--unsigned=42"), }, .expected_vars = { .i = 0, .u = 42, .s = NULL, .a = false, 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("--signed=0xB00B"), }, .expected_vars = { .i = 0xB00B, .u = 0, .s = NULL, .a = false, 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("--string"), MUTSTR("wololo"), }, .expected_vars = { .i = 0, .u = 0, .s = "wololo", .a = false, 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("--poop"), }, .expected_vars = {0}, .expected_rc = CLI_RC_BAD_ARGS, }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, .argv = cases[i].argv, }; int rc = parse_long(&ctx); asserteq(rc, cases[i].expected_rc); asserteq(sopt.value, cases[i].expected_vars.i); asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(stropt.value, NULL); asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { asserteq(stropt.value, NULL); } asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } TEST_END TEST_BEGIN(test_parse_short) { struct tcase { int argc; char *argv[2]; struct expected expected_vars; enum cli_rc expected_rc; } cases[] = { { .argc = 2, .argv = { MUTSTR("-s-2"), MUTSTR("-b"), }, .expected_vars = { .i = -2, .u = 0, .s = NULL, .a = false, .b = false, .c = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("-cab"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .a = true, .b = true, .c = true, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("-Sab"), }, .expected_vars = { .i = 0, .u = 0, .s = "ab", .a = false, .b = false, .c = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("-abu"), MUTSTR("24"), }, .expected_vars = { .i = 0, .u = 24, .s = NULL, .a = true, .b = true, .c = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("-ebu"), MUTSTR("24"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .a = false, .b = false, .c = false, }, .expected_rc = CLI_RC_BAD_ARGS, }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, .argv = cases[i].argv, }; int rc = parse_short(&ctx); asserteq(rc, cases[i].expected_rc); asserteq(sopt.value, cases[i].expected_vars.i); asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(stropt.value, NULL); asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { asserteq(stropt.value, NULL); } asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } TEST_END TEST_BEGIN(test_parse_options) { struct tcase { int argc; char *argv[8]; struct expected expected_vars; enum cli_rc expected_rc; } cases[] = { { .argc = 5, .argv = { MUTSTR("-s69"), MUTSTR("--unsigned"), MUTSTR("0xBEEF"), MUTSTR("-Shore"), MUTSTR("-bac"), }, .expected_vars = { .i = 69, .u = 0xBEEF, .s = "hore", .a = true, .b = true, .c = true, }, .expected_rc = CLI_RC_OK, }, { .argc = 6, .argv = { MUTSTR("-c"), MUTSTR("--unsigned=0777"), MUTSTR("--signed"), MUTSTR("-96"), MUTSTR("--string=bebop"), MUTSTR("--flag-b"), }, .expected_vars = { .i = -96, .u = 0777, .s = "bebop", .a = false, .b = true, .c = true, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("-aStronomical"), }, .expected_vars = { .i = 0, .u = 0, .s = "tronomical", .a = true, .b = false, .c = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 4, .argv = { MUTSTR("-cab"), MUTSTR("--"), MUTSTR("--string"), MUTSTR("hello"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .a = true, .b = true, .c = true, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("-cab"), MUTSTR("--string"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .a = true, .b = true, .c = true, }, .expected_rc = CLI_RC_BAD_ARGS, }, { .argc = 3, .argv = { MUTSTR("-s86"), MUTSTR("--beef"), MUTSTR("steak"), }, .expected_vars = { .i = 86, .u = 0, .s = NULL, .a = false, .b = false, .c = false, }, .expected_rc = CLI_RC_BAD_ARGS, }, }; for (size_t i = 0; i < ARRAY_SIZE(cases); i++) { reset_opts(); struct cli_ctx ctx = { .opts = options, .argc = cases[i].argc, .argv = cases[i].argv, }; int rc = parse_options(&ctx); asserteq(rc, cases[i].expected_rc); asserteq(sopt.value, cases[i].expected_vars.i); asserteq(uopt.value, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(stropt.value, NULL); asserteq(strcmp(stropt.value, cases[i].expected_vars.s), 0); } else { asserteq(stropt.value, NULL); } asserteq(aopt.value, cases[i].expected_vars.a); } TEST_OUT } TEST_END RUN_TESTS(test_parse_long, test_parse_short, test_parse_options)