LCOV - code coverage report
Current view: top level - tests - catch_validator.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 98.9 % 976 965
Test Date: 2026-01-18 09:38:57 Functions: 88.5 % 26 23
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2006-2025  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/math.h>
      36              : #include    <snapdev/ostream_int128.h>
      37              : 
      38              : 
      39              : // C++
      40              : //
      41              : #include    <cmath>
      42              : #include    <fstream>
      43              : #include    <iomanip>
      44              : 
      45              : 
      46              : // last include
      47              : //
      48              : #include    <snapdev/poison.h>
      49              : 
      50              : 
      51              : 
      52              : namespace
      53              : {
      54              : 
      55              : struct duration_t
      56              : {
      57              :     char const * const      f_suffix = nullptr;
      58              :     double                  f_factor = 1.0;
      59              : };
      60              : 
      61              : constexpr duration_t const g_duration_suffixes[] =
      62              : {
      63              :     { "",                      1.0 },
      64              :     { "s",                     1.0 },
      65              :     { "second",                1.0 },
      66              :     { "seconds",               1.0 },
      67              : 
      68              :     { "m",                    -1.0 },   // may represent minutes or months
      69              :     { "minute",               60.0 },
      70              :     { "minutes",              60.0 },
      71              : 
      72              :     { "h",                  3600.0 },
      73              :     { "hour",               3600.0 },
      74              :     { "hours",              3600.0 },
      75              : 
      76              :     { "d",                 86400.0 },
      77              :     { "day",               86400.0 },
      78              :     { "days",              86400.0 },
      79              : 
      80              :     { "w",           86400.0 * 7.0 },
      81              :     { "week",        86400.0 * 7.0 },
      82              :     { "weeks",       86400.0 * 7.0 },
      83              : 
      84              :     { "month",      86400.0 * 30.0 },
      85              :     { "months",     86400.0 * 30.0 },
      86              : 
      87              :     { "y",         86400.0 * 365.0 },
      88              :     { "year",      86400.0 * 365.0 },
      89              :     { "years",     86400.0 * 365.0 },
      90              : };
      91              : 
      92              : struct size_suffix_t
      93              : {
      94              :     char const * const      f_suffix = nullptr;
      95              :     int                     f_base = 1000.0;
      96              :     int                     f_power = 0.0;
      97              : };
      98              : 
      99              : constexpr size_suffix_t const g_size_suffixes[] =
     100              : {
     101              :     { "",     1000, 0 },
     102              :     { "B",    1000, 0 },
     103              : 
     104              :     { "kB",   1000, 1 },
     105              :     { "KiB",  1024, 1 },
     106              : 
     107              :     { "MB",   1000, 2 },
     108              :     { "MiB",  1024, 2 },
     109              : 
     110              :     { "GB",   1000, 3 },
     111              :     { "GiB",  1024, 3 },
     112              : 
     113              :     { "TB",   1000, 4 },
     114              :     { "TiB",  1024, 4 },
     115              : 
     116              :     { "PB",   1000, 5 },
     117              :     { "PiB",  1024, 5 },
     118              : 
     119              :     { "EB",   1000, 6 },
     120              :     { "EiB",  1024, 6 },
     121              : 
     122              :     { "ZB",   1000, 7 },
     123              :     { "ZiB",  1024, 7 },
     124              : 
     125              :     { "YB",   1000, 8 },
     126              :     { "YiB",  1024, 8 },
     127              : 
     128              :     { "RB",   1000, 9 },
     129              :     { "RiB",  1024, 9 },
     130              : 
     131              :     { "QB",   1000, 10 },
     132              :     { "QiB",  1024, 10 },
     133              : };
     134              : 
     135       123768 : std::int64_t large_rnd(bool zero_allowed = true)
     136              : {
     137              :     for(;;)
     138              :     {
     139       123768 :         std::int64_t const result((static_cast<std::int64_t>(rand()) <<  0)
     140       123768 :                                 ^ (static_cast<std::int64_t>(rand()) << 16)
     141       123768 :                                 ^ (static_cast<std::int64_t>(rand()) << 32)
     142       123768 :                                 ^ (static_cast<std::int64_t>(rand()) << 48));
     143       123768 :         if(result != 0
     144            0 :         || zero_allowed)
     145              :         {
     146       123768 :             return result;
     147              :         }
     148            0 :     }
     149              : }
     150              : 
     151              : }
     152              : 
     153              : 
     154              : 
     155            2 : CATCH_TEST_CASE("unknown_validator", "[validator][valid][validation]")
     156              : {
     157            2 :     CATCH_START_SECTION("unknown_validator: undefined validator")
     158              :     {
     159              :         // this is a valid case, it does not throw, it just returns a nullptr
     160              :         //
     161            3 :         CATCH_REQUIRE(advgetopt::validator::create("unknown", advgetopt::string_list_t()) == nullptr);
     162              :     }
     163            2 :     CATCH_END_SECTION()
     164              : 
     165            2 :     CATCH_START_SECTION("unknown_validator: empty string")
     166              :     {
     167            1 :         CATCH_REQUIRE(advgetopt::validator::create(std::string()) == nullptr);
     168              :     }
     169            2 :     CATCH_END_SECTION()
     170            2 : }
     171              : 
     172              : 
     173              : 
     174            3 : CATCH_TEST_CASE("email_validator", "[invalid][validation]")
     175              : {
     176            3 :     CATCH_START_SECTION("email_validator: verify that email verification works.")
     177              :     {
     178            3 :         advgetopt::validator::pointer_t email(advgetopt::validator::create("email"));
     179              : 
     180            1 :         CATCH_REQUIRE(email != nullptr);
     181            1 :         CATCH_REQUIRE(email->name() == "email");
     182              : 
     183            3 :         CATCH_REQUIRE_FALSE(email->validate(""));
     184            3 :         CATCH_REQUIRE(email->validate("user@example.com"));
     185            3 :         CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
     186            3 :         CATCH_REQUIRE_FALSE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
     187            3 :         CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
     188            3 :         CATCH_REQUIRE_FALSE(email->validate("@example.com"));
     189            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
     190            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
     191            1 :     }
     192            3 :     CATCH_END_SECTION()
     193              : 
     194            3 :     CATCH_START_SECTION("email_validator: verify that one email verification works (single explicitly).")
     195              :     {
     196            3 :         advgetopt::validator::pointer_t email(advgetopt::validator::create("email(single)"));
     197              : 
     198            1 :         CATCH_REQUIRE(email != nullptr);
     199            1 :         CATCH_REQUIRE(email->name() == "email");
     200              : 
     201            3 :         CATCH_REQUIRE_FALSE(email->validate(""));
     202            3 :         CATCH_REQUIRE(email->validate("user@example.com"));
     203            3 :         CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
     204            3 :         CATCH_REQUIRE_FALSE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
     205            3 :         CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
     206            3 :         CATCH_REQUIRE_FALSE(email->validate("@example.com"));
     207            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
     208            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
     209            1 :     }
     210            3 :     CATCH_END_SECTION()
     211              : 
     212            3 :     CATCH_START_SECTION("email_validator: verify that multiple emails verification works.")
     213              :     {
     214            3 :         advgetopt::validator::pointer_t email(advgetopt::validator::create("email(multiple)"));
     215              : 
     216            1 :         CATCH_REQUIRE(email != nullptr);
     217            1 :         CATCH_REQUIRE(email->name() == "email");
     218              : 
     219            3 :         CATCH_REQUIRE_FALSE(email->validate(""));
     220            3 :         CATCH_REQUIRE(email->validate("user1@example.com, user2@example.com, user3@example.com"));
     221            3 :         CATCH_REQUIRE(email->validate("USER@EXAMPLE.COM"));
     222            3 :         CATCH_REQUIRE_FALSE(email->validate("User!example.com"));
     223            3 :         CATCH_REQUIRE_FALSE(email->validate("@example.com"));
     224            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@"));
     225            3 :         CATCH_REQUIRE_FALSE(email->validate("uSeR@com"));
     226            1 :     }
     227            3 :     CATCH_END_SECTION()
     228            3 : }
     229              : 
     230              : 
     231              : 
     232            3 : CATCH_TEST_CASE("integer_validator", "[validator][valid][validation]")
     233              : {
     234            3 :     CATCH_START_SECTION("integer_validator: verify the integer validator")
     235              :     {
     236            3 :         advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", advgetopt::string_list_t()));
     237              : 
     238            1 :         CATCH_REQUIRE(integer_validator != nullptr);
     239            1 :         CATCH_REQUIRE(integer_validator->name() == "integer");
     240              : 
     241            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate(""));
     242            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("+"));
     243            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("-"));
     244              : 
     245         1001 :         for(int idx(0); idx < 1000; ++idx)
     246              :         {
     247         1000 :             std::int64_t value(large_rnd());
     248         1000 :             std::string const v(std::to_string(value));
     249              : 
     250         1000 :             CATCH_REQUIRE(integer_validator->validate(v));
     251              : 
     252         1000 :             if(value >= 0)
     253              :             {
     254          497 :                 CATCH_REQUIRE(integer_validator->validate('+' + v));
     255              :             }
     256              : 
     257         1000 :             std::string const space_before(' ' + v);
     258         1000 :             CATCH_REQUIRE_FALSE(integer_validator->validate(space_before));
     259              : 
     260         1000 :             std::string const space_after(v + ' ');
     261         1000 :             CATCH_REQUIRE_FALSE(integer_validator->validate(space_after));
     262              : 
     263         1000 :             std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
     264         1000 :             CATCH_REQUIRE_FALSE(integer_validator->validate(before));
     265              : 
     266         1000 :             std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
     267         1000 :             CATCH_REQUIRE_FALSE(integer_validator->validate(after));
     268         1000 :         }
     269              : 
     270              :         // max number
     271            3 :         CATCH_REQUIRE(integer_validator->validate("9223372036854775807"));
     272            3 :         CATCH_REQUIRE(integer_validator->validate("+9223372036854775807"));
     273              : 
     274              :         // overflow
     275            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("9223372036854775808"));
     276            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("+9223372036854775808"));
     277              : 
     278              :         // min number
     279            3 :         CATCH_REQUIRE(integer_validator->validate("-9223372036854775808"));
     280              : 
     281              :         // underflow
     282            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("-9223372036854775809"));
     283              : 
     284              :         // too many digits
     285            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("92233720368547758091"));
     286            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("+92233720368547758092"));
     287            3 :         CATCH_REQUIRE_FALSE(integer_validator->validate("-92233720368547758093"));
     288            1 :     }
     289            3 :     CATCH_END_SECTION()
     290              : 
     291            3 :     CATCH_START_SECTION("integer_validator: verify the integer ranges")
     292              :     {
     293            1 :         bool had_standalone(false);
     294           21 :         for(int count(0); count < 20 || !had_standalone; ++count)
     295              :         {
     296           20 :             std::int64_t min(large_rnd());
     297           20 :             std::int64_t max(large_rnd());
     298           20 :             if(min > max)
     299              :             {
     300            7 :                 std::swap(min, max);
     301              :             }
     302              : 
     303           20 :             std::string const & smin(std::to_string(min));
     304           20 :             std::string const & smax(std::to_string(max));
     305              : 
     306           60 :             std::string range("...");
     307           80 :             for(int three(0); three < 3; ++three)
     308              :             {
     309           60 :                 if(rand() % 5 == 0)
     310              :                 {
     311           10 :                     range = ' ' + range;
     312              :                 }
     313           60 :                 if(rand() % 5 == 0)
     314              :                 {
     315           16 :                     range = range + ' ';
     316              :                 }
     317              :             }
     318           20 :             range = smin + range + smax;
     319           80 :             for(int three(0); three < 3; ++three)
     320              :             {
     321           60 :                 if(rand() % 5 == 0)
     322              :                 {
     323           11 :                     range = ' ' + range;
     324              :                 }
     325           60 :                 if(rand() % 5 == 0)
     326              :                 {
     327           15 :                     range = range + ' ';
     328              :                 }
     329              :             }
     330              : 
     331           20 :             std::int64_t standalone(0);
     332           20 :             bool standalone_included(rand() % 4 == 0);
     333           20 :             if(standalone_included)
     334              :             {
     335            5 :                 if(min == std::numeric_limits<std::int64_t>::min()
     336            5 :                 && max == std::numeric_limits<std::int64_t>::max())
     337              :                 {
     338            0 :                     standalone_included = false;
     339              :                 }
     340              :                 else
     341              :                 {
     342            5 :                     had_standalone = true;
     343              :                     do
     344              :                     {
     345            5 :                         standalone = large_rnd();
     346              :                     }
     347            5 :                     while(standalone >= min && standalone <= max);
     348              : 
     349           15 :                     std::string sep(",");
     350            5 :                     if(rand() % 3 == 0)
     351              :                     {
     352            2 :                         sep = ' ' + sep;
     353              :                     }
     354            5 :                     if(rand() % 3 == 0)
     355              :                     {
     356            1 :                         sep = sep + ' ';
     357              :                     }
     358            5 :                     if(rand() % 2 == 0)
     359              :                     {
     360            1 :                         range = std::to_string(standalone) + "," + range;
     361              :                     }
     362              :                     else
     363              :                     {
     364            4 :                         range = range + "," + std::to_string(standalone);
     365              :                     }
     366            5 :                 }
     367              :             }
     368           20 :             advgetopt::string_list_t range_list;
     369           60 :             advgetopt::split_string(range
     370              :                        , range_list
     371              :                        , {","});
     372           60 :             advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range_list));
     373              : 
     374           20 :             CATCH_REQUIRE(integer_validator != nullptr);
     375           20 :             CATCH_REQUIRE(integer_validator->name() == "integer");
     376              : 
     377        20020 :             for(int idx(0); idx < 1000; ++idx)
     378              :             {
     379        20000 :                 std::int64_t value(large_rnd());
     380              : 
     381              :                 // force valid values otherwise we're likely to only have
     382              :                 // invalid ones
     383              :                 //
     384        20000 :                 if(idx % 10 == 0)
     385              :                 {
     386         2000 :                     value %= max - min + 1;
     387         2000 :                     value += min;
     388              :                 }
     389        18000 :                 else if(idx % 50 == 1 && standalone_included)
     390              :                 {
     391          100 :                     value = standalone;
     392              :                 }
     393              : 
     394        20000 :                 std::string const v(std::to_string(value));
     395              : 
     396        20000 :                 if((standalone_included && value == standalone)
     397        19900 :                 || (value >= min && value <= max))
     398              :                 {
     399         6533 :                     CATCH_REQUIRE(integer_validator->validate(v));
     400         6533 :                 }
     401              :                 else
     402              :                 {
     403        13467 :                     CATCH_REQUIRE_FALSE(integer_validator->validate(v));
     404              :                 }
     405              : 
     406        20000 :                 if(value >= 0)
     407              :                 {
     408        10057 :                     if((standalone_included && value == standalone)
     409         9977 :                     || (value >= min && value <= max))
     410              :                     {
     411         3246 :                         CATCH_REQUIRE(integer_validator->validate('+' + v));
     412         3246 :                     }
     413              :                     else
     414              :                     {
     415         6811 :                         CATCH_REQUIRE_FALSE(integer_validator->validate('+' + v));
     416              :                     }
     417              :                 }
     418              : 
     419        20000 :                 std::string const space_before(' ' + v);
     420        20000 :                 CATCH_REQUIRE_FALSE(integer_validator->validate(space_before));
     421              : 
     422        20000 :                 std::string const space_after(v + ' ');
     423        20000 :                 CATCH_REQUIRE_FALSE(integer_validator->validate(space_after));
     424              : 
     425        20000 :                 std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
     426        20000 :                 CATCH_REQUIRE_FALSE(integer_validator->validate(before));
     427              : 
     428        20000 :                 std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
     429        20000 :                 CATCH_REQUIRE_FALSE(integer_validator->validate(after));
     430        20000 :             }
     431           20 :         }
     432              :     }
     433            3 :     CATCH_END_SECTION()
     434              : 
     435            3 :     CATCH_START_SECTION("integer_validator: verify the integer standalone list")
     436              :     {
     437           21 :         for(int count(0); count < 20; ++count)
     438              :         {
     439           20 :             int valid(rand() % 10 + 5);
     440           20 :             std::vector<std::int64_t> numbers;
     441           20 :             numbers.reserve(valid);
     442           20 :             std::string standalone_values;
     443          207 :             for(int idx(0); idx < valid; ++idx)
     444              :             {
     445          187 :                 std::int64_t const value(large_rnd());
     446          187 :                 numbers.push_back(value);
     447          187 :                 std::string const & svalue(std::to_string(value));
     448          187 :                 if(rand() % 5 == 0)
     449              :                 {
     450           25 :                     standalone_values += ' ';
     451              :                 }
     452          187 :                 if(idx != 0)
     453              :                 {
     454          167 :                     standalone_values += ',';
     455              :                 }
     456          187 :                 if(rand() % 5 == 0)
     457              :                 {
     458           39 :                     standalone_values += ' ';
     459              :                 }
     460          187 :                 standalone_values += svalue;
     461          187 :             }
     462           20 :             if(rand() % 5 == 0)
     463              :             {
     464            8 :                 standalone_values += ' ';
     465              :             }
     466           20 :             advgetopt::string_list_t range_list;
     467           60 :             advgetopt::split_string(standalone_values
     468              :                        , range_list
     469              :                        , {","});
     470              : 
     471           60 :             advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range_list));
     472              : 
     473           20 :             CATCH_REQUIRE(integer_validator != nullptr);
     474           20 :             CATCH_REQUIRE(integer_validator->name() == "integer");
     475              : 
     476          207 :             for(size_t idx(0); idx < numbers.size(); ++idx)
     477              :             {
     478          187 :                 std::string const svalue(std::to_string(numbers[idx]));
     479              : 
     480          187 :                 CATCH_REQUIRE(integer_validator->validate(svalue));
     481          187 :             }
     482              : 
     483        20020 :             for(int idx(0); idx < 1000; ++idx)
     484              :             {
     485        20000 :                 std::int64_t value;
     486              : 
     487              :                 for(;;)
     488              :                 {
     489        20000 :                     value = large_rnd();
     490        20000 :                     if(std::find(numbers.begin(), numbers.end(), value) == numbers.end())
     491              :                     {
     492        20000 :                         break;
     493              :                     }
     494              :                 }
     495              : 
     496        20000 :                 CATCH_REQUIRE_FALSE(integer_validator->validate(std::to_string(value)));
     497              :             }
     498           20 :         }
     499              :     }
     500            3 :     CATCH_END_SECTION()
     501            3 : }
     502              : 
     503              : 
     504              : 
     505              : 
     506            3 : CATCH_TEST_CASE("length_validator", "[validator][valid][validation]")
     507              : {
     508            3 :     CATCH_START_SECTION("length_validator: verify the length validator")
     509              :     {
     510            3 :         advgetopt::validator::pointer_t length_validator(advgetopt::validator::create("length", advgetopt::string_list_t()));
     511              : 
     512            1 :         CATCH_REQUIRE(length_validator != nullptr);
     513            1 :         CATCH_REQUIRE(length_validator->name() == "length");
     514              : 
     515            3 :         CATCH_REQUIRE(length_validator->validate("Anything works in this case"));
     516            3 :         CATCH_REQUIRE(length_validator->validate("since the length won't be checked"));
     517            3 :         CATCH_REQUIRE(length_validator->validate(""));
     518            3 :         CATCH_REQUIRE(length_validator->validate("even an empty string"));
     519            1 :     }
     520            3 :     CATCH_END_SECTION()
     521              : 
     522            3 :     CATCH_START_SECTION("length_validator: verify the length ranges")
     523              :     {
     524            1 :         bool had_standalone(false);
     525           21 :         for(int count(0); count < 20 || !had_standalone; ++count)
     526              :         {
     527           20 :             std::uint64_t min(rand() % 25 + 5);
     528           20 :             std::uint64_t max(rand() % 25 + 5);
     529           20 :             if(min > max)
     530              :             {
     531           12 :                 std::swap(min, max);
     532              :             }
     533              : 
     534           20 :             std::string const & smin(std::to_string(min));
     535           20 :             std::string const & smax(std::to_string(max));
     536              : 
     537           60 :             std::string range("...");
     538           80 :             for(int three(0); three < 3; ++three)
     539              :             {
     540           60 :                 if(rand() % 5 == 0)
     541              :                 {
     542           14 :                     range = ' ' + range;
     543              :                 }
     544           60 :                 if(rand() % 5 == 0)
     545              :                 {
     546            7 :                     range = range + ' ';
     547              :                 }
     548              :             }
     549           20 :             range = smin + range + smax;
     550           80 :             for(int three(0); three < 3; ++three)
     551              :             {
     552           60 :                 if(rand() % 5 == 0)
     553              :                 {
     554            7 :                     range = ' ' + range;
     555              :                 }
     556           60 :                 if(rand() % 5 == 0)
     557              :                 {
     558           11 :                     range = range + ' ';
     559              :                 }
     560              :             }
     561              : 
     562           20 :             std::uint64_t standalone(0);
     563           20 :             bool const standalone_included(rand() % 4 == 0);
     564           20 :             if(standalone_included)
     565              :             {
     566            5 :                 had_standalone = true;
     567              :                 do
     568              :                 {
     569            9 :                     standalone = rand() % 35;
     570              :                 }
     571            9 :                 while(standalone >= min && standalone <= max);
     572              : 
     573           15 :                 std::string sep(",");
     574            5 :                 if(rand() % 3 == 0)
     575              :                 {
     576            1 :                     sep = ' ' + sep;
     577              :                 }
     578            5 :                 if(rand() % 3 == 0)
     579              :                 {
     580            2 :                     sep = sep + ' ';
     581              :                 }
     582            5 :                 if(rand() % 2 == 0)
     583              :                 {
     584            2 :                     range = std::to_string(standalone) + "," + range;
     585              :                 }
     586              :                 else
     587              :                 {
     588            3 :                     range = range + "," + std::to_string(standalone);
     589              :                 }
     590            5 :             }
     591           20 :             advgetopt::string_list_t range_list;
     592           60 :             advgetopt::split_string(range
     593              :                        , range_list
     594              :                        , {","});
     595           60 :             advgetopt::validator::pointer_t length_validator(advgetopt::validator::create("length", range_list));
     596              : 
     597           20 :             CATCH_REQUIRE(length_validator != nullptr);
     598           20 :             CATCH_REQUIRE(length_validator->name() == "length");
     599              : 
     600          590 :             for(std::size_t idx(0); idx < std::max(max, standalone) + 5; ++idx)
     601              :             {
     602          570 :                 std::string value;
     603         8855 :                 for(std::size_t n(0); n < idx; ++n)
     604              :                 {
     605         8285 :                     value += rand() % 26 + 'a';
     606              :                 }
     607              : 
     608          174 :                 if((standalone_included && value.length() == standalone)
     609          744 :                 || (value.length() >= min && value.length() <= max))
     610              :                 {
     611          157 :                     CATCH_REQUIRE(length_validator->validate(value));
     612              :                 }
     613              :                 else
     614              :                 {
     615          413 :                     CATCH_REQUIRE_FALSE(length_validator->validate(value));
     616              :                 }
     617          570 :             }
     618           20 :         }
     619              :     }
     620            3 :     CATCH_END_SECTION()
     621              : 
     622            3 :     CATCH_START_SECTION("length_validator: verify the length standalone list")
     623              :     {
     624           21 :         for(int count(0); count < 20; ++count)
     625              :         {
     626           20 :             int valid(rand() % 10 + 5);
     627           20 :             std::vector<std::uint64_t> string_lengths;
     628           20 :             string_lengths.reserve(valid);
     629           20 :             std::string standalone_lengths;
     630          184 :             for(int idx(0); idx < valid; ++idx)
     631              :             {
     632          164 :                 std::int64_t const length(rand() % 25 + 5);
     633          164 :                 string_lengths.push_back(length);
     634          164 :                 std::string const & slength(std::to_string(length));
     635          164 :                 if(rand() % 5 == 0)
     636              :                 {
     637           37 :                     standalone_lengths += ' ';
     638              :                 }
     639          164 :                 if(idx != 0)
     640              :                 {
     641          144 :                     standalone_lengths += ',';
     642              :                 }
     643          164 :                 if(rand() % 5 == 0)
     644              :                 {
     645           32 :                     standalone_lengths += ' ';
     646              :                 }
     647          164 :                 standalone_lengths += slength;
     648          164 :             }
     649           20 :             if(rand() % 5 == 0)
     650              :             {
     651            2 :                 standalone_lengths += ' ';
     652              :             }
     653           20 :             advgetopt::string_list_t range_list;
     654           60 :             advgetopt::split_string(standalone_lengths
     655              :                        , range_list
     656              :                        , {","});
     657              : 
     658           60 :             advgetopt::validator::pointer_t length_validator(advgetopt::validator::create("length", range_list));
     659              : 
     660           20 :             CATCH_REQUIRE(length_validator != nullptr);
     661           20 :             CATCH_REQUIRE(length_validator->name() == "length");
     662              : 
     663          184 :             for(std::size_t idx(0); idx < string_lengths.size(); ++idx)
     664              :             {
     665          164 :                 std::string value;
     666         2925 :                 for(std::uint64_t n(0); n < string_lengths[idx]; ++n)
     667              :                 {
     668         2761 :                     value += rand() % 26 + 'a';
     669              :                 }
     670          164 :                 CATCH_REQUIRE(length_validator->validate(value));
     671          164 :             }
     672              : 
     673           20 :             std::size_t const longest(*std::max_element(string_lengths.begin(), string_lengths.end()));
     674          666 :             for(std::size_t idx(0); idx <= longest + 5; ++idx)
     675              :             {
     676          646 :                 if(std::find(string_lengths.begin(), string_lengths.end(), idx) != string_lengths.end())
     677              :                 {
     678          141 :                     continue;
     679              :                 }
     680              : 
     681          505 :                 std::string value;
     682         8380 :                 for(std::size_t n(0); n < idx; ++n)
     683              :                 {
     684         7875 :                     value += rand() % 26 + 'a';
     685              :                 }
     686              : 
     687          505 :                 CATCH_REQUIRE_FALSE(length_validator->validate(value));
     688          505 :             }
     689           20 :         }
     690              :     }
     691            3 :     CATCH_END_SECTION()
     692            3 : }
     693              : 
     694              : 
     695              : 
     696              : 
     697            1 : CATCH_TEST_CASE("multi_validators", "[validator][valid][validation]")
     698              : {
     699            1 :     CATCH_START_SECTION("multi_validators: verify an integer along a few keywords")
     700              :     {
     701            3 :         advgetopt::validator::pointer_t list_validator(advgetopt::validator::create("keywords(off,min,max) | integer(1...100)"));
     702              : 
     703            1 :         CATCH_REQUIRE(list_validator != nullptr);
     704            1 :         CATCH_REQUIRE(list_validator->name() == "list");
     705              : 
     706            3 :         CATCH_REQUIRE(list_validator->validate("off"));
     707            3 :         CATCH_REQUIRE(list_validator->validate("min"));
     708            3 :         CATCH_REQUIRE(list_validator->validate("max"));
     709              : 
     710          122 :         for(int idx(-10); idx <= 110; ++idx)
     711              :         {
     712          121 :             std::string const v(std::to_string(idx));
     713              : 
     714          121 :             if(idx < 1 || idx > 100)
     715              :             {
     716           21 :                 CATCH_REQUIRE_FALSE(list_validator->validate(v));
     717           21 :             }
     718              :             else
     719              :             {
     720          100 :                 CATCH_REQUIRE(list_validator->validate(v));
     721              :             }
     722          121 :         }
     723            1 :     }
     724            1 :     CATCH_END_SECTION()
     725            1 : }
     726              : 
     727              : 
     728              : 
     729            1 : CATCH_TEST_CASE("keywords_validator", "[validator][valid][validation]")
     730              : {
     731            1 :     CATCH_START_SECTION("keywords_validator: verify simple keywords")
     732              :     {
     733            3 :         advgetopt::validator::pointer_t list_validator(advgetopt::validator::create("keywords(angle, corner ,, ceiling)"));
     734              : 
     735            1 :         CATCH_REQUIRE(list_validator != nullptr);
     736            1 :         CATCH_REQUIRE(list_validator->name() == "keywords");
     737              : 
     738            3 :         CATCH_REQUIRE(list_validator->validate("angle"));
     739            3 :         CATCH_REQUIRE(list_validator->validate("corner"));
     740            3 :         CATCH_REQUIRE(list_validator->validate("ceiling"));
     741              : 
     742            3 :         CATCH_REQUIRE_FALSE(list_validator->validate(""));
     743            3 :         CATCH_REQUIRE_FALSE(list_validator->validate("other"));
     744            1 :     }
     745            1 :     CATCH_END_SECTION()
     746            1 : }
     747              : 
     748              : 
     749              : 
     750              : 
     751            3 : CATCH_TEST_CASE("double_validator", "[validator][valid][validation]")
     752              : {
     753            3 :     CATCH_START_SECTION("double_validator: verify the double validator")
     754              :     {
     755            3 :         advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", advgetopt::string_list_t()));
     756              : 
     757            1 :         CATCH_REQUIRE(double_validator != nullptr);
     758            1 :         CATCH_REQUIRE(double_validator->name() == "double");
     759              : 
     760            3 :         CATCH_REQUIRE_FALSE(double_validator->validate(""));
     761            3 :         CATCH_REQUIRE_FALSE(double_validator->validate("+"));
     762            3 :         CATCH_REQUIRE_FALSE(double_validator->validate("-"));
     763            3 :         CATCH_REQUIRE_FALSE(double_validator->validate("alpha"));
     764              : 
     765         1001 :         for(int idx(0); idx < 1000; ++idx)
     766              :         {
     767         1000 :             double value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
     768         1000 :             std::string const v(std::to_string(value));
     769              : 
     770         1000 :             CATCH_REQUIRE(double_validator->validate(v));
     771              : 
     772         1000 :             if(value >= 0)
     773              :             {
     774          485 :                 CATCH_REQUIRE(double_validator->validate('+' + v));
     775              :             }
     776              : 
     777         1000 :             std::string const space_before(' ' + v);
     778         1000 :             CATCH_REQUIRE_FALSE(double_validator->validate(space_before));
     779              : 
     780         1000 :             std::string const space_after(v + ' ');
     781         1000 :             CATCH_REQUIRE_FALSE(double_validator->validate(space_after));
     782              : 
     783         1000 :             std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
     784         1000 :             CATCH_REQUIRE_FALSE(double_validator->validate(before));
     785              : 
     786         1000 :             std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
     787         1000 :             CATCH_REQUIRE_FALSE(double_validator->validate(after));
     788         1000 :         }
     789            1 :     }
     790            3 :     CATCH_END_SECTION()
     791              : 
     792            3 :     CATCH_START_SECTION("double_validator: verify the double ranges")
     793              :     {
     794            1 :         bool had_standalone(false);
     795           21 :         for(int count(0); count < 20 || !had_standalone; ++count)
     796              :         {
     797           20 :             double min(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
     798           20 :             double max(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
     799           20 :             if(min > max)
     800              :             {
     801            9 :                 std::swap(min, max);
     802              :             }
     803              : 
     804           20 :             std::string const & smin(std::to_string(min));
     805           20 :             std::string const & smax(std::to_string(max));
     806              : 
     807           60 :             std::string range("...");
     808           80 :             for(int three(0); three < 3; ++three)
     809              :             {
     810           60 :                 if(rand() % 5 == 0)
     811              :                 {
     812           12 :                     range = ' ' + range;
     813              :                 }
     814           60 :                 if(rand() % 5 == 0)
     815              :                 {
     816           11 :                     range = range + ' ';
     817              :                 }
     818              :             }
     819           20 :             range = smin + range + smax;
     820           80 :             for(int three(0); three < 3; ++three)
     821              :             {
     822           60 :                 if(rand() % 5 == 0)
     823              :                 {
     824           10 :                     range = ' ' + range;
     825              :                 }
     826           60 :                 if(rand() % 5 == 0)
     827              :                 {
     828           13 :                     range = range + ' ';
     829              :                 }
     830              :             }
     831              : 
     832           20 :             double standalone(0);
     833           20 :             bool standalone_included(rand() % 4 == 0);
     834           20 :             if(standalone_included)
     835              :             {
     836            7 :                 if(min <= std::numeric_limits<double>::min()
     837            7 :                 && max >= std::numeric_limits<double>::max())
     838              :                 {
     839            0 :                     standalone_included = false;
     840              :                 }
     841              :                 else
     842              :                 {
     843            7 :                     had_standalone = true;
     844              :                     do
     845              :                     {
     846           23 :                         standalone = static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false));
     847              :                     }
     848           23 :                     while(standalone >= min && standalone <= max);
     849              : 
     850           21 :                     std::string sep(",");
     851            7 :                     if(rand() % 3 == 0)
     852              :                     {
     853            1 :                         sep = ' ' + sep;
     854              :                     }
     855            7 :                     if(rand() % 3 == 0)
     856              :                     {
     857            2 :                         sep = sep + ' ';
     858              :                     }
     859            7 :                     if(rand() % 2 == 0)
     860              :                     {
     861            2 :                         range = std::to_string(standalone) + "," + range;
     862              :                     }
     863              :                     else
     864              :                     {
     865            5 :                         range = range + "," + std::to_string(standalone);
     866              :                     }
     867            7 :                 }
     868              :             }
     869           20 :             advgetopt::string_list_t range_list;
     870           60 :             advgetopt::split_string(range
     871              :                        , range_list
     872              :                        , {","});
     873           60 :             advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", range_list));
     874              : 
     875           20 :             CATCH_REQUIRE(double_validator != nullptr);
     876           20 :             CATCH_REQUIRE(double_validator->name() == "double");
     877              : 
     878        20020 :             for(int idx(0); idx < 1000; ++idx)
     879              :             {
     880        20000 :                 double value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
     881              : 
     882              :                 // force valid values otherwise we're likely to only have
     883              :                 // invalid ones
     884              :                 //
     885        20000 :                 if(idx % 10 == 0)
     886              :                 {
     887         2000 :                     value = fmod(value, max - min + 1.0) + min;
     888              :                 }
     889        18000 :                 else if(idx % 50 == 1 && standalone_included)
     890              :                 {
     891          140 :                     value = standalone;
     892              :                 }
     893              : 
     894        20000 :                 std::string const v(std::to_string(value));
     895              : 
     896              : #pragma GCC diagnostic push
     897              : #pragma GCC diagnostic ignored "-Wfloat-equal"
     898        20000 :                 if((standalone_included && value == standalone)
     899        19860 :                 || (value >= min && value <= max))
     900              :                 {
     901         7692 :                     CATCH_REQUIRE(double_validator->validate(v));
     902         7692 :                 }
     903              :                 else
     904              :                 {
     905        12308 :                     CATCH_REQUIRE_FALSE(double_validator->validate(v));
     906              :                 }
     907              : 
     908        20000 :                 if(value >= 0.0)
     909              :                 {
     910         9775 :                     if((standalone_included && value == standalone)
     911         9715 :                     || (value >= min && value <= max))
     912              :                     {
     913         4357 :                         CATCH_REQUIRE(double_validator->validate('+' + v));
     914         4357 :                     }
     915              :                     else
     916              :                     {
     917         5418 :                         CATCH_REQUIRE_FALSE(double_validator->validate('+' + v));
     918              :                     }
     919              :                 }
     920              : #pragma GCC diagnostic pop
     921              : 
     922        20000 :                 std::string const space_before(' ' + v);
     923        20000 :                 CATCH_REQUIRE_FALSE(double_validator->validate(space_before));
     924              : 
     925        20000 :                 std::string const space_after(v + ' ');
     926        20000 :                 CATCH_REQUIRE_FALSE(double_validator->validate(space_after));
     927              : 
     928        20000 :                 std::string const before(static_cast<char>(rand() % 26 + 'a') + v);
     929        20000 :                 CATCH_REQUIRE_FALSE(double_validator->validate(before));
     930              : 
     931        20000 :                 std::string const after(v + static_cast<char>(rand() % 26 + 'a'));
     932        20000 :                 CATCH_REQUIRE_FALSE(double_validator->validate(after));
     933        20000 :             }
     934           20 :         }
     935              :     }
     936            3 :     CATCH_END_SECTION()
     937              : 
     938            3 :     CATCH_START_SECTION("double_validator: verify the double standalone list")
     939              :     {
     940           21 :         for(int count(0); count < 20; ++count)
     941              :         {
     942           20 :             int valid(rand() % 10 + 5);
     943           20 :             std::vector<double> numbers;
     944           20 :             numbers.reserve(valid);
     945           20 :             std::string standalone_values;
     946          225 :             for(int idx(0); idx < valid; ++idx)
     947              :             {
     948          205 :                 double const value(static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false)));
     949          205 :                 numbers.push_back(value);
     950          205 :                 std::string const & svalue(std::to_string(value));
     951          205 :                 if(rand() % 5 == 0)
     952              :                 {
     953           38 :                     standalone_values += ' ';
     954              :                 }
     955          205 :                 if(idx != 0)
     956              :                 {
     957          185 :                     standalone_values += ',';
     958              :                 }
     959          205 :                 if(rand() % 5 == 0)
     960              :                 {
     961           49 :                     standalone_values += ' ';
     962              :                 }
     963          205 :                 standalone_values += svalue;
     964          205 :             }
     965           20 :             if(rand() % 5 == 0)
     966              :             {
     967            3 :                 standalone_values += ' ';
     968              :             }
     969           20 :             advgetopt::string_list_t range_list;
     970           60 :             advgetopt::split_string(standalone_values
     971              :                        , range_list
     972              :                        , {","});
     973              : 
     974           60 :             advgetopt::validator::pointer_t double_validator(advgetopt::validator::create("double", range_list));
     975              : 
     976           20 :             CATCH_REQUIRE(double_validator != nullptr);
     977           20 :             CATCH_REQUIRE(double_validator->name() == "double");
     978              : 
     979          225 :             for(size_t idx(0); idx < numbers.size(); ++idx)
     980              :             {
     981          205 :                 std::string const svalue(std::to_string(numbers[idx]));
     982              : 
     983          205 :                 CATCH_REQUIRE(double_validator->validate(svalue));
     984          205 :             }
     985              : 
     986        20020 :             for(int idx(0); idx < 1000; ++idx)
     987              :             {
     988        20000 :                 std::int64_t value;
     989              : 
     990              :                 for(;;)
     991              :                 {
     992        20000 :                     value = static_cast<double>(large_rnd()) / static_cast<double>(large_rnd(false));
     993        20000 :                     if(std::find(numbers.begin(), numbers.end(), value) == numbers.end())
     994              :                     {
     995        20000 :                         break;
     996              :                     }
     997              :                 }
     998              : 
     999        20000 :                 CATCH_REQUIRE_FALSE(double_validator->validate(std::to_string(value)));
    1000              :             }
    1001           20 :         }
    1002              :     }
    1003            3 :     CATCH_END_SECTION()
    1004            3 : }
    1005              : 
    1006              : 
    1007              : 
    1008              : 
    1009            4 : CATCH_TEST_CASE("duration_validator", "[validator][valid][validation]")
    1010              : {
    1011            4 :     CATCH_START_SECTION("duration_validator: verify the duration validator (simple values)")
    1012              :     {
    1013            1 :         double duration(0.0);
    1014              : 
    1015              :         // simple seconds with decimal point
    1016              :         //
    1017            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string("22.3s", 0, duration));
    1018            1 :         CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 22.3, 0.0));
    1019              : 
    1020              :         // "seconds" is the default
    1021              :         //
    1022            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1.05", 0, duration));
    1023            1 :         CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 1.05, 0.0));
    1024              : 
    1025              :         // number can start with a decimal point
    1026              :         //
    1027            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string(".0503", 0, duration));
    1028            1 :         CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(duration, 0.0503, 0.0));
    1029              :     }
    1030            4 :     CATCH_END_SECTION()
    1031              : 
    1032            4 :     CATCH_START_SECTION("duration_validator: verify the duration validator (multiple values)")
    1033              :     {
    1034            1 :         double duration(0.0);
    1035            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1d 3h 2m 15.3s", 0, duration));
    1036            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));
    1037              : 
    1038              :         // same in uppercase
    1039            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string("1D 3H 2M 15.3S", 0, duration));
    1040            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));
    1041              : 
    1042            3 :         CATCH_REQUIRE(advgetopt::validator_duration::convert_string("3d 15h 52m 21.801s", 0, duration));
    1043            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));
    1044              :     }
    1045            4 :     CATCH_END_SECTION()
    1046              : 
    1047            4 :     CATCH_START_SECTION("duration_validator: verify the duration validator (one value)")
    1048              :     {
    1049              :         // this test does not verify that double conversion works since we
    1050              :         // have a separate test for that specific validator
    1051              :         //
    1052            4 :         for(int size(0); size < 3; ++size)
    1053              :         {
    1054            3 :             advgetopt::validator_duration::flag_t flg(advgetopt::validator_duration::VALIDATOR_DURATION_DEFAULT_FLAGS);
    1055            3 :             advgetopt::string_list_t flags;
    1056            3 :             if(size == 1)
    1057              :             {
    1058            3 :                 flags.push_back("small");
    1059              :             }
    1060            2 :             else if(size == 2)
    1061              :             {
    1062            3 :                 flags.push_back("large");
    1063            1 :                 flg = advgetopt::validator_duration::VALIDATOR_DURATION_LONG;
    1064              :             }
    1065            9 :             advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", flags));
    1066              : 
    1067            3 :             CATCH_REQUIRE(duration_validator != nullptr);
    1068            3 :             CATCH_REQUIRE(duration_validator->name() == "duration");
    1069              : 
    1070         3003 :             for(int idx(0); idx < 1000; ++idx)
    1071              :             {
    1072              :                 // use smaller values between 0 and 1
    1073              :                 // (the loop is to make sure we don't end up with "123e-10"
    1074              :                 // type of numbers... which do not work here)
    1075              :                 //
    1076         3000 :                 double value(0.0);
    1077              :                 do
    1078              :                 {
    1079         3000 :                     value = static_cast<double>(rand()) / static_cast<double>(RAND_MAX);
    1080              :                 }
    1081         3000 :                 while(value < 0.0001 && snapdev::quiet_floating_point_not_equal(value, 0.0));
    1082         3000 :                 if(rand() % 2 == 0)
    1083              :                 {
    1084         1453 :                     value *= -1.0;
    1085              :                 }
    1086         3000 :                 std::stringstream ss;
    1087         3000 :                 ss.precision(std::numeric_limits<double>::max_digits10);
    1088         3000 :                 ss << value;
    1089         3000 :                 std::string const v(ss.str());
    1090              : 
    1091       132000 :                 for(std::size_t i(0); i < std::size(g_duration_suffixes); ++i)
    1092              :                 {
    1093       441000 :                     for(int j(0); j <= 5; ++j)
    1094              :                     {
    1095       378000 :                         std::string duration(v);
    1096      1323000 :                         for(int k(0); k < j; ++k)
    1097              :                         {
    1098              :                             // any number of spaces in between are allowed
    1099              :                             //
    1100       945000 :                             duration += ' ';
    1101              :                         }
    1102       378000 :                         duration += g_duration_suffixes[i].f_suffix;
    1103              : 
    1104       378000 :                         CATCH_REQUIRE(duration_validator->validate(duration));
    1105       378000 :                         if(value >= 0)
    1106              :                         {
    1107       194922 :                             CATCH_REQUIRE(duration_validator->validate('+' + duration));
    1108              :                         }
    1109              : 
    1110       378000 :                         double result(0.0);
    1111       378000 :                         CATCH_REQUIRE(advgetopt::validator_duration::convert_string(duration, flg, result));
    1112       378000 :                         if(g_duration_suffixes[i].f_factor < 0.0)
    1113              :                         {
    1114              :                             // the 'm' special case
    1115              :                             //
    1116        18000 :                             if(size == 2)
    1117              :                             {
    1118              :                                 // 'large' -- 1 month
    1119              :                                 //
    1120         6000 :                                 CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * (86400.0 * 30.0)));
    1121              :                             }
    1122              :                             else
    1123              :                             {
    1124              :                                 // 'small' -- 1 minute
    1125              :                                 //
    1126        12000 :                                 CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * 60.0));
    1127              :                             }
    1128              :                         }
    1129              :                         else
    1130              :                         {
    1131       360000 :                             CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(result, value * g_duration_suffixes[i].f_factor));
    1132              :                         }
    1133       378000 :                     }
    1134              :                 }
    1135         3000 :             }
    1136            3 :         }
    1137              :     }
    1138            4 :     CATCH_END_SECTION()
    1139              : 
    1140            4 :     CATCH_START_SECTION("duration_validator: verify the duration validator (within range)")
    1141              :     {
    1142              :         struct range_t
    1143              :         {
    1144              :             typedef std::vector<range_t>    vector_t;
    1145              : 
    1146              :             double                  f_min = 0.0;
    1147              :             double                  f_max = 0.0;
    1148              :         };
    1149              :         struct verify_t
    1150              :         {
    1151              :             typedef std::vector<verify_t>   vector_t;
    1152              : 
    1153              :             double                  f_value = 0.0;
    1154              :             bool                    f_valid = false;
    1155              :         };
    1156              :         struct test_t
    1157              :         {
    1158              :             typedef std::vector<test_t>     vector_t;
    1159              : 
    1160              :             range_t::vector_t       f_ranges = range_t::vector_t();
    1161              :             verify_t::vector_t      f_values = verify_t::vector_t();
    1162              :         };
    1163              : 
    1164            1 :         test_t::vector_t tests = {
    1165              :             {
    1166              :                 {
    1167              :                     { 0.0, 1.0 },
    1168              :                 },
    1169              :                 {
    1170              :                     { 0.0, true },
    1171              :                     { 0.5, true },
    1172              :                     { 1.0, true },
    1173              :                     { -1.0, false },
    1174              :                     { 2.0, false },
    1175              :                 },
    1176              :             },
    1177              :             {
    1178              :                 {
    1179              :                     { 1.0, 1.0 },
    1180              :                     { 2.0, 2.0 },
    1181              :                     { 3.0, 3.0 },
    1182              :                     { 4.0, 4.0 },
    1183              :                 },
    1184              :                 {
    1185              :                     { 0.0, false },
    1186              :                     { 0.5, false },
    1187              :                     { 1.0, true },
    1188              :                     { -1.0, false },
    1189              :                     { 2.0, true },
    1190              :                     { 3.1, false },
    1191              :                     { 5.0, false },
    1192              :                 },
    1193              :             },
    1194              :             {
    1195              :                 {
    1196              :                     { -1.0, 1.0 },
    1197              :                     { -52.0, -52.0 },
    1198              :                     { 3.0, 3.0 },
    1199              :                 },
    1200              :                 {
    1201              :                     { 0.0, true },
    1202              :                     { 0.5, true },
    1203              :                     { 1.0, true },
    1204              :                     { -1.0, true },
    1205              :                     { -0.5, true },
    1206              :                     { 2.0, false },
    1207              :                     { 3.0, true },
    1208              :                     { -52.0, true },
    1209              :                     { -51.0, false },
    1210              :                     { -52.1, false },
    1211              :                 },
    1212              :             },
    1213            7 :         };
    1214              : 
    1215            4 :         for(auto t : tests)
    1216              :         {
    1217              :             // create the set of parameters which are ranges unless the min/max
    1218              :             // are equal in which case we may use either syntax
    1219              :             //
    1220            3 :             advgetopt::string_list_t params;
    1221           11 :             for(auto p : t.f_ranges)
    1222              :             {
    1223            8 :                 if(snapdev::quiet_floating_point_equal(p.f_min, p.f_max) && (rand() & 1) == 0)
    1224              :                 {
    1225              :                     // if equal, then enter it as a single value 50% of the time
    1226              :                     //
    1227            2 :                     params.push_back(std::to_string(p.f_min));
    1228              :                 }
    1229              :                 else
    1230              :                 {
    1231            6 :                     params.push_back(std::to_string(p.f_min) + "..." + std::to_string(p.f_max));
    1232              :                 }
    1233              :             }
    1234              : 
    1235            9 :             advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", params));
    1236            3 :             CATCH_REQUIRE(duration_validator != nullptr);
    1237            3 :             CATCH_REQUIRE(duration_validator->name() == "duration");
    1238              : 
    1239           25 :             for(auto v : t.f_values)
    1240              :             {
    1241           22 :                 std::string duration(std::to_string(v.f_value));
    1242           22 :                 CATCH_REQUIRE(duration_validator->validate(duration) == v.f_valid);
    1243           22 :             }
    1244            3 :         }
    1245            1 :     }
    1246            4 :     CATCH_END_SECTION()
    1247            8 : }
    1248              : 
    1249              : 
    1250              : 
    1251              : 
    1252            1 : CATCH_TEST_CASE("size_validator", "[validator][valid][validation]")
    1253              : {
    1254              : #pragma GCC diagnostic push
    1255              : #pragma GCC diagnostic ignored "-Wpedantic"
    1256            1 :     CATCH_START_SECTION("size_validator: verify the size validator")
    1257              :     {
    1258              :         // this test does not verify that double conversion works since we
    1259              :         // have a separate test for that specific validator
    1260              :         //
    1261            4 :         for(int mode(0); mode < 3; ++mode)
    1262              :         {
    1263            3 :             advgetopt::validator_size::flag_t flg(advgetopt::validator_size::VALIDATOR_SIZE_DEFAULT_FLAGS);
    1264            3 :             advgetopt::string_list_t flags;
    1265            3 :             if(mode == 1)
    1266              :             {
    1267            3 :                 flags.push_back("si");
    1268              :             }
    1269            2 :             else if(mode == 2)
    1270              :             {
    1271            3 :                 flags.push_back("legacy");
    1272            1 :                 flg = advgetopt::validator_size::VALIDATOR_SIZE_POWER_OF_TWO;
    1273              :             }
    1274            9 :             advgetopt::validator::pointer_t size_validator(advgetopt::validator::create("size", flags));
    1275              : 
    1276            3 :             CATCH_REQUIRE(size_validator != nullptr);
    1277            3 :             CATCH_REQUIRE(size_validator->name() == "size");
    1278              : 
    1279         3003 :             for(int idx(0); idx < 1000; ++idx)
    1280              :             {
    1281              :                 // use smaller values between 0 and about 5
    1282              :                 //
    1283         3000 :                 double value(static_cast<double>(rand()) / static_cast<double>(RAND_MAX / 5));
    1284         3000 :                 if(rand() % 2 == 0)
    1285              :                 {
    1286         1512 :                     value *= -1.0;
    1287              :                 }
    1288         3000 :                 std::stringstream ss;
    1289         3000 :                 ss.precision(std::numeric_limits<double>::max_digits10);
    1290         3000 :                 ss << value;
    1291         3000 :                 std::string const v(ss.str());
    1292              : 
    1293       138000 :                 for(std::size_t i(0); i < std::size(g_size_suffixes); ++i)
    1294              :                 {
    1295       462000 :                     for(int j(0); j <= 5; ++j)
    1296              :                     {
    1297       396000 :                         std::string size(v);
    1298      1386000 :                         for(int k(0); k < j; ++k)
    1299              :                         {
    1300              :                             // any number of spaces in between are allowed
    1301              :                             //
    1302       990000 :                             size += ' ';
    1303              :                         }
    1304       396000 :                         size += g_size_suffixes[i].f_suffix;
    1305              : 
    1306       396000 :                         CATCH_REQUIRE(size_validator->validate(size));
    1307       396000 :                         if(value >= 0)
    1308              :                         {
    1309       196416 :                             CATCH_REQUIRE(size_validator->validate('+' + size));
    1310              :                         }
    1311              : 
    1312       396000 :                         __int128 result(0.0);
    1313       396000 :                         CATCH_REQUIRE(advgetopt::validator_size::convert_string(size, flg, result));
    1314              : 
    1315       396000 :                         long double const base(mode == 2 ? 1024.0L : g_size_suffixes[i].f_base);
    1316       396000 :                         long double expected(1);
    1317      2376000 :                         for(int p(0); p < g_size_suffixes[i].f_power; ++p)
    1318              :                         {
    1319      1980000 :                             expected *= base;
    1320              :                         }
    1321       396000 :                         __int128 int_expected(expected * static_cast<long double>(value));
    1322              : 
    1323              : //std::cerr << "converted [" << size << "] to [" << result << "] wanted [" << int_expected << "]\n";
    1324       396000 :                         CATCH_REQUIRE(result == int_expected);
    1325       396000 :                     }
    1326              :                 }
    1327         3000 :             }
    1328            3 :         }
    1329              :     }
    1330            1 :     CATCH_END_SECTION()
    1331              : #pragma GCC diagnostic pop
    1332            1 : }
    1333              : 
    1334              : 
    1335              : 
    1336              : 
    1337            4 : CATCH_TEST_CASE("regex_validator", "[validator][valid][validation]")
    1338              : {
    1339            4 :     CATCH_START_SECTION("regex_validator: verify the regex validator")
    1340              :     {
    1341            5 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {".*@.*\\..*"}));
    1342              : 
    1343            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1344            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1345              : 
    1346            3 :         CATCH_REQUIRE(regex_validator->validate("@m2osw."));
    1347            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1348            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
    1349            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
    1350              : 
    1351            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1352            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1353            1 :     }
    1354            4 :     CATCH_END_SECTION()
    1355              : 
    1356            4 :     CATCH_START_SECTION("regex_validator: verify the regex string (case sensitive)")
    1357              :     {
    1358            5 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/"}));
    1359              : 
    1360            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1361            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1362              : 
    1363            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1364            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1365            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
    1366            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
    1367              : 
    1368            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1369            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1370            1 :     }
    1371            4 :     CATCH_END_SECTION()
    1372              : 
    1373            4 :     CATCH_START_SECTION("regex_validator: verify the regex string (case insensitive)")
    1374              :     {
    1375            5 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/i"}));
    1376              : 
    1377            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1378            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1379              : 
    1380            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1381            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1382            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
    1383            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
    1384              : 
    1385            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1386            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1387            1 :     }
    1388            4 :     CATCH_END_SECTION()
    1389              : 
    1390            4 :     CATCH_START_SECTION("regex_validator: verify direct regex string (case insensitive)")
    1391              :     {
    1392            3 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("/contact@.*\\..*/i"));
    1393              : 
    1394            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1395            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1396              : 
    1397            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1398            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1399            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@m2osw.com"));
    1400            3 :         CATCH_REQUIRE(regex_validator->validate("Contact@M2OSW.com"));
    1401              : 
    1402            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1403            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1404            1 :     }
    1405            4 :     CATCH_END_SECTION()
    1406            4 : }
    1407              : 
    1408              : 
    1409              : 
    1410              : 
    1411              : 
    1412              : 
    1413              : 
    1414              : 
    1415            1 : CATCH_TEST_CASE("invalid_validator_factory", "[validator][invalid][validation]")
    1416              : {
    1417            1 :     CATCH_START_SECTION("invalid_validator_factory: register duplicated factories")
    1418              :     {
    1419              :         class duplicate_integer
    1420              :             : public advgetopt::validator
    1421              :         {
    1422              :         public:
    1423            0 :             virtual std::string name() const override
    1424              :             {
    1425            0 :                 return "integer";
    1426              :             }
    1427              : 
    1428            0 :             virtual bool validate(std::string const & value) const override
    1429              :             {
    1430            0 :                 return value == "123";
    1431              :             }
    1432              :         };
    1433              :         class duplicate_factory
    1434              :             : public advgetopt::validator_factory
    1435              :         {
    1436              :         public:
    1437            2 :             virtual std::string get_name() const override
    1438              :             {
    1439            6 :                 return "integer";
    1440              :             }
    1441              : 
    1442            0 :             virtual std::shared_ptr<advgetopt::validator> create(advgetopt::string_list_t const & data) const override
    1443              :             {
    1444            0 :                 snapdev::NOT_USED(data); // ignore `data`
    1445            0 :                 return std::make_shared<duplicate_integer>();
    1446              :             }
    1447              :         };
    1448            1 :         std::unique_ptr<advgetopt::validator_factory> factory(new duplicate_factory());
    1449            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1450              :                   advgetopt::validator::register_validator(*factory.get())
    1451              :                 , advgetopt::getopt_logic_error
    1452              :                 , Catch::Matchers::ExceptionMessage(
    1453              :                           "getopt_logic_error: you have two or more validator factories named \"integer\"."));
    1454            1 :     }
    1455            1 :     CATCH_END_SECTION()
    1456            1 : }
    1457              : 
    1458            1 : CATCH_TEST_CASE("invalid_validator_create", "[validator][invalid][validation]")
    1459              : {
    1460            1 :     CATCH_START_SECTION("invalid_validator_create: verify missing ')' in string based create")
    1461              :     {
    1462            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameter list must end with ')'. Remaining input: \"...EOS\"");
    1463            3 :         advgetopt::validator::pointer_t validator(advgetopt::validator::create("integer(1...7, 11...15"));
    1464            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1465            1 :         CATCH_REQUIRE(validator == nullptr);
    1466              : 
    1467            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameter list must end with ')'. Remaining input: \"...EOS\"");
    1468            3 :         validator = advgetopt::validator::create("regex([a-z]+");
    1469            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1470            1 :         CATCH_REQUIRE(validator == nullptr);
    1471              : 
    1472            3 :         validator = advgetopt::validator::create(" ");
    1473            1 :         CATCH_REQUIRE(validator == nullptr);
    1474              : 
    1475            3 :         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]+))\"");
    1476            3 :         validator = advgetopt::validator::create("regex(([a-z]+))");
    1477            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1478            1 :         CATCH_REQUIRE(validator == nullptr);
    1479              : 
    1480            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): parameters must be separated by ','. Remaining input: \"...EOS\"");
    1481            3 :         validator = advgetopt::validator::create("keywords(foo, blah error)");
    1482            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1483            1 :         CATCH_REQUIRE(validator == nullptr);
    1484              : 
    1485            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected token in validator definition; expected an identifier. Remaining input: \"missing, name)\".");
    1486            3 :         validator = advgetopt::validator::create("(missing, name)");
    1487            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1488            1 :         CATCH_REQUIRE(validator == nullptr);
    1489              : 
    1490            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
    1491            3 :         validator = advgetopt::validator::create("keywords(missing, name)\n|\ninteger(33)");
    1492            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1493            1 :         CATCH_REQUIRE(validator == nullptr);
    1494              : 
    1495            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): validator definitions must be separated by '|'. Remaining input: \"33)\"");
    1496            3 :         validator = advgetopt::validator::create("keywords(missing, name) integer(33)");
    1497            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1498            1 :         CATCH_REQUIRE(validator == nullptr);
    1499            1 :     }
    1500            1 :     CATCH_END_SECTION()
    1501            1 : }
    1502              : 
    1503            1 : CATCH_TEST_CASE("invalid_length_validator", "[validator][invalid][validation]")
    1504              : {
    1505            1 :     CATCH_START_SECTION("invalid_length_validator: verify invalid length ranges")
    1506              :     {
    1507            1 :         advgetopt::string_list_t range{
    1508              :             "abc",
    1509              :             "abc...6",
    1510              :             "3...def",
    1511            8 :             "10...1"};
    1512              : 
    1513            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid standalone value for your ranges; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1514            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid value for your range's start; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1515            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: def is not a valid value for your range's end; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1516            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: 10 has to be smaller or equal to 1; you have an invalid range.");
    1517              : 
    1518            3 :         advgetopt::validator::pointer_t length_validator(advgetopt::validator::create("length", range));
    1519            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1520            1 :     }
    1521            1 :     CATCH_END_SECTION()
    1522            4 : }
    1523              : 
    1524            1 : CATCH_TEST_CASE("invalid_integer_validator", "[validator][invalid][validation]")
    1525              : {
    1526            1 :     CATCH_START_SECTION("invalid_integer_validator: verify invalid integer ranges")
    1527              :     {
    1528            1 :         advgetopt::string_list_t range{
    1529              :             "abc",
    1530              :             "abc...6",
    1531              :             "3...def",
    1532            8 :             "10...1"};
    1533              : 
    1534            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid standalone value for your ranges; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1535            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid value for your range's start; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1536            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: def is not a valid value for your range's end; it must only be digits, optionally preceded by a sign (+ or -) and not overflow an int64_t value.");
    1537            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: 10 has to be smaller or equal to 1; you have an invalid range.");
    1538              : 
    1539            3 :         advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("integer", range));
    1540            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1541            1 :     }
    1542            1 :     CATCH_END_SECTION()
    1543            4 : }
    1544              : 
    1545            1 : CATCH_TEST_CASE("invalid_double_validator", "[validator][invalid][validation]")
    1546              : {
    1547            1 :     CATCH_START_SECTION("invalid_double_validator: verify invalid double ranges")
    1548              :     {
    1549            1 :         advgetopt::string_list_t range{
    1550              :             "abc",
    1551              :             "abc...6.3",
    1552              :             "13.3...def",
    1553            8 :             "10.5...1.2"};
    1554              : 
    1555            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: abc is not a valid standalone value; it must be a valid floating point, optionally preceded by a sign (+ or -).");
    1556            3 :         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 preceded by a sign (+ or -).");
    1557            3 :         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 preceded by a sign (+ or -).");
    1558            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: 10.5 has to be smaller or equal to 1.2; you have an invalid range.");
    1559              : 
    1560            3 :         advgetopt::validator::pointer_t integer_validator(advgetopt::validator::create("double", range));
    1561            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1562            1 :     }
    1563            1 :     CATCH_END_SECTION()
    1564            4 : }
    1565              : 
    1566            6 : CATCH_TEST_CASE("invalid_duration_validator", "[invalid][validation]")
    1567              : {
    1568            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration flags")
    1569              :     {
    1570            1 :         advgetopt::string_list_t range{
    1571              :             "small",
    1572              :             "medium",
    1573            7 :             "large"};
    1574              : 
    1575            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: medium is not a valid duration or flag.");
    1576            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1577            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1578              : 
    1579            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate(""));
    1580            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("  "));
    1581            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("+"));
    1582            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-"));
    1583            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("alpha"));
    1584            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("3.5 beta"));
    1585            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("7.5delta"));
    1586            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("+8.1 gamma"));
    1587            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-2.3eta"));
    1588            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-202.3   HERO"));
    1589            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-7.31Hr"));
    1590            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-1.32mom"));
    1591            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("-5.36 secs"));
    1592            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("28.901 wkS"));
    1593            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("28 YY"));
    1594            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("2..8 year"));
    1595            1 :     }
    1596            6 :     CATCH_END_SECTION()
    1597              : 
    1598            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration (text)")
    1599              :     {
    1600            5 :         advgetopt::string_list_t range{"alpha"};
    1601              : 
    1602            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: alpha is not a valid duration or flag.");
    1603            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1604            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1605              : 
    1606            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("2..8 year"));
    1607            1 :     }
    1608            6 :     CATCH_END_SECTION()
    1609              : 
    1610            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration (two periods)")
    1611              :     {
    1612            5 :         advgetopt::string_list_t range{"3..91"};
    1613              : 
    1614            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: 3..91 is not a valid duration or flag.");
    1615            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1616            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1617              : 
    1618            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("year"));
    1619            1 :     }
    1620            6 :     CATCH_END_SECTION()
    1621              : 
    1622            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration (bad start)")
    1623              :     {
    1624            5 :         advgetopt::string_list_t range{"D...9.1"};
    1625              : 
    1626            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: D is not a valid value for your range's start; it must be a valid duration, optionally preceded by a sign (+ or -).");
    1627            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1628            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1629              : 
    1630            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("days"));
    1631            1 :     }
    1632            6 :     CATCH_END_SECTION()
    1633              : 
    1634            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration (bad end)")
    1635              :     {
    1636            5 :         advgetopt::string_list_t range{"3.1...E"};
    1637              : 
    1638            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: E is not a valid value for your range's end; it must be a valid duration, optionally preceded by a sign (+ or -).");
    1639            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1640            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1641              : 
    1642            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("days"));
    1643            1 :     }
    1644            6 :     CATCH_END_SECTION()
    1645              : 
    1646            6 :     CATCH_START_SECTION("invalid_duration_validator: verify invalid duration (start > end)")
    1647              :     {
    1648            5 :         advgetopt::string_list_t range{"3.1...2.9"};
    1649              : 
    1650            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: 3.1 has to be smaller or equal to 2.9; you have an invalid duration range.");
    1651            3 :         advgetopt::validator::pointer_t duration_validator(advgetopt::validator::create("duration", range));
    1652            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1653              : 
    1654            3 :         CATCH_REQUIRE_FALSE(duration_validator->validate("days"));
    1655            1 :     }
    1656            6 :     CATCH_END_SECTION()
    1657           24 : }
    1658              : 
    1659              : 
    1660              : 
    1661            1 : CATCH_TEST_CASE("invalid_email_validator", "[invalid][validation]")
    1662              : {
    1663            1 :     CATCH_START_SECTION("invalid_email_validator: verify emails with invalid parameters.")
    1664              :     {
    1665            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_email() supports zero or one parameter.");
    1666            3 :         advgetopt::validator::pointer_t keywords(advgetopt::validator::create("email(single, multiple)"));
    1667            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1668            1 :         CATCH_REQUIRE(keywords != nullptr);
    1669              : 
    1670            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_email(): unknown parameter \"orange\".");
    1671            3 :         keywords = advgetopt::validator::create("email(orange)");
    1672            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1673            1 :         CATCH_REQUIRE(keywords != nullptr);
    1674            1 :     }
    1675            1 :     CATCH_END_SECTION()
    1676            1 : }
    1677              : 
    1678              : 
    1679              : 
    1680            1 : CATCH_TEST_CASE("invalid_keywords_validator", "[invalid][validation]")
    1681              : {
    1682            1 :     CATCH_START_SECTION("invalid_keywords_validator: verify that keywords without parameters fail.")
    1683              :     {
    1684            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_keywords() requires at least one parameter.");
    1685            3 :         advgetopt::validator::pointer_t keywords(advgetopt::validator::create("keywords"));
    1686            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1687            1 :         CATCH_REQUIRE(keywords != nullptr);
    1688              : 
    1689            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_keywords() requires at least one parameter.");
    1690            3 :         keywords = advgetopt::validator::create("keywords()");
    1691            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1692            1 :         CATCH_REQUIRE(keywords != nullptr);
    1693            1 :     }
    1694            1 :     CATCH_END_SECTION()
    1695            1 : }
    1696              : 
    1697              : 
    1698              : 
    1699            1 : CATCH_TEST_CASE("invalid_list_validator", "[invalid][validation]")
    1700              : {
    1701            1 :     CATCH_START_SECTION("invalid_list_validator: verify that list validators do not accept parameters.")
    1702              :     {
    1703            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_list() does not support any parameter.");
    1704            3 :         advgetopt::validator::pointer_t list(advgetopt::validator::create("list(with, parameters)"));
    1705            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1706            1 :         CATCH_REQUIRE(list != nullptr);
    1707            1 :     }
    1708            1 :     CATCH_END_SECTION()
    1709            1 : }
    1710              : 
    1711              : 
    1712              : 
    1713            1 : CATCH_TEST_CASE("invalid_size_validator", "[invalid][validation]")
    1714              : {
    1715            1 :     CATCH_START_SECTION("invalid_size_validator: verify invalid duration flags")
    1716              :     {
    1717            1 :         advgetopt::string_list_t flags{
    1718              :             "si",
    1719              :             "future",
    1720            7 :             "legacy"};
    1721              : 
    1722            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: future is not a valid flag for the size validator.");
    1723            3 :         advgetopt::validator::pointer_t size_validator(advgetopt::validator::create("size", flags));
    1724            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1725              : 
    1726            3 :         CATCH_REQUIRE_FALSE(size_validator->validate(""));
    1727            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("  "));
    1728            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("+"));
    1729            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-"));
    1730            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("size"));
    1731            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("3.5 large"));
    1732            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-1.31body"));
    1733            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("7.5small"));
    1734            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("+8.1 tiny"));
    1735            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-2.3medium"));
    1736            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("1000kbit"));
    1737            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("7 monster"));
    1738            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-101.101egret"));
    1739            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("11 products"));
    1740            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("1.01 tractor"));
    1741            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("+7.0 years"));
    1742            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-51.7zeroes"));
    1743            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("+121gruffalos"));
    1744            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("++1.7 KiB"));
    1745            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-+3.1 MiB"));
    1746            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("+-9.2 GiB"));
    1747            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("--19.4 PiB"));
    1748            3 :         CATCH_REQUIRE_FALSE(size_validator->validate("-3.5.4B"));
    1749            1 :     }
    1750            1 :     CATCH_END_SECTION()
    1751            4 : }
    1752              : 
    1753            6 : CATCH_TEST_CASE("invalid_regex_validator", "[validator][invalid][validation]")
    1754              : {
    1755            6 :     CATCH_START_SECTION("invalid_regex_validator: verify invalid regex flags")
    1756              :     {
    1757            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag f in regular expression \"/contact@.*\\..*/f\".");
    1758            5 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*/f"}));
    1759            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1760              : 
    1761            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1762            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1763              : 
    1764            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1765            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1766            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
    1767            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
    1768              : 
    1769            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1770            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1771            1 :     }
    1772            6 :     CATCH_END_SECTION()
    1773              : 
    1774            6 :     CATCH_START_SECTION("invalid_regex_validator: verify invalid regex character")
    1775              :     {
    1776            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for a regular expression (10).");
    1777            3 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex(/contact@.*\n..*/)"));
    1778            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1779            1 :         CATCH_REQUIRE(regex_validator == nullptr);
    1780              : 
    1781            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected escaped character for a regular expression (13).");
    1782            3 :         regex_validator = advgetopt::validator::create("regex(/contact@.*\\\r..*/)");
    1783            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1784            1 :         CATCH_REQUIRE(regex_validator == nullptr);
    1785              : 
    1786            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected flag character for a regular expression (57).");
    1787            3 :         regex_validator = advgetopt::validator::create("regex(/contact@.*..*/91)");
    1788            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1789            1 :         CATCH_REQUIRE(regex_validator == nullptr);
    1790              : 
    1791            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
    1792            3 :         regex_validator = advgetopt::validator::create("regex(not\nexpected)");
    1793            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1794            1 :         CATCH_REQUIRE(regex_validator == nullptr);
    1795            1 :     }
    1796            6 :     CATCH_END_SECTION()
    1797              : 
    1798            6 :     CATCH_START_SECTION("invalid_regex_validator: verify invalid regex: missing ending /")
    1799              :     {
    1800            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag * in regular expression \"/contact@.*\\..*\".");
    1801            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
    1802            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
    1803            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag \\ in regular expression \"/contact@.*\\..*\".");
    1804            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag * in regular expression \"/contact@.*\\..*\".");
    1805            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag . in regular expression \"/contact@.*\\..*\".");
    1806            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag @ in regular expression \"/contact@.*\\..*\".");
    1807            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag t in regular expression \"/contact@.*\\..*\".");
    1808            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag c in regular expression \"/contact@.*\\..*\".");
    1809            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag a in regular expression \"/contact@.*\\..*\".");
    1810            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag t in regular expression \"/contact@.*\\..*\".");
    1811            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag n in regular expression \"/contact@.*\\..*\".");
    1812            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag o in regular expression \"/contact@.*\\..*\".");
    1813            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: unsupported regex flag c in regular expression \"/contact@.*\\..*\".");
    1814            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: invalid regex definition, ending / is missing in \"/contact@.*\\..*\".");
    1815              : 
    1816            5 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex", {"/contact@.*\\..*"}));
    1817            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1818              : 
    1819            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1820            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1821              : 
    1822            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1823            3 :         CATCH_REQUIRE(regex_validator->validate("contact@m2osw.com"));
    1824            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
    1825            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
    1826              : 
    1827            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1828            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1829            1 :     }
    1830            6 :     CATCH_END_SECTION()
    1831              : 
    1832            6 :     CATCH_START_SECTION("invalid_regex_validator: verify regex refuses more than one parameter")
    1833              :     {
    1834            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    1835              :                           "error: validator_regex() only supports one parameter;"
    1836              :                           " 2 were supplied;"
    1837              :                           " single or double quotation may be required?");
    1838            5 :         advgetopt::validator::create("regex", {"[a-z]+", "[0-9]+"});
    1839            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1840              : 
    1841            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    1842              :                           "error: validator_regex() only supports one parameter;"
    1843              :                           " 2 were supplied;"
    1844              :                           " single or double quotation may be required?");
    1845            3 :         advgetopt::validator::create("regex([a-z]+, [0-9]+)");
    1846            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1847              : 
    1848            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    1849              :                           "error: validator_regex() only supports one parameter;"
    1850              :                           " 3 were supplied;"
    1851              :                           " single or double quotation may be required?");
    1852            5 :         advgetopt::validator::create("regex", {"[a-z]+", "[0-9]+", "[#!@]"});
    1853            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1854              : 
    1855            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log(
    1856              :                           "error: validator_regex() only supports one parameter;"
    1857              :                           " 3 were supplied;"
    1858              :                           " single or double quotation may be required?");
    1859            3 :         advgetopt::validator::create("regex(\"[a-z]+\", \"[0-9]+\", \"[#!@]\")");
    1860            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1861              :     }
    1862            6 :     CATCH_END_SECTION()
    1863              : 
    1864            6 :     CATCH_START_SECTION("invalid_regex_validator: verify two regex params")
    1865              :     {
    1866            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator_regex() only supports one parameter; 2 were supplied; single or double quotation may be required?");
    1867            3 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex(/one/a, /two/b)"));
    1868            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1869              : 
    1870            1 :         CATCH_REQUIRE(regex_validator != nullptr);
    1871            1 :         CATCH_REQUIRE(regex_validator->name() == "regex");
    1872              : 
    1873            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("@m2osw."));
    1874            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw.com"));
    1875            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@m2osw.com"));
    1876            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("Contact@M2OSW.com"));
    1877              : 
    1878            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact@m2osw:com"));
    1879            3 :         CATCH_REQUIRE_FALSE(regex_validator->validate("contact!m2osw.com"));
    1880            1 :     }
    1881            6 :     CATCH_END_SECTION()
    1882              : 
    1883            6 :     CATCH_START_SECTION("invalid_regex_validator: verify two regex params")
    1884              :     {
    1885            3 :         SNAP_CATCH2_NAMESPACE::push_expected_log("error: validator(): unexpected character for an identifier (10).");
    1886            3 :         advgetopt::validator::pointer_t regex_validator(advgetopt::validator::create("regex('/one/'\n,'/two/b')"));
    1887            1 :         SNAP_CATCH2_NAMESPACE::expected_logs_stack_is_empty();
    1888            1 :         CATCH_REQUIRE(regex_validator == nullptr);
    1889            1 :     }
    1890            6 :     CATCH_END_SECTION()
    1891              : 
    1892            6 : }
    1893              : 
    1894              : 
    1895              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions