Line data Source code
1 : // Copyright (c) 2006-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/advgetopt
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 :
20 : // advgetopt
21 : //
22 : #include <advgetopt/validator_duration.h>
23 : #include <advgetopt/validator_size.h>
24 :
25 : #include <advgetopt/exception.h>
26 :
27 :
28 : // self
29 : //
30 : #include "catch_main.h"
31 :
32 :
33 : // snapdev
34 : //
35 : #include <snapdev/ostream_int128.h>
36 :
37 :
38 : // C++
39 : //
40 : #include <cmath>
41 : #include <fstream>
42 : #include <iomanip>
43 :
44 :
45 : // last include
46 : //
47 : #include <snapdev/poison.h>
48 :
49 :
50 :
51 :
52 :
53 : namespace
54 : {
55 :
56 : struct duration_t
57 : {
58 : char const * const f_suffix = nullptr;
59 : double f_factor = 1.0;
60 : };
61 :
62 : constexpr duration_t const g_duration_suffixes[] =
63 : {
64 : { "", 1.0 },
65 : { "s", 1.0 },
66 : { "second", 1.0 },
67 : { "seconds", 1.0 },
68 :
69 : { "m", -1.0 }, // may represent minutes or months
70 : { "minute", 60.0 },
71 : { "minutes", 60.0 },
72 :
73 : { "h", 3600.0 },
74 : { "hour", 3600.0 },
75 : { "hours", 3600.0 },
76 :
77 : { "d", 86400.0 },
78 : { "day", 86400.0 },
79 : { "days", 86400.0 },
80 :
81 : { "w", 86400.0 * 7.0 },
82 : { "week", 86400.0 * 7.0 },
83 : { "weeks", 86400.0 * 7.0 },
84 :
85 : { "month", 86400.0 * 30.0 },
86 : { "months", 86400.0 * 30.0 },
87 :
88 : { "y", 86400.0 * 365.0 },
89 : { "year", 86400.0 * 365.0 },
90 : { "years", 86400.0 * 365.0 },
91 : };
92 :
93 : struct size_suffix_t
94 : {
95 : char const * const f_suffix = nullptr;
96 : int f_base = 1000.0;
97 : int f_power = 0.0;
98 : };
99 :
100 : constexpr size_suffix_t const g_size_suffixes[] =
101 : {
102 : { "", 1000, 0 },
103 : { "B", 1000, 0 },
104 :
105 : { "kB", 1000, 1 },
106 : { "KiB", 1024, 1 },
107 :
108 : { "MB", 1000, 2 },
109 : { "MiB", 1024, 2 },
110 :
111 : { "GB", 1000, 3 },
112 : { "GiB", 1024, 3 },
113 :
114 : { "TB", 1000, 4 },
115 : { "TiB", 1024, 4 },
116 :
117 : { "PB", 1000, 5 },
118 : { "PiB", 1024, 5 },
119 :
120 : { "EB", 1000, 6 },
121 : { "EiB", 1024, 6 },
122 :
123 : { "ZB", 1000, 7 },
124 : { "ZiB", 1024, 7 },
125 :
126 : { "YB", 1000, 8 },
127 : { "YiB", 1024, 8 },
128 : };
129 :
130 123717 : std::int64_t large_rnd(bool zero_allowed = true)
131 : {
132 : for(;;)
133 : {
134 123717 : std::int64_t const result((static_cast<std::int64_t>(rand()) << 0)
135 123717 : ^ (static_cast<std::int64_t>(rand()) << 16)
136 123717 : ^ (static_cast<std::int64_t>(rand()) << 32)
137 123717 : ^ (static_cast<std::int64_t>(rand()) << 48));
138 123717 : if(result != 0
139 0 : || zero_allowed)
140 : {
141 247434 : return result;
142 : }
143 0 : }
144 : }
145 :
146 : }
147 :
148 :
149 :
150 4 : CATCH_TEST_CASE("unknown_validator", "[validator][valid][validation]")
151 : {
152 4 : CATCH_START_SECTION("Undefined validator")
153 : // this is a valid case, it does not throw, it just returns a nullptr
154 : //
155 1 : CATCH_REQUIRE(advgetopt::validator::create("unknown", advgetopt::string_list_t()) == nullptr);
156 : CATCH_END_SECTION()
157 :
158 4 : CATCH_START_SECTION("Empty string")
159 1 : CATCH_REQUIRE(advgetopt::validator::create(std::string()) == nullptr);
160 : CATCH_END_SECTION()
161 2 : }
162 :
163 :
164 :
165 5 : CATCH_TEST_CASE("email_validator", "[invalid][validation]")
166 : {
167 6 : CATCH_START_SECTION("email_validator: Verify that email verification works.")
168 : {
169 2 : advgetopt::validator::pointer_t email(advgetopt::validator::create("email"));
170 :
171 1 : CATCH_REQUIRE(email != nullptr);
172 1 : CATCH_REQUIRE(email->name() == "email");
173 :
174 1 : CATCH_REQUIRE_FALSE(email->validate(""));
175 1 : CATCH_REQUIRE(email->validate("user@example.com"));
176 1 : CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
177 1 : CATCH_REQUIRE_FALSE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
178 1 : CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
179 1 : CATCH_REQUIRE_FALSE(email->validate("@example.com"));
180 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
181 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
182 : }
183 : CATCH_END_SECTION()
184 :
185 6 : CATCH_START_SECTION("email_validator: Verify that one email verification works (single explicitly).")
186 : {
187 2 : advgetopt::validator::pointer_t email(advgetopt::validator::create("email(single)"));
188 :
189 1 : CATCH_REQUIRE(email != nullptr);
190 1 : CATCH_REQUIRE(email->name() == "email");
191 :
192 1 : CATCH_REQUIRE_FALSE(email->validate(""));
193 1 : CATCH_REQUIRE(email->validate("user@example.com"));
194 1 : CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
195 1 : CATCH_REQUIRE_FALSE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
196 1 : CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
197 1 : CATCH_REQUIRE_FALSE(email->validate("@example.com"));
198 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
199 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
200 : }
201 : CATCH_END_SECTION()
202 :
203 6 : CATCH_START_SECTION("email_validator: Verify that multiple emails verification works.")
204 : {
205 2 : advgetopt::validator::pointer_t email(advgetopt::validator::create("email(multiple)"));
206 :
207 1 : CATCH_REQUIRE(email != nullptr);
208 1 : CATCH_REQUIRE(email->name() == "email");
209 :
210 1 : CATCH_REQUIRE_FALSE(email->validate(""));
211 1 : CATCH_REQUIRE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
212 1 : CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
213 1 : CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
214 1 : CATCH_REQUIRE_FALSE(email->validate("@example.com"));
215 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
216 1 : CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
217 : }
218 : CATCH_END_SECTION()
219 3 : }
220 :
221 :
222 :
223 5 : CATCH_TEST_CASE("integer_validator", "[validator][valid][validation]")
224 : {
225 6 : CATCH_START_SECTION("integer_validator: Verify the integer validator")
226 : {
227 2 : advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", advgetopt::string_list_t()));
228 :
229 1 : CATCH_REQUIRE(integer_validator != nullptr);
230 1 : CATCH_REQUIRE(integer_validator->name() == "integer");
231 :
232 1 : CATCH_REQUIRE_FALSE(integer_validator->validate(""));
233 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("+"));
234 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("-"));
235 :
236 1001 : for(int idx(0); idx < 1000; ++idx)
237 : {
238 1000 : std::int64_t value(large_rnd());
239 2000 : std::string const v(std::to_string(value));
240 :
241 1000 : CATCH_REQUIRE(integer_validator->validate(v));
242 :
243 1000 : if(value >= 0)
244 : {
245 513 : CATCH_REQUIRE(integer_validator->validate('+' + v));
246 : }
247 :
248 2000 : std::string const space_before(' ' + v);
249 1000 : CATCH_REQUIRE_FALSE(integer_validator->validate(space_before));
250 :
251 2000 : std::string const space_after(v + ' ');
252 1000 : CATCH_REQUIRE_FALSE(integer_validator->validate(space_after));
253 :
254 2000 : std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
255 1000 : CATCH_REQUIRE_FALSE(integer_validator->validate(before));
256 :
257 2000 : std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
258 1000 : CATCH_REQUIRE_FALSE(integer_validator->validate(after));
259 : }
260 :
261 : // max number
262 1 : CATCH_REQUIRE(integer_validator->validate("9223372036854775807"));
263 1 : CATCH_REQUIRE(integer_validator->validate("+9223372036854775807"));
264 :
265 : // overflow
266 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("9223372036854775808"));
267 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("+9223372036854775808"));
268 :
269 : // min number
270 1 : CATCH_REQUIRE(integer_validator->validate("-9223372036854775808"));
271 :
272 : // underflow
273 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("-9223372036854775809"));
274 :
275 : // too many digits
276 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("92233720368547758091"));
277 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("+92233720368547758092"));
278 1 : CATCH_REQUIRE_FALSE(integer_validator->validate("-92233720368547758093"));
279 : }
280 : CATCH_END_SECTION()
281 :
282 6 : CATCH_START_SECTION("integer_validator: Verify the integer ranges")
283 : {
284 1 : bool had_standalone(false);
285 21 : for(int count(0); count < 20 || !had_standalone; ++count)
286 : {
287 20 : std::int64_t min(large_rnd());
288 20 : std::int64_t max(large_rnd());
289 20 : if(min > max)
290 : {
291 9 : std::swap(min, max);
292 : }
293 :
294 40 : std::string const & smin(std::to_string(min));
295 40 : std::string const & smax(std::to_string(max));
296 :
297 40 : std::string range("...");
298 80 : for(int three(0); three < 3; ++three)
299 : {
300 60 : if(rand() % 5 == 0)
301 : {
302 11 : range = ' ' + range;
303 : }
304 60 : if(rand() % 5 == 0)
305 : {
306 11 : range = range + ' ';
307 : }
308 : }
309 20 : range = smin + range + smax;
310 80 : for(int three(0); three < 3; ++three)
311 : {
312 60 : if(rand() % 5 == 0)
313 : {
314 18 : range = ' ' + range;
315 : }
316 60 : if(rand() % 5 == 0)
317 : {
318 13 : range = range + ' ';
319 : }
320 : }
321 :
322 20 : std::int64_t standalone(0);
323 20 : bool standalone_included(rand() % 4 == 0);
324 20 : if(standalone_included)
325 : {
326 4 : if(min == std::numeric_limits<std::int64_t>::min()
327 2 : && max == std::numeric_limits<std::int64_t>::max())
328 : {
329 0 : standalone_included = false;
330 : }
331 : else
332 : {
333 2 : had_standalone = true;
334 0 : do
335 : {
336 2 : standalone = large_rnd();
337 : }
338 2 : while(standalone >= min && standalone <= max);
339 :
340 4 : std::string sep(",");
341 2 : if(rand() % 3 == 0)
342 : {
343 0 : sep = ' ' + sep;
344 : }
345 2 : if(rand() % 3 == 0)
346 : {
347 2 : sep = sep + ' ';
348 : }
349 2 : if(rand() % 2 == 0)
350 : {
351 1 : range = std::to_string(standalone) + "," + range;
352 : }
353 : else
354 : {
355 1 : range = range + "," + std::to_string(standalone);
356 : }
357 : }
358 : }
359 40 : advgetopt::string_list_t range_list;
360 40 : advgetopt::split_string(range
361 : , range_list
362 20 : , {","});
363 40 : advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range_list));
364 :
365 20 : CATCH_REQUIRE(integer_validator != nullptr);
366 20 : CATCH_REQUIRE(integer_validator->name() == "integer");
367 :
368 20020 : for(int idx(0); idx < 1000; ++idx)
369 : {
370 20000 : std::int64_t value(large_rnd());
371 :
372 : // force valid values otherwise we're likely to only have
373 : // invalid ones
374 : //
375 20000 : if(idx % 10 == 0)
376 : {
377 2000 : value %= max - min + 1;
378 2000 : value += min;
379 : }
380 18000 : else if(idx % 50 == 1 && standalone_included)
381 : {
382 40 : value = standalone;
383 : }
384 :
385 40000 : std::string const v(std::to_string(value));
386 :
387 20000 : if((standalone_included && value == standalone)
388 19960 : || (value >= min && value <= max))
389 : {
390 6934 : CATCH_REQUIRE(integer_validator->validate(v));
391 : }
392 : else
393 : {
394 13066 : CATCH_REQUIRE_FALSE(integer_validator->validate(v));
395 : }
396 :
397 20000 : if(value >= 0)
398 : {
399 9604 : if((standalone_included && value == standalone)
400 9584 : || (value >= min && value <= max))
401 : {
402 1711 : CATCH_REQUIRE(integer_validator->validate('+' + v));
403 : }
404 : else
405 : {
406 7893 : CATCH_REQUIRE_FALSE(integer_validator->validate('+' + v));
407 : }
408 : }
409 :
410 40000 : std::string const space_before(' ' + v);
411 20000 : CATCH_REQUIRE_FALSE(integer_validator->validate(space_before));
412 :
413 40000 : std::string const space_after(v + ' ');
414 20000 : CATCH_REQUIRE_FALSE(integer_validator->validate(space_after));
415 :
416 40000 : std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
417 20000 : CATCH_REQUIRE_FALSE(integer_validator->validate(before));
418 :
419 40000 : std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
420 20000 : CATCH_REQUIRE_FALSE(integer_validator->validate(after));
421 : }
422 : }
423 : }
424 : CATCH_END_SECTION()
425 :
426 6 : CATCH_START_SECTION("integer_validator: Verify the integer standalone list")
427 : {
428 21 : for(int count(0); count < 20; ++count)
429 : {
430 20 : int valid(rand() % 10 + 5);
431 40 : std::vector<std::int64_t> numbers;
432 20 : numbers.reserve(valid);
433 40 : std::string standalone_values;
434 199 : for(int idx(0); idx < valid; ++idx)
435 : {
436 179 : std::int64_t const value(large_rnd());
437 179 : numbers.push_back(value);
438 358 : std::string const & svalue(std::to_string(value));
439 179 : if(rand() % 5 == 0)
440 : {
441 38 : standalone_values += ' ';
442 : }
443 179 : if(idx != 0)
444 : {
445 159 : standalone_values += ',';
446 : }
447 179 : if(rand() % 5 == 0)
448 : {
449 29 : standalone_values += ' ';
450 : }
451 179 : standalone_values += svalue;
452 : }
453 20 : if(rand() % 5 == 0)
454 : {
455 7 : standalone_values += ' ';
456 : }
457 40 : advgetopt::string_list_t range_list;
458 40 : advgetopt::split_string(standalone_values
459 : , range_list
460 20 : , {","});
461 :
462 40 : advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range_list));
463 :
464 20 : CATCH_REQUIRE(integer_validator != nullptr);
465 20 : CATCH_REQUIRE(integer_validator->name() == "integer");
466 :
467 199 : for(size_t idx(0); idx < numbers.size(); ++idx)
468 : {
469 358 : std::string const svalue(std::to_string(numbers[idx]));
470 :
471 179 : CATCH_REQUIRE(integer_validator->validate(svalue));
472 : }
473 :
474 20020 : for(int idx(0); idx < 1000; ++idx)
475 : {
476 20000 : std::int64_t value;
477 :
478 : for(;;)
479 : {
480 20000 : value = large_rnd();
481 20000 : if(std::find(numbers.begin(), numbers.end(), value) == numbers.end())
482 : {
483 20000 : break;
484 : }
485 : }
486 :
487 20000 : CATCH_REQUIRE_FALSE(integer_validator->validate(std::to_string(value)));
488 : }
489 : }
490 : }
491 : CATCH_END_SECTION()
492 3 : }
493 :
494 :
495 :
496 :
497 3 : CATCH_TEST_CASE("multi_validators", "[validator][valid][validation]")
498 : {
499 2 : CATCH_START_SECTION("multi_validators: Verify an integer along a few keywords")
500 : {
501 2 : advgetopt::validator::pointer_t list_validator(advgetopt::validator::create("keywords(off,min,max) | integer(1...100)"));
502 :
503 1 : CATCH_REQUIRE(list_validator != nullptr);
504 1 : CATCH_REQUIRE(list_validator->name() == "list");
505 :
506 1 : CATCH_REQUIRE(list_validator->validate("off"));
507 1 : CATCH_REQUIRE(list_validator->validate("min"));
508 1 : CATCH_REQUIRE(list_validator->validate("max"));
509 :
510 122 : for(int idx(-10); idx <= 110; ++idx)
511 : {
512 242 : std::string const v(std::to_string(idx));
513 :
514 121 : if(idx < 1 || idx > 100)
515 : {
516 21 : CATCH_REQUIRE_FALSE(list_validator->validate(v));
517 : }
518 : else
519 : {
520 100 : CATCH_REQUIRE(list_validator->validate(v));
521 : }
522 : }
523 : }
524 : CATCH_END_SECTION()
525 1 : }
526 :
527 :
528 :
529 3 : CATCH_TEST_CASE("keywords_validator", "[validator][valid][validation]")
530 : {
531 2 : CATCH_START_SECTION("keywords_validator: Verify simple keywords")
532 : {
533 2 : advgetopt::validator::pointer_t list_validator(advgetopt::validator::create("keywords(angle, corner ,, ceiling)"));
534 :
535 1 : CATCH_REQUIRE(list_validator != nullptr);
536 1 : CATCH_REQUIRE(list_validator->name() == "keywords");
537 :
538 1 : CATCH_REQUIRE(list_validator->validate("angle"));
539 1 : CATCH_REQUIRE(list_validator->validate("corner"));
540 1 : CATCH_REQUIRE(list_validator->validate("ceiling"));
541 :
542 1 : CATCH_REQUIRE_FALSE(list_validator->validate(""));
543 1 : CATCH_REQUIRE_FALSE(list_validator->validate("other"));
544 : }
545 : CATCH_END_SECTION()
546 1 : }
547 :
548 :
549 :
550 :
551 5 : CATCH_TEST_CASE("double_validator", "[validator][valid][validation]")
552 : {
553 6 : CATCH_START_SECTION("Verify the double validator")
554 2 : advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", advgetopt::string_list_t()));
555 :
556 1 : CATCH_REQUIRE(double_validator != nullptr);
557 1 : CATCH_REQUIRE(double_validator->name() == "double");
558 :
559 1 : CATCH_REQUIRE_FALSE(double_validator->validate(""));
560 1 : CATCH_REQUIRE_FALSE(double_validator->validate("+"));
561 1 : CATCH_REQUIRE_FALSE(double_validator->validate("-"));
562 1 : CATCH_REQUIRE_FALSE(double_validator->validate("alpha"));
563 :
564 1001 : for(int idx(0); idx < 1000; ++idx)
565 : {
566 1000 : double value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
567 2000 : std::string const v(std::to_string(value));
568 :
569 1000 : CATCH_REQUIRE(double_validator->validate(v));
570 :
571 1000 : if(value >= 0)
572 : {
573 490 : CATCH_REQUIRE(double_validator->validate('+' + v));
574 : }
575 :
576 2000 : std::string const space_before(' ' + v);
577 1000 : CATCH_REQUIRE_FALSE(double_validator->validate(space_before));
578 :
579 2000 : std::string const space_after(v + ' ');
580 1000 : CATCH_REQUIRE_FALSE(double_validator->validate(space_after));
581 :
582 2000 : std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
583 1000 : CATCH_REQUIRE_FALSE(double_validator->validate(before));
584 :
585 2000 : std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
586 1000 : CATCH_REQUIRE_FALSE(double_validator->validate(after));
587 : }
588 : CATCH_END_SECTION()
589 :
590 6 : CATCH_START_SECTION("Verify the double ranges")
591 1 : bool had_standalone(false);
592 21 : for(int count(0); count < 20 || !had_standalone; ++count)
593 : {
594 20 : double min(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
595 20 : double max(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
596 20 : if(min > max)
597 : {
598 7 : std::swap(min, max);
599 : }
600 :
601 40 : std::string const & smin(std::to_string(min));
602 40 : std::string const & smax(std::to_string(max));
603 :
604 40 : std::string range("...");
605 80 : for(int three(0); three < 3; ++three)
606 : {
607 60 : if(rand() % 5 == 0)
608 : {
609 12 : range = ' ' + range;
610 : }
611 60 : if(rand() % 5 == 0)
612 : {
613 12 : range = range + ' ';
614 : }
615 : }
616 20 : range = smin + range + smax;
617 80 : for(int three(0); three < 3; ++three)
618 : {
619 60 : if(rand() % 5 == 0)
620 : {
621 11 : range = ' ' + range;
622 : }
623 60 : if(rand() % 5 == 0)
624 : {
625 12 : range = range + ' ';
626 : }
627 : }
628 :
629 20 : double standalone(0);
630 20 : bool standalone_included(rand() % 4 == 0);
631 20 : if(standalone_included)
632 : {
633 4 : if(min <= std::numeric_limits<double>::min()
634 2 : && max >= std::numeric_limits<double>::max())
635 : {
636 0 : standalone_included = false;
637 : }
638 : else
639 : {
640 2 : had_standalone = true;
641 1 : do
642 : {
643 3 : standalone = static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false));
644 : }
645 3 : while(standalone >= min && standalone <= max);
646 :
647 4 : std::string sep(",");
648 2 : if(rand() % 3 == 0)
649 : {
650 1 : sep = ' ' + sep;
651 : }
652 2 : if(rand() % 3 == 0)
653 : {
654 1 : sep = sep + ' ';
655 : }
656 2 : if(rand() % 2 == 0)
657 : {
658 0 : range = std::to_string(standalone) + "," + range;
659 : }
660 : else
661 : {
662 2 : range = range + "," + std::to_string(standalone);
663 : }
664 : }
665 : }
666 40 : advgetopt::string_list_t range_list;
667 40 : advgetopt::split_string(range
668 : , range_list
669 20 : , {","});
670 40 : advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", range_list));
671 :
672 20 : CATCH_REQUIRE(double_validator != nullptr);
673 20 : CATCH_REQUIRE(double_validator->name() == "double");
674 :
675 20020 : for(int idx(0); idx < 1000; ++idx)
676 : {
677 20000 : double value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
678 :
679 : // force valid values otherwise we're likely to only have
680 : // invalid ones
681 : //
682 20000 : if(idx % 10 == 0)
683 : {
684 2000 : value = fmod(value, max - min + 1.0) + min;
685 : }
686 18000 : else if(idx % 50 == 1 && standalone_included)
687 : {
688 40 : value = standalone;
689 : }
690 :
691 40000 : std::string const v(std::to_string(value));
692 :
693 : #pragma GCC diagnostic push
694 : #pragma GCC diagnostic ignored "-Wfloat-equal"
695 20000 : if((standalone_included && value == standalone)
696 19960 : || (value >= min && value <= max))
697 : {
698 9260 : CATCH_REQUIRE(double_validator->validate(v));
699 : }
700 : else
701 : {
702 10740 : CATCH_REQUIRE_FALSE(double_validator->validate(v));
703 : }
704 :
705 20000 : if(value >= 0.0)
706 : {
707 9338 : if((standalone_included && value == standalone)
708 9298 : || (value >= min && value <= max))
709 : {
710 3818 : CATCH_REQUIRE(double_validator->validate('+' + v));
711 : }
712 : else
713 : {
714 5520 : CATCH_REQUIRE_FALSE(double_validator->validate('+' + v));
715 : }
716 : }
717 : #pragma GCC diagnostic pop
718 :
719 40000 : std::string const space_before(' ' + v);
720 20000 : CATCH_REQUIRE_FALSE(double_validator->validate(space_before));
721 :
722 40000 : std::string const space_after(v + ' ');
723 20000 : CATCH_REQUIRE_FALSE(double_validator->validate(space_after));
724 :
725 40000 : std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
726 20000 : CATCH_REQUIRE_FALSE(double_validator->validate(before));
727 :
728 40000 : std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
729 20000 : CATCH_REQUIRE_FALSE(double_validator->validate(after));
730 : }
731 : }
732 : CATCH_END_SECTION()
733 :
734 6 : CATCH_START_SECTION("Verify the double standalone list")
735 21 : for(int count(0); count < 20; ++count)
736 : {
737 20 : int valid(rand() % 10 + 5);
738 40 : std::vector<double> numbers;
739 20 : numbers.reserve(valid);
740 40 : std::string standalone_values;
741 225 : for(int idx(0); idx < valid; ++idx)
742 : {
743 205 : double const value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
744 205 : numbers.push_back(value);
745 410 : std::string const & svalue(std::to_string(value));
746 205 : if(rand() % 5 == 0)
747 : {
748 35 : standalone_values += ' ';
749 : }
750 205 : if(idx != 0)
751 : {
752 185 : standalone_values += ',';
753 : }
754 205 : if(rand() % 5 == 0)
755 : {
756 33 : standalone_values += ' ';
757 : }
758 205 : standalone_values += svalue;
759 : }
760 20 : if(rand() % 5 == 0)
761 : {
762 4 : standalone_values += ' ';
763 : }
764 40 : advgetopt::string_list_t range_list;
765 40 : advgetopt::split_string(standalone_values
766 : , range_list
767 20 : , {","});
768 :
769 40 : advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", range_list));
770 :
771 20 : CATCH_REQUIRE(double_validator != nullptr);
772 20 : CATCH_REQUIRE(double_validator->name() == "double");
773 :
774 225 : for(size_t idx(0); idx < numbers.size(); ++idx)
775 : {
776 410 : std::string const svalue(std::to_string(numbers[idx]));
777 :
778 205 : CATCH_REQUIRE(double_validator->validate(svalue));
779 : }
780 :
781 20020 : for(int idx(0); idx < 1000; ++idx)
782 : {
783 20000 : std::int64_t value;
784 :
785 : for(;;)
786 : {
787 20000 : value = static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false));
788 20000 : if(std::find(numbers.begin(), numbers.end(), value) == numbers.end())
789 : {
790 20000 : break;
791 : }
792 : }
793 :
794 20000 : CATCH_REQUIRE_FALSE(double_validator->validate(std::to_string(value)));
795 : }
796 : }
797 : CATCH_END_SECTION()
798 3 : }
799 :
800 :
801 :
802 :
803 5 : CATCH_TEST_CASE("duration_validator", "[validator][valid][validation]")
804 : {
805 6 : CATCH_START_SECTION("Verify the duration validator (simple values)")
806 : {
807 1 : double duration(0.0);
808 :
809 : // simple seconds with decimal point
810 : //
811 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string("22.3s", 0, duration));
812 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 22.3, 0.0));
813 :
814 : // "seconds" is the default
815 : //
816 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1.05", 0, duration));
817 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 1.05, 0.0));
818 :
819 : // number can start with a decimal point
820 : //
821 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string(".0503", 0, duration));
822 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 0.0503, 0.0));
823 : }
824 : CATCH_END_SECTION()
825 :
826 6 : CATCH_START_SECTION("Verify the duration validator (multiple values)")
827 : {
828 1 : double duration(0.0);
829 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1d 3h 2m 15.3s", 0, duration));
830 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 1.0 * 86400.0 + 3.0 * 3600.0 + 2.0 * 60.0 + 15.3, 0.0));
831 :
832 : // same in uppercase
833 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1D 3H 2M 15.3S", 0, duration));
834 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 1.0 * 86400.0 + 3.0 * 3600.0 + 2.0 * 60.0 + 15.3, 0.0));
835 :
836 1 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string("3d 15h 52m 21.801s", 0, duration));
837 1 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 3.0 * 86400.0 + 15.0 * 3600.0 + 52.0 * 60.0 + 21.801, 0.0));
838 : }
839 : CATCH_END_SECTION()
840 :
841 6 : CATCH_START_SECTION("Verify the duration validator (one value)")
842 : {
843 : // this test does not verify that double conversion works since we
844 : // have a separate test for that specific validator
845 : //
846 4 : for(int size(0); size < 3; ++size)
847 : {
848 3 : advgetopt::validator_duration::flag_t flg(advgetopt::validator_duration::VALIDATOR_DURATION_DEFAULT_FLAGS);
849 6 : advgetopt::string_list_t flags;
850 3 : if(size == 1)
851 : {
852 1 : flags.push_back("small");
853 : }
854 2 : else if(size == 2)
855 : {
856 1 : flags.push_back("large");
857 1 : flg = advgetopt::validator_duration::VALIDATOR_DURATION_LONG;
858 : }
859 6 : advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", flags));
860 :
861 3 : CATCH_REQUIRE(duration_validator != nullptr);
862 3 : CATCH_REQUIRE(duration_validator->name() == "duration");
863 :
864 3003 : for(int idx(0); idx < 1000; ++idx)
865 : {
866 : // use smaller values between 0 and 1
867 : // (the loop is to make sure we don't end up with "123e-10"
868 : // type of numbers... which do not work here)
869 : //
870 3000 : double value(0.0);
871 : #pragma GCC diagnostic push
872 : #pragma GCC diagnostic ignored "-Wfloat-equal"
873 1 : do
874 : {
875 3001 : value = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
876 : }
877 3001 : while(value < 0.0001 && value != 0.0);
878 : #pragma GCC diagnostic pop
879 3000 : if(rand() % 2 == 0)
880 : {
881 1518 : value *= -1.0;
882 : }
883 6000 : std::stringstream ss;
884 3000 : ss.precision(std::numeric_limits<double>::max_digits10);
885 3000 : ss << value;
886 6000 : std::string const v(ss.str());
887 :
888 66000 : for(std::size_t i(0); i < std::size(g_duration_suffixes); ++i)
889 : {
890 441000 : for(int j(0); j <= 5; ++j)
891 : {
892 756000 : std::string duration(v);
893 1323000 : for(int k(0); k < j; ++k)
894 : {
895 : // any number of spaces in between are allowed
896 : //
897 945000 : duration += ' ';
898 : }
899 378000 : duration += g_duration_suffixes[i].f_suffix;
900 :
901 378000 : CATCH_REQUIRE(duration_validator->validate(duration));
902 378000 : if(value >= 0)
903 : {
904 186732 : CATCH_REQUIRE(duration_validator->validate('+' + duration));
905 : }
906 :
907 378000 : double result(0.0);
908 378000 : CATCH_REQUIRE(advgetopt::validator_duration::convert_string(duration, flg, result));
909 378000 : if(g_duration_suffixes[i].f_factor < 0.0)
910 : {
911 : // the 'm' special case
912 : //
913 18000 : if(size == 2)
914 : {
915 : // 'large' -- 1 month
916 : //
917 6000 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * (86400.0 * 30.0)));
918 : }
919 : else
920 : {
921 : // 'small' -- 1 minute
922 : //
923 12000 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * 60.0));
924 : }
925 : }
926 : else
927 : {
928 360000 : CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * g_duration_suffixes[i].f_factor));
929 : }
930 : }
931 : }
932 : }
933 : }
934 : }
935 : CATCH_END_SECTION()
936 3 : }
937 :
938 :
939 :
940 :
941 3 : CATCH_TEST_CASE("size_validator", "[validator][valid][validation]")
942 : {
943 : #pragma GCC diagnostic push
944 : #pragma GCC diagnostic ignored "-Wpedantic"
945 2 : CATCH_START_SECTION("Verify the size validator")
946 : {
947 : // this test does not verify that double conversion works since we
948 : // have a separate test for that specific validator
949 : //
950 4 : for(int mode(0); mode < 3; ++mode)
951 : {
952 3 : advgetopt::validator_size::flag_t flg(advgetopt::validator_size::VALIDATOR_SIZE_DEFAULT_FLAGS);
953 6 : advgetopt::string_list_t flags;
954 3 : if(mode == 1)
955 : {
956 1 : flags.push_back("si");
957 : }
958 2 : else if(mode == 2)
959 : {
960 1 : flags.push_back("legacy");
961 1 : flg = advgetopt::validator_size::VALIDATOR_SIZE_POWER_OF_TWO;
962 : }
963 6 : advgetopt::validator::pointer_t size_validator(advgetopt::validator::create("size", flags));
964 :
965 3 : CATCH_REQUIRE(size_validator != nullptr);
966 3 : CATCH_REQUIRE(size_validator->name() == "size");
967 :
968 3003 : for(int idx(0); idx < 1000; ++idx)
969 : {
970 : // use smaller values between 0 and about 5
971 : //
972 3000 : double value(static_cast<double>(rand()) / static_cast<double>(RAND_MAX / 5));
973 3000 : if(rand() % 2 == 0)
974 : {
975 1516 : value *= -1.0;
976 : }
977 6000 : std::stringstream ss;
978 3000 : ss.precision(std::numeric_limits<double>::max_digits10);
979 3000 : ss << value;
980 6000 : std::string const v(ss.str());
981 :
982 57000 : for(std::size_t i(0); i < std::size(g_size_suffixes); ++i)
983 : {
984 378000 : for(int j(0); j <= 5; ++j)
985 : {
986 648000 : std::string size(v);
987 1134000 : for(int k(0); k < j; ++k)
988 : {
989 : // any number of spaces in between are allowed
990 : //
991 810000 : size += ' ';
992 : }
993 324000 : size += g_size_suffixes[i].f_suffix;
994 :
995 324000 : CATCH_REQUIRE(size_validator->validate(size));
996 324000 : if(value >= 0)
997 : {
998 160272 : CATCH_REQUIRE(size_validator->validate('+' + size));
999 : }
1000 :
1001 324000 : __int128 result(0.0);
1002 324000 : CATCH_REQUIRE(advgetopt::validator_size::convert_string(size, flg, result));
1003 :
1004 324000 : long double const base(mode == 2 ? 1024.0L : g_size_suffixes[i].f_base);
1005 324000 : long double expected(1);
1006 1620000 : for(int p(0); p < g_size_suffixes[i].f_power; ++p)
1007 : {
1008 1296000 : expected *= base;
1009 : }
1010 324000 : __int128 int_expected(expected * static_cast<long double>(value));
1011 :
1012 : //std::cerr << "converted [" << size << "] to [" << result << "] wanted [" << int_expected << "]\n";
1013 324000 : CATCH_REQUIRE(result == int_expected);
1014 : }
1015 : }
1016 : }
1017 : }
1018 : }
1019 : CATCH_END_SECTION()
1020 : #pragma GCC diagnostic pop
1021 1 : }
1022 :
1023 :
1024 :
1025 :
1026 6 : CATCH_TEST_CASE("regex_validator", "[validator][valid][validation]")
1027 : {
1028 8 : CATCH_START_SECTION("regex_validator: Verify the regex validator")
1029 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {".*@.*\\..*"}));
1030 :
1031 1 : CATCH_REQUIRE(regex_validator != nullptr);
1032 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1033 :
1034 1 : CATCH_REQUIRE(regex_validator->validate("@m2osw."));
1035 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1036 1 : CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
1037 1 : CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
1038 :
1039 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1040 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1041 : CATCH_END_SECTION()
1042 :
1043 8 : CATCH_START_SECTION("regex_validator: Verify the regex string (case sensitive)")
1044 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/"}));
1045 :
1046 1 : CATCH_REQUIRE(regex_validator != nullptr);
1047 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1048 :
1049 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1050 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1051 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
1052 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
1053 :
1054 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1055 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1056 : CATCH_END_SECTION()
1057 :
1058 8 : CATCH_START_SECTION("regex_validator: Verify the regex string (case insensitive)")
1059 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/i"}));
1060 :
1061 1 : CATCH_REQUIRE(regex_validator != nullptr);
1062 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1063 :
1064 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1065 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1066 1 : CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
1067 1 : CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
1068 :
1069 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1070 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1071 : CATCH_END_SECTION()
1072 :
1073 8 : CATCH_START_SECTION("regex_validator: Verify direct regex string (case insensitive)")
1074 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("/contact@.*\\..*/i"));
1075 :
1076 1 : CATCH_REQUIRE(regex_validator != nullptr);
1077 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1078 :
1079 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1080 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1081 1 : CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
1082 1 : CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
1083 :
1084 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1085 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1086 : CATCH_END_SECTION()
1087 4 : }
1088 :
1089 :
1090 :
1091 :
1092 :
1093 :
1094 :
1095 :
1096 3 : CATCH_TEST_CASE("invalid_validator_factory", "[validator][invalid][validation]")
1097 : {
1098 2 : CATCH_START_SECTION("invalid_validator_factory: Register duplicated factories")
1099 : {
1100 0 : class duplicate_integer
1101 : : public advgetopt::validator
1102 : {
1103 : public:
1104 0 : virtual std::string name() const override
1105 : {
1106 0 : return "integer";
1107 : }
1108 :
1109 0 : virtual bool validate(std::string const & value) const override
1110 : {
1111 0 : return value == "123";
1112 : }
1113 : };
1114 3 : class duplicate_factory
1115 : : public advgetopt::validator_factory
1116 : {
1117 : public:
1118 2 : virtual std::string get_name() const override
1119 : {
1120 2 : return "integer";
1121 : }
1122 :
1123 0 : virtual std::shared_ptr<advgetopt::validator> create(advgetopt::string_list_t const & data) const override
1124 : {
1125 0 : snapdev::NOT_USED(data); // ignore `data`
1126 0 : return std::make_shared<duplicate_integer>();
1127 : }
1128 : };
1129 2 : std::unique_ptr<advgetopt::validator_factory> factory(new duplicate_factory());
1130 1 : CATCH_REQUIRE_THROWS_MATCHES(
1131 : advgetopt::validator::register_validator(*factory.get())
1132 : , advgetopt::getopt_logic_error
1133 : , Catch::Matchers::ExceptionMessage(
1134 : "getopt_logic_error: you have two or more validator factories named \"integer\"."));
1135 : }
1136 : CATCH_END_SECTION()
1137 1 : }
1138 :
1139 3 : CATCH_TEST_CASE("invalid_validator_create", "[validator][invalid][validation]")
1140 : {
1141 2 : CATCH_START_SECTION("invalid_validator_create: Verify missing ')' in string based create")
1142 : {
1143 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameter list must end with ')'. Remaining input: \"...EOS\"");
1144 2 : advgetopt::validator::pointer_t validator(advgetopt::validator::create("integer(1...7, 11...15"));
1145 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1146 1 : CATCH_REQUIRE(validator == nullptr);
1147 :
1148 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameter list must end with ')'. Remaining input: \"...EOS\"");
1149 1 : validator = advgetopt::validator::create("regex([a-z]+");
1150 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1151 1 : CATCH_REQUIRE(validator == nullptr);
1152 :
1153 1 : validator = advgetopt::validator::create(" ");
1154 1 : CATCH_REQUIRE(validator == nullptr);
1155 :
1156 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): expected a regex, an identifier or a string inside the () of a parameter. Remaining input: \"[a-z]+))\"");
1157 1 : validator = advgetopt::validator::create("regex(([a-z]+))");
1158 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1159 1 : CATCH_REQUIRE(validator == nullptr);
1160 :
1161 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameters must be separated by ','. Remaining input: \"...EOS\"");
1162 1 : validator = advgetopt::validator::create("keywords(foo, blah error)");
1163 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1164 1 : CATCH_REQUIRE(validator == nullptr);
1165 :
1166 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected token in validator definition; expected an identifier. Remaining input: \"missing, name)\".");
1167 1 : validator = advgetopt::validator::create("(missing, name)");
1168 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1169 1 : CATCH_REQUIRE(validator == nullptr);
1170 :
1171 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
1172 1 : validator = advgetopt::validator::create("keywords(missing, name)\n|\ninteger(33)");
1173 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1174 1 : CATCH_REQUIRE(validator == nullptr);
1175 :
1176 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): validator definitions must be separated by '|'. Remaining input: \"33)\"");
1177 1 : validator = advgetopt::validator::create("keywords(missing, name) integer(33)");
1178 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1179 1 : CATCH_REQUIRE(validator == nullptr);
1180 : }
1181 : CATCH_END_SECTION()
1182 1 : }
1183 :
1184 3 : CATCH_TEST_CASE("invalid_integer_validator", "[validator][invalid][validation]")
1185 : {
1186 2 : CATCH_START_SECTION("invalid_integer_validator: Verify invalid integer ranges")
1187 : {
1188 1 : advgetopt::string_list_t range{
1189 : "abc",
1190 : "abc...6",
1191 : "3...def",
1192 2 : "10...1"};
1193 :
1194 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid standalone value for your ranges; it must only be digits, optionally preceeded by a sign (+ or -) and not overflow an int64_t value.");
1195 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid value for your range's start; it must only be digits, optionally preceeded by a sign (+ or -) and not overflow an int64_t value.");
1196 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: def is not a valid value for your range's end; it must only be digits, optionally preceeded by a sign (+ or -) and not overflow an int64_t value.");
1197 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: 10 has to be smaller or equal to 1; you have an invalid range.");
1198 :
1199 2 : advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range));
1200 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1201 : }
1202 : CATCH_END_SECTION()
1203 1 : }
1204 :
1205 3 : CATCH_TEST_CASE("invalid_double_validator", "[validator][invalid][validation]")
1206 : {
1207 2 : CATCH_START_SECTION("invalid_double_validator: Verify invalid double ranges")
1208 : {
1209 1 : advgetopt::string_list_t range{
1210 : "abc",
1211 : "abc...6.3",
1212 : "13.3...def",
1213 2 : "10.5...1.2"};
1214 :
1215 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid standalone value; it must be a valid floating point, optionally preceeded by a sign (+ or -).");
1216 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid value for your range's start; it must be a valid floating point, optionally preceeded by a sign (+ or -).");
1217 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: def is not a valid value for your range's end; it must be a valid floating point, optionally preceeded by a sign (+ or -).");
1218 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: 10.5 has to be smaller or equal to 1.2; you have an invalid range.");
1219 :
1220 2 : advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("double", range));
1221 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1222 : }
1223 : CATCH_END_SECTION()
1224 1 : }
1225 :
1226 3 : CATCH_TEST_CASE("invalid_duration_validator", "[invalid][validation]")
1227 : {
1228 2 : CATCH_START_SECTION("invalid_duration_validator: Verify invalid duration flags")
1229 : {
1230 1 : advgetopt::string_list_t range{
1231 : "small",
1232 : "medium",
1233 2 : "large"};
1234 :
1235 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: medium is not a valid flag for the duration validator.");
1236 2 : advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
1237 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1238 :
1239 1 : CATCH_REQUIRE_FALSE(duration_validator->validate(""));
1240 1 : CATCH_REQUIRE_FALSE(duration_validator->validate(" "));
1241 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("+"));
1242 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-"));
1243 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("alpha"));
1244 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("3.5 beta"));
1245 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("7.5delta"));
1246 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("+8.1 gamma"));
1247 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-2.3eta"));
1248 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-202.3 HERO"));
1249 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-7.31Hr"));
1250 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-1.32mom"));
1251 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("-5.36 secs"));
1252 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("28.901 wkS"));
1253 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("28 YY"));
1254 1 : CATCH_REQUIRE_FALSE(duration_validator->validate("2..8 year"));
1255 : }
1256 : CATCH_END_SECTION()
1257 1 : }
1258 :
1259 :
1260 :
1261 3 : CATCH_TEST_CASE("invalid_email_validator", "[invalid][validation]")
1262 : {
1263 2 : CATCH_START_SECTION("invalid_email_validator: Verify emails with invalid parameters.")
1264 : {
1265 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_email() supports zero or one parameter.");
1266 2 : advgetopt::validator::pointer_t keywords(advgetopt::validator::create("email(single, multiple)"));
1267 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1268 1 : CATCH_REQUIRE(keywords != nullptr);
1269 :
1270 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_email(): unknown parameter \"orange\".");
1271 1 : keywords = advgetopt::validator::create("email(orange)");
1272 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1273 1 : CATCH_REQUIRE(keywords != nullptr);
1274 : }
1275 : CATCH_END_SECTION()
1276 1 : }
1277 :
1278 :
1279 :
1280 3 : CATCH_TEST_CASE("invalid_keywords_validator", "[invalid][validation]")
1281 : {
1282 2 : CATCH_START_SECTION("invalid_keywords_validator: Verify that keywords without parameters fail.")
1283 : {
1284 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_keywords() requires at least one parameter.");
1285 2 : advgetopt::validator::pointer_t keywords(advgetopt::validator::create("keywords"));
1286 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1287 1 : CATCH_REQUIRE(keywords != nullptr);
1288 :
1289 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_keywords() requires at least one parameter.");
1290 1 : keywords = advgetopt::validator::create("keywords()");
1291 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1292 1 : CATCH_REQUIRE(keywords != nullptr);
1293 : }
1294 : CATCH_END_SECTION()
1295 1 : }
1296 :
1297 :
1298 :
1299 3 : CATCH_TEST_CASE("invalid_list_validator", "[invalid][validation]")
1300 : {
1301 2 : CATCH_START_SECTION("invalid_list_validator: Verify that list validators do not accept parameters.")
1302 : {
1303 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_list() does not support any parameter.");
1304 2 : advgetopt::validator::pointer_t list(advgetopt::validator::create("list(with, parameters)"));
1305 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1306 1 : CATCH_REQUIRE(list != nullptr);
1307 : }
1308 : CATCH_END_SECTION()
1309 1 : }
1310 :
1311 :
1312 :
1313 3 : CATCH_TEST_CASE("invalid_size_validator", "[invalid][validation]")
1314 : {
1315 2 : CATCH_START_SECTION("invalid_size_validator: Verify invalid duration flags")
1316 : {
1317 1 : advgetopt::string_list_t flags{
1318 : "si",
1319 : "future",
1320 2 : "legacy"};
1321 :
1322 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: future is not a valid flag for the size validator.");
1323 2 : advgetopt::validator::pointer_t size_validator(advgetopt::validator::create("size", flags));
1324 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1325 :
1326 1 : CATCH_REQUIRE_FALSE(size_validator->validate(""));
1327 1 : CATCH_REQUIRE_FALSE(size_validator->validate(" "));
1328 1 : CATCH_REQUIRE_FALSE(size_validator->validate("+"));
1329 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-"));
1330 1 : CATCH_REQUIRE_FALSE(size_validator->validate("size"));
1331 1 : CATCH_REQUIRE_FALSE(size_validator->validate("3.5 large"));
1332 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-1.31body"));
1333 1 : CATCH_REQUIRE_FALSE(size_validator->validate("7.5small"));
1334 1 : CATCH_REQUIRE_FALSE(size_validator->validate("+8.1 tiny"));
1335 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-2.3medium"));
1336 1 : CATCH_REQUIRE_FALSE(size_validator->validate("1000kbit"));
1337 1 : CATCH_REQUIRE_FALSE(size_validator->validate("7 monster"));
1338 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-101.101egret"));
1339 1 : CATCH_REQUIRE_FALSE(size_validator->validate("11 products"));
1340 1 : CATCH_REQUIRE_FALSE(size_validator->validate("1.01 tractor"));
1341 1 : CATCH_REQUIRE_FALSE(size_validator->validate("+7.0 years"));
1342 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-51.7zeroes"));
1343 1 : CATCH_REQUIRE_FALSE(size_validator->validate("+121gruffalos"));
1344 1 : CATCH_REQUIRE_FALSE(size_validator->validate("++1.7 KiB"));
1345 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-+3.1 MiB"));
1346 1 : CATCH_REQUIRE_FALSE(size_validator->validate("+-9.2 GiB"));
1347 1 : CATCH_REQUIRE_FALSE(size_validator->validate("--19.4 PiB"));
1348 1 : CATCH_REQUIRE_FALSE(size_validator->validate("-3.5.4B"));
1349 : }
1350 : CATCH_END_SECTION()
1351 1 : }
1352 :
1353 8 : CATCH_TEST_CASE("invalid_regex_validator", "[validator][invalid][validation]")
1354 : {
1355 12 : CATCH_START_SECTION("invalid_regex_validator: Verify invalid regex flags")
1356 : {
1357 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag f in regular expression \"/contact@.*\\..*/f\".");
1358 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/f"}));
1359 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1360 :
1361 1 : CATCH_REQUIRE(regex_validator != nullptr);
1362 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1363 :
1364 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1365 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1366 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
1367 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
1368 :
1369 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1370 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1371 : }
1372 : CATCH_END_SECTION()
1373 :
1374 12 : CATCH_START_SECTION("invalid_regex_validator: Verify invalid regex character")
1375 : {
1376 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for a regular expression (10).");
1377 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex(/contact@.*\n..*/)"));
1378 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1379 1 : CATCH_REQUIRE(regex_validator == nullptr);
1380 :
1381 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected escaped character for a regular expression (13).");
1382 1 : regex_validator = advgetopt::validator::create("regex(/contact@.*\\\r..*/)");
1383 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1384 1 : CATCH_REQUIRE(regex_validator == nullptr);
1385 :
1386 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected flag character for a regular expression (57).");
1387 1 : regex_validator = advgetopt::validator::create("regex(/contact@.*..*/91)");
1388 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1389 1 : CATCH_REQUIRE(regex_validator == nullptr);
1390 :
1391 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
1392 1 : regex_validator = advgetopt::validator::create("regex(not\nexpected)");
1393 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1394 1 : CATCH_REQUIRE(regex_validator == nullptr);
1395 : }
1396 : CATCH_END_SECTION()
1397 :
1398 12 : CATCH_START_SECTION("invalid_regex_validator: Verify invalid regex: missing ending /")
1399 : {
1400 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag * in regular expression \"/contact@.*\\..*\".");
1401 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
1402 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
1403 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag \\ in regular expression \"/contact@.*\\..*\".");
1404 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag * in regular expression \"/contact@.*\\..*\".");
1405 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
1406 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag @ in regular expression \"/contact@.*\\..*\".");
1407 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag t in regular expression \"/contact@.*\\..*\".");
1408 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag c in regular expression \"/contact@.*\\..*\".");
1409 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag a in regular expression \"/contact@.*\\..*\".");
1410 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag t in regular expression \"/contact@.*\\..*\".");
1411 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag n in regular expression \"/contact@.*\\..*\".");
1412 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag o in regular expression \"/contact@.*\\..*\".");
1413 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag c in regular expression \"/contact@.*\\..*\".");
1414 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: invalid regex definition, ending / is missing in \"/contact@.*\\..*\".");
1415 :
1416 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*"}));
1417 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1418 :
1419 1 : CATCH_REQUIRE(regex_validator != nullptr);
1420 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1421 :
1422 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1423 1 : CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
1424 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
1425 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
1426 :
1427 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1428 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1429 : }
1430 : CATCH_END_SECTION()
1431 :
1432 12 : CATCH_START_SECTION("invalid_regex_validator: Verify regex refuses more than one parameter")
1433 : {
1434 1 : SNAP_CATCH2_NAMESPACE::push_expected_log(
1435 : "error: validator_regex() only supports one parameter;"
1436 : " 2 were supplied;"
1437 : " single or double quotation may be required?");
1438 1 : advgetopt::validator::create("regex", {"[a-z]+", "[0-9]+"});
1439 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1440 :
1441 1 : SNAP_CATCH2_NAMESPACE::push_expected_log(
1442 : "error: validator_regex() only supports one parameter;"
1443 : " 2 were supplied;"
1444 : " single or double quotation may be required?");
1445 1 : advgetopt::validator::create("regex([a-z]+, [0-9]+)");
1446 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1447 :
1448 1 : SNAP_CATCH2_NAMESPACE::push_expected_log(
1449 : "error: validator_regex() only supports one parameter;"
1450 : " 3 were supplied;"
1451 : " single or double quotation may be required?");
1452 1 : advgetopt::validator::create("regex", {"[a-z]+", "[0-9]+", "[#!@]"});
1453 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1454 :
1455 1 : SNAP_CATCH2_NAMESPACE::push_expected_log(
1456 : "error: validator_regex() only supports one parameter;"
1457 : " 3 were supplied;"
1458 : " single or double quotation may be required?");
1459 1 : advgetopt::validator::create("regex(\"[a-z]+\", \"[0-9]+\", \"[#!@]\")");
1460 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1461 : }
1462 : CATCH_END_SECTION()
1463 :
1464 12 : CATCH_START_SECTION("invalid_regex_validator: Verify two regex params")
1465 : {
1466 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_regex() only supports one parameter; 2 were supplied; single or double quotation may be required?");
1467 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex(/one/a, /two/b)"));
1468 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1469 :
1470 1 : CATCH_REQUIRE(regex_validator != nullptr);
1471 1 : CATCH_REQUIRE(regex_validator->name() == "regex");
1472 :
1473 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
1474 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw.com"));
1475 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
1476 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
1477 :
1478 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
1479 1 : CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
1480 : }
1481 : CATCH_END_SECTION()
1482 :
1483 12 : CATCH_START_SECTION("invalid_regex_validator: Verify two regex params")
1484 : {
1485 1 : SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
1486 2 : advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex('/one/'\n,'/two/b')"));
1487 1 : SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
1488 1 : CATCH_REQUIRE(regex_validator == nullptr);
1489 : }
1490 : CATCH_END_SECTION()
1491 :
1492 12 : }
1493 :
1494 :
1495 : // vim: ts=4 sw=4 et
|