From 2cb3c3df4099297b0a0554bb482e2de04fe86b5c Mon Sep 17 00:00:00 2001 From: sanine Date: Wed, 24 Aug 2022 00:02:17 -0500 Subject: add command-line arguments --- libs/cargs/test/definitions.h | 9 + libs/cargs/test/main.c | 105 +++++++ libs/cargs/test/option_test.c | 665 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 779 insertions(+) create mode 100755 libs/cargs/test/definitions.h create mode 100755 libs/cargs/test/main.c create mode 100755 libs/cargs/test/option_test.c (limited to 'libs/cargs/test') diff --git a/libs/cargs/test/definitions.h b/libs/cargs/test/definitions.h new file mode 100755 index 0000000..cdb820e --- /dev/null +++ b/libs/cargs/test/definitions.h @@ -0,0 +1,9 @@ +#pragma once + +#include "tests.h" +/** + * Creates prototypes for all test functions. + */ +#define XX(u, t) int u##_##t(void); +UNIT_TESTS(XX) +#undef XX diff --git a/libs/cargs/test/main.c b/libs/cargs/test/main.c new file mode 100755 index 0000000..229cf80 --- /dev/null +++ b/libs/cargs/test/main.c @@ -0,0 +1,105 @@ +#include "definitions.h" +#include +#include +#include +#include +#include + +struct cag_test +{ + const char *unit_name; + const char *test_name; + const char *full_name; + int (*fn)(void); +}; + +static struct cag_test tests[] = { +#define XX(u, t) \ + {.unit_name = #u, .test_name = #t, .full_name = #u "/" #t, .fn = u##_##t}, + UNIT_TESTS(XX) +#undef XX +}; + +static int call_test(struct cag_test *test) +{ + size_t i; + + printf(" Running '%s' ", test->full_name); + for (i = strlen(test->full_name); i < 40; ++i) { + fputs(".", stdout); + } + + if (test->fn() == EXIT_FAILURE) { + fputs(" FAILURE\n", stdout); + return EXIT_FAILURE; + } + + fputs(" SUCCESS\n", stdout); + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) +{ + size_t i, count, failed, succeeded; + const char *requested_unit_name, *requested_test_name; + struct cag_test *test; + double rate; + + count = 0; + failed = 0; + if (argc < 2) { + fputs("No unit specified. Running all tests.\n\n", stdout); + for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) { + test = &tests[i]; + ++count; + if (call_test(test) == EXIT_FAILURE) { + ++failed; + } + } + } else if (argc < 3) { + requested_unit_name = argv[1]; + printf("Running all unit tests of '%s'.\n\n", requested_unit_name); + for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) { + test = &tests[i]; + if (strcmp(test->unit_name, requested_unit_name) == 0) { + ++count; + if (call_test(test) == EXIT_FAILURE) { + ++failed; + } + } + } + } else { + requested_unit_name = argv[1]; + requested_test_name = argv[2]; + printf("Running a single test '%s/%s'.\n\n", requested_unit_name, + requested_test_name); + for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) { + test = &tests[i]; + if (strcmp(test->unit_name, requested_unit_name) == 0 && + strcmp(test->test_name, requested_test_name) == 0) { + ++count; + if (call_test(test) == EXIT_FAILURE) { + ++failed; + } + } + } + } + + if (count == 1) { + fputs("\nThe test has been executed.\n", stdout); + } else if (count > 0) { + succeeded = count - failed; + rate = (double)succeeded / (double)count * 100; + printf("\n%zu/%zu (%.2f%%) of those tests succeeded.\n", succeeded, count, + rate); + } else { + printf("\nNo tests found.\n"); + return EXIT_FAILURE; + } + + if (failed > 0) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/libs/cargs/test/option_test.c b/libs/cargs/test/option_test.c new file mode 100755 index 0000000..c2dfff4 --- /dev/null +++ b/libs/cargs/test/option_test.c @@ -0,0 +1,665 @@ +#include "definitions.h" +#include +#include +#include +#include +#include +#include +#include + +static struct cag_option options[] = { + + {.identifier = 's', + .access_letters = "s", + .access_name = NULL, + .value_name = NULL, + .description = "Simple flag"}, + + {.identifier = 'a', + .access_letters = "a", + .access_name = NULL, + .value_name = NULL, + .description = "Another simple flag"}, + + {.identifier = 'm', + .access_letters = "mMoO", + .access_name = NULL, + .value_name = NULL, + .description = "Multiple access letters"}, + + {.identifier = 'l', + .access_letters = NULL, + .access_name = "long", + .value_name = NULL, + .description = "Long parameter name"}, + + {.identifier = 'k', + .access_letters = "k", + .access_name = "key", + .value_name = "VALUE", + .description = "Parameter value"}}; + +struct cag_result +{ + bool simple; + bool another; + bool multi_access; + bool long_parameter; + bool value_parameter; + bool unknown; + bool def; + const char *value; +}; + +static struct cag_result result; +static char **argv; +static int argc; + +static int make_args(const char *str) +{ + const char *c; + int argIndex, argStart, argLength; + + argc = 0; + c = str; + do { + if (*c == ' ' || *c == '\0') { + ++argc; + } + } while (*(c++)); + + argv = malloc(sizeof(char *) * (argc + 1)); + if (argv == NULL) { + return 1; + } + + c = str; + argIndex = 0; + argStart = 0; + argLength = 0; + do { + if (*c == ' ' || *c == '\0') { + argv[argIndex] = malloc(argLength + 1); + memcpy(argv[argIndex], &str[argStart], argLength); + argv[argIndex][argLength] = '\0'; + ++argIndex; + argStart += argLength + 1; + argLength = 0; + } else { + ++argLength; + } + + } while (*(c++)); + + argv[argc] = NULL; + + return 0; +} + +static void destroy_args() +{ + int i; + + for (i = 0; i < argc; ++i) { + free(argv[i]); + } + + free(argv); +} + +static int option_test_run(int currentArgc, char *currentArgv[]) +{ + int index; + char identifier; + cag_option_context context; + + cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), currentArgc, + currentArgv); + + memset(&result, 0, sizeof(result)); + + while (cag_option_fetch(&context)) { + identifier = cag_option_get(&context); + switch (identifier) { + case 's': + result.simple = true; + break; + case 'a': + result.another = true; + break; + case 'm': + result.multi_access = true; + break; + case 'l': + result.long_parameter = true; + break; + case 'k': + result.value_parameter = true; + result.value = cag_option_get_value(&context); + break; + case '?': + result.unknown = true; + break; + default: + result.def = true; + break; + } + } + + index = cag_option_get_index(&context); + if (cag_option_fetch(&context) != false) { + return -1; + } + + if (cag_option_get_index(&context) != index) { + return -1; + } + + return cag_option_get_index(&context); +} + +int option_complex(void) +{ + int status; + + status = make_args("test file1 -s -- -a -- a file"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (!result.simple || result.another || result.multi_access || + result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_mixed(void) +{ + int status, i; + + const char *values[] = {"file1", "file2", "mixed", "file3", "--", "-m", + "parameters", "file4"}; + + status = make_args( + "test -s file1 -k=value file2 -a mixed file3 -- -m parameters file4"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0 || argc - status != 8) { + goto err_wrong; + } + + for (i = 0; i < (int)CAG_ARRAY_SIZE(values); ++i) { + if (strcmp(argv[status + i], values[i]) != 0) { + goto err_wrong; + } + } + + if (!result.simple || !result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value == NULL || + strcmp(result.value, "value") != 0) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_ending(void) +{ + int status; + + status = make_args("test -s -- -a"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (!result.simple || result.another || result.multi_access || + result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_long_missing_value(void) +{ + int status; + + status = make_args("test --key"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_short_missing_value(void) +{ + int status; + + status = make_args("test -k"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_long_space_value(void) +{ + int status; + + status = make_args("test --key super_value"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value == NULL || + strcmp(result.value, "super_value") != 0) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_short_space_value(void) +{ + int status; + + status = make_args("test -k test_value"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value == NULL || + strcmp(result.value, "test_value") != 0) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_long_equal_value(void) +{ + int status; + + status = make_args("test --key=super_value"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value == NULL || + strcmp(result.value, "super_value") != 0) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_short_equal_value(void) +{ + int status; + + status = make_args("test -k=test_value"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || !result.value_parameter || result.unknown || + result.def || result.value == NULL || + strcmp(result.value, "test_value") != 0) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_combined(void) +{ + int status; + + status = make_args("test -sma"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (!result.simple || !result.another || !result.multi_access || + result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_unknown_long(void) +{ + int status; + + status = make_args("test --unknown"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || result.value_parameter || !result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_unknown_short(void) +{ + int status; + + status = make_args("test -u"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + result.long_parameter || result.value_parameter || !result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_alias(void) +{ + int status; + + status = make_args("test -O"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || !result.multi_access || + result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_simple_long(void) +{ + int status; + + status = make_args("test --long"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (result.simple || result.another || result.multi_access || + !result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_simple(void) +{ + int status; + + status = make_args("test -s"); + if (status != 0) { + goto err_setup; + } + + status = option_test_run(argc, argv); + if (status < 0) { + goto err_wrong; + } + + if (!result.simple || result.another || result.multi_access || + result.long_parameter || result.value_parameter || result.unknown || + result.def || result.value != NULL) { + goto err_wrong; + } + + destroy_args(); + + return EXIT_SUCCESS; + +err_wrong: + destroy_args(); +err_setup: + return EXIT_FAILURE; +} + +int option_print(void) +{ + char buf[255]; + const char *expected; + FILE *test_file; + + expected = " -s Simple flag\n" + " -a Another simple flag\n" + " -m, -M, -o, -O Multiple access letters\n" + " --long Long parameter name\n" + " -k, --key=VALUE Parameter value\n"; + + test_file = tmpfile(); + if (test_file == NULL) { + goto err_open; + } + + cag_option_print(options, CAG_ARRAY_SIZE(options), test_file); + if (fseek(test_file, 0, SEEK_SET) != 0) { + goto err_seek; + } + + if (fread(buf, sizeof(buf), 1, test_file) != 1 && feof(test_file) == 0) { + goto err_read; + } + + if (memcmp(buf, expected, strlen(expected)) != 0) { + goto err_test; + } + + fclose(test_file); + return EXIT_SUCCESS; + +err_test: +err_read: +err_seek: + fclose(test_file); +err_open: + return EXIT_FAILURE; +} -- cgit v1.2.1