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