2 * Copyright (c) 2019-2021 Philippe Proulx <pproulx@efficios.com>
3 * Copyright (c) 2020-2021 Simon Marchi <simon.marchi@efficios.com>
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.
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.
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.
27 #include "argpar/argpar.h"
30 * Formats `item` and appends the resulting string to `res_str` to
31 * incrementally build an expected command line string.
35 * * Prefers the `--long-opt=arg` style over the `-s arg` style.
37 * * Uses the `arg<A,B>` form for non-option arguments, where `A` is the
38 * original argument index and `B` is the non-option argument index.
41 void append_to_res_str(GString
* const res_str
,
42 const struct argpar_item
* const item
)
44 if (res_str
->len
> 0) {
45 g_string_append_c(res_str
, ' ');
48 switch (argpar_item_type(item
)) {
49 case ARGPAR_ITEM_TYPE_OPT
:
51 const struct argpar_opt_descr
* const descr
=
52 argpar_item_opt_descr(item
);
53 const char * const arg
= argpar_item_opt_arg(item
);
55 if (descr
->long_name
) {
56 g_string_append_printf(res_str
, "--%s",
60 g_string_append_printf(res_str
, "=%s", arg
);
62 } else if (descr
->short_name
) {
63 g_string_append_printf(res_str
, "-%c",
67 g_string_append_printf(res_str
, " %s", arg
);
73 case ARGPAR_ITEM_TYPE_NON_OPT
:
75 const char * const arg
= argpar_item_non_opt_arg(item
);
76 const unsigned int orig_index
=
77 argpar_item_non_opt_orig_index(item
);
78 const unsigned int non_opt_index
=
79 argpar_item_non_opt_non_opt_index(item
);
81 g_string_append_printf(res_str
, "%s<%u,%u>", arg
, orig_index
,
91 * Parses `cmdline` with the argpar API using the option descriptors
92 * `descrs`, and ensures that the resulting effective command line is
93 * `expected_cmd_line` and that the number of ingested original
94 * arguments is `expected_ingested_orig_args`.
96 * This function splits `cmdline` on spaces to create an original
99 * This function builds the resulting command line from parsing items
100 * by space-separating each formatted item (see append_to_res_str()).
103 void test_succeed(const char * const cmdline
,
104 const char * const expected_cmd_line
,
105 const struct argpar_opt_descr
* const descrs
,
106 const unsigned int expected_ingested_orig_args
)
108 struct argpar_iter
*iter
= NULL
;
109 const struct argpar_item
*item
= NULL
;
111 GString
* const res_str
= g_string_new(NULL
);
112 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
113 unsigned int i
, actual_ingested_orig_args
;
117 iter
= argpar_iter_create(g_strv_length(argv
),
118 (const char * const *) argv
, descrs
);
122 enum argpar_iter_next_status status
;
124 ARGPAR_ITEM_DESTROY_AND_RESET(item
);
125 status
= argpar_iter_next(iter
, &item
, &error
);
127 ok(status
== ARGPAR_ITER_NEXT_STATUS_OK
||
128 status
== ARGPAR_ITER_NEXT_STATUS_END
||
129 status
== ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
,
130 "argpar_iter_next() returns the expected status "
131 "(%d) for command line `%s` (call %u)",
132 status
, cmdline
, i
+ 1);
134 if (status
== ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
) {
136 "argpar_iter_next() sets an error for "
137 "status `ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` "
138 "and command line `%s` (call %u)",
142 "argpar_iter_next() doesn't set an error "
143 "for other status than "
144 "`ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` "
145 "and command line `%s` (call %u)",
149 if (status
== ARGPAR_ITER_NEXT_STATUS_END
||
150 status
== ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
) {
152 "argpar_iter_next() doesn't set an item "
153 "for status `ARGPAR_ITER_NEXT_STATUS_END` "
154 "or `ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` "
155 "and command line `%s` (call %u)",
160 append_to_res_str(res_str
, item
);
163 actual_ingested_orig_args
= argpar_iter_ingested_orig_args(iter
);
164 ok(actual_ingested_orig_args
== expected_ingested_orig_args
,
165 "argpar_iter_ingested_orig_args() returns the expected "
166 "number of ingested original arguments for command line `%s`",
169 if (actual_ingested_orig_args
!= expected_ingested_orig_args
) {
170 diag("Expected: %u Got: %u", expected_ingested_orig_args
,
171 actual_ingested_orig_args
);
174 ok(strcmp(expected_cmd_line
, res_str
->str
) == 0,
175 "argpar_iter_next() returns the expected parsing items "
176 "for command line `%s`", cmdline
);
178 if (strcmp(expected_cmd_line
, res_str
->str
) != 0) {
179 diag("Expected: `%s`", expected_cmd_line
);
180 diag("Got: `%s`", res_str
->str
);
183 argpar_item_destroy(item
);
184 argpar_iter_destroy(iter
);
185 g_string_free(res_str
, TRUE
);
191 void succeed_tests(void)
195 const struct argpar_opt_descr descrs
[] = {
196 ARGPAR_OPT_DESCR_SENTINEL
205 /* Single long option */
207 const struct argpar_opt_descr descrs
[] = {
208 { 0, '\0', "salut", false },
209 ARGPAR_OPT_DESCR_SENTINEL
218 /* Single short option */
220 const struct argpar_opt_descr descrs
[] = {
221 { 0, 'f', NULL
, false },
222 ARGPAR_OPT_DESCR_SENTINEL
231 /* Short and long option (aliases) */
233 const struct argpar_opt_descr descrs
[] = {
234 { 0, 'f', "flaw", false },
235 ARGPAR_OPT_DESCR_SENTINEL
244 /* Long option with argument (space form) */
246 const struct argpar_opt_descr descrs
[] = {
247 { 0, '\0', "tooth", true },
248 ARGPAR_OPT_DESCR_SENTINEL
257 /* Long option with argument (equal form) */
259 const struct argpar_opt_descr descrs
[] = {
260 { 0, '\0', "polish", true },
261 ARGPAR_OPT_DESCR_SENTINEL
270 /* Short option with argument (space form) */
272 const struct argpar_opt_descr descrs
[] = {
273 { 0, 'c', NULL
, true },
274 ARGPAR_OPT_DESCR_SENTINEL
283 /* Short option with argument (glued form) */
285 const struct argpar_opt_descr descrs
[] = {
286 { 0, 'c', NULL
, true },
287 ARGPAR_OPT_DESCR_SENTINEL
296 /* Short and long option (aliases) with argument (all forms) */
298 const struct argpar_opt_descr descrs
[] = {
299 { 0, 'd', "dry", true },
300 ARGPAR_OPT_DESCR_SENTINEL
304 "--dry=rate -dthing --dry street --dry=shape",
305 "--dry=rate --dry=thing --dry=street --dry=shape",
309 /* Many short options, last one with argument (glued form) */
311 const struct argpar_opt_descr descrs
[] = {
312 { 0, 'd', NULL
, false },
313 { 0, 'e', NULL
, false },
314 { 0, 'f', NULL
, true },
315 ARGPAR_OPT_DESCR_SENTINEL
326 const struct argpar_opt_descr descrs
[] = {
327 { 0, 'd', NULL
, false },
328 { 0, 'e', "east", true },
329 { 0, '\0', "mind", false },
330 ARGPAR_OPT_DESCR_SENTINEL
334 "-d --mind -destart --mind --east cough -d --east=itch",
335 "-d --mind -d --east=start --mind --east=cough -d --east=itch",
339 /* Single non-option argument */
341 const struct argpar_opt_descr descrs
[] = {
342 ARGPAR_OPT_DESCR_SENTINEL
351 /* Two non-option arguments */
353 const struct argpar_opt_descr descrs
[] = {
354 ARGPAR_OPT_DESCR_SENTINEL
359 "kilojoule<0,0> mitaine<1,1>",
363 /* Single non-option argument mixed with options */
365 const struct argpar_opt_descr descrs
[] = {
366 { 0, 'd', NULL
, false },
367 { 0, '\0', "squeeze", true },
368 ARGPAR_OPT_DESCR_SENTINEL
372 "-d sprout yes --squeeze little bag -d",
373 "-d sprout<1,0> yes<2,1> --squeeze=little bag<5,2> -d",
377 /* Unknown short option (space form) */
379 const struct argpar_opt_descr descrs
[] = {
380 { 0, 'd', NULL
, true },
381 ARGPAR_OPT_DESCR_SENTINEL
385 "-d salut -e -d meow",
390 /* Unknown short option (glued form) */
392 const struct argpar_opt_descr descrs
[] = {
393 { 0, 'd', NULL
, true },
394 ARGPAR_OPT_DESCR_SENTINEL
398 "-dsalut -e -d meow",
403 /* Unknown long option (space form) */
405 const struct argpar_opt_descr descrs
[] = {
406 { 0, '\0', "sink", true },
407 ARGPAR_OPT_DESCR_SENTINEL
411 "--sink party --food --sink impulse",
416 /* Unknown long option (equal form) */
418 const struct argpar_opt_descr descrs
[] = {
419 { 0, '\0', "sink", true },
420 ARGPAR_OPT_DESCR_SENTINEL
424 "--sink=party --food --sink=impulse",
429 /* Unknown option before non-option argument */
431 const struct argpar_opt_descr descrs
[] = {
432 { 0, '\0', "thumb", true },
433 ARGPAR_OPT_DESCR_SENTINEL
437 "--thumb=party --food bateau --thumb waves",
442 /* Unknown option after non-option argument */
444 const struct argpar_opt_descr descrs
[] = {
445 { 0, '\0', "thumb", true },
446 ARGPAR_OPT_DESCR_SENTINEL
450 "--thumb=party wound --food --thumb waves",
451 "--thumb=party wound<1,0>",
457 const struct argpar_opt_descr descrs
[] = {
458 { 0, '\0', "-fuel", true },
459 ARGPAR_OPT_DESCR_SENTINEL
468 /* Long option containing `=` in argument (equal form) */
470 const struct argpar_opt_descr descrs
[] = {
471 { 0, '\0', "zebra", true },
472 ARGPAR_OPT_DESCR_SENTINEL
481 /* Short option's argument starting with `-` (glued form) */
483 const struct argpar_opt_descr descrs
[] = {
484 { 0, 'z', NULL
, true },
485 ARGPAR_OPT_DESCR_SENTINEL
494 /* Short option's argument starting with `-` (space form) */
496 const struct argpar_opt_descr descrs
[] = {
497 { 0, 'z', NULL
, true },
498 ARGPAR_OPT_DESCR_SENTINEL
507 /* Long option's argument starting with `-` (space form) */
509 const struct argpar_opt_descr descrs
[] = {
510 { 0, '\0', "janine", true },
511 ARGPAR_OPT_DESCR_SENTINEL
520 /* Long option's argument starting with `-` (equal form) */
522 const struct argpar_opt_descr descrs
[] = {
523 { 0, '\0', "janine", true },
524 ARGPAR_OPT_DESCR_SENTINEL
533 /* Long option's empty argument (equal form) */
535 const struct argpar_opt_descr descrs
[] = {
536 { 0, 'f', NULL
, false },
537 { 0, '\0', "yeah", true },
538 ARGPAR_OPT_DESCR_SENTINEL
547 /* `-` non-option argument */
549 const struct argpar_opt_descr descrs
[] = {
550 { 0, 'f', NULL
, false },
551 ARGPAR_OPT_DESCR_SENTINEL
560 /* `--` non-option argument */
562 const struct argpar_opt_descr descrs
[] = {
563 { 0, 'f', NULL
, false },
564 ARGPAR_OPT_DESCR_SENTINEL
573 /* Very long name of long option */
575 const char opt_name
[] =
576 "kale-chips-waistcoat-yr-bicycle-rights-gochujang-"
577 "woke-tumeric-flexitarian-biodiesel-chillwave-cliche-"
578 "ethical-cardigan-listicle-pok-pok-sustainable-live-"
579 "edge-jianbing-gochujang-butcher-disrupt-tattooed-"
580 "tumeric-prism-photo-booth-vape-kogi-jean-shorts-"
581 "blog-williamsburg-fingerstache-palo-santo-artisan-"
582 "affogato-occupy-skateboard-adaptogen-neutra-celiac-"
583 "put-a-bird-on-it-kombucha-everyday-carry-hot-chicken-"
584 "craft-beer-subway-tile-tote-bag-disrupt-selvage-"
585 "raclette-art-party-readymade-paleo-heirloom-trust-"
586 "fund-small-batch-kinfolk-woke-cardigan-prism-"
587 "chambray-la-croix-hashtag-unicorn-edison-bulb-tbh-"
588 "cornhole-cliche-tattooed-green-juice-adaptogen-"
589 "kitsch-lo-fi-vexillologist-migas-gentrify-"
591 const struct argpar_opt_descr descrs
[] = {
592 { 0, '\0', opt_name
, true },
593 ARGPAR_OPT_DESCR_SENTINEL
597 sprintf(cmdline
, "--%s=23", opt_name
);
598 test_succeed(cmdline
, cmdline
, descrs
, 1);
603 * Parses `cmdline` with the argpar API using the option descriptors
604 * `descrs`, and ensures that argpar_iter_next() fails with status
605 * `expected_status` and that it sets an error which is equal to
608 * This function splits `cmdline` on spaces to create an original
612 void test_fail(const char * const cmdline
, const char * const expected_error
,
613 const enum argpar_iter_next_status expected_status
,
614 const struct argpar_opt_descr
* const descrs
)
616 struct argpar_iter
*iter
= NULL
;
617 const struct argpar_item
*item
= NULL
;
618 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
622 iter
= argpar_iter_create(g_strv_length(argv
),
623 (const char * const *) argv
, descrs
);
627 enum argpar_iter_next_status status
;
629 ARGPAR_ITEM_DESTROY_AND_RESET(item
);
630 status
= argpar_iter_next(iter
, &item
, &error
);
631 ok(status
== ARGPAR_ITER_NEXT_STATUS_OK
||
632 status
== expected_status
,
633 "argpar_iter_next() returns the expected status "
634 "(%d) for command line `%s` (call %u)",
635 status
, cmdline
, i
+ 1);
637 if (status
!= ARGPAR_ITER_NEXT_STATUS_OK
) {
639 "argpar_iter_next() doesn't set an item "
640 "for other status than "
641 "`ARGPAR_ITER_NEXT_STATUS_OK` "
642 "and command line `%s` (call %u)",
645 "argpar_iter_next() sets an error for "
647 " `ARGPAR_ITER_NEXT_STATUS_OK` "
648 "and command line `%s` (call %u)",
654 "argpar_iter_next() sets an item for status "
655 "`ARGPAR_ITER_NEXT_STATUS_OK` "
656 "and command line `%s` (call %u)",
659 "argpar_iter_next() doesn't set an error for status "
660 "`ARGPAR_ITER_NEXT_STATUS_OK` "
661 "and command line `%s` (call %u)",
665 ok(strcmp(expected_error
, error
) == 0,
666 "argpar_iter_next() sets the expected error string "
667 "for command line `%s`", cmdline
);
669 if (strcmp(expected_error
, error
) != 0) {
670 diag("Expected: `%s`", expected_error
);
671 diag("Got: `%s`", error
);
674 argpar_item_destroy(item
);
675 argpar_iter_destroy(iter
);
681 void fail_tests(void)
683 /* Unknown long option */
685 const struct argpar_opt_descr descrs
[] = {
686 { 0, '\0', "thumb", true },
687 ARGPAR_OPT_DESCR_SENTINEL
691 "--thumb=party --meow",
692 "While parsing argument #2 (`--meow`): Unknown option `--meow`",
693 ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
,
697 /* Unknown short option */
699 const struct argpar_opt_descr descrs
[] = {
700 { 0, '\0', "thumb", true },
701 ARGPAR_OPT_DESCR_SENTINEL
706 "While parsing argument #2 (`-x`): Unknown option `-x`",
707 ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
,
711 /* Missing long option argument */
713 const struct argpar_opt_descr descrs
[] = {
714 { 0, '\0', "thumb", true },
715 ARGPAR_OPT_DESCR_SENTINEL
720 "While parsing argument #1 (`--thumb`): Missing required argument for option `--thumb`",
721 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
725 /* Missing short option argument */
727 const struct argpar_opt_descr descrs
[] = {
728 { 0, 'k', NULL
, true },
729 ARGPAR_OPT_DESCR_SENTINEL
734 "While parsing argument #1 (`-k`): Missing required argument for option `-k`",
735 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
739 /* Missing short option argument (multiple glued) */
741 const struct argpar_opt_descr descrs
[] = {
742 { 0, 'a', NULL
, false },
743 { 0, 'b', NULL
, false },
744 { 0, 'c', NULL
, true },
745 ARGPAR_OPT_DESCR_SENTINEL
750 "While parsing argument #1 (`-abc`): Missing required argument for option `-c`",
751 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
755 /* Unexpected long option argument */
757 const struct argpar_opt_descr descrs
[] = {
758 { 0, 'c', "chevre", false },
759 ARGPAR_OPT_DESCR_SENTINEL
764 "While parsing argument #1 (`--chevre=fromage`): Unexpected argument for option `--chevre`",
765 ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG
,
775 return exit_status();