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