From 712879877179ace27c51eee0b21c0bf587e897bf Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 30 Dec 2023 16:22:16 +0100 Subject: [PATCH] [getopts/getopts_long] Use test cases generated from C code --- spec/getopts/getopts_long.spec | 16 +++---- spec/getopts/getopts_long_test.c | 71 +++++++++++++++++++++++++++++--- spec/getopts/getopts_long_test.h | 11 +++++ 3 files changed, 85 insertions(+), 13 deletions(-) diff --git a/spec/getopts/getopts_long.spec b/spec/getopts/getopts_long.spec index 5d14480..6b0e2ab 100644 --- a/spec/getopts/getopts_long.spec +++ b/spec/getopts/getopts_long.spec @@ -93,7 +93,7 @@ Describe 'getopts/getopts_long' The variable OPTIND_LONG should equal 2 End - It 'extracts a required argument in next ARGV' + It 'extracts required arguments in next ARGV' When call getopts_long 'required:' _opt --required foo The status should be success @@ -106,7 +106,7 @@ Describe 'getopts/getopts_long' End It 'extracts a required argument in next ARGV starting with -' - When call getopts_long 'required:' _opt '--required' '-not-an-option' + When call getopts_long 'required:' _opt --required -not-an-option The status should be success The stdout should equal '' @@ -118,7 +118,7 @@ Describe 'getopts/getopts_long' End It 'extracts a required argument in next ARGV starting with --' - When call getopts_long 'required:' _opt '--required' '--not-an-option' + When call getopts_long 'required:' _opt --required --not-an-option The status should be success The stdout should equal '' @@ -130,7 +130,7 @@ Describe 'getopts/getopts_long' End It 'extracts required arguments after =' - When call getopts_long 'required:' _opt --required=foo + When call getopts_long 'required:' _opt --required='foo' The status should be success The stdout should equal '' @@ -149,7 +149,7 @@ Describe 'getopts/getopts_long' The stderr should equal 'option '\''--required'\'' requires an argument' The variable _opt should equal '?' - The variable OPTARG_LONG should not be defined + The variable OPTARG_LONG should be undefined The variable OPTIND_LONG should equal 2 End @@ -190,7 +190,7 @@ Describe 'getopts/getopts_long' End It 'extracts optional arguments after =' - When call getopts_long 'optional?' _opt --optional=foo + When call getopts_long 'optional?' _opt --optional='foo' The status should be success The stdout should equal '' @@ -229,7 +229,7 @@ Describe 'getopts/getopts_long' It 'prints an error if an unexpected argument is passed for a flag option (after =)' OPTARG_LONG='something' - When call getopts_long 'flag' _opt --flag=2 + When call getopts_long 'flag' _opt --flag='2' The status should be success The stdout should equal '' @@ -241,7 +241,7 @@ Describe 'getopts/getopts_long' End It 'handles an unexpected argument passed after a flag= option (optstring leading :)' - When call getopts_long ':flag' _opt --flag=2 + When call getopts_long ':flag' _opt --flag='2' The status should be success The stdout should equal '' diff --git a/spec/getopts/getopts_long_test.c b/spec/getopts/getopts_long_test.c index 9485394..31cb9eb 100644 --- a/spec/getopts/getopts_long_test.c +++ b/spec/getopts/getopts_long_test.c @@ -8,9 +8,14 @@ #include #include #include +#include #define DEBUG 0 +#define PROGNAME "getopts_long_test" + +char *MYNAME; + struct test_data { const char *it; const char *optstring; @@ -20,7 +25,7 @@ struct test_data { int argc; }; #define NUM_STR_ARGS(...) (sizeof((char *[]){__VA_ARGS__})/sizeof(char *)) -#define TEST_ARGV(...) ((char *[]){__FILE__, __VA_ARGS__}), .argc = (NUM_STR_ARGS(__VA_ARGS__)+1) +#define TEST_ARGV(...) ((char *[]){PROGNAME, __VA_ARGS__}), .argc = (1+NUM_STR_ARGS(__VA_ARGS__)) struct test_data TESTS[] = { { @@ -253,7 +258,18 @@ void spec_test(struct test_data *test_data) { char *optarg_q = (res->optarg ? shquot(res->optarg) : NULL); char *stdout_q = shquot(res->stdout_data); - char *stderr_q = shquot(res->stderr_data); + + /* getopt_long(3) prefixes the error message with the executable name, + * strip it because the shell implementation won't do so either */ + char *stderr_q = NULL; + if (res->stderr_data == strstr(res->stderr_data, MYNAME) + && ':' == res->stderr_data[strlen(MYNAME)]) { + stderr_q = res->stderr_data + strlen(MYNAME) + 1; + while (' ' == *stderr_q) { ++stderr_q; } + stderr_q = shquot(stderr_q); + } else { + stderr_q = shquot(res->stderr_data); + } printf(" It %s\n", (example_name_q ? example_name_q : "''")); @@ -268,9 +284,49 @@ void spec_test(struct test_data *test_data) { printf(" When call getopts_long %s _opt", (optstring_q ? optstring_q : "''")); for (int i = 1; i < test_data->argc; ++i) { - char *s = shquot(test_data->argv[i]); - printf(" %s", s); - if (s) { free(s); } + char *p = NULL, *q = NULL, *r = NULL; + + const char *argstr = test_data->argv[i]; + + if (EMPTY_STRING(argstr)) { + printf(" ''"); + continue; + } + + /* skip over leading dashes and store in p */ + while ('-' == *argstr) { ++argstr; } + if (argstr > test_data->argv[i]) { + p = strndup(test_data->argv[i], (argstr - test_data->argv[i])); + } + + /* iterate over rest of argument string and check if there is something + * to quote */ + const char *c; + for (c = argstr; *c; ++c) { + if ('=' == *c) { + /* quote all (non-empty) values after a = */ + q = strndup(argstr, (c+1 - argstr)); + if (NUL != *(c+1)) { r = shquot(c+1); } + break; + } + if (!isalnum((int)*c) && '-' != *c && '_' != *c) { + /* quote all arguments containing non [[:alnum:]-_] characters + * completely */ + q = shquot(argstr); + break; + } + } + if (c > argstr && NUL == *c) { + /* no quoting was needed (we reached the end of string) */ + r = strdup(argstr); + } + + /* print result and free allocated memory */ + printf(" %s%s%s", (p ? p : ""), (q ? q : ""), (r ? r : "")); + + if (p) { free(p); } + if (q) { free(q); } + if (r) { free(r); } } printf("\n\n"); if (0 == status) { @@ -315,6 +371,11 @@ void spec_test(struct test_data *test_data) { #define MODE_SPEC 2 int main(int argc, char *argv[]) { + /* getopt_long(3) seems to take the basename of argv[0] for error messages. + * we store this value in MYNAME for later manipulation of output error + * messages. */ + MYNAME = filename(argv[0]); + int mode = MODE_RUN; if (1 < argc) { if (0 == strcasecmp(argv[1], "run")) { diff --git a/spec/getopts/getopts_long_test.h b/spec/getopts/getopts_long_test.h index eb84868..cb78f5b 100644 --- a/spec/getopts/getopts_long_test.h +++ b/spec/getopts/getopts_long_test.h @@ -12,6 +12,17 @@ // helpers +char *filename(const char *s) { + char *res = ""; + for (const char *c = s; *c; ++c) { + if ('/' == *c) { + res = (char *)c+1; + } + } + + return res; +} + char *shquot(const char *s) { if (NULL == s) { return NULL; }