Skip to content

Commit

Permalink
[getopts/getopts_long] Use test cases generated from C code
Browse files Browse the repository at this point in the history
  • Loading branch information
sideeffect42 committed Dec 30, 2023
1 parent 4efaa45 commit 7128798
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 13 deletions.
16 changes: 8 additions & 8 deletions spec/getopts/getopts_long.spec
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 ''
Expand All @@ -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 ''
Expand All @@ -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 ''
Expand All @@ -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

Expand Down Expand Up @@ -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 ''
Expand Down Expand Up @@ -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 ''
Expand All @@ -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 ''
Expand Down
71 changes: 66 additions & 5 deletions spec/getopts/getopts_long_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@
#include <string.h>
#include <inttypes.h>
#include <unistd.h>
#include <ctype.h>

#define DEBUG 0

#define PROGNAME "getopts_long_test"

char *MYNAME;

struct test_data {
const char *it;
const char *optstring;
Expand All @@ -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[] = {
{
Expand Down Expand Up @@ -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 : "''"));

Expand All @@ -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) {
Expand Down Expand Up @@ -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")) {
Expand Down
11 changes: 11 additions & 0 deletions spec/getopts/getopts_long_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down

0 comments on commit 7128798

Please sign in to comment.