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.
26 #include "argpar/argpar.h"
29 * Formats `item` and appends the resulting string to `res_str` to
30 * incrementally build an expected command line string.
34 * * Prefers the `--long-opt=arg` style over the `-s arg` style.
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.
40 void append_to_res_str(GString
* const res_str
,
41 const struct argpar_item
* const item
)
43 if (res_str
->len
> 0) {
44 g_string_append_c(res_str
, ' ');
47 switch (argpar_item_type(item
)) {
48 case ARGPAR_ITEM_TYPE_OPT
:
50 const struct argpar_opt_descr
* const descr
=
51 argpar_item_opt_descr(item
);
52 const char * const arg
= argpar_item_opt_arg(item
);
54 if (descr
->long_name
) {
55 g_string_append_printf(res_str
, "--%s",
59 g_string_append_printf(res_str
, "=%s", arg
);
61 } else if (descr
->short_name
) {
62 g_string_append_printf(res_str
, "-%c",
66 g_string_append_printf(res_str
, " %s", arg
);
72 case ARGPAR_ITEM_TYPE_NON_OPT
:
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
);
80 g_string_append_printf(res_str
, "%s<%u,%u>", arg
, orig_index
,
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`.
95 * This function splits `cmdline` on spaces to create an original
98 * This function builds the resulting command line from parsing items
99 * by space-separating each formatted item (see append_to_res_str()).
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
)
107 struct argpar_parse_ret parse_ret
;
108 GString
* const res_str
= g_string_new(NULL
);
109 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
114 parse_ret
= argpar_parse(g_strv_length(argv
),
115 (const char * const *) argv
, descrs
, false);
117 "argpar_parse() succeeds for command line `%s`", cmdline
);
119 "argpar_parse() doesn't set an error for command line `%s`",
121 ok(parse_ret
.ingested_orig_args
== expected_ingested_orig_args
,
122 "argpar_parse() returns the correct number of ingested "
123 "original arguments for command line `%s`", cmdline
);
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
);
130 if (!parse_ret
.items
) {
131 fail("argpar_parse() returns the expected parsing items "
132 "for command line `%s`", cmdline
);
136 for (i
= 0; i
< parse_ret
.items
->n_items
; i
++) {
137 append_to_res_str(res_str
, parse_ret
.items
->items
[i
]);
140 ok(strcmp(expected_cmd_line
, res_str
->str
) == 0,
141 "argpar_parse() returns the expected parsed arguments "
142 "for command line `%s`", cmdline
);
144 if (strcmp(expected_cmd_line
, res_str
->str
) != 0) {
145 diag("Expected: `%s`", expected_cmd_line
);
146 diag("Got: `%s`", res_str
->str
);
150 argpar_parse_ret_fini(&parse_ret
);
151 g_string_free(res_str
, TRUE
);
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`.
161 * This function splits `cmdline` on spaces to create an original
164 * This function builds the resulting command line from parsing items
165 * by space-separating each formatted item (see append_to_res_str()).
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
)
173 struct argpar_iter
*iter
= NULL
;
174 const struct argpar_item
*item
= NULL
;
176 GString
* const res_str
= g_string_new(NULL
);
177 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
178 unsigned int i
, actual_ingested_orig_args
;
182 iter
= argpar_iter_create(g_strv_length(argv
),
183 (const char * const *) argv
, descrs
);
187 enum argpar_iter_next_status status
;
189 ARGPAR_ITEM_DESTROY_AND_RESET(item
);
190 status
= argpar_iter_next(iter
, &item
, &error
);
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 "
196 "(%d) for command line `%s` (call %u)",
197 status
, cmdline
, i
+ 1);
199 if (status
== ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
) {
201 "argpar_iter_next() sets an error for "
202 "status `ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` "
203 "and command line `%s` (call %u)",
207 "argpar_iter_next() doesn't set an error "
208 "for other status than "
209 "`ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT` "
210 "and command line `%s` (call %u)",
214 if (status
== ARGPAR_ITER_NEXT_STATUS_END
||
215 status
== ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
) {
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` "
220 "and command line `%s` (call %u)",
225 append_to_res_str(res_str
, item
);
228 actual_ingested_orig_args
= argpar_iter_ingested_orig_args(iter
);
229 ok(actual_ingested_orig_args
== expected_ingested_orig_args
,
230 "argpar_iter_ingested_orig_args() returns the expected "
231 "number of ingested original arguments for command line `%s`",
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
);
239 ok(strcmp(expected_cmd_line
, res_str
->str
) == 0,
240 "argpar_iter_next() returns the expected parsing items "
241 "for command line `%s`", cmdline
);
243 if (strcmp(expected_cmd_line
, res_str
->str
) != 0) {
244 diag("Expected: `%s`", expected_cmd_line
);
245 diag("Got: `%s`", res_str
->str
);
248 argpar_item_destroy(item
);
249 argpar_iter_destroy(iter
);
250 g_string_free(res_str
, TRUE
);
256 * Calls test_succeed_argpar_parse() and test_succeed_argpar_iter()
257 * with the provided parameters.
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
)
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
);
272 void succeed_tests(void)
276 const struct argpar_opt_descr descrs
[] = {
277 ARGPAR_OPT_DESCR_SENTINEL
286 /* Single long option */
288 const struct argpar_opt_descr descrs
[] = {
289 { 0, '\0', "salut", false },
290 ARGPAR_OPT_DESCR_SENTINEL
299 /* Single short option */
301 const struct argpar_opt_descr descrs
[] = {
302 { 0, 'f', NULL
, false },
303 ARGPAR_OPT_DESCR_SENTINEL
312 /* Short and long option (aliases) */
314 const struct argpar_opt_descr descrs
[] = {
315 { 0, 'f', "flaw", false },
316 ARGPAR_OPT_DESCR_SENTINEL
325 /* Long option with argument (space form) */
327 const struct argpar_opt_descr descrs
[] = {
328 { 0, '\0', "tooth", true },
329 ARGPAR_OPT_DESCR_SENTINEL
338 /* Long option with argument (equal form) */
340 const struct argpar_opt_descr descrs
[] = {
341 { 0, '\0', "polish", true },
342 ARGPAR_OPT_DESCR_SENTINEL
351 /* Short option with argument (space form) */
353 const struct argpar_opt_descr descrs
[] = {
354 { 0, 'c', NULL
, true },
355 ARGPAR_OPT_DESCR_SENTINEL
364 /* Short option with argument (glued form) */
366 const struct argpar_opt_descr descrs
[] = {
367 { 0, 'c', NULL
, true },
368 ARGPAR_OPT_DESCR_SENTINEL
377 /* Short and long option (aliases) with argument (all forms) */
379 const struct argpar_opt_descr descrs
[] = {
380 { 0, 'd', "dry", true },
381 ARGPAR_OPT_DESCR_SENTINEL
385 "--dry=rate -dthing --dry street --dry=shape",
386 "--dry=rate --dry=thing --dry=street --dry=shape",
390 /* Many short options, last one with argument (glued form) */
392 const struct argpar_opt_descr descrs
[] = {
393 { 0, 'd', NULL
, false },
394 { 0, 'e', NULL
, false },
395 { 0, 'f', NULL
, true },
396 ARGPAR_OPT_DESCR_SENTINEL
407 const struct argpar_opt_descr descrs
[] = {
408 { 0, 'd', NULL
, false },
409 { 0, 'e', "east", true },
410 { 0, '\0', "mind", false },
411 ARGPAR_OPT_DESCR_SENTINEL
415 "-d --mind -destart --mind --east cough -d --east=itch",
416 "-d --mind -d --east=start --mind --east=cough -d --east=itch",
420 /* Single non-option argument */
422 const struct argpar_opt_descr descrs
[] = {
423 ARGPAR_OPT_DESCR_SENTINEL
432 /* Two non-option arguments */
434 const struct argpar_opt_descr descrs
[] = {
435 ARGPAR_OPT_DESCR_SENTINEL
440 "kilojoule<0,0> mitaine<1,1>",
444 /* Single non-option argument mixed with options */
446 const struct argpar_opt_descr descrs
[] = {
447 { 0, 'd', NULL
, false },
448 { 0, '\0', "squeeze", true },
449 ARGPAR_OPT_DESCR_SENTINEL
453 "-d sprout yes --squeeze little bag -d",
454 "-d sprout<1,0> yes<2,1> --squeeze=little bag<5,2> -d",
458 /* Unknown short option (space form) */
460 const struct argpar_opt_descr descrs
[] = {
461 { 0, 'd', NULL
, true },
462 ARGPAR_OPT_DESCR_SENTINEL
466 "-d salut -e -d meow",
471 /* Unknown short option (glued form) */
473 const struct argpar_opt_descr descrs
[] = {
474 { 0, 'd', NULL
, true },
475 ARGPAR_OPT_DESCR_SENTINEL
479 "-dsalut -e -d meow",
484 /* Unknown long option (space form) */
486 const struct argpar_opt_descr descrs
[] = {
487 { 0, '\0', "sink", true },
488 ARGPAR_OPT_DESCR_SENTINEL
492 "--sink party --food --sink impulse",
497 /* Unknown long option (equal form) */
499 const struct argpar_opt_descr descrs
[] = {
500 { 0, '\0', "sink", true },
501 ARGPAR_OPT_DESCR_SENTINEL
505 "--sink=party --food --sink=impulse",
510 /* Unknown option before non-option argument */
512 const struct argpar_opt_descr descrs
[] = {
513 { 0, '\0', "thumb", true },
514 ARGPAR_OPT_DESCR_SENTINEL
518 "--thumb=party --food bateau --thumb waves",
523 /* Unknown option after non-option argument */
525 const struct argpar_opt_descr descrs
[] = {
526 { 0, '\0', "thumb", true },
527 ARGPAR_OPT_DESCR_SENTINEL
531 "--thumb=party wound --food --thumb waves",
532 "--thumb=party wound<1,0>",
538 const struct argpar_opt_descr descrs
[] = {
539 { 0, '\0', "-fuel", true },
540 ARGPAR_OPT_DESCR_SENTINEL
549 /* Long option containing `=` in argument (equal form) */
551 const struct argpar_opt_descr descrs
[] = {
552 { 0, '\0', "zebra", true },
553 ARGPAR_OPT_DESCR_SENTINEL
562 /* Short option's argument starting with `-` (glued form) */
564 const struct argpar_opt_descr descrs
[] = {
565 { 0, 'z', NULL
, true },
566 ARGPAR_OPT_DESCR_SENTINEL
575 /* Short option's argument starting with `-` (space form) */
577 const struct argpar_opt_descr descrs
[] = {
578 { 0, 'z', NULL
, true },
579 ARGPAR_OPT_DESCR_SENTINEL
588 /* Long option's argument starting with `-` (space form) */
590 const struct argpar_opt_descr descrs
[] = {
591 { 0, '\0', "janine", true },
592 ARGPAR_OPT_DESCR_SENTINEL
601 /* Long option's argument starting with `-` (equal form) */
603 const struct argpar_opt_descr descrs
[] = {
604 { 0, '\0', "janine", true },
605 ARGPAR_OPT_DESCR_SENTINEL
614 /* Long option's empty argument (equal form) */
616 const struct argpar_opt_descr descrs
[] = {
617 { 0, 'f', NULL
, false },
618 { 0, '\0', "yeah", true },
619 ARGPAR_OPT_DESCR_SENTINEL
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`.
634 * This function splits `cmdline` on spaces to create an original
638 void test_fail_argpar_parse(const char * const cmdline
,
639 const char * const expected_error
,
640 const struct argpar_opt_descr
* const descrs
)
642 struct argpar_parse_ret parse_ret
;
643 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
645 parse_ret
= argpar_parse(g_strv_length(argv
),
646 (const char * const *) argv
, descrs
, true);
648 "argpar_parse() fails for command line `%s`", cmdline
);
650 "argpar_parse() sets an error string for command line `%s`",
653 if (parse_ret
.items
) {
654 fail("argpar_parse() sets the expected error string");
658 ok(strcmp(expected_error
, parse_ret
.error
) == 0,
659 "argpar_parse() sets the expected error string "
660 "for command line `%s`", cmdline
);
662 if (strcmp(expected_error
, parse_ret
.error
) != 0) {
663 diag("Expected: `%s`", expected_error
);
664 diag("Got: `%s`", parse_ret
.error
);
668 argpar_parse_ret_fini(&parse_ret
);
673 * Parses `cmdline` with the iterator API using the option descriptors
674 * `descrs`, and ensures that argpar_iter_next() fails with status
675 * `expected_status` and that it sets an error which is equal to
678 * This function splits `cmdline` on spaces to create an original
682 void test_fail_argpar_iter(const char * const cmdline
,
683 const char * const expected_error
,
684 const enum argpar_iter_next_status expected_status
,
685 const struct argpar_opt_descr
* const descrs
)
687 struct argpar_iter
*iter
= NULL
;
688 const struct argpar_item
*item
= NULL
;
689 gchar
** const argv
= g_strsplit(cmdline
, " ", 0);
693 iter
= argpar_iter_create(g_strv_length(argv
),
694 (const char * const *) argv
, descrs
);
698 enum argpar_iter_next_status status
;
700 ARGPAR_ITEM_DESTROY_AND_RESET(item
);
701 status
= argpar_iter_next(iter
, &item
, &error
);
702 ok(status
== ARGPAR_ITER_NEXT_STATUS_OK
||
703 status
== expected_status
,
704 "argpar_iter_next() returns the expected status "
705 "(%d) for command line `%s` (call %u)",
706 status
, cmdline
, i
+ 1);
708 if (status
!= ARGPAR_ITER_NEXT_STATUS_OK
) {
710 "argpar_iter_next() doesn't set an item "
711 "for other status than "
712 "`ARGPAR_ITER_NEXT_STATUS_OK` "
713 "and command line `%s` (call %u)",
716 "argpar_iter_next() sets an error for "
718 " `ARGPAR_ITER_NEXT_STATUS_OK` "
719 "and command line `%s` (call %u)",
725 "argpar_iter_next() sets an item for status "
726 "`ARGPAR_ITER_NEXT_STATUS_OK` "
727 "and command line `%s` (call %u)",
730 "argpar_iter_next() doesn't set an error for status "
731 "`ARGPAR_ITER_NEXT_STATUS_OK` "
732 "and command line `%s` (call %u)",
736 ok(strcmp(expected_error
, error
) == 0,
737 "argpar_iter_next() sets the expected error string "
738 "for command line `%s`", cmdline
);
740 if (strcmp(expected_error
, error
) != 0) {
741 diag("Expected: `%s`", expected_error
);
742 diag("Got: `%s`", error
);
745 argpar_item_destroy(item
);
746 argpar_iter_destroy(iter
);
752 * Calls test_fail_argpar_parse() and test_fail_argpar_iter() with the
753 * provided parameters.
756 void test_fail(const char * const cmdline
, const char * const expected_error
,
757 const enum argpar_iter_next_status expected_iter_next_status
,
758 const struct argpar_opt_descr
* const descrs
)
760 test_fail_argpar_parse(cmdline
, expected_error
, descrs
);
761 test_fail_argpar_iter(cmdline
, expected_error
,
762 expected_iter_next_status
, descrs
);
766 void fail_tests(void)
768 /* Unknown long option */
770 const struct argpar_opt_descr descrs
[] = {
771 { 0, '\0', "thumb", true },
772 ARGPAR_OPT_DESCR_SENTINEL
776 "--thumb=party --meow",
777 "While parsing argument #2 (`--meow`): Unknown option `--meow`",
778 ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
,
782 /* Unknown short option */
784 const struct argpar_opt_descr descrs
[] = {
785 { 0, '\0', "thumb", true },
786 ARGPAR_OPT_DESCR_SENTINEL
791 "While parsing argument #2 (`-x`): Unknown option `-x`",
792 ARGPAR_ITER_NEXT_STATUS_ERROR_UNKNOWN_OPT
,
796 /* Missing long option argument */
798 const struct argpar_opt_descr descrs
[] = {
799 { 0, '\0', "thumb", true },
800 ARGPAR_OPT_DESCR_SENTINEL
805 "While parsing argument #1 (`--thumb`): Missing required argument for option `--thumb`",
806 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
810 /* Missing short option argument */
812 const struct argpar_opt_descr descrs
[] = {
813 { 0, 'k', NULL
, true },
814 ARGPAR_OPT_DESCR_SENTINEL
819 "While parsing argument #1 (`-k`): Missing required argument for option `-k`",
820 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
824 /* Missing short option argument (multiple glued) */
826 const struct argpar_opt_descr descrs
[] = {
827 { 0, 'a', NULL
, false },
828 { 0, 'b', NULL
, false },
829 { 0, 'c', NULL
, true },
830 ARGPAR_OPT_DESCR_SENTINEL
835 "While parsing argument #1 (`-abc`): Missing required argument for option `-c`",
836 ARGPAR_ITER_NEXT_STATUS_ERROR_MISSING_OPT_ARG
,
842 const struct argpar_opt_descr descrs
[] = {
843 { 0, 'a', NULL
, false },
844 { 0, 'b', NULL
, false },
845 { 0, 'c', NULL
, true },
846 ARGPAR_OPT_DESCR_SENTINEL
851 "While parsing argument #2 (`-`): Invalid argument",
852 ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG
,
858 const struct argpar_opt_descr descrs
[] = {
859 { 0, 'a', NULL
, false },
860 { 0, 'b', NULL
, false },
861 { 0, 'c', NULL
, true },
862 ARGPAR_OPT_DESCR_SENTINEL
867 "While parsing argument #2 (`--`): Invalid argument",
868 ARGPAR_ITER_NEXT_STATUS_ERROR_INVALID_ARG
,
873 const struct argpar_opt_descr descrs
[] = {
874 { 0, 'c', "chevre", false },
875 ARGPAR_OPT_DESCR_SENTINEL
880 "While parsing argument #1 (`--chevre=fromage`): Unexpected argument for option `--chevre`",
881 ARGPAR_ITER_NEXT_STATUS_ERROR_UNEXPECTED_OPT_ARG
,
891 return exit_status();