webrtc-streamer 0.8.13 → 0.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1193 @@
1
+ #include "catch.hpp"
2
+ #include <iostream>
3
+
4
+ #include <initializer_list>
5
+
6
+ #include "cxxopts.hpp"
7
+
8
+ class Argv {
9
+ public:
10
+
11
+ Argv(std::initializer_list<const char*> args)
12
+ : m_argv(new const char*[args.size()])
13
+ , m_argc(static_cast<int>(args.size()))
14
+ {
15
+ int i = 0;
16
+ auto iter = args.begin();
17
+ while (iter != args.end()) {
18
+ auto len = strlen(*iter) + 1;
19
+ auto ptr = std::unique_ptr<char[]>(new char[len]);
20
+
21
+ strcpy(ptr.get(), *iter);
22
+ m_args.push_back(std::move(ptr));
23
+ m_argv.get()[i] = m_args.back().get();
24
+
25
+ ++iter;
26
+ ++i;
27
+ }
28
+ }
29
+
30
+ const char** argv() const {
31
+ return m_argv.get();
32
+ }
33
+
34
+ int argc() const {
35
+ return m_argc;
36
+ }
37
+
38
+ private:
39
+
40
+ std::vector<std::unique_ptr<char[]>> m_args{};
41
+ std::unique_ptr<const char*[]> m_argv;
42
+ int m_argc;
43
+ };
44
+
45
+ TEST_CASE("Basic options", "[options]")
46
+ {
47
+
48
+ cxxopts::Options options("tester", " - test basic options");
49
+
50
+ options.add_options()
51
+ ("long", "a long option")
52
+ ("s,short", "a short option")
53
+ ("quick,brown", "An option with multiple long names and no short name")
54
+ ("f,ox,jumped", "An option with multiple long names and a short name")
55
+ ("over,z,lazy,dog", "An option with multiple long names and a short name, not listed first")
56
+ ("value", "an option with a value", cxxopts::value<std::string>())
57
+ ("a,av", "a short option with a value", cxxopts::value<std::string>())
58
+ ("6,six", "a short number option")
59
+ ("p, space", "an option with space between short and long")
60
+ ("period.delimited", "an option with a period in the long name")
61
+ ("nothing", "won't exist", cxxopts::value<std::string>())
62
+ ;
63
+
64
+ Argv argv({
65
+ "tester",
66
+ "--long",
67
+ "-s",
68
+ "--value",
69
+ "value",
70
+ "-a",
71
+ "b",
72
+ "-6",
73
+ "-p",
74
+ "--space",
75
+ "--quick",
76
+ "--ox",
77
+ "-f",
78
+ "--brown",
79
+ "-z",
80
+ "--over",
81
+ "--dog",
82
+ "--lazy",
83
+ "--period.delimited",
84
+ });
85
+
86
+ auto** actual_argv = argv.argv();
87
+ auto argc = argv.argc();
88
+
89
+ auto result = options.parse(argc, actual_argv);
90
+
91
+ CHECK(result.count("long") == 1);
92
+ CHECK(result.contains("long"));
93
+ CHECK(result.count("s") == 1);
94
+ CHECK(result.count("value") == 1);
95
+ CHECK(result.count("a") == 1);
96
+ CHECK(result["value"].as<std::string>() == "value");
97
+ CHECK(result["a"].as<std::string>() == "b");
98
+ CHECK(result.count("6") == 1);
99
+ CHECK(result.count("p") == 2);
100
+ CHECK(result.count("space") == 2);
101
+ CHECK(result.count("quick") == 2);
102
+ CHECK(result.count("f") == 2);
103
+ CHECK(result.count("z") == 4);
104
+ CHECK(result.count("period.delimited") == 1);
105
+
106
+ auto& arguments = result.arguments();
107
+ REQUIRE(arguments.size() == 16);
108
+ CHECK(arguments[0].key() == "long");
109
+ CHECK(arguments[0].value() == "true");
110
+ CHECK(arguments[0].as<bool>() == true);
111
+
112
+ CHECK(arguments[1].key() == "short");
113
+ CHECK(arguments[2].key() == "value");
114
+ CHECK(arguments[3].key() == "av");
115
+
116
+ CHECK(result.count("nothing") == 0);
117
+ CHECK_FALSE(result.contains("nothing"));
118
+ CHECK_THROWS_AS(result["nothing"].as<std::string>(), cxxopts::exceptions::option_has_no_value);
119
+
120
+ CHECK(options.program() == "tester");
121
+ }
122
+
123
+ TEST_CASE("Short options", "[options]")
124
+ {
125
+ cxxopts::Options options("test_short", " - test short options");
126
+
127
+ options.add_options()
128
+ ("a", "a short option", cxxopts::value<std::string>())
129
+ ("b", "b option")
130
+ ("c", "c option", cxxopts::value<std::string>());
131
+
132
+ Argv argv({"test_short", "-a", "value", "-bcfoo=something"});
133
+
134
+ auto actual_argv = argv.argv();
135
+ auto argc = argv.argc();
136
+
137
+ auto result = options.parse(argc, actual_argv);
138
+
139
+ CHECK(result.count("a") == 1);
140
+ CHECK(result["a"].as<std::string>() == "value");
141
+
142
+ auto& arguments = result.arguments();
143
+ REQUIRE(arguments.size() == 3);
144
+ CHECK(arguments[0].key() == "a");
145
+ CHECK(arguments[0].value() == "value");
146
+
147
+ CHECK(result.count("b") == 1);
148
+ CHECK(result.count("c") == 1);
149
+
150
+ CHECK(result["c"].as<std::string>() == "foo=something");
151
+
152
+ REQUIRE_THROWS_AS(options.add_options()("", "nothing option"),
153
+ cxxopts::exceptions::invalid_option_format);
154
+ }
155
+
156
+ TEST_CASE("No positional", "[positional]")
157
+ {
158
+ cxxopts::Options options("test_no_positional",
159
+ " - test no positional options");
160
+
161
+ Argv av({"tester", "a", "b", "def"});
162
+
163
+ auto** argv = av.argv();
164
+ auto argc = av.argc();
165
+ auto result = options.parse(argc, argv);
166
+
167
+ REQUIRE(argc == 4);
168
+ CHECK(strcmp(argv[1], "a") == 0);
169
+ }
170
+
171
+ TEST_CASE("All positional", "[positional]")
172
+ {
173
+ std::vector<std::string> positional;
174
+
175
+ cxxopts::Options options("test_all_positional", " - test all positional");
176
+ options.add_options()
177
+ ("positional", "Positional parameters",
178
+ cxxopts::value<std::vector<std::string>>(positional))
179
+ ;
180
+
181
+ Argv av({"tester", "a", "b", "c"});
182
+
183
+ auto argc = av.argc();
184
+ auto argv = av.argv();
185
+
186
+ std::vector<std::string> pos_names = {"positional"};
187
+
188
+ options.parse_positional(pos_names.begin(), pos_names.end());
189
+
190
+ auto result = options.parse(argc, argv);
191
+
192
+ CHECK(result.unmatched().size() == 0);
193
+ REQUIRE(positional.size() == 3);
194
+
195
+ CHECK(positional[0] == "a");
196
+ CHECK(positional[1] == "b");
197
+ CHECK(positional[2] == "c");
198
+ }
199
+
200
+ TEST_CASE("Some positional explicit", "[positional]")
201
+ {
202
+ cxxopts::Options options("positional_explicit", " - test positional");
203
+
204
+ options.add_options()
205
+ ("input", "Input file", cxxopts::value<std::string>())
206
+ ("output", "Output file", cxxopts::value<std::string>())
207
+ ("positional", "Positional parameters",
208
+ cxxopts::value<std::vector<std::string>>())
209
+ ;
210
+
211
+ options.parse_positional({"input", "output", "positional"});
212
+
213
+ Argv av({"tester", "--output", "a", "b", "c", "d"});
214
+
215
+ auto** argv = av.argv();
216
+ auto argc = av.argc();
217
+
218
+ auto result = options.parse(argc, argv);
219
+
220
+ CHECK(result.unmatched().size() == 0);
221
+ CHECK(result.count("output"));
222
+ CHECK(result["input"].as<std::string>() == "b");
223
+ CHECK(result["output"].as<std::string>() == "a");
224
+
225
+ auto& positional = result["positional"].as<std::vector<std::string>>();
226
+
227
+ REQUIRE(positional.size() == 2);
228
+ CHECK(positional[0] == "c");
229
+ CHECK(positional[1] == "d");
230
+ }
231
+
232
+ TEST_CASE("No positional with extras", "[positional]")
233
+ {
234
+ cxxopts::Options options("posargmaster", "shows incorrect handling");
235
+ options.add_options()
236
+ ("dummy", "oh no", cxxopts::value<std::string>())
237
+ ;
238
+
239
+ Argv av({"extras", "--", "a", "b", "c", "d"});
240
+
241
+ auto** argv = av.argv();
242
+ auto argc = av.argc();
243
+
244
+ auto result = options.parse(argc, argv);
245
+
246
+ auto& unmatched = result.unmatched();
247
+ CHECK((unmatched == std::vector<std::string>{"a", "b", "c", "d"}));
248
+ }
249
+
250
+ TEST_CASE("Positional not valid", "[positional]") {
251
+ cxxopts::Options options("positional_invalid", "invalid positional argument");
252
+ options.add_options()
253
+ ("long", "a long option", cxxopts::value<std::string>())
254
+ ;
255
+
256
+ options.parse_positional("something");
257
+
258
+ Argv av({"foobar", "bar", "baz"});
259
+
260
+ auto** argv = av.argv();
261
+ auto argc = av.argc();
262
+
263
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
264
+ }
265
+
266
+ TEST_CASE("Positional with empty arguments", "[positional]") {
267
+ cxxopts::Options options("positional_with_empty_arguments", "positional with empty argument");
268
+ options.add_options()
269
+ ("long", "a long option", cxxopts::value<std::string>())
270
+ ("program", "program to run", cxxopts::value<std::string>())
271
+ ("programArgs", "program arguments", cxxopts::value<std::vector<std::string>>())
272
+ ;
273
+
274
+ options.parse_positional("program", "programArgs");
275
+
276
+ Argv av({"foobar", "--long", "long_value", "--", "someProgram", "ab", "-c", "d", "--ef", "gh", "--ijk=lm", "n", "", "o", });
277
+ std::vector<std::string> expected({"ab", "-c", "d", "--ef", "gh", "--ijk=lm", "n", "", "o", });
278
+
279
+ auto** argv = av.argv();
280
+ auto argc = av.argc();
281
+
282
+ auto result = options.parse(argc, argv);
283
+ auto actual = result["programArgs"].as<std::vector<std::string>>();
284
+
285
+ REQUIRE(result.count("program") == 1);
286
+ REQUIRE(result["program"].as<std::string>() == "someProgram");
287
+ REQUIRE(result.count("programArgs") == expected.size());
288
+ REQUIRE(actual == expected);
289
+ }
290
+
291
+ TEST_CASE("Positional with list delimiter", "[positional]") {
292
+ std::string single;
293
+ std::vector<std::string> positional;
294
+
295
+ cxxopts::Options options("test_all_positional_list_delimiter", " - test all positional with list delimiters");
296
+ options.add_options()
297
+ ("single", "Single positional param",
298
+ cxxopts::value<std::string>(single))
299
+ ("positional", "Positional parameters vector",
300
+ cxxopts::value<std::vector<std::string>>(positional))
301
+ ;
302
+
303
+ Argv av({"tester", "a,b", "c,d", "e"});
304
+
305
+ auto argc = av.argc();
306
+ auto argv = av.argv();
307
+
308
+ std::vector<std::string> pos_names = {"single", "positional"};
309
+
310
+ options.parse_positional(pos_names.begin(), pos_names.end());
311
+
312
+ auto result = options.parse(argc, argv);
313
+
314
+ CHECK(result.unmatched().size() == 0);
315
+ REQUIRE(positional.size() == 2);
316
+
317
+ CHECK(single == "a,b");
318
+ CHECK(positional[0] == "c,d");
319
+ CHECK(positional[1] == "e");
320
+ }
321
+
322
+ TEST_CASE("Empty with implicit value", "[implicit]")
323
+ {
324
+ cxxopts::Options options("empty_implicit", "doesn't handle empty");
325
+ options.add_options()
326
+ ("implicit", "Has implicit", cxxopts::value<std::string>()
327
+ ->implicit_value("foo"));
328
+
329
+ Argv av({"implicit", "--implicit="});
330
+
331
+ auto** argv = av.argv();
332
+ auto argc = av.argc();
333
+
334
+ auto result = options.parse(argc, argv);
335
+
336
+ REQUIRE(result.count("implicit") == 1);
337
+ REQUIRE(result["implicit"].as<std::string>() == "");
338
+ }
339
+
340
+ TEST_CASE("Boolean without implicit value", "[implicit]")
341
+ {
342
+ cxxopts::Options options("no_implicit", "bool without an implicit value");
343
+ options.add_options()
344
+ ("bool", "Boolean without implicit", cxxopts::value<bool>()
345
+ ->no_implicit_value());
346
+
347
+ SECTION("When no value provided") {
348
+ Argv av({"no_implicit", "--bool"});
349
+
350
+ auto** argv = av.argv();
351
+ auto argc = av.argc();
352
+
353
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::missing_argument);
354
+ }
355
+
356
+ SECTION("With equal-separated true") {
357
+ Argv av({"no_implicit", "--bool=true"});
358
+
359
+ auto** argv = av.argv();
360
+ auto argc = av.argc();
361
+
362
+ auto result = options.parse(argc, argv);
363
+ CHECK(result.count("bool") == 1);
364
+ CHECK(result["bool"].as<bool>() == true);
365
+ }
366
+
367
+ SECTION("With equal-separated false") {
368
+ Argv av({"no_implicit", "--bool=false"});
369
+
370
+ auto** argv = av.argv();
371
+ auto argc = av.argc();
372
+
373
+ auto result = options.parse(argc, argv);
374
+ CHECK(result.count("bool") == 1);
375
+ CHECK(result["bool"].as<bool>() == false);
376
+ }
377
+
378
+ SECTION("With space-separated true") {
379
+ Argv av({"no_implicit", "--bool", "true"});
380
+
381
+ auto** argv = av.argv();
382
+ auto argc = av.argc();
383
+
384
+ auto result = options.parse(argc, argv);
385
+ CHECK(result.count("bool") == 1);
386
+ CHECK(result["bool"].as<bool>() == true);
387
+ }
388
+
389
+ SECTION("With space-separated false") {
390
+ Argv av({"no_implicit", "--bool", "false"});
391
+
392
+ auto** argv = av.argv();
393
+ auto argc = av.argc();
394
+
395
+ auto result = options.parse(argc, argv);
396
+ CHECK(result.count("bool") == 1);
397
+ CHECK(result["bool"].as<bool>() == false);
398
+ }
399
+ }
400
+
401
+ TEST_CASE("Default values", "[default]")
402
+ {
403
+ cxxopts::Options options("defaults", "has defaults");
404
+ options.add_options()
405
+ ("default", "Has implicit", cxxopts::value<int>()->default_value("42"))
406
+ ("v,vector", "Default vector", cxxopts::value<std::vector<int>>()
407
+ ->default_value("1,4"))
408
+ ;
409
+
410
+ SECTION("Sets defaults") {
411
+ Argv av({"implicit"});
412
+
413
+ auto** argv = av.argv();
414
+ auto argc = av.argc();
415
+
416
+ auto result = options.parse(argc, argv);
417
+ CHECK(result.count("default") == 0);
418
+ CHECK(result["default"].as<int>() == 42);
419
+
420
+ auto& v = result["vector"].as<std::vector<int>>();
421
+ REQUIRE(v.size() == 2);
422
+ CHECK(v[0] == 1);
423
+ CHECK(v[1] == 4);
424
+ }
425
+
426
+ SECTION("When values provided") {
427
+ Argv av({"implicit", "--default", "5"});
428
+
429
+ auto** argv = av.argv();
430
+ auto argc = av.argc();
431
+
432
+ auto result = options.parse(argc, argv);
433
+ CHECK(result.count("default") == 1);
434
+ CHECK(result["default"].as<int>() == 5);
435
+ }
436
+ }
437
+
438
+ TEST_CASE("Parse into a reference", "[reference]")
439
+ {
440
+ int value = 0;
441
+ bool b_value = true;
442
+
443
+ cxxopts::Options options("into_reference", "parses into a reference");
444
+ options.add_options()
445
+ ("ref", "A reference", cxxopts::value(value))
446
+ ("bool", "A bool", cxxopts::value(b_value));
447
+
448
+ Argv av({"into_reference", "--ref", "42"});
449
+
450
+ auto argv = av.argv();
451
+ auto argc = av.argc();
452
+
453
+ auto result = options.parse(argc, argv);
454
+ CHECK(result.count("ref") == 1);
455
+ CHECK(value == 42);
456
+ CHECK(result.count("bool") == 0);
457
+ CHECK(b_value == true);
458
+ }
459
+
460
+ TEST_CASE("Integers", "[options]")
461
+ {
462
+ cxxopts::Options options("parses_integers", "parses integers correctly");
463
+ options.add_options()
464
+ ("positional", "Integers", cxxopts::value<std::vector<int>>());
465
+
466
+ Argv av({"ints", "--", "5", "6", "-6", "0", "0xab", "0xAf", "0x0"});
467
+
468
+ auto** argv = av.argv();
469
+ auto argc = av.argc();
470
+
471
+ options.parse_positional("positional");
472
+ auto result = options.parse(argc, argv);
473
+
474
+ REQUIRE(result.count("positional") == 7);
475
+
476
+ auto& positional = result["positional"].as<std::vector<int>>();
477
+ REQUIRE(positional.size() == 7);
478
+ CHECK(positional[0] == 5);
479
+ CHECK(positional[1] == 6);
480
+ CHECK(positional[2] == -6);
481
+ CHECK(positional[3] == 0);
482
+ CHECK(positional[4] == 0xab);
483
+ CHECK(positional[5] == 0xaf);
484
+ CHECK(positional[6] == 0x0);
485
+ }
486
+
487
+ TEST_CASE("Leading zero integers", "[options]")
488
+ {
489
+ cxxopts::Options options("parses_integers", "parses integers correctly");
490
+ options.add_options()
491
+ ("positional", "Integers", cxxopts::value<std::vector<int>>());
492
+
493
+ Argv av({"ints", "--", "05", "06", "0x0ab", "0x0001"});
494
+
495
+ auto** argv = av.argv();
496
+ auto argc = av.argc();
497
+
498
+ options.parse_positional("positional");
499
+ auto result = options.parse(argc, argv);
500
+
501
+ REQUIRE(result.count("positional") == 4);
502
+
503
+ auto& positional = result["positional"].as<std::vector<int>>();
504
+ REQUIRE(positional.size() == 4);
505
+ CHECK(positional[0] == 5);
506
+ CHECK(positional[1] == 6);
507
+ CHECK(positional[2] == 0xab);
508
+ CHECK(positional[3] == 0x1);
509
+ }
510
+
511
+ TEST_CASE("Unsigned integers", "[options]")
512
+ {
513
+ cxxopts::Options options("parses_unsigned", "detects unsigned errors");
514
+ options.add_options()
515
+ ("positional", "Integers", cxxopts::value<std::vector<unsigned int>>());
516
+
517
+ Argv av({"ints", "--", "-2"});
518
+
519
+ auto** argv = av.argv();
520
+ auto argc = av.argc();
521
+
522
+ options.parse_positional("positional");
523
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
524
+ }
525
+
526
+ TEST_CASE("Integer bounds", "[integer]")
527
+ {
528
+ cxxopts::Options options("integer_boundaries", "check min/max integer");
529
+ options.add_options()
530
+ ("positional", "Integers", cxxopts::value<std::vector<int8_t>>());
531
+
532
+ SECTION("No overflow")
533
+ {
534
+ Argv av({"ints", "--", "127", "-128", "0x7f", "-0x80", "0x7e"});
535
+
536
+ auto argv = av.argv();
537
+ auto argc = av.argc();
538
+
539
+ options.parse_positional("positional");
540
+ auto result = options.parse(argc, argv);
541
+
542
+ REQUIRE(result.count("positional") == 5);
543
+
544
+ auto& positional = result["positional"].as<std::vector<int8_t>>();
545
+ CHECK(positional[0] == 127);
546
+ CHECK(positional[1] == -128);
547
+ CHECK(positional[2] == 0x7f);
548
+ CHECK(positional[3] == -0x80);
549
+ CHECK(positional[4] == 0x7e);
550
+ }
551
+ }
552
+
553
+ TEST_CASE("Overflow on boundary", "[integer]")
554
+ {
555
+ using namespace cxxopts::values;
556
+
557
+ int8_t si;
558
+ int16_t si16;
559
+ int64_t si64;
560
+ uint8_t ui;
561
+ uint16_t ui16;
562
+ uint64_t ui64;
563
+
564
+ CHECK_THROWS_AS((integer_parser("128", si)), cxxopts::exceptions::incorrect_argument_type);
565
+ CHECK_THROWS_AS((integer_parser("-129", si)), cxxopts::exceptions::incorrect_argument_type);
566
+ CHECK_THROWS_AS((integer_parser("256", ui)), cxxopts::exceptions::incorrect_argument_type);
567
+ CHECK_THROWS_AS((integer_parser("-0x81", si)), cxxopts::exceptions::incorrect_argument_type);
568
+ CHECK_THROWS_AS((integer_parser("0x80", si)), cxxopts::exceptions::incorrect_argument_type);
569
+ CHECK_THROWS_AS((integer_parser("0x100", ui)), cxxopts::exceptions::incorrect_argument_type);
570
+
571
+ CHECK_THROWS_AS((integer_parser("65536", ui16)), cxxopts::exceptions::incorrect_argument_type);
572
+ CHECK_THROWS_AS((integer_parser("75536", ui16)), cxxopts::exceptions::incorrect_argument_type);
573
+ CHECK_THROWS_AS((integer_parser("32768", si16)), cxxopts::exceptions::incorrect_argument_type);
574
+ CHECK_THROWS_AS((integer_parser("-32769", si16)), cxxopts::exceptions::incorrect_argument_type);
575
+ CHECK_THROWS_AS((integer_parser("-42769", si16)), cxxopts::exceptions::incorrect_argument_type);
576
+ CHECK_THROWS_AS((integer_parser("-75536", si16)), cxxopts::exceptions::incorrect_argument_type);
577
+
578
+ CHECK_THROWS_AS((integer_parser("18446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
579
+ CHECK_THROWS_AS((integer_parser("28446744073709551616", ui64)), cxxopts::exceptions::incorrect_argument_type);
580
+ CHECK_THROWS_AS((integer_parser("9223372036854775808", si64)), cxxopts::exceptions::incorrect_argument_type);
581
+ CHECK_THROWS_AS((integer_parser("-9223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
582
+ CHECK_THROWS_AS((integer_parser("-10223372036854775809", si64)), cxxopts::exceptions::incorrect_argument_type);
583
+ CHECK_THROWS_AS((integer_parser("-28446744073709551616", si64)), cxxopts::exceptions::incorrect_argument_type);
584
+ }
585
+
586
+ TEST_CASE("Integer overflow", "[options]")
587
+ {
588
+ using namespace cxxopts::values;
589
+
590
+ cxxopts::Options options("reject_overflow", "rejects overflowing integers");
591
+ options.add_options()
592
+ ("positional", "Integers", cxxopts::value<std::vector<int8_t>>());
593
+
594
+ Argv av({"ints", "--", "128"});
595
+
596
+ auto argv = av.argv();
597
+ auto argc = av.argc();
598
+
599
+ options.parse_positional("positional");
600
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
601
+
602
+ int integer = 0;
603
+ CHECK_THROWS_AS((integer_parser("23423423423", integer)), cxxopts::exceptions::incorrect_argument_type);
604
+ CHECK_THROWS_AS((integer_parser("234234234234", integer)), cxxopts::exceptions::incorrect_argument_type);
605
+ }
606
+
607
+ TEST_CASE("Floats", "[options]")
608
+ {
609
+ cxxopts::Options options("parses_floats", "parses floats correctly");
610
+ options.add_options()
611
+ ("double", "Double precision", cxxopts::value<double>())
612
+ ("positional", "Floats", cxxopts::value<std::vector<float>>());
613
+
614
+ Argv av({"floats", "--double", "0.5", "--", "4", "-4", "1.5e6", "-1.5e6"});
615
+
616
+ auto** argv = av.argv();
617
+ auto argc = av.argc();
618
+
619
+ options.parse_positional("positional");
620
+ auto result = options.parse(argc, argv);
621
+
622
+ REQUIRE(result.count("double") == 1);
623
+ REQUIRE(result.count("positional") == 4);
624
+
625
+ CHECK(result["double"].as<double>() == 0.5);
626
+
627
+ auto& positional = result["positional"].as<std::vector<float>>();
628
+ CHECK(positional[0] == 4);
629
+ CHECK(positional[1] == -4);
630
+ CHECK(positional[2] == 1.5e6);
631
+ CHECK(positional[3] == -1.5e6);
632
+ }
633
+
634
+ TEST_CASE("Invalid integers", "[integer]") {
635
+ cxxopts::Options options("invalid_integers", "rejects invalid integers");
636
+ options.add_options()
637
+ ("positional", "Integers", cxxopts::value<std::vector<int>>());
638
+
639
+ Argv av({"ints", "--", "Ae"});
640
+
641
+ auto** argv = av.argv();
642
+ auto argc = av.argc();
643
+
644
+ options.parse_positional("positional");
645
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::incorrect_argument_type);
646
+ }
647
+
648
+ TEST_CASE("Booleans", "[boolean]") {
649
+ cxxopts::Options options("parses_floats", "parses floats correctly");
650
+ options.add_options()
651
+ ("bool", "A Boolean", cxxopts::value<bool>())
652
+ ("debug", "Debugging", cxxopts::value<bool>())
653
+ ("timing", "Timing", cxxopts::value<bool>())
654
+ ("verbose", "Verbose", cxxopts::value<bool>())
655
+ ("dry-run", "Dry Run", cxxopts::value<bool>())
656
+ ("noExplicitDefault", "No Explicit Default", cxxopts::value<bool>())
657
+ ("defaultTrue", "Timing", cxxopts::value<bool>()->default_value("true"))
658
+ ("defaultFalse", "Timing", cxxopts::value<bool>()->default_value("false"))
659
+ ("others", "Other arguments", cxxopts::value<std::vector<std::string>>())
660
+ ;
661
+
662
+ options.parse_positional("others");
663
+
664
+ Argv av({"booleans", "--bool=false", "--debug=true", "--timing", "--verbose=1", "--dry-run=0", "extra"});
665
+
666
+ auto** argv = av.argv();
667
+ auto argc = av.argc();
668
+
669
+ auto result = options.parse(argc, argv);
670
+
671
+ REQUIRE(result.count("bool") == 1);
672
+ REQUIRE(result.count("debug") == 1);
673
+ REQUIRE(result.count("timing") == 1);
674
+ REQUIRE(result.count("verbose") == 1);
675
+ REQUIRE(result.count("dry-run") == 1);
676
+ REQUIRE(result.count("noExplicitDefault") == 0);
677
+ REQUIRE(result.count("defaultTrue") == 0);
678
+ REQUIRE(result.count("defaultFalse") == 0);
679
+
680
+ CHECK(result["bool"].as<bool>() == false);
681
+ CHECK(result["debug"].as<bool>() == true);
682
+ CHECK(result["timing"].as<bool>() == true);
683
+ CHECK(result["verbose"].as<bool>() == true);
684
+ CHECK(result["dry-run"].as<bool>() == false);
685
+ CHECK(result["noExplicitDefault"].as<bool>() == false);
686
+ CHECK(result["defaultTrue"].as<bool>() == true);
687
+ CHECK(result["defaultFalse"].as<bool>() == false);
688
+
689
+ REQUIRE(result.count("others") == 1);
690
+ }
691
+
692
+ TEST_CASE("std::vector", "[vector]") {
693
+ std::vector<double> vector;
694
+ cxxopts::Options options("vector", " - tests vector");
695
+ options.add_options()
696
+ ("vector", "an vector option", cxxopts::value<std::vector<double>>(vector));
697
+
698
+ Argv av({"vector", "--vector", "1,-2.1,3,4.5"});
699
+
700
+ auto** argv = av.argv();
701
+ auto argc = av.argc();
702
+
703
+ options.parse(argc, argv);
704
+
705
+ REQUIRE(vector.size() == 4);
706
+ CHECK(vector[0] == 1);
707
+ CHECK(vector[1] == -2.1);
708
+ CHECK(vector[2] == 3);
709
+ CHECK(vector[3] == 4.5);
710
+ }
711
+
712
+ TEST_CASE("empty std::vector", "[vector]") {
713
+ std::vector<double> double_vector;
714
+ std::vector<std::string> string_vector;
715
+
716
+ cxxopts::Options options("empty vector", " - tests behaviour of empty vector options");
717
+
718
+ SECTION("string vector") {
719
+ options.add_options()
720
+ ("string_vector", "vector of strings", cxxopts::value(string_vector)->default_value(""));
721
+
722
+ Argv av({"empty vector"});
723
+ auto** argv = av.argv();
724
+ auto argc = av.argc();
725
+
726
+ options.parse(argc, argv);
727
+
728
+ CHECK(string_vector.empty());
729
+ }
730
+
731
+ SECTION("double vector") {
732
+ options.add_options()
733
+ ("double_vector", "vector of doubles", cxxopts::value(double_vector)->default_value(""));
734
+
735
+ Argv av({"empty vector"});
736
+ auto** argv = av.argv();
737
+ auto argc = av.argc();
738
+
739
+ options.parse(argc, argv); // throws
740
+
741
+ CHECK(double_vector.empty());
742
+ }
743
+
744
+ }
745
+
746
+ #ifdef CXXOPTS_HAS_OPTIONAL
747
+ TEST_CASE("std::optional", "[optional]") {
748
+ std::optional<std::string> optional;
749
+ std::optional<bool> opt_bool;
750
+ cxxopts::Options options("optional", " - tests optional");
751
+ options.add_options()
752
+ ("optional", "an optional option", cxxopts::value<std::optional<std::string>>(optional))
753
+ ("optional_bool", "an boolean optional", cxxopts::value<std::optional<bool>>(opt_bool)->default_value("false"));
754
+
755
+ Argv av({"optional", "--optional", "foo", "--optional_bool", "true"});
756
+
757
+ auto** argv = av.argv();
758
+ auto argc = av.argc();
759
+
760
+ options.parse(argc, argv);
761
+
762
+ REQUIRE(optional.has_value());
763
+ CHECK(*optional == "foo");
764
+ CHECK(opt_bool.has_value());
765
+ CHECK(*opt_bool);
766
+ }
767
+ #endif
768
+
769
+ #ifdef CXXOPTS_HAS_FILESYSTEM
770
+ TEST_CASE("std::filesystem::path", "[path]") {
771
+ std::filesystem::path path;
772
+ cxxopts::Options options("path", " - tests path");
773
+ options.add_options()
774
+ ("path", "a path", cxxopts::value<std::filesystem::path>(path));
775
+
776
+ Argv av({"path", "--path", "Hello World.txt"});
777
+
778
+ auto** argv = av.argv();
779
+ auto argc = av.argc();
780
+
781
+ REQUIRE(path.empty());
782
+
783
+ options.parse(argc, argv);
784
+
785
+ REQUIRE(!path.empty());
786
+ CHECK(path == "Hello World.txt");
787
+ }
788
+ #endif
789
+
790
+ TEST_CASE("Unrecognised options", "[options]") {
791
+ cxxopts::Options options("unknown_options", " - test unknown options");
792
+
793
+ options.add_options()
794
+ ("long", "a long option")
795
+ ("s,short", "a short option");
796
+
797
+ Argv av({
798
+ "unknown_options",
799
+ "--unknown",
800
+ "--long",
801
+ "-su",
802
+ "--another_unknown",
803
+ "-a",
804
+ });
805
+
806
+ auto** argv = av.argv();
807
+ auto argc = av.argc();
808
+
809
+ SECTION("Default behaviour") {
810
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
811
+ }
812
+
813
+ SECTION("After allowing unrecognised options") {
814
+ options.allow_unrecognised_options();
815
+ auto result = options.parse(argc, argv);
816
+ auto& unmatched = result.unmatched();
817
+ CHECK((unmatched == std::vector<std::string>{"--unknown", "-u", "--another_unknown", "-a"}));
818
+ }
819
+ }
820
+
821
+ TEST_CASE("Allow bad short syntax", "[options]") {
822
+ cxxopts::Options options("unknown_options", " - test unknown options");
823
+
824
+ options.add_options()
825
+ ("long", "a long option")
826
+ ("s,short", "a short option");
827
+
828
+ Argv av({
829
+ "--ab?",
830
+ "-?b?#@"
831
+ });
832
+
833
+ auto** argv = av.argv();
834
+ auto argc = av.argc();
835
+
836
+ SECTION("Default behaviour") {
837
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax);
838
+ }
839
+
840
+ SECTION("After allowing unrecognised options") {
841
+ options.allow_unrecognised_options();
842
+ CHECK_NOTHROW(options.parse(argc, argv));
843
+ REQUIRE(argc == 2);
844
+ CHECK_THAT(argv[1], Catch::Equals("-?b?#@"));
845
+ }
846
+ }
847
+
848
+ TEST_CASE("Invalid option syntax", "[options]") {
849
+ cxxopts::Options options("invalid_syntax", " - test invalid syntax");
850
+
851
+ Argv av({
852
+ "invalid_syntax",
853
+ "--a",
854
+ });
855
+
856
+ auto** argv = av.argv();
857
+ auto argc = av.argc();
858
+
859
+ SECTION("Default behaviour") {
860
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::invalid_option_syntax);
861
+ }
862
+ }
863
+
864
+ TEST_CASE("Options empty", "[options]") {
865
+ cxxopts::Options options("Options list empty", " - test empty option list");
866
+ options.add_options();
867
+ options.add_options("");
868
+ options.add_options("", {});
869
+ options.add_options("test");
870
+
871
+ Argv argv_({
872
+ "test",
873
+ "--unknown"
874
+ });
875
+ auto argc = argv_.argc();
876
+ auto** argv = argv_.argv();
877
+
878
+ CHECK(options.groups().empty());
879
+ CHECK_THROWS_AS(options.parse(argc, argv), cxxopts::exceptions::no_such_option);
880
+ }
881
+
882
+ #ifdef CXXOPTS_HAS_OPTIONAL
883
+ TEST_CASE("Optional value", "[optional]")
884
+ {
885
+ cxxopts::Options options("options", "query as std::optional");
886
+ options.add_options()
887
+ ("int", "Integer", cxxopts::value<int>())
888
+ ("float", "Float", cxxopts::value<float>())
889
+ ("string", "String", cxxopts::value<std::string>())
890
+ ;
891
+
892
+ SECTION("Available") {
893
+ Argv av({
894
+ "available",
895
+ "--int",
896
+ "42",
897
+ "--float",
898
+ "3.141",
899
+ "--string",
900
+ "Hello"
901
+ });
902
+
903
+ auto** argv = av.argv();
904
+ auto argc = av.argc();
905
+
906
+ auto result = options.parse(argc, argv);
907
+
908
+ CHECK(result.as_optional<int>("int"));
909
+ CHECK(result.as_optional<float>("float"));
910
+ CHECK(result.as_optional<std::string>("string"));
911
+
912
+ CHECK(result.as_optional<int>("int") == 42);
913
+ CHECK(result.as_optional<float>("float") == 3.141f);
914
+ CHECK(result.as_optional<std::string>("string") == "Hello");
915
+ }
916
+
917
+ SECTION("Unavailable") {
918
+ Argv av({
919
+ "unavailable"
920
+ });
921
+
922
+ auto** argv = av.argv();
923
+ auto argc = av.argc();
924
+
925
+ auto result = options.parse(argc, argv);
926
+
927
+ CHECK(!result.as_optional<int>("int"));
928
+ CHECK(!result.as_optional<float>("float"));
929
+ CHECK(!result.as_optional<std::string>("string"));
930
+ }
931
+
932
+ }
933
+ #endif
934
+
935
+ #ifdef CXXOPTS_HAS_OPTIONAL
936
+ TEST_CASE("std::filesystem::path value", "[path]")
937
+ {
938
+ cxxopts::Options options("options", "query as std::fileystem::path");
939
+ options.add_options()
940
+ ("a", "Path", cxxopts::value<std::filesystem::path>())
941
+ ("b", "Path", cxxopts::value<std::filesystem::path>())
942
+ ("c", "Path", cxxopts::value<std::filesystem::path>())
943
+ ("d", "Path", cxxopts::value<std::filesystem::path>())
944
+ ("e", "Path", cxxopts::value<std::filesystem::path>())
945
+ ;
946
+
947
+ SECTION("Available") {
948
+ Argv av({
949
+ "available",
950
+ "-a", "hello.txt",
951
+ "-b", "C:\\Users\\JoeCitizen\\hello world.txt",
952
+ "-c", "/home/joecitzen/hello world.txt",
953
+ "-d", "../world.txt"
954
+ });
955
+
956
+ auto** argv = av.argv();
957
+ auto argc = av.argc();
958
+
959
+ auto result = options.parse(argc, argv);
960
+
961
+ CHECK(result.as_optional<std::filesystem::path>("a"));
962
+ CHECK(result.as_optional<std::filesystem::path>("b"));
963
+ CHECK(result.as_optional<std::filesystem::path>("c"));
964
+ CHECK(result.as_optional<std::filesystem::path>("d"));
965
+ CHECK(!result.as_optional<std::filesystem::path>("e"));
966
+
967
+ CHECK(result.as_optional<std::filesystem::path>("a") == "hello.txt");
968
+ CHECK(result.as_optional<std::filesystem::path>("b") == "C:\\Users\\JoeCitizen\\hello world.txt");
969
+ CHECK(result.as_optional<std::filesystem::path>("c") == "/home/joecitzen/hello world.txt");
970
+ CHECK(result.as_optional<std::filesystem::path>("d") == "../world.txt");
971
+ }
972
+
973
+ SECTION("Unavailable") {
974
+ Argv av({
975
+ "unavailable"
976
+ });
977
+
978
+ auto** argv = av.argv();
979
+ auto argc = av.argc();
980
+
981
+ auto result = options.parse(argc, argv);
982
+
983
+ CHECK(!result.as_optional<std::filesystem::path>("a"));
984
+ }
985
+
986
+ }
987
+ #endif
988
+
989
+ TEST_CASE("Initializer list with group", "[options]") {
990
+ cxxopts::Options options("Initializer list group", " - test initializer list with group");
991
+
992
+ options.add_options("", {
993
+ {"a, address", "server address", cxxopts::value<std::string>()->default_value("127.0.0.1")},
994
+ {"p, port", "server port", cxxopts::value<std::string>()->default_value("7110"), "PORT"},
995
+ });
996
+
997
+ cxxopts::Option help{"h,help", "Help"};
998
+
999
+ options.add_options("TEST_GROUP", {
1000
+ {"t, test", "test option"},
1001
+ help
1002
+ });
1003
+
1004
+ Argv argv({
1005
+ "test",
1006
+ "--address",
1007
+ "10.0.0.1",
1008
+ "-p",
1009
+ "8000",
1010
+ "-t",
1011
+ });
1012
+ auto** actual_argv = argv.argv();
1013
+ auto argc = argv.argc();
1014
+ auto result = options.parse(argc, actual_argv);
1015
+
1016
+ CHECK(options.groups().size() == 2);
1017
+ CHECK(result.count("address") == 1);
1018
+ CHECK(result.count("port") == 1);
1019
+ CHECK(result.count("test") == 1);
1020
+ CHECK(result.count("help") == 0);
1021
+ CHECK(result["address"].as<std::string>() == "10.0.0.1");
1022
+ CHECK(result["port"].as<std::string>() == "8000");
1023
+ CHECK(result["test"].as<bool>() == true);
1024
+ }
1025
+
1026
+ TEST_CASE("Option add with add_option(string, Option)", "[options]") {
1027
+ cxxopts::Options options("Option add with add_option", " - test Option add with add_option(string, Option)");
1028
+
1029
+ cxxopts::Option option_1("t,test", "test option", cxxopts::value<int>()->default_value("7"), "TEST");
1030
+
1031
+ options.add_option("", option_1);
1032
+ options.add_option("TEST", {"a,aggregate", "test option 2", cxxopts::value<int>(), "AGGREGATE"});
1033
+ options.add_option("TEST", {"multilong,m,multilong-alias", "test option 3", cxxopts::value<int>(), "An option with multiple long names"});
1034
+
1035
+ Argv argv_({
1036
+ "test",
1037
+ "--test",
1038
+ "5",
1039
+ "-a",
1040
+ "4",
1041
+ "--multilong-alias",
1042
+ "6"
1043
+ });
1044
+ auto argc = argv_.argc();
1045
+ auto** argv = argv_.argv();
1046
+ auto result = options.parse(argc, argv);
1047
+
1048
+ CHECK(result.arguments().size() == 3);
1049
+ CHECK(options.groups().size() == 2);
1050
+ CHECK(result.count("address") == 0);
1051
+ CHECK(result.count("aggregate") == 1);
1052
+ CHECK(result.count("test") == 1);
1053
+ CHECK(result["aggregate"].as<int>() == 4);
1054
+ CHECK(result["multilong"].as<int>() == 6);
1055
+ CHECK(result["multilong-alias"].as<int>() == 6);
1056
+ CHECK(result["m"].as<int>() == 6);
1057
+ CHECK(result["test"].as<int>() == 5);
1058
+ }
1059
+
1060
+ TEST_CASE("Const array", "[const]") {
1061
+ const char* const option_list[] = {"empty", "options"};
1062
+ cxxopts::Options options("Empty options", " - test constness");
1063
+ auto result = options.parse(2, option_list);
1064
+ }
1065
+
1066
+ TEST_CASE("Parameter follow option", "[parameter]") {
1067
+ cxxopts::Options options("param_follow_opt", " - test parameter follow option without space.");
1068
+ options.add_options()
1069
+ ("j,job", "Job", cxxopts::value<std::vector<unsigned>>());
1070
+ Argv av({"implicit",
1071
+ "-j", "9",
1072
+ "--job", "7",
1073
+ "--job=10",
1074
+ "-j5",
1075
+ });
1076
+
1077
+ auto ** argv = av.argv();
1078
+ auto argc = av.argc();
1079
+
1080
+ auto result = options.parse(argc, argv);
1081
+
1082
+ REQUIRE(result.count("job") == 4);
1083
+
1084
+ auto job_values = result["job"].as<std::vector<unsigned>>();
1085
+ CHECK(job_values[0] == 9);
1086
+ CHECK(job_values[1] == 7);
1087
+ CHECK(job_values[2] == 10);
1088
+ CHECK(job_values[3] == 5);
1089
+ }
1090
+
1091
+ TEST_CASE("Iterator", "[iterator]") {
1092
+ cxxopts::Options options("tester", " - test iterating over parse result");
1093
+
1094
+ options.add_options()
1095
+ ("long", "a long option")
1096
+ ("s,short", "a short option")
1097
+ ("a", "a short-only option")
1098
+ ("value", "an option with a value", cxxopts::value<std::string>())
1099
+ ("default", "an option with default value", cxxopts::value<int>()->default_value("42"))
1100
+ ("nothing", "won't exist", cxxopts::value<std::string>())
1101
+ ;
1102
+
1103
+ Argv argv({
1104
+ "tester",
1105
+ "--long",
1106
+ "-s",
1107
+ "-a",
1108
+ "--value",
1109
+ "value",
1110
+ });
1111
+
1112
+ auto** actual_argv = argv.argv();
1113
+ auto argc = argv.argc();
1114
+
1115
+ auto result = options.parse(argc, actual_argv);
1116
+
1117
+ auto iter = result.begin();
1118
+
1119
+ REQUIRE(iter != result.end());
1120
+ CHECK(iter->key() == "long");
1121
+ CHECK(iter->value() == "true");
1122
+
1123
+ REQUIRE(++iter != result.end());
1124
+ CHECK(iter->key() == "short");
1125
+ CHECK(iter->value() == "true");
1126
+
1127
+ REQUIRE(++iter != result.end());
1128
+ CHECK(iter->key() == "a");
1129
+ CHECK(iter->value() == "true");
1130
+
1131
+ REQUIRE(++iter != result.end());
1132
+ CHECK(iter->key() == "value");
1133
+ CHECK(iter->value() == "value");
1134
+
1135
+ REQUIRE(++iter != result.end());
1136
+ CHECK(iter->key() == "default");
1137
+ CHECK(iter->value() == "42");
1138
+
1139
+ REQUIRE(++iter == result.end());
1140
+ }
1141
+
1142
+ TEST_CASE("Iterator no args", "[iterator]") {
1143
+ cxxopts::Options options("tester", " - test iterating over parse result");
1144
+
1145
+ options.add_options()
1146
+ ("value", "an option with a value", cxxopts::value<std::string>())
1147
+ ("default", "an option with default value", cxxopts::value<int>()->default_value("42"))
1148
+ ("nothing", "won't exist", cxxopts::value<std::string>())
1149
+ ;
1150
+
1151
+ Argv argv({
1152
+ "tester",
1153
+ });
1154
+
1155
+ auto** actual_argv = argv.argv();
1156
+ auto argc = argv.argc();
1157
+
1158
+ auto result = options.parse(argc, actual_argv);
1159
+
1160
+ auto iter = result.begin();
1161
+
1162
+ REQUIRE(iter != result.end());
1163
+ CHECK(iter->key() == "default");
1164
+ CHECK(iter->value() == "42");
1165
+
1166
+ ++iter;
1167
+ CHECK(iter == result.end());
1168
+ }
1169
+
1170
+
1171
+ TEST_CASE("No Options help", "[options]")
1172
+ {
1173
+ std::vector<std::string> positional;
1174
+
1175
+ cxxopts::Options options("test", "test no options help");
1176
+
1177
+ // explicitly setting custom help empty to overwrite
1178
+ // default "[OPTION...]" when there are no options
1179
+ options.positional_help("<posArg1>...<posArgN>")
1180
+ .custom_help("")
1181
+ .add_options()
1182
+ ("positional", "", cxxopts::value<std::vector<std::string>>(positional));
1183
+
1184
+ Argv av({"test", "posArg1", "posArg2", "posArg3"});
1185
+
1186
+ auto argc = av.argc();
1187
+ auto** argv = av.argv();
1188
+
1189
+ options.parse_positional({"positional"});
1190
+
1191
+ CHECK_NOTHROW(options.parse(argc, argv));
1192
+ CHECK(options.help().find("test <posArg1>...<posArgN>") != std::string::npos);
1193
+ }