1 /*
2 * Copyright (c) 2021 Nordic Semiconductor
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /** @file
8 * @brief Interactive getopt test suite
9 *
10 */
11
12 #include <getopt.h>
13 #include <string.h>
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/posix/unistd.h>
17 #include <zephyr/ztest.h>
18
ZTEST(posix_c_lib_ext,test_getopt_basic)19 ZTEST(posix_c_lib_ext, test_getopt_basic)
20 {
21 static const char *const nargv[] = {
22 "cmd_name", "-b", "-a", "-h", "-c", "-l", "-h", "-a", "-i", "-w",
23 };
24 static const char *accepted_opt = "abchw";
25 static const char *expected = "bahc?ha?w";
26 size_t argc = ARRAY_SIZE(nargv);
27 int cnt = 0;
28 int c;
29 char **argv;
30
31 argv = (char **)nargv;
32
33 /* Get state of the current thread */
34 getopt_init();
35
36 do {
37 c = getopt(argc, argv, accepted_opt);
38 if (cnt >= strlen(expected)) {
39 break;
40 }
41
42 zassert_equal(c, expected[cnt++], "unexpected opt character");
43 } while (c != -1);
44
45 c = getopt(argc, argv, accepted_opt);
46 zassert_equal(c, -1, "unexpected opt character");
47 }
48
49 enum getopt_idx {
50 GETOPT_IDX_CMD_NAME,
51 GETOPT_IDX_OPTION1,
52 GETOPT_IDX_OPTION2,
53 GETOPT_IDX_OPTARG
54 };
55
ZTEST(posix_c_lib_ext,test_getopt)56 ZTEST(posix_c_lib_ext, test_getopt)
57 {
58 struct getopt_state *state;
59 static const char *test_opts = "ac:";
60 static const char *const nargv[] = {
61 [GETOPT_IDX_CMD_NAME] = "cmd_name",
62 [GETOPT_IDX_OPTION1] = "-a",
63 [GETOPT_IDX_OPTION2] = "-c",
64 [GETOPT_IDX_OPTARG] = "foo",
65 };
66 int argc = ARRAY_SIZE(nargv);
67 char **argv;
68 int c;
69
70 /* Get state of the current thread */
71 getopt_init();
72
73 argv = (char **)nargv;
74
75 /* Test uknown option */
76
77 c = getopt(argc, argv, test_opts);
78 zassert_equal(c, 'a', "unexpected opt character");
79 c = getopt(argc, argv, test_opts);
80 zassert_equal(c, 'c', "unexpected opt character");
81
82 c = getopt(argc, argv, test_opts);
83 state = getopt_state_get();
84
85 /* Thread safe usge: */
86 zassert_equal(0, strcmp(argv[GETOPT_IDX_OPTARG], state->optarg),
87 "unexpected optarg result");
88 /* Non thread safe usage: */
89 zassert_equal(0, strcmp(argv[GETOPT_IDX_OPTARG], optarg), "unexpected optarg result");
90 }
91
92 enum getopt_long_idx {
93 GETOPT_LONG_IDX_CMD_NAME,
94 GETOPT_LONG_IDX_VERBOSE,
95 GETOPT_LONG_IDX_OPT,
96 GETOPT_LONG_IDX_OPTARG
97 };
98
ZTEST(posix_c_lib_ext,test_getopt_long)99 ZTEST(posix_c_lib_ext, test_getopt_long)
100 {
101 /* Below test is based on example
102 * https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
103 */
104 struct getopt_state *state;
105 int verbose_flag = 0;
106 /* getopt_long stores the option index here. */
107 int option_index = 0;
108 char **argv;
109 int c;
110 struct option long_options[] = {
111 /* These options set a flag. */
112 {"verbose", no_argument, &verbose_flag, 1},
113 {"brief", no_argument, &verbose_flag, 0},
114 /* These options don’t set a flag.
115 * We distinguish them by their indices.
116 */
117 {"add", no_argument, 0, 'a'},
118 {"create", required_argument, 0, 'c'},
119 {"delete", required_argument, 0, 'd'},
120 {"long", required_argument, 0, 'e'},
121 {0, 0, 0, 0},
122 };
123 static const char *accepted_opt = "ac:d:e:";
124
125 static const char *const argv1[] = {
126 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
127 [GETOPT_LONG_IDX_VERBOSE] = "--verbose",
128 [GETOPT_LONG_IDX_OPT] = "--create",
129 [GETOPT_LONG_IDX_OPTARG] = "some_file",
130 };
131 int argc1 = ARRAY_SIZE(argv1);
132
133 static const char *const argv2[] = {
134 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
135 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
136 [GETOPT_LONG_IDX_OPT] = "-d",
137 [GETOPT_LONG_IDX_OPTARG] = "other_file",
138 };
139 int argc2 = ARRAY_SIZE(argv2);
140
141 static const char *const argv3[] = {
142 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
143 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
144 [GETOPT_LONG_IDX_OPT] = "-a",
145 };
146 int argc3 = ARRAY_SIZE(argv3);
147
148 /* this test distinguish getopt_long and getopt_long_only functions */
149 static const char *const argv4[] = {
150 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
151 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
152 /* below should not be interpreted as "--long/-e" option */
153 [GETOPT_LONG_IDX_OPT] = "-l",
154 [GETOPT_LONG_IDX_OPTARG] = "long_argument",
155 };
156 int argc4 = ARRAY_SIZE(argv4);
157
158 /* Test scenario 1 */
159 /* Get state of the current thread */
160 getopt_init();
161 argv = (char **)argv1;
162 c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
163 zassert_equal(verbose_flag, 1, "verbose flag expected");
164 c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
165 state = getopt_state_get();
166 zassert_equal('c', c, "unexpected option");
167 zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
168 c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
169 zassert_equal(-1, c, "getopt_long shall return -1");
170
171 /* Test scenario 2 */
172 argv = (char **)argv2;
173 getopt_init();
174 c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
175 zassert_equal(verbose_flag, 0, "verbose flag expected");
176 c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
177 zassert_equal('d', c, "unexpected option");
178 state = getopt_state_get();
179 zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
180 c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
181 zassert_equal(-1, c, "getopt_long shall return -1");
182
183 /* Test scenario 3 */
184 argv = (char **)argv3;
185 getopt_init();
186 c = getopt_long(argc3, argv, accepted_opt, long_options, &option_index);
187 zassert_equal(verbose_flag, 0, "verbose flag expected");
188 c = getopt_long(argc3, argv, accepted_opt, long_options, &option_index);
189 zassert_equal('a', c, "unexpected option");
190 c = getopt_long(argc3, argv, accepted_opt, long_options, &option_index);
191 zassert_equal(-1, c, "getopt_long shall return -1");
192
193 /* Test scenario 4 */
194 argv = (char **)argv4;
195 getopt_init();
196 c = getopt_long(argc4, argv, accepted_opt, long_options, &option_index);
197 zassert_equal(verbose_flag, 0, "verbose flag expected");
198 c = getopt_long(argc4, argv, accepted_opt, long_options, &option_index);
199 /* Function was called with option '-l'. It is expected it will be
200 * NOT evaluated to '--long' which has flag 'e'.
201 */
202 zassert_not_equal('e', c, "unexpected option match");
203 c = getopt_long(argc4, argv, accepted_opt, long_options, &option_index);
204 }
205
ZTEST(posix_c_lib_ext,test_getopt_long_only)206 ZTEST(posix_c_lib_ext, test_getopt_long_only)
207 {
208 /* Below test is based on example
209 * https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
210 */
211 struct getopt_state *state;
212 int verbose_flag = 0;
213 /* getopt_long stores the option index here. */
214 int option_index = 0;
215 char **argv;
216 int c;
217 struct option long_options[] = {
218 /* These options set a flag. */
219 {"verbose", no_argument, &verbose_flag, 1},
220 {"brief", no_argument, &verbose_flag, 0},
221 /* These options don’t set a flag.
222 * We distinguish them by their indices.
223 */
224 {"add", no_argument, 0, 'a'},
225 {"create", required_argument, 0, 'c'},
226 {"delete", required_argument, 0, 'd'},
227 {"long", required_argument, 0, 'e'},
228 {0, 0, 0, 0},
229 };
230 static const char *accepted_opt = "ac:d:e:";
231
232 static const char *const argv1[] = {
233 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
234 [GETOPT_LONG_IDX_VERBOSE] = "--verbose",
235 [GETOPT_LONG_IDX_OPT] = "--create",
236 [GETOPT_LONG_IDX_OPTARG] = "some_file",
237 };
238 int argc1 = ARRAY_SIZE(argv1);
239
240 static const char *const argv2[] = {
241 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
242 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
243 [GETOPT_LONG_IDX_OPT] = "-d",
244 [GETOPT_LONG_IDX_OPTARG] = "other_file",
245 };
246 int argc2 = ARRAY_SIZE(argv2);
247
248 static const char *const argv3[] = {
249 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
250 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
251 [GETOPT_LONG_IDX_OPT] = "-a",
252 };
253 int argc3 = ARRAY_SIZE(argv3);
254
255 /* this test distinguish getopt_long and getopt_long_only functions */
256 static const char *const argv4[] = {
257 [GETOPT_LONG_IDX_CMD_NAME] = "cmd_name",
258 [GETOPT_LONG_IDX_VERBOSE] = "--brief",
259 /* below should be interpreted as "--long/-e" option */
260 [GETOPT_LONG_IDX_OPT] = "-l",
261 [GETOPT_LONG_IDX_OPTARG] = "long_argument",
262 };
263 int argc4 = ARRAY_SIZE(argv4);
264
265 /* Test scenario 1 */
266 argv = (char **)argv1;
267 getopt_init();
268 c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
269 zassert_equal(verbose_flag, 1, "verbose flag expected");
270 c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
271 state = getopt_state_get();
272 zassert_equal('c', c, "unexpected option");
273 zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
274 c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
275 zassert_equal(-1, c, "getopt_long_only shall return -1");
276
277 /* Test scenario 2 */
278 argv = (char **)argv2;
279 getopt_init();
280 state = getopt_state_get();
281 c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
282 zassert_equal(verbose_flag, 0, "verbose flag expected");
283 c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
284 state = getopt_state_get();
285 zassert_equal('d', c, "unexpected option");
286 zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
287 c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
288 zassert_equal(-1, c, "getopt_long_only shall return -1");
289
290 /* Test scenario 3 */
291 argv = (char **)argv3;
292 getopt_init();
293 c = getopt_long_only(argc3, argv, accepted_opt, long_options, &option_index);
294 zassert_equal(verbose_flag, 0, "verbose flag expected");
295 c = getopt_long_only(argc3, argv, accepted_opt, long_options, &option_index);
296 zassert_equal('a', c, "unexpected option");
297 c = getopt_long_only(argc3, argv, accepted_opt, long_options, &option_index);
298 zassert_equal(-1, c, "getopt_long_only shall return -1");
299
300 /* Test scenario 4 */
301 argv = (char **)argv4;
302 getopt_init();
303 c = getopt_long_only(argc4, argv, accepted_opt, long_options, &option_index);
304 zassert_equal(verbose_flag, 0, "verbose flag expected");
305 c = getopt_long_only(argc4, argv, accepted_opt, long_options, &option_index);
306
307 /* Function was called with option '-l'. It is expected it will be
308 * evaluated to '--long' which has flag 'e'.
309 */
310 zassert_equal('e', c, "unexpected option");
311 c = getopt_long_only(argc4, argv, accepted_opt, long_options, &option_index);
312 }
313