#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; \ }) TEST_BEGIN(test_parse_long) { struct vars { long i; unsigned long u; const char *s; bool f; } vars; struct cli_opt options[] = { { .type = CLI_OT_INT, .lon = "signed", .value.i = &vars.i, }, { .type = CLI_OT_FLAG, .lon = "flag", .value.f = &vars.f, }, { .type = CLI_OT_UINT, .lon = "unsigned", .value.u = &vars.u, }, { .type = CLI_OT_STRING, .lon = "string", .value.s = &vars.s, }, {0}, }; struct tcase { int argc; char *argv[2]; struct vars expected_vars; enum cli_rc expected_rc; } cases[] = { { .argc = 2, .argv = { MUTSTR("--signed"), MUTSTR("-2"), }, .expected_vars = { .i = -2, .u = 0, .s = NULL, .f = 0, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("--flag"), MUTSTR("--unsigned=42"), }, .expected_vars = { .i = 0, .u = 0, .s = NULL, .f = true, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("--unsigned=42"), }, .expected_vars = { .i = 0, .u = 42, .s = NULL, .f = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 1, .argv = { MUTSTR("--signed=0xB00B"), }, .expected_vars = { .i = 0xB00B, .u = 0, .s = NULL, .f = false, }, .expected_rc = CLI_RC_OK, }, { .argc = 2, .argv = { MUTSTR("--string"), MUTSTR("wololo"), }, .expected_vars = { .i = 0, .u = 0, .s = "wololo", .f = false, }, .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++) { vars = (struct vars){0}; 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(vars.i, cases[i].expected_vars.i); asserteq(vars.u, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(vars.s, NULL); asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); } else { asserteq(vars.s, NULL); } asserteq(vars.f, cases[i].expected_vars.f); } TEST_OUT } TEST_END TEST_BEGIN(test_parse_short) { struct vars { long i; unsigned long u; const char *s; bool a; bool b; bool c; } vars; struct cli_opt options[] = { { .type = CLI_OT_INT, .shor = 'i', .value.i = &vars.i, }, { .type = CLI_OT_FLAG, .shor = 'a', .value.f = &vars.a, }, { .type = CLI_OT_FLAG, .shor = 'b', .value.f = &vars.b, }, { .type = CLI_OT_FLAG, .shor = 'c', .value.f = &vars.c, }, { .type = CLI_OT_UINT, .shor = 'u', .value.u = &vars.u, }, { .type = CLI_OT_STRING, .shor = 's', .value.s = &vars.s, }, {0}, }; struct tcase { int argc; char *argv[2]; struct vars expected_vars; enum cli_rc expected_rc; } cases[] = { { .argc = 2, .argv = { MUTSTR("-i-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++) { vars = (struct vars){0}; 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(vars.i, cases[i].expected_vars.i); asserteq(vars.u, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(vars.s, NULL); asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); } else { asserteq(vars.s, NULL); } asserteq(vars.a, cases[i].expected_vars.a); asserteq(vars.b, cases[i].expected_vars.b); asserteq(vars.c, cases[i].expected_vars.c); } TEST_OUT } TEST_END TEST_BEGIN(test_parse_options) { struct vars { long i; unsigned long u; const char *s; bool a; bool b; bool c; } vars; struct cli_opt options[] = { { .type = CLI_OT_INT, .lon = "signed", .shor = 's', .value.i = &vars.i, }, { .type = CLI_OT_FLAG, .lon = "flag-a", .shor = 'a', .value.f = &vars.a, }, { .type = CLI_OT_FLAG, .lon = "flag-b", .shor = 'b', .value.f = &vars.b, }, { .type = CLI_OT_FLAG, .lon = "flag-c", .shor = 'c', .value.f = &vars.c, }, { .type = CLI_OT_UINT, .lon = "unsigned", .shor = 'u', .value.u = &vars.u, }, { .type = CLI_OT_STRING, .lon = "string", .shor = 'S', .value.s = &vars.s, }, {0}, }; struct tcase { int argc; char *argv[8]; struct vars 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++) { vars = (struct vars){0}; 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(vars.i, cases[i].expected_vars.i); asserteq(vars.u, cases[i].expected_vars.u); if (cases[i].expected_vars.s) { assertneq(vars.s, NULL); asserteq(strcmp(vars.s, cases[i].expected_vars.s), 0); } else { asserteq(vars.s, NULL); } asserteq(vars.a, cases[i].expected_vars.a); asserteq(vars.b, cases[i].expected_vars.b); asserteq(vars.c, cases[i].expected_vars.c); } TEST_OUT } TEST_END RUN_TESTS(test_parse_long, test_parse_short, test_parse_options)