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