diff options
| author | Lion Kortlepel <[email protected]> | 2026-01-25 16:22:45 +0000 |
|---|---|---|
| committer | Lion Kortlepel <[email protected]> | 2026-01-25 16:22:45 +0000 |
| commit | c781f2c7a4a41fad1a7e5e66ebb258c1fc8a415a (patch) | |
| tree | f56f9198b5edf96269c3fc1d13726c4f7bcc0a80 | |
| parent | 4ad26f81a127d7be032bbe53d8141d6bc6f20e40 (diff) | |
| download | args-c781f2c7a4a41fad1a7e5e66ebb258c1fc8a415a.tar.zst args-c781f2c7a4a41fad1a7e5e66ebb258c1fc8a415a.zip | |
feat: implement required arguments, add tests
bump ls_test to 1.3 for ASSERT_STR_{EQ, NEQ}
| -rw-r--r-- | ls_args.h | 20 | ||||
| -rw-r--r-- | tests/ls_test.h | 36 | ||||
| -rw-r--r-- | tests/tests.c | 43 |
3 files changed, 88 insertions, 11 deletions
@@ -103,6 +103,7 @@ typedef struct ls_args_arg { ls_args_type type; void* val_ptr; ls_args_mode mode; + int found; } ls_args_arg; typedef struct ls_args { @@ -325,6 +326,7 @@ end: } void _lsa_apply(ls_args_arg* arg, ls_args_arg** prev_arg) { + arg->found = 1; switch (arg->type) { case LS_ARGS_TYPE_BOOL: *(int*)arg->val_ptr = 1; @@ -403,6 +405,11 @@ int ls_args_parse(ls_args* a, int argc, char** argv) { ls_args_arg* prev_arg = NULL; assert(a != NULL); assert(argv != NULL); + a->last_error = "Success"; + /* set all args to not found in case this is called multiple times */ + for (i = 0; i < (int)a->args_len; ++i) { + a->args[i].found = 0; + } for (i = 1; i < argc; ++i) { _lsa_parsed parsed = _lsa_parse(argv[i]); if (prev_arg) { @@ -462,6 +469,19 @@ int ls_args_parse(ls_args* a, int argc, char** argv) { a->last_error = a->_allocated_error; return 0; } + + for (i = 0; i < (int)a->args_len; ++i) { + if (a->args[i].mode == LS_ARGS_REQUIRED && !a->args[i].found) { + const size_t len = 64 + strlen(a->args[i].long_opt); + a->_allocated_error = LS_REALLOC(a->_allocated_error, len); + memset(a->_allocated_error, 0, len); + sprintf(a->_allocated_error, "Required argument '--%s' not found", + a->args[i].long_opt); + a->last_error = a->_allocated_error; + return 0; + } + } + return 1; } diff --git a/tests/ls_test.h b/tests/ls_test.h index 31d87f8..2077477 100644 --- a/tests/ls_test.h +++ b/tests/ls_test.h @@ -1,6 +1,6 @@ /* Lion's Standard (LS) test harness. * - * Version: 1.2 + * Version: 1.3 * Website: https://libls.org * Repo: https://github.com/libls/test * SPDX-License-Identifier: MIT @@ -20,6 +20,8 @@ * you use asserts other than `ASSERT` (e.g. ASSERT_EQ), and the constructor * attribute __attribute__((destructor)) for automatic test registration. * + * Supports string comparisons using ASSERT_STR_EQ and ASSERT_STR_NEQ. + * * ==== 2. HOW TO USE ==== * * 1. Copy this file into your project and include it: @@ -119,6 +121,38 @@ } \ } while (0) +#define ASSERT_STR_EQ(a, b) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (strcmp(_a, _b) != 0) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s == %s (actual: \"%s\" != \"%s\") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + } while (0) + +#define ASSERT_STR_NEQ(a, b) \ + do { \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + if (strcmp(_a, _b) == 0) { \ + const char* _func = __func__; \ + if (strncmp(_func, "lst_t_", 6) == 0) \ + _func += 6; \ + fprintf(stderr, \ + "%s: FAILED: %s != %s (actual: \"%s\" == \"%s\") (%s:%d)\n", \ + _func, #a, #b, _a, _b, __FILE__, __LINE__); \ + ++lst_fail; \ + return 1; \ + } \ + } while (0) + #define ASSERT_NEQ(a, b, fmt) \ do { \ __typeof__(a) _a = (a); \ diff --git a/tests/tests.c b/tests/tests.c index 8b22e06..74ddb3e 100644 --- a/tests/tests.c +++ b/tests/tests.c @@ -42,6 +42,29 @@ TEST_CASE(basic_args) { return 0; } +TEST_CASE(basic_args_required) { + int help = 0; + int test = 0; + ls_args args; + char* argv[] = { "./hello", "-h", NULL }; + int argc = sizeof(argv) / sizeof(*argv) - 1; + + ls_args_init(&args); + ls_args_bool(&args, &help, "h", "help", "Provides help", 0); + ls_args_bool(&args, &test, "t", "test", "A test argument", LS_ARGS_REQUIRED); + ASSERT(!ls_args_parse(&args, argc, argv)); + ASSERT_STR_EQ(args.last_error, "Required argument '--test' not found"); + + char* argv2[] = { "./hello", "-h", "-t", NULL }; + int argc2 = sizeof(argv2) / sizeof(*argv2) - 1; + int ret = ls_args_parse(&args, argc2, argv2); + ASSERT_STR_EQ(args.last_error, "Success"); + + ls_args_free(&args); + return 0; +} + + TEST_CASE(basic_args_only_short) { int help = 0; int test = 0; @@ -120,7 +143,7 @@ TEST_CASE(error_invalid_argument) { ls_args_init(&args); ls_args_bool(&args, &help, "h", "help", "Provides help", 0); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Invalid argument '--test'") == 0); + ASSERT_STR_EQ(args.last_error, "Invalid argument '--test'"); ls_args_free(&args); return 0; } @@ -136,7 +159,7 @@ TEST_CASE(error_expected_argument) { ls_args_bool(&args, &help, "h", "help", "Provides help", 0); ls_args_string(&args, &file, "f", "file", "File to work on", 0); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Expected argument following '--file'") == 0); + ASSERT_STR_EQ(args.last_error, "Expected argument following '--file'"); ls_args_free(&args); return 0; } @@ -150,7 +173,7 @@ TEST_CASE(error_expected_argument_last_arg) { ls_args_init(&args); ls_args_string(&args, &file, "f", "file", "File to work on", 0); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Expected argument following '--file'") == 0); + ASSERT_STR_EQ(args.last_error, "Expected argument following '--file'"); ls_args_free(&args); return 0; } @@ -166,7 +189,7 @@ TEST_CASE(error_expected_argument_short_combined) { ls_args_bool(&args, &help, "h", "help", "Provides help", 0); ls_args_string(&args, &file, "f", "file", "File to work on", 0); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Expected argument following '-f', instead got another argument '-h'") == 0); + ASSERT_STR_EQ(args.last_error, "Expected argument following '-f', instead got another argument '-h'"); ls_args_free(&args); return 0; } @@ -179,7 +202,7 @@ TEST_CASE(error_parse_fail) { ls_args_init(&args); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Invalid argument '-'") == 0); + ASSERT_STR_EQ(args.last_error, "Invalid argument '-'"); ls_args_free(&args); return 0; } @@ -245,7 +268,7 @@ TEST_CASE(alloc_fail) { ret = ls_args_bool(&args, &help, "h", "help", "Provides help", 0); fail_alloc = 0; ASSERT(!ret); - ASSERT(strcmp(args.last_error, "Allocation failure") == 0); + ASSERT_STR_EQ(args.last_error, "Allocation failure"); /* there is no documented error state for this; we simply fail to add the * argument? */ ASSERT_EQ(args.args_len, 0, "%uz"); @@ -261,7 +284,7 @@ TEST_CASE(error_parse_fail_empty) { ls_args_init(&args); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Invalid argument ''") == 0); + ASSERT_STR_EQ(args.last_error, "Invalid argument ''"); ls_args_free(&args); return 0; } @@ -287,7 +310,7 @@ TEST_CASE(error_invalid_argument_short) { ls_args_init(&args); ls_args_bool(&args, &help, "h", "help", "Provides help", 0); ASSERT(!ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(args.last_error, "Invalid argument '-t'") == 0); + ASSERT_STR_EQ(args.last_error, "Invalid argument '-t'"); ls_args_free(&args); return 0; } @@ -306,8 +329,8 @@ TEST_CASE(string_args) { ls_args_string(&args, &output, "o", "output", "Output file path", 0); ls_args_bool(&args, &verbose, "v", "verbose", "Verbose output", 0); ASSERT(ls_args_parse(&args, argc, argv)); - ASSERT(strcmp(input, "file.txt") == 0); - ASSERT(strcmp(output, "output.txt") == 0); + ASSERT_STR_EQ(input, "file.txt"); + ASSERT_STR_EQ(output, "output.txt"); ASSERT_EQ(verbose, 1, "%d"); ls_args_free(&args); return 0; |
