Commit | Line | Data |
---|---|---|
903a5b8a | 1 | /* |
fc07e526 SM |
2 | * Copyright (c) 2019-2021 Philippe Proulx <pproulx@efficios.com> |
3 | * Copyright (c) 2020-2021 Simon Marchi <simon.marchi@efficios.com> | |
903a5b8a SM |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; under version 2 of the License. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
15 | * with this program; if not, write to the Free Software Foundation, Inc., | |
16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
17 | */ | |
18 | ||
7ac57709 | 19 | #include <assert.h> |
903a5b8a SM |
20 | #include <stdlib.h> |
21 | #include <string.h> | |
11003cd5 | 22 | #include <stdbool.h> |
903a5b8a SM |
23 | #include <glib.h> |
24 | ||
25 | #include "tap/tap.h" | |
903a5b8a SM |
26 | #include "argpar/argpar.h" |
27 | ||
28 | /* | |
11003cd5 PP |
29 | * Formats `item` and appends the resulting string to `res_str` to |
30 | * incrementally build an expected command line string. | |
31 | * | |
32 | * This function: | |
33 | * | |
34 | * * Prefers the `--long-opt=arg` style over the `-s arg` style. | |
35 | * | |
36 | * * Uses the `arg<A,B>` form for non-option arguments, where `A` is the | |
37 | * original argument index and `B` is the non-option argument index. | |
903a5b8a SM |
38 | */ |
39 | static | |
11003cd5 PP |
40 | void append_to_res_str(GString * const res_str, |
41 | const struct argpar_item * const item) | |
fc07e526 SM |
42 | { |
43 | if (res_str->len > 0) { | |
44 | g_string_append_c(res_str, ' '); | |
45 | } | |
46 | ||
d4539a90 | 47 | switch (argpar_item_type(item)) { |
fc07e526 SM |
48 | case ARGPAR_ITEM_TYPE_OPT: |
49 | { | |
d4539a90 PP |
50 | const struct argpar_opt_descr * const descr = |
51 | argpar_item_opt_descr(item); | |
52 | const char * const arg = argpar_item_opt_arg(item); | |
fc07e526 | 53 | |
d4539a90 | 54 | if (descr->long_name) { |
fc07e526 | 55 | g_string_append_printf(res_str, "--%s", |
d4539a90 | 56 | descr->long_name); |
fc07e526 | 57 | |
d4539a90 PP |
58 | if (arg) { |
59 | g_string_append_printf(res_str, "=%s", arg); | |
fc07e526 | 60 | } |
d4539a90 | 61 | } else if (descr->short_name) { |
fc07e526 | 62 | g_string_append_printf(res_str, "-%c", |
d4539a90 | 63 | descr->short_name); |
fc07e526 | 64 | |
d4539a90 PP |
65 | if (arg) { |
66 | g_string_append_printf(res_str, " %s", arg); | |
fc07e526 SM |
67 | } |
68 | } | |
69 | ||
70 | break; | |
71 | } | |
72 | case ARGPAR_ITEM_TYPE_NON_OPT: | |
73 | { | |
d4539a90 PP |
74 | const char * const arg = argpar_item_non_opt_arg(item); |
75 | const unsigned int orig_index = | |
76 | argpar_item_non_opt_orig_index(item); | |
77 | const unsigned int non_opt_index = | |
78 | argpar_item_non_opt_non_opt_index(item); | |
fc07e526 | 79 | |
d4539a90 PP |
80 | g_string_append_printf(res_str, "%s<%u,%u>", arg, orig_index, |
81 | non_opt_index); | |
fc07e526 SM |
82 | break; |
83 | } | |
84 | default: | |
85 | abort(); | |
86 | } | |
87 | } | |
88 | ||
11003cd5 PP |
89 | /* |
90 | * Parses `cmdline` with argpar_parse() using the option descriptors | |
91 | * `descrs`, and ensures that the resulting effective command line is | |
92 | * `expected_cmd_line` and that the number of ingested original | |
93 | * arguments is `expected_ingested_orig_args`. | |
94 | * | |
95 | * This function splits `cmdline` on spaces to create an original | |
96 | * argument array. | |
97 | * | |
98 | * This function builds the resulting command line from parsing items | |
99 | * by space-separating each formatted item (see append_to_res_str()). | |
100 | */ | |
fc07e526 | 101 | static |
11003cd5 PP |
102 | void test_succeed_argpar_parse(const char * const cmdline, |
103 | const char * const expected_cmd_line, | |
104 | const struct argpar_opt_descr * const descrs, | |
105 | const unsigned int expected_ingested_orig_args) | |
903a5b8a | 106 | { |
1c9a6bde | 107 | struct argpar_parse_ret parse_ret; |
11003cd5 PP |
108 | GString * const res_str = g_string_new(NULL); |
109 | gchar ** const argv = g_strsplit(cmdline, " ", 0); | |
903a5b8a SM |
110 | unsigned int i; |
111 | ||
7ac57709 SM |
112 | assert(argv); |
113 | assert(res_str); | |
1c9a6bde | 114 | parse_ret = argpar_parse(g_strv_length(argv), |
903a5b8a SM |
115 | (const char * const *) argv, descrs, false); |
116 | ok(parse_ret.items, | |
1c9a6bde | 117 | "argpar_parse() succeeds for command line `%s`", cmdline); |
903a5b8a | 118 | ok(!parse_ret.error, |
11003cd5 PP |
119 | "argpar_parse() doesn't set an error for command line `%s`", |
120 | cmdline); | |
903a5b8a | 121 | ok(parse_ret.ingested_orig_args == expected_ingested_orig_args, |
1c9a6bde | 122 | "argpar_parse() returns the correct number of ingested " |
903a5b8a | 123 | "original arguments for command line `%s`", cmdline); |
11003cd5 | 124 | |
903a5b8a SM |
125 | if (parse_ret.ingested_orig_args != expected_ingested_orig_args) { |
126 | diag("Expected: %u Got: %u", expected_ingested_orig_args, | |
127 | parse_ret.ingested_orig_args); | |
128 | } | |
129 | ||
130 | if (!parse_ret.items) { | |
11003cd5 | 131 | fail("argpar_parse() returns the expected parsing items " |
903a5b8a SM |
132 | "for command line `%s`", cmdline); |
133 | goto end; | |
134 | } | |
135 | ||
7ac57709 | 136 | for (i = 0; i < parse_ret.items->n_items; i++) { |
11003cd5 | 137 | append_to_res_str(res_str, parse_ret.items->items[i]); |
fc07e526 SM |
138 | } |
139 | ||
140 | ok(strcmp(expected_cmd_line, res_str->str) == 0, | |
141 | "argpar_parse() returns the expected parsed arguments " | |
142 | "for command line `%s`", cmdline); | |
11003cd5 | 143 | |
fc07e526 SM |
144 | if (strcmp(expected_cmd_line, res_str->str) != 0) { |
145 | diag("Expected: `%s`", expected_cmd_line); | |
146 | diag("Got: `%s`", res_str->str); | |
147 | } | |
148 | ||
149 | end: | |
150 | argpar_parse_ret_fini(&parse_ret); | |
151 | g_string_free(res_str, TRUE); | |
152 | g_strfreev(argv); | |
153 | } | |
154 | ||
11003cd5 PP |
155 | /* |
156 | * Parses `cmdline` with the iterator API using the option descriptors | |
157 | * `descrs`, and ensures that the resulting effective command line is | |
158 | * `expected_cmd_line` and that the number of ingested original | |
159 | * arguments is `expected_ingested_orig_args`. | |
160 | * | |
161 | * This function splits `cmdline` on spaces to create an original | |
162 | * argument array. | |
163 | * | |
164 | * This function builds the resulting command line from parsing items | |
165 | * by space-separating each formatted item (see append_to_res_str()). | |
166 | */ | |
fc07e526 | 167 | static |
11003cd5 PP |
168 | void test_succeed_argpar_iter(const char * const cmdline, |
169 | const char * const expected_cmd_line, | |
170 | const struct argpar_opt_descr * const descrs, | |
171 | const unsigned int expected_ingested_orig_args) | |
fc07e526 SM |
172 | { |
173 | struct argpar_iter *iter = NULL; | |
174 | const struct argpar_item *item = NULL; | |
175 | char *error = NULL; | |
11003cd5 PP |
176 | GString * const res_str = g_string_new(NULL); |
177 | gchar ** const argv = g_strsplit(cmdline, " ", 0); | |
fc07e526 SM |
178 | unsigned int i, actual_ingested_orig_args; |
179 | ||
180 | assert(argv); | |
181 | assert(res_str); | |
fc07e526 SM |
182 | iter = argpar_iter_create(g_strv_length(argv), |
183 | (const char * const *) argv, descrs); | |
184 | assert(iter); | |
185 | ||
186 | for (i = 0; ; i++) { | |
2af370d0 | 187 | enum argpar_iter_next_status status; |
fc07e526 SM |
188 | |
189 | ARGPAR_ITEM_DESTROY_AND_RESET(item); | |
2af370d0 | 190 | status = argpar_iter_next(iter, &item, &error); |
fc07e526 | 191 | |
2af370d0 PP |
192 | ok(status == ARGPAR_ITER_NEXT_STATUS_OK || |
193 | status == ARGPAR_ITER_NEXT_STATUS_END || | |
194 | status == ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT, | |
195 | "argpar_iter_next() returns the expected status " | |
11003cd5 PP |
196 | "(%d) for command line `%s` (call %u)", |
197 | status, cmdline, i + 1); | |
fc07e526 | 198 | |
2af370d0 | 199 | if (status == ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT) { |
11003cd5 | 200 | ok(error, |
2af370d0 PP |
201 | "argpar_iter_next() sets an error for " |
202 | "status `ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` " | |
11003cd5 PP |
203 | "and command line `%s` (call %u)", |
204 | cmdline, i + 1); | |
fc07e526 | 205 | } else { |
11003cd5 | 206 | ok(!error, |
2af370d0 | 207 | "argpar_iter_next() doesn't set an error " |
11003cd5 | 208 | "for other status than " |
2af370d0 | 209 | "`ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` " |
11003cd5 PP |
210 | "and command line `%s` (call %u)", |
211 | cmdline, i + 1); | |
903a5b8a | 212 | } |
fc07e526 | 213 | |
2af370d0 PP |
214 | if (status == ARGPAR_ITER_NEXT_STATUS_END || |
215 | status == ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT) { | |
11003cd5 | 216 | ok(!item, |
2af370d0 PP |
217 | "argpar_iter_next() doesn't set an item " |
218 | "for status `ARGPAR_ITER_NEXT_STATUS_END` " | |
219 | "or `ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` " | |
11003cd5 PP |
220 | "and command line `%s` (call %u)", |
221 | cmdline, i + 1); | |
903a5b8a SM |
222 | break; |
223 | } | |
fc07e526 SM |
224 | |
225 | append_to_res_str(res_str, item); | |
903a5b8a SM |
226 | } |
227 | ||
f3ab5ca1 | 228 | actual_ingested_orig_args = argpar_iter_ingested_orig_args(iter); |
fc07e526 | 229 | ok(actual_ingested_orig_args == expected_ingested_orig_args, |
f3ab5ca1 | 230 | "argpar_iter_ingested_orig_args() returns the expected " |
11003cd5 PP |
231 | "number of ingested original arguments for command line `%s`", |
232 | cmdline); | |
233 | ||
fc07e526 SM |
234 | if (actual_ingested_orig_args != expected_ingested_orig_args) { |
235 | diag("Expected: %u Got: %u", expected_ingested_orig_args, | |
236 | actual_ingested_orig_args); | |
903a5b8a SM |
237 | } |
238 | ||
239 | ok(strcmp(expected_cmd_line, res_str->str) == 0, | |
2af370d0 | 240 | "argpar_iter_next() returns the expected parsing items " |
903a5b8a | 241 | "for command line `%s`", cmdline); |
11003cd5 | 242 | |
903a5b8a SM |
243 | if (strcmp(expected_cmd_line, res_str->str) != 0) { |
244 | diag("Expected: `%s`", expected_cmd_line); | |
245 | diag("Got: `%s`", res_str->str); | |
246 | } | |
247 | ||
fc07e526 SM |
248 | argpar_item_destroy(item); |
249 | argpar_iter_destroy(iter); | |
903a5b8a SM |
250 | g_string_free(res_str, TRUE); |
251 | g_strfreev(argv); | |
fc07e526 SM |
252 | free(error); |
253 | } | |
254 | ||
255 | /* | |
11003cd5 PP |
256 | * Calls test_succeed_argpar_parse() and test_succeed_argpar_iter() |
257 | * with the provided parameters. | |
fc07e526 SM |
258 | */ |
259 | static | |
11003cd5 PP |
260 | void test_succeed(const char * const cmdline, |
261 | const char * const expected_cmd_line, | |
262 | const struct argpar_opt_descr * const descrs, | |
263 | const unsigned int expected_ingested_orig_args) | |
fc07e526 SM |
264 | { |
265 | test_succeed_argpar_parse(cmdline, expected_cmd_line, descrs, | |
266 | expected_ingested_orig_args); | |
267 | test_succeed_argpar_iter(cmdline, expected_cmd_line, descrs, | |
268 | expected_ingested_orig_args); | |
903a5b8a SM |
269 | } |
270 | ||
271 | static | |
272 | void succeed_tests(void) | |
273 | { | |
274 | /* No arguments */ | |
275 | { | |
1c9a6bde SM |
276 | const struct argpar_opt_descr descrs[] = { |
277 | ARGPAR_OPT_DESCR_SENTINEL | |
903a5b8a SM |
278 | }; |
279 | ||
280 | test_succeed( | |
281 | "", | |
282 | "", | |
283 | descrs, 0); | |
284 | } | |
285 | ||
286 | /* Single long option */ | |
287 | { | |
1c9a6bde | 288 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 289 | { 0, '\0', "salut", false }, |
1c9a6bde | 290 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
291 | }; |
292 | ||
293 | test_succeed( | |
294 | "--salut", | |
295 | "--salut", | |
296 | descrs, 1); | |
297 | } | |
298 | ||
299 | /* Single short option */ | |
300 | { | |
1c9a6bde | 301 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 302 | { 0, 'f', NULL, false }, |
1c9a6bde | 303 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
304 | }; |
305 | ||
306 | test_succeed( | |
307 | "-f", | |
308 | "-f", | |
309 | descrs, 1); | |
310 | } | |
311 | ||
312 | /* Short and long option (aliases) */ | |
313 | { | |
1c9a6bde | 314 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 315 | { 0, 'f', "flaw", false }, |
1c9a6bde | 316 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
317 | }; |
318 | ||
319 | test_succeed( | |
320 | "-f --flaw", | |
321 | "--flaw --flaw", | |
322 | descrs, 2); | |
323 | } | |
324 | ||
325 | /* Long option with argument (space form) */ | |
326 | { | |
1c9a6bde | 327 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 328 | { 0, '\0', "tooth", true }, |
1c9a6bde | 329 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
330 | }; |
331 | ||
332 | test_succeed( | |
333 | "--tooth 67", | |
334 | "--tooth=67", | |
335 | descrs, 2); | |
336 | } | |
337 | ||
338 | /* Long option with argument (equal form) */ | |
339 | { | |
1c9a6bde | 340 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 341 | { 0, '\0', "polish", true }, |
1c9a6bde | 342 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
343 | }; |
344 | ||
345 | test_succeed( | |
346 | "--polish=brick", | |
347 | "--polish=brick", | |
348 | descrs, 1); | |
349 | } | |
350 | ||
351 | /* Short option with argument (space form) */ | |
352 | { | |
1c9a6bde | 353 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 354 | { 0, 'c', NULL, true }, |
1c9a6bde | 355 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
356 | }; |
357 | ||
358 | test_succeed( | |
359 | "-c chilly", | |
360 | "-c chilly", | |
361 | descrs, 2); | |
362 | } | |
363 | ||
364 | /* Short option with argument (glued form) */ | |
365 | { | |
1c9a6bde | 366 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 367 | { 0, 'c', NULL, true }, |
1c9a6bde | 368 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
369 | }; |
370 | ||
371 | test_succeed( | |
372 | "-cchilly", | |
373 | "-c chilly", | |
374 | descrs, 1); | |
375 | } | |
376 | ||
377 | /* Short and long option (aliases) with argument (all forms) */ | |
378 | { | |
1c9a6bde | 379 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 380 | { 0, 'd', "dry", true }, |
1c9a6bde | 381 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
382 | }; |
383 | ||
384 | test_succeed( | |
385 | "--dry=rate -dthing --dry street --dry=shape", | |
386 | "--dry=rate --dry=thing --dry=street --dry=shape", | |
387 | descrs, 5); | |
388 | } | |
389 | ||
390 | /* Many short options, last one with argument (glued form) */ | |
391 | { | |
1c9a6bde | 392 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
393 | { 0, 'd', NULL, false }, |
394 | { 0, 'e', NULL, false }, | |
395 | { 0, 'f', NULL, true }, | |
1c9a6bde | 396 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
397 | }; |
398 | ||
399 | test_succeed( | |
400 | "-defmeow", | |
401 | "-d -e -f meow", | |
402 | descrs, 1); | |
403 | } | |
404 | ||
405 | /* Many options */ | |
406 | { | |
1c9a6bde | 407 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
408 | { 0, 'd', NULL, false }, |
409 | { 0, 'e', "east", true }, | |
410 | { 0, '\0', "mind", false }, | |
1c9a6bde | 411 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
412 | }; |
413 | ||
414 | test_succeed( | |
415 | "-d --mind -destart --mind --east cough -d --east=itch", | |
416 | "-d --mind -d --east=start --mind --east=cough -d --east=itch", | |
417 | descrs, 8); | |
418 | } | |
419 | ||
420 | /* Single non-option argument */ | |
421 | { | |
1c9a6bde SM |
422 | const struct argpar_opt_descr descrs[] = { |
423 | ARGPAR_OPT_DESCR_SENTINEL | |
903a5b8a SM |
424 | }; |
425 | ||
426 | test_succeed( | |
427 | "kilojoule", | |
428 | "kilojoule<0,0>", | |
429 | descrs, 1); | |
430 | } | |
431 | ||
432 | /* Two non-option arguments */ | |
433 | { | |
1c9a6bde SM |
434 | const struct argpar_opt_descr descrs[] = { |
435 | ARGPAR_OPT_DESCR_SENTINEL | |
903a5b8a SM |
436 | }; |
437 | ||
438 | test_succeed( | |
439 | "kilojoule mitaine", | |
440 | "kilojoule<0,0> mitaine<1,1>", | |
441 | descrs, 2); | |
442 | } | |
443 | ||
444 | /* Single non-option argument mixed with options */ | |
445 | { | |
1c9a6bde | 446 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
447 | { 0, 'd', NULL, false }, |
448 | { 0, '\0', "squeeze", true }, | |
1c9a6bde | 449 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
450 | }; |
451 | ||
452 | test_succeed( | |
453 | "-d sprout yes --squeeze little bag -d", | |
454 | "-d sprout<1,0> yes<2,1> --squeeze=little bag<5,2> -d", | |
455 | descrs, 7); | |
456 | } | |
457 | ||
458 | /* Unknown short option (space form) */ | |
459 | { | |
1c9a6bde | 460 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 461 | { 0, 'd', NULL, true }, |
1c9a6bde | 462 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
463 | }; |
464 | ||
465 | test_succeed( | |
466 | "-d salut -e -d meow", | |
467 | "-d salut", | |
468 | descrs, 2); | |
469 | } | |
470 | ||
471 | /* Unknown short option (glued form) */ | |
472 | { | |
1c9a6bde | 473 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 474 | { 0, 'd', NULL, true }, |
1c9a6bde | 475 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
476 | }; |
477 | ||
478 | test_succeed( | |
479 | "-dsalut -e -d meow", | |
480 | "-d salut", | |
481 | descrs, 1); | |
482 | } | |
483 | ||
484 | /* Unknown long option (space form) */ | |
485 | { | |
1c9a6bde | 486 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 487 | { 0, '\0', "sink", true }, |
1c9a6bde | 488 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
489 | }; |
490 | ||
491 | test_succeed( | |
492 | "--sink party --food --sink impulse", | |
493 | "--sink=party", | |
494 | descrs, 2); | |
495 | } | |
496 | ||
497 | /* Unknown long option (equal form) */ | |
498 | { | |
1c9a6bde | 499 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 500 | { 0, '\0', "sink", true }, |
1c9a6bde | 501 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
502 | }; |
503 | ||
504 | test_succeed( | |
505 | "--sink=party --food --sink=impulse", | |
506 | "--sink=party", | |
507 | descrs, 1); | |
508 | } | |
509 | ||
510 | /* Unknown option before non-option argument */ | |
511 | { | |
1c9a6bde | 512 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 513 | { 0, '\0', "thumb", true }, |
1c9a6bde | 514 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
515 | }; |
516 | ||
517 | test_succeed( | |
518 | "--thumb=party --food bateau --thumb waves", | |
519 | "--thumb=party", | |
520 | descrs, 1); | |
521 | } | |
522 | ||
523 | /* Unknown option after non-option argument */ | |
524 | { | |
1c9a6bde | 525 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 526 | { 0, '\0', "thumb", true }, |
1c9a6bde | 527 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
528 | }; |
529 | ||
530 | test_succeed( | |
531 | "--thumb=party wound --food --thumb waves", | |
532 | "--thumb=party wound<1,0>", | |
533 | descrs, 2); | |
534 | } | |
535 | ||
536 | /* Valid `---opt` */ | |
537 | { | |
1c9a6bde | 538 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 539 | { 0, '\0', "-fuel", true }, |
1c9a6bde | 540 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
541 | }; |
542 | ||
543 | test_succeed( | |
544 | "---fuel=three", | |
545 | "---fuel=three", | |
546 | descrs, 1); | |
547 | } | |
548 | ||
549 | /* Long option containing `=` in argument (equal form) */ | |
550 | { | |
1c9a6bde | 551 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 552 | { 0, '\0', "zebra", true }, |
1c9a6bde | 553 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
554 | }; |
555 | ||
556 | test_succeed( | |
557 | "--zebra=three=yes", | |
558 | "--zebra=three=yes", | |
559 | descrs, 1); | |
560 | } | |
561 | ||
562 | /* Short option's argument starting with `-` (glued form) */ | |
563 | { | |
1c9a6bde | 564 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 565 | { 0, 'z', NULL, true }, |
1c9a6bde | 566 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
567 | }; |
568 | ||
569 | test_succeed( | |
570 | "-z-will", | |
571 | "-z -will", | |
572 | descrs, 1); | |
573 | } | |
574 | ||
575 | /* Short option's argument starting with `-` (space form) */ | |
576 | { | |
1c9a6bde | 577 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 578 | { 0, 'z', NULL, true }, |
1c9a6bde | 579 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
580 | }; |
581 | ||
582 | test_succeed( | |
583 | "-z -will", | |
584 | "-z -will", | |
585 | descrs, 2); | |
586 | } | |
587 | ||
588 | /* Long option's argument starting with `-` (space form) */ | |
589 | { | |
1c9a6bde | 590 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 591 | { 0, '\0', "janine", true }, |
1c9a6bde | 592 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
593 | }; |
594 | ||
595 | test_succeed( | |
596 | "--janine -sutto", | |
597 | "--janine=-sutto", | |
598 | descrs, 2); | |
599 | } | |
600 | ||
601 | /* Long option's argument starting with `-` (equal form) */ | |
602 | { | |
1c9a6bde | 603 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 604 | { 0, '\0', "janine", true }, |
1c9a6bde | 605 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
606 | }; |
607 | ||
608 | test_succeed( | |
609 | "--janine=-sutto", | |
610 | "--janine=-sutto", | |
611 | descrs, 1); | |
612 | } | |
613 | ||
614 | /* Long option's empty argument (equal form) */ | |
615 | { | |
1c9a6bde | 616 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
617 | { 0, 'f', NULL, false }, |
618 | { 0, '\0', "yeah", true }, | |
1c9a6bde | 619 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
620 | }; |
621 | ||
622 | test_succeed( | |
623 | "-f --yeah= -f", | |
624 | "-f --yeah= -f", | |
625 | descrs, 3); | |
626 | } | |
627 | } | |
628 | ||
11003cd5 PP |
629 | /* |
630 | * Parses `cmdline` with argpar_parse() using the option descriptors | |
631 | * `descrs`, and ensures that the function fails and that it sets an | |
632 | * error which is equal to `expected_error`. | |
633 | * | |
634 | * This function splits `cmdline` on spaces to create an original | |
635 | * argument array. | |
636 | */ | |
903a5b8a | 637 | static |
11003cd5 PP |
638 | void test_fail_argpar_parse(const char * const cmdline, |
639 | const char * const expected_error, | |
640 | const struct argpar_opt_descr * const descrs) | |
903a5b8a | 641 | { |
1c9a6bde | 642 | struct argpar_parse_ret parse_ret; |
11003cd5 | 643 | gchar ** const argv = g_strsplit(cmdline, " ", 0); |
903a5b8a | 644 | |
1c9a6bde | 645 | parse_ret = argpar_parse(g_strv_length(argv), |
903a5b8a SM |
646 | (const char * const *) argv, descrs, true); |
647 | ok(!parse_ret.items, | |
1c9a6bde | 648 | "argpar_parse() fails for command line `%s`", cmdline); |
903a5b8a | 649 | ok(parse_ret.error, |
11003cd5 | 650 | "argpar_parse() sets an error string for command line `%s`", |
903a5b8a | 651 | cmdline); |
11003cd5 | 652 | |
903a5b8a | 653 | if (parse_ret.items) { |
11003cd5 | 654 | fail("argpar_parse() sets the expected error string"); |
903a5b8a SM |
655 | goto end; |
656 | } | |
657 | ||
7ac57709 | 658 | ok(strcmp(expected_error, parse_ret.error) == 0, |
11003cd5 | 659 | "argpar_parse() sets the expected error string " |
903a5b8a | 660 | "for command line `%s`", cmdline); |
11003cd5 | 661 | |
7ac57709 | 662 | if (strcmp(expected_error, parse_ret.error) != 0) { |
903a5b8a | 663 | diag("Expected: `%s`", expected_error); |
7ac57709 | 664 | diag("Got: `%s`", parse_ret.error); |
903a5b8a SM |
665 | } |
666 | ||
667 | end: | |
1c9a6bde | 668 | argpar_parse_ret_fini(&parse_ret); |
903a5b8a SM |
669 | g_strfreev(argv); |
670 | } | |
671 | ||
11003cd5 PP |
672 | /* |
673 | * Parses `cmdline` with the iterator API using the option descriptors | |
2af370d0 | 674 | * `descrs`, and ensures that argpar_iter_next() fails with status |
d4d05805 PP |
675 | * `expected_status` and that it sets an error which is equal to |
676 | * `expected_error`. | |
11003cd5 PP |
677 | * |
678 | * This function splits `cmdline` on spaces to create an original | |
679 | * argument array. | |
680 | */ | |
fc07e526 | 681 | static |
11003cd5 PP |
682 | void test_fail_argpar_iter(const char * const cmdline, |
683 | const char * const expected_error, | |
2af370d0 | 684 | const enum argpar_iter_next_status expected_status, |
11003cd5 | 685 | const struct argpar_opt_descr * const descrs) |
fc07e526 SM |
686 | { |
687 | struct argpar_iter *iter = NULL; | |
688 | const struct argpar_item *item = NULL; | |
11003cd5 | 689 | gchar ** const argv = g_strsplit(cmdline, " ", 0); |
fc07e526 SM |
690 | unsigned int i; |
691 | char *error = NULL; | |
692 | ||
693 | iter = argpar_iter_create(g_strv_length(argv), | |
694 | (const char * const *) argv, descrs); | |
695 | assert(iter); | |
696 | ||
697 | for (i = 0; ; i++) { | |
2af370d0 | 698 | enum argpar_iter_next_status status; |
fc07e526 SM |
699 | |
700 | ARGPAR_ITEM_DESTROY_AND_RESET(item); | |
2af370d0 PP |
701 | status = argpar_iter_next(iter, &item, &error); |
702 | ok(status == ARGPAR_ITER_NEXT_STATUS_OK || | |
d4d05805 | 703 | status == expected_status, |
2af370d0 | 704 | "argpar_iter_next() returns the expected status " |
11003cd5 PP |
705 | "(%d) for command line `%s` (call %u)", |
706 | status, cmdline, i + 1); | |
fc07e526 | 707 | |
2af370d0 | 708 | if (status != ARGPAR_ITER_NEXT_STATUS_OK) { |
11003cd5 | 709 | ok(!item, |
2af370d0 | 710 | "argpar_iter_next() doesn't set an item " |
11003cd5 | 711 | "for other status than " |
2af370d0 | 712 | "`ARGPAR_ITER_NEXT_STATUS_OK` " |
11003cd5 PP |
713 | "and command line `%s` (call %u)", |
714 | cmdline, i + 1); | |
715 | ok(error, | |
2af370d0 | 716 | "argpar_iter_next() sets an error for " |
11003cd5 | 717 | "other status than " |
2af370d0 | 718 | " `ARGPAR_ITER_NEXT_STATUS_OK` " |
11003cd5 PP |
719 | "and command line `%s` (call %u)", |
720 | cmdline, i + 1); | |
fc07e526 SM |
721 | break; |
722 | } | |
723 | ||
11003cd5 | 724 | ok(item, |
2af370d0 PP |
725 | "argpar_iter_next() sets an item for status " |
726 | "`ARGPAR_ITER_NEXT_STATUS_OK` " | |
11003cd5 PP |
727 | "and command line `%s` (call %u)", |
728 | cmdline, i + 1); | |
729 | ok(!error, | |
2af370d0 PP |
730 | "argpar_iter_next() doesn't set an error for status " |
731 | "`ARGPAR_ITER_NEXT_STATUS_OK` " | |
11003cd5 PP |
732 | "and command line `%s` (call %u)", |
733 | cmdline, i + 1); | |
fc07e526 SM |
734 | } |
735 | ||
736 | ok(strcmp(expected_error, error) == 0, | |
2af370d0 | 737 | "argpar_iter_next() sets the expected error string " |
fc07e526 | 738 | "for command line `%s`", cmdline); |
11003cd5 | 739 | |
fc07e526 SM |
740 | if (strcmp(expected_error, error) != 0) { |
741 | diag("Expected: `%s`", expected_error); | |
742 | diag("Got: `%s`", error); | |
743 | } | |
744 | ||
745 | argpar_item_destroy(item); | |
746 | argpar_iter_destroy(iter); | |
747 | free(error); | |
748 | g_strfreev(argv); | |
749 | } | |
750 | ||
751 | /* | |
11003cd5 PP |
752 | * Calls test_fail_argpar_parse() and test_fail_argpar_iter() with the |
753 | * provided parameters. | |
fc07e526 | 754 | */ |
fc07e526 | 755 | static |
11003cd5 | 756 | void test_fail(const char * const cmdline, const char * const expected_error, |
2af370d0 | 757 | const enum argpar_iter_next_status expected_iter_next_status, |
11003cd5 | 758 | const struct argpar_opt_descr * const descrs) |
fc07e526 SM |
759 | { |
760 | test_fail_argpar_parse(cmdline, expected_error, descrs); | |
d4d05805 PP |
761 | test_fail_argpar_iter(cmdline, expected_error, |
762 | expected_iter_next_status, descrs); | |
fc07e526 SM |
763 | } |
764 | ||
903a5b8a SM |
765 | static |
766 | void fail_tests(void) | |
767 | { | |
768 | /* Unknown long option */ | |
769 | { | |
1c9a6bde | 770 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 771 | { 0, '\0', "thumb", true }, |
1c9a6bde | 772 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
773 | }; |
774 | ||
775 | test_fail( | |
776 | "--thumb=party --meow", | |
777 | "While parsing argument #2 (`--meow`): Unknown option `--meow`", | |
2af370d0 | 778 | ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT, |
903a5b8a SM |
779 | descrs); |
780 | } | |
781 | ||
782 | /* Unknown short option */ | |
783 | { | |
1c9a6bde | 784 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 785 | { 0, '\0', "thumb", true }, |
1c9a6bde | 786 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
787 | }; |
788 | ||
789 | test_fail( | |
790 | "--thumb=party -x", | |
791 | "While parsing argument #2 (`-x`): Unknown option `-x`", | |
2af370d0 | 792 | ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT, |
903a5b8a SM |
793 | descrs); |
794 | } | |
795 | ||
796 | /* Missing long option argument */ | |
797 | { | |
1c9a6bde | 798 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 799 | { 0, '\0', "thumb", true }, |
1c9a6bde | 800 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
801 | }; |
802 | ||
803 | test_fail( | |
804 | "--thumb", | |
805 | "While parsing argument #1 (`--thumb`): Missing required argument for option `--thumb`", | |
2af370d0 | 806 | ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG, |
903a5b8a SM |
807 | descrs); |
808 | } | |
809 | ||
810 | /* Missing short option argument */ | |
811 | { | |
1c9a6bde | 812 | const struct argpar_opt_descr descrs[] = { |
903a5b8a | 813 | { 0, 'k', NULL, true }, |
1c9a6bde | 814 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
815 | }; |
816 | ||
817 | test_fail( | |
818 | "-k", | |
819 | "While parsing argument #1 (`-k`): Missing required argument for option `-k`", | |
2af370d0 | 820 | ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG, |
903a5b8a SM |
821 | descrs); |
822 | } | |
823 | ||
824 | /* Missing short option argument (multiple glued) */ | |
825 | { | |
1c9a6bde | 826 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
827 | { 0, 'a', NULL, false }, |
828 | { 0, 'b', NULL, false }, | |
829 | { 0, 'c', NULL, true }, | |
1c9a6bde | 830 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
831 | }; |
832 | ||
833 | test_fail( | |
834 | "-abc", | |
835 | "While parsing argument #1 (`-abc`): Missing required argument for option `-c`", | |
2af370d0 | 836 | ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG, |
903a5b8a SM |
837 | descrs); |
838 | } | |
839 | ||
840 | /* Invalid `-` */ | |
841 | { | |
1c9a6bde | 842 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
843 | { 0, 'a', NULL, false }, |
844 | { 0, 'b', NULL, false }, | |
845 | { 0, 'c', NULL, true }, | |
1c9a6bde | 846 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
847 | }; |
848 | ||
849 | test_fail( | |
850 | "-ab - -c", | |
851 | "While parsing argument #2 (`-`): Invalid argument", | |
2af370d0 | 852 | ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG, |
903a5b8a SM |
853 | descrs); |
854 | } | |
855 | ||
856 | /* Invalid `--` */ | |
857 | { | |
1c9a6bde | 858 | const struct argpar_opt_descr descrs[] = { |
903a5b8a SM |
859 | { 0, 'a', NULL, false }, |
860 | { 0, 'b', NULL, false }, | |
861 | { 0, 'c', NULL, true }, | |
1c9a6bde | 862 | ARGPAR_OPT_DESCR_SENTINEL |
903a5b8a SM |
863 | }; |
864 | ||
865 | test_fail( | |
866 | "-ab -- -c", | |
867 | "While parsing argument #2 (`--`): Invalid argument", | |
2af370d0 | 868 | ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG, |
903a5b8a SM |
869 | descrs); |
870 | } | |
430fe886 SM |
871 | |
872 | { | |
873 | const struct argpar_opt_descr descrs[] = { | |
874 | { 0, 'c', "chevre", false }, | |
875 | ARGPAR_OPT_DESCR_SENTINEL | |
876 | }; | |
877 | ||
878 | test_fail( | |
879 | "--chevre=fromage", | |
880 | "While parsing argument #1 (`--chevre=fromage`): Unexpected argument for option `--chevre`", | |
2af370d0 | 881 | ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG, |
430fe886 SM |
882 | descrs); |
883 | } | |
903a5b8a SM |
884 | } |
885 | ||
886 | int main(void) | |
887 | { | |
fc07e526 | 888 | plan_tests(419); |
903a5b8a SM |
889 | succeed_tests(); |
890 | fail_tests(); | |
891 | return exit_status(); | |
892 | } |