1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
4  *
5  * Portions of these tests were inspired by glibc's posix/bug-getopt1.c and
6  * posix/tst-getopt-cancel.c
7  */
8 
9 #include <getopt.h>
10 #include <test/lib.h>
11 #include <test/test.h>
12 #include <test/ut.h>
13 
do_test_getopt(struct unit_test_state * uts,int line,struct getopt_state * gs,const char * optstring,int args,char * argv[],int expected_count,int expected[])14 static int do_test_getopt(struct unit_test_state *uts, int line,
15 			  struct getopt_state *gs, const char *optstring,
16 			  int args, char *argv[], int expected_count,
17 			  int expected[])
18 {
19 	int opt;
20 
21 	getopt_init_state(gs);
22 	for (int i = 0; i < expected_count; i++) {
23 		opt = getopt_silent(gs, args, argv, optstring);
24 		if (expected[i] != opt) {
25 			/*
26 			 * Fudge the line number so we can tell which test
27 			 * failed
28 			 */
29 			ut_failf(uts, __FILE__, line, __func__,
30 				 "expected[i] == getopt()",
31 				 "Expected '%c' (%d) with i=%d, got '%c' (%d)",
32 				 expected[i], expected[i], i, opt, opt);
33 			return CMD_RET_FAILURE;
34 		}
35 	}
36 
37 	opt = getopt_silent(gs, args, argv, optstring);
38 	if (opt != -1) {
39 		ut_failf(uts, __FILE__, line, __func__,
40 			 "getopt() != -1",
41 			 "Expected -1, got '%c' (%d)", opt, opt);
42 		return CMD_RET_FAILURE;
43 	}
44 
45 	return 0;
46 }
47 
48 #define test_getopt(optstring, argv, expected) do { \
49 	int ret = do_test_getopt(uts, __LINE__, &gs, optstring, \
50 				 ARRAY_SIZE(argv) - 1, argv, \
51 				 ARRAY_SIZE(expected), expected); \
52 	if (ret) \
53 		return ret; \
54 } while (0)
55 
lib_test_getopt(struct unit_test_state * uts)56 static int lib_test_getopt(struct unit_test_state *uts)
57 {
58 	struct getopt_state gs;
59 
60 	/* Happy path */
61 	test_getopt("ab:c",
62 		    ((char *[]){ "program", "-cb", "x", "-a", "foo", 0 }),
63 		    ((int []){ 'c', 'b', 'a' }));
64 	ut_asserteq(4, gs.index);
65 
66 	/* Make sure we pick up the optional argument */
67 	test_getopt("a::b:c",
68 		    ((char *[]){ "program", "-cbx", "-a", "foo", 0 }),
69 		    ((int []){ 'c', 'b', 'a' }));
70 	ut_asserteq(4, gs.index);
71 
72 	/* Test required arguments */
73 	test_getopt("a:b", ((char *[]){ "program", "-a", 0 }),
74 		    ((int []){ ':' }));
75 	ut_asserteq('a', gs.opt);
76 	test_getopt("a:b", ((char *[]){ "program", "-b", "-a", 0 }),
77 		    ((int []){ 'b', ':' }));
78 	ut_asserteq('a', gs.opt);
79 
80 	/* Test invalid arguments */
81 	test_getopt("ab:c", ((char *[]){ "program", "-d", 0 }),
82 		    ((int []){ '?' }));
83 	ut_asserteq('d', gs.opt);
84 
85 	/* Test arg */
86 	test_getopt("a::b:c",
87 		    ((char *[]){ "program", "-a", 0 }),
88 		    ((int []){ 'a' }));
89 	ut_asserteq(2, gs.index);
90 	ut_assertnull(gs.arg);
91 
92 	test_getopt("a::b:c",
93 		    ((char *[]){ "program", "-afoo", 0 }),
94 		    ((int []){ 'a' }));
95 	ut_asserteq(2, gs.index);
96 	ut_assertnonnull(gs.arg);
97 	ut_asserteq_str("foo", gs.arg);
98 
99 	test_getopt("a::b:c",
100 		    ((char *[]){ "program", "-a", "foo", 0 }),
101 		    ((int []){ 'a' }));
102 	ut_asserteq(3, gs.index);
103 	ut_assertnonnull(gs.arg);
104 	ut_asserteq_str("foo", gs.arg);
105 
106 	test_getopt("a::b:c",
107 		    ((char *[]){ "program", "-bfoo", 0 }),
108 		    ((int []){ 'b' }));
109 	ut_asserteq(2, gs.index);
110 	ut_assertnonnull(gs.arg);
111 	ut_asserteq_str("foo", gs.arg);
112 
113 	test_getopt("a::b:c",
114 		    ((char *[]){ "program", "-b", "foo", 0 }),
115 		    ((int []){ 'b' }));
116 	ut_asserteq(3, gs.index);
117 	ut_assertnonnull(gs.arg);
118 	ut_asserteq_str("foo", gs.arg);
119 
120 	return 0;
121 }
122 LIB_TEST(lib_test_getopt, 0);
123