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