f4944eac1e37d1a836e80966ae4118d1e166c45e
[argpar.git] / tests / test_argpar.c
1 /*
2 * Copyright (c) 2019 Philippe Proulx <pproulx@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; under version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #include <assert.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <glib.h>
22
23 #include "tap/tap.h"
24 #include "argpar/argpar.h"
25
26 /*
27 * Tests that the command line `cmdline`, with non-quoted
28 * space-delimited arguments, once parsed given the option descriptors
29 * `descrs` and the option `fail_on_unknown_opt`, succeeds and gives the
30 * expected command line `expected_cmd_line` and number of ingested
31 * original arguments `expected_ingested_orig_args`.
32 *
33 * The resulting command-line is built from the resulting arguments,
34 * space-delimiting each argument, preferring the `--long-opt=arg` style
35 * over the `-s arg` style, and using the `arg<A,B>` form for non-option
36 * arguments where `A` is the original argument index and `B` is the
37 * non-option argument index.
38 */
39 static
40 void test_succeed(const char *cmdline,
41 const char *expected_cmd_line,
42 const struct argpar_opt_descr *descrs,
43 unsigned int expected_ingested_orig_args)
44 {
45 struct argpar_parse_ret parse_ret;
46 GString *res_str = g_string_new(NULL);
47 gchar **argv = g_strsplit(cmdline, " ", 0);
48 unsigned int i;
49
50 assert(argv);
51 assert(res_str);
52 parse_ret = argpar_parse(g_strv_length(argv),
53 (const char * const *) argv, descrs, false);
54 ok(parse_ret.items,
55 "argpar_parse() succeeds for command line `%s`", cmdline);
56 ok(!parse_ret.error,
57 "argpar_parse() does not write an error for command line `%s`", cmdline);
58 ok(parse_ret.ingested_orig_args == expected_ingested_orig_args,
59 "argpar_parse() returns the correct number of ingested "
60 "original arguments for command line `%s`", cmdline);
61 if (parse_ret.ingested_orig_args != expected_ingested_orig_args) {
62 diag("Expected: %u Got: %u", expected_ingested_orig_args,
63 parse_ret.ingested_orig_args);
64 }
65
66 if (!parse_ret.items) {
67 fail("argpar_parse() returns the expected parsed arguments "
68 "for command line `%s`", cmdline);
69 goto end;
70 }
71
72 for (i = 0; i < parse_ret.items->n_items; i++) {
73 const struct argpar_item *arg = parse_ret.items->items[i];
74
75 switch (arg->type) {
76 case ARGPAR_ITEM_TYPE_OPT:
77 {
78 const struct argpar_item_opt *arg_opt =
79 (const void *) arg;
80
81 if (arg_opt->descr->long_name) {
82 g_string_append_printf(res_str, "--%s",
83 arg_opt->descr->long_name);
84
85 if (arg_opt->arg) {
86 g_string_append_printf(res_str, "=%s",
87 arg_opt->arg);
88 }
89
90 g_string_append_c(res_str, ' ');
91 } else if (arg_opt->descr->short_name) {
92 g_string_append_printf(res_str, "-%c",
93 arg_opt->descr->short_name);
94
95 if (arg_opt->arg) {
96 g_string_append_printf(res_str, " %s",
97 arg_opt->arg);
98 }
99
100 g_string_append_c(res_str, ' ');
101 }
102
103 break;
104 }
105 case ARGPAR_ITEM_TYPE_NON_OPT:
106 {
107 const struct argpar_item_non_opt *arg_non_opt =
108 (const void *) arg;
109
110 g_string_append_printf(res_str, "%s<%u,%u> ",
111 arg_non_opt->arg, arg_non_opt->orig_index,
112 arg_non_opt->non_opt_index);
113 break;
114 }
115 default:
116 abort();
117 }
118 }
119
120 if (res_str->len > 0) {
121 g_string_truncate(res_str, res_str->len - 1);
122 }
123
124 ok(strcmp(expected_cmd_line, res_str->str) == 0,
125 "argpar_parse() returns the expected parsed arguments "
126 "for command line `%s`", cmdline);
127 if (strcmp(expected_cmd_line, res_str->str) != 0) {
128 diag("Expected: `%s`", expected_cmd_line);
129 diag("Got: `%s`", res_str->str);
130 }
131
132 end:
133 argpar_parse_ret_fini(&parse_ret);
134 g_string_free(res_str, TRUE);
135 g_strfreev(argv);
136 }
137
138 static
139 void succeed_tests(void)
140 {
141 /* No arguments */
142 {
143 const struct argpar_opt_descr descrs[] = {
144 ARGPAR_OPT_DESCR_SENTINEL
145 };
146
147 test_succeed(
148 "",
149 "",
150 descrs, 0);
151 }
152
153 /* Single long option */
154 {
155 const struct argpar_opt_descr descrs[] = {
156 { 0, '\0', "salut", false },
157 ARGPAR_OPT_DESCR_SENTINEL
158 };
159
160 test_succeed(
161 "--salut",
162 "--salut",
163 descrs, 1);
164 }
165
166 /* Single short option */
167 {
168 const struct argpar_opt_descr descrs[] = {
169 { 0, 'f', NULL, false },
170 ARGPAR_OPT_DESCR_SENTINEL
171 };
172
173 test_succeed(
174 "-f",
175 "-f",
176 descrs, 1);
177 }
178
179 /* Short and long option (aliases) */
180 {
181 const struct argpar_opt_descr descrs[] = {
182 { 0, 'f', "flaw", false },
183 ARGPAR_OPT_DESCR_SENTINEL
184 };
185
186 test_succeed(
187 "-f --flaw",
188 "--flaw --flaw",
189 descrs, 2);
190 }
191
192 /* Long option with argument (space form) */
193 {
194 const struct argpar_opt_descr descrs[] = {
195 { 0, '\0', "tooth", true },
196 ARGPAR_OPT_DESCR_SENTINEL
197 };
198
199 test_succeed(
200 "--tooth 67",
201 "--tooth=67",
202 descrs, 2);
203 }
204
205 /* Long option with argument (equal form) */
206 {
207 const struct argpar_opt_descr descrs[] = {
208 { 0, '\0', "polish", true },
209 ARGPAR_OPT_DESCR_SENTINEL
210 };
211
212 test_succeed(
213 "--polish=brick",
214 "--polish=brick",
215 descrs, 1);
216 }
217
218 /* Short option with argument (space form) */
219 {
220 const struct argpar_opt_descr descrs[] = {
221 { 0, 'c', NULL, true },
222 ARGPAR_OPT_DESCR_SENTINEL
223 };
224
225 test_succeed(
226 "-c chilly",
227 "-c chilly",
228 descrs, 2);
229 }
230
231 /* Short option with argument (glued form) */
232 {
233 const struct argpar_opt_descr descrs[] = {
234 { 0, 'c', NULL, true },
235 ARGPAR_OPT_DESCR_SENTINEL
236 };
237
238 test_succeed(
239 "-cchilly",
240 "-c chilly",
241 descrs, 1);
242 }
243
244 /* Short and long option (aliases) with argument (all forms) */
245 {
246 const struct argpar_opt_descr descrs[] = {
247 { 0, 'd', "dry", true },
248 ARGPAR_OPT_DESCR_SENTINEL
249 };
250
251 test_succeed(
252 "--dry=rate -dthing --dry street --dry=shape",
253 "--dry=rate --dry=thing --dry=street --dry=shape",
254 descrs, 5);
255 }
256
257 /* Many short options, last one with argument (glued form) */
258 {
259 const struct argpar_opt_descr descrs[] = {
260 { 0, 'd', NULL, false },
261 { 0, 'e', NULL, false },
262 { 0, 'f', NULL, true },
263 ARGPAR_OPT_DESCR_SENTINEL
264 };
265
266 test_succeed(
267 "-defmeow",
268 "-d -e -f meow",
269 descrs, 1);
270 }
271
272 /* Many options */
273 {
274 const struct argpar_opt_descr descrs[] = {
275 { 0, 'd', NULL, false },
276 { 0, 'e', "east", true },
277 { 0, '\0', "mind", false },
278 ARGPAR_OPT_DESCR_SENTINEL
279 };
280
281 test_succeed(
282 "-d --mind -destart --mind --east cough -d --east=itch",
283 "-d --mind -d --east=start --mind --east=cough -d --east=itch",
284 descrs, 8);
285 }
286
287 /* Single non-option argument */
288 {
289 const struct argpar_opt_descr descrs[] = {
290 ARGPAR_OPT_DESCR_SENTINEL
291 };
292
293 test_succeed(
294 "kilojoule",
295 "kilojoule<0,0>",
296 descrs, 1);
297 }
298
299 /* Two non-option arguments */
300 {
301 const struct argpar_opt_descr descrs[] = {
302 ARGPAR_OPT_DESCR_SENTINEL
303 };
304
305 test_succeed(
306 "kilojoule mitaine",
307 "kilojoule<0,0> mitaine<1,1>",
308 descrs, 2);
309 }
310
311 /* Single non-option argument mixed with options */
312 {
313 const struct argpar_opt_descr descrs[] = {
314 { 0, 'd', NULL, false },
315 { 0, '\0', "squeeze", true },
316 ARGPAR_OPT_DESCR_SENTINEL
317 };
318
319 test_succeed(
320 "-d sprout yes --squeeze little bag -d",
321 "-d sprout<1,0> yes<2,1> --squeeze=little bag<5,2> -d",
322 descrs, 7);
323 }
324
325 /* Unknown short option (space form) */
326 {
327 const struct argpar_opt_descr descrs[] = {
328 { 0, 'd', NULL, true },
329 ARGPAR_OPT_DESCR_SENTINEL
330 };
331
332 test_succeed(
333 "-d salut -e -d meow",
334 "-d salut",
335 descrs, 2);
336 }
337
338 /* Unknown short option (glued form) */
339 {
340 const struct argpar_opt_descr descrs[] = {
341 { 0, 'd', NULL, true },
342 ARGPAR_OPT_DESCR_SENTINEL
343 };
344
345 test_succeed(
346 "-dsalut -e -d meow",
347 "-d salut",
348 descrs, 1);
349 }
350
351 /* Unknown long option (space form) */
352 {
353 const struct argpar_opt_descr descrs[] = {
354 { 0, '\0', "sink", true },
355 ARGPAR_OPT_DESCR_SENTINEL
356 };
357
358 test_succeed(
359 "--sink party --food --sink impulse",
360 "--sink=party",
361 descrs, 2);
362 }
363
364 /* Unknown long option (equal form) */
365 {
366 const struct argpar_opt_descr descrs[] = {
367 { 0, '\0', "sink", true },
368 ARGPAR_OPT_DESCR_SENTINEL
369 };
370
371 test_succeed(
372 "--sink=party --food --sink=impulse",
373 "--sink=party",
374 descrs, 1);
375 }
376
377 /* Unknown option before non-option argument */
378 {
379 const struct argpar_opt_descr descrs[] = {
380 { 0, '\0', "thumb", true },
381 ARGPAR_OPT_DESCR_SENTINEL
382 };
383
384 test_succeed(
385 "--thumb=party --food bateau --thumb waves",
386 "--thumb=party",
387 descrs, 1);
388 }
389
390 /* Unknown option after non-option argument */
391 {
392 const struct argpar_opt_descr descrs[] = {
393 { 0, '\0', "thumb", true },
394 ARGPAR_OPT_DESCR_SENTINEL
395 };
396
397 test_succeed(
398 "--thumb=party wound --food --thumb waves",
399 "--thumb=party wound<1,0>",
400 descrs, 2);
401 }
402
403 /* Valid `---opt` */
404 {
405 const struct argpar_opt_descr descrs[] = {
406 { 0, '\0', "-fuel", true },
407 ARGPAR_OPT_DESCR_SENTINEL
408 };
409
410 test_succeed(
411 "---fuel=three",
412 "---fuel=three",
413 descrs, 1);
414 }
415
416 /* Long option containing `=` in argument (equal form) */
417 {
418 const struct argpar_opt_descr descrs[] = {
419 { 0, '\0', "zebra", true },
420 ARGPAR_OPT_DESCR_SENTINEL
421 };
422
423 test_succeed(
424 "--zebra=three=yes",
425 "--zebra=three=yes",
426 descrs, 1);
427 }
428
429 /* Short option's argument starting with `-` (glued form) */
430 {
431 const struct argpar_opt_descr descrs[] = {
432 { 0, 'z', NULL, true },
433 ARGPAR_OPT_DESCR_SENTINEL
434 };
435
436 test_succeed(
437 "-z-will",
438 "-z -will",
439 descrs, 1);
440 }
441
442 /* Short option's argument starting with `-` (space form) */
443 {
444 const struct argpar_opt_descr descrs[] = {
445 { 0, 'z', NULL, true },
446 ARGPAR_OPT_DESCR_SENTINEL
447 };
448
449 test_succeed(
450 "-z -will",
451 "-z -will",
452 descrs, 2);
453 }
454
455 /* Long option's argument starting with `-` (space form) */
456 {
457 const struct argpar_opt_descr descrs[] = {
458 { 0, '\0', "janine", true },
459 ARGPAR_OPT_DESCR_SENTINEL
460 };
461
462 test_succeed(
463 "--janine -sutto",
464 "--janine=-sutto",
465 descrs, 2);
466 }
467
468 /* Long option's argument starting with `-` (equal form) */
469 {
470 const struct argpar_opt_descr descrs[] = {
471 { 0, '\0', "janine", true },
472 ARGPAR_OPT_DESCR_SENTINEL
473 };
474
475 test_succeed(
476 "--janine=-sutto",
477 "--janine=-sutto",
478 descrs, 1);
479 }
480
481 /* Long option's empty argument (equal form) */
482 {
483 const struct argpar_opt_descr descrs[] = {
484 { 0, 'f', NULL, false },
485 { 0, '\0', "yeah", true },
486 ARGPAR_OPT_DESCR_SENTINEL
487 };
488
489 test_succeed(
490 "-f --yeah= -f",
491 "-f --yeah= -f",
492 descrs, 3);
493 }
494 }
495
496 /*
497 * Tests that the command line `cmdline`, with non-quoted
498 * space-delimited arguments, once parsed given the option descriptors
499 * `descrs`, fails and gives the expected error `expected_error`.
500 */
501 static
502 void test_fail(const char *cmdline, const char *expected_error,
503 const struct argpar_opt_descr *descrs)
504 {
505 struct argpar_parse_ret parse_ret;
506 gchar **argv = g_strsplit(cmdline, " ", 0);
507
508 parse_ret = argpar_parse(g_strv_length(argv),
509 (const char * const *) argv, descrs, true);
510 ok(!parse_ret.items,
511 "argpar_parse() fails for command line `%s`", cmdline);
512 ok(parse_ret.error,
513 "argpar_parse() writes an error string for command line `%s`",
514 cmdline);
515 if (parse_ret.items) {
516 fail("argpar_parse() writes the expected error string");
517 goto end;
518 }
519
520 ok(strcmp(expected_error, parse_ret.error) == 0,
521 "argpar_parse() writes the expected error string "
522 "for command line `%s`", cmdline);
523 if (strcmp(expected_error, parse_ret.error) != 0) {
524 diag("Expected: `%s`", expected_error);
525 diag("Got: `%s`", parse_ret.error);
526 }
527
528 end:
529 argpar_parse_ret_fini(&parse_ret);
530 g_strfreev(argv);
531 }
532
533 static
534 void fail_tests(void)
535 {
536 /* Unknown long option */
537 {
538 const struct argpar_opt_descr descrs[] = {
539 { 0, '\0', "thumb", true },
540 ARGPAR_OPT_DESCR_SENTINEL
541 };
542
543 test_fail(
544 "--thumb=party --meow",
545 "While parsing argument #2 (`--meow`): Unknown option `--meow`",
546 descrs);
547 }
548
549 /* Unknown short option */
550 {
551 const struct argpar_opt_descr descrs[] = {
552 { 0, '\0', "thumb", true },
553 ARGPAR_OPT_DESCR_SENTINEL
554 };
555
556 test_fail(
557 "--thumb=party -x",
558 "While parsing argument #2 (`-x`): Unknown option `-x`",
559 descrs);
560 }
561
562 /* Missing long option argument */
563 {
564 const struct argpar_opt_descr descrs[] = {
565 { 0, '\0', "thumb", true },
566 ARGPAR_OPT_DESCR_SENTINEL
567 };
568
569 test_fail(
570 "--thumb",
571 "While parsing argument #1 (`--thumb`): Missing required argument for option `--thumb`",
572 descrs);
573 }
574
575 /* Missing short option argument */
576 {
577 const struct argpar_opt_descr descrs[] = {
578 { 0, 'k', NULL, true },
579 ARGPAR_OPT_DESCR_SENTINEL
580 };
581
582 test_fail(
583 "-k",
584 "While parsing argument #1 (`-k`): Missing required argument for option `-k`",
585 descrs);
586 }
587
588 /* Missing short option argument (multiple glued) */
589 {
590 const struct argpar_opt_descr descrs[] = {
591 { 0, 'a', NULL, false },
592 { 0, 'b', NULL, false },
593 { 0, 'c', NULL, true },
594 ARGPAR_OPT_DESCR_SENTINEL
595 };
596
597 test_fail(
598 "-abc",
599 "While parsing argument #1 (`-abc`): Missing required argument for option `-c`",
600 descrs);
601 }
602
603 /* Invalid `-` */
604 {
605 const struct argpar_opt_descr descrs[] = {
606 { 0, 'a', NULL, false },
607 { 0, 'b', NULL, false },
608 { 0, 'c', NULL, true },
609 ARGPAR_OPT_DESCR_SENTINEL
610 };
611
612 test_fail(
613 "-ab - -c",
614 "While parsing argument #2 (`-`): Invalid argument",
615 descrs);
616 }
617
618 /* Invalid `--` */
619 {
620 const struct argpar_opt_descr descrs[] = {
621 { 0, 'a', NULL, false },
622 { 0, 'b', NULL, false },
623 { 0, 'c', NULL, true },
624 ARGPAR_OPT_DESCR_SENTINEL
625 };
626
627 test_fail(
628 "-ab -- -c",
629 "While parsing argument #2 (`--`): Invalid argument",
630 descrs);
631 }
632
633 {
634 const struct argpar_opt_descr descrs[] = {
635 { 0, 'c', "chevre", false },
636 ARGPAR_OPT_DESCR_SENTINEL
637 };
638
639 test_fail(
640 "--chevre=fromage",
641 "While parsing argument #1 (`--chevre=fromage`): Unexpected argument for option `--chevre`",
642 descrs);
643 }
644 }
645
646 int main(void)
647 {
648 plan_tests(132);
649 succeed_tests();
650 fail_tests();
651 return exit_status();
652 }
This page took 0.040134 seconds and 3 git commands to generate.