LCOV - code coverage report
Current view: top level - tests - catch_decimal.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 228 229 99.6 %
Date: 2023-01-24 22:36:19 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/versiontheca
       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 3 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
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : // tested file
      20             : //
      21             : #include    "versiontheca/decimal.h"
      22             : 
      23             : 
      24             : // self
      25             : //
      26             : #include    "catch_main.h"
      27             : 
      28             : 
      29             : 
      30             : // versiontheca
      31             : //
      32             : #include    "versiontheca/exception.h"
      33             : #include    "versiontheca/versiontheca.h"
      34             : 
      35             : 
      36             : // C++
      37             : //
      38             : #include    <cstring>
      39             : #include    <iomanip>
      40             : #include    <stdexcept>
      41             : 
      42             : 
      43             : 
      44             : 
      45             : namespace
      46             : {
      47             : 
      48             : 
      49             : 
      50       10017 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
      51             : {
      52       20034 :     versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
      53       10017 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      54       10017 :     if(verify == nullptr)
      55             :     {
      56             :         // if not specified, same as version
      57             :         //
      58        5012 :         verify = version;
      59             :     }
      60       10017 :     CATCH_REQUIRE(v->get_version() == verify);
      61       10017 :     double expected(strtod(verify, nullptr));
      62       10017 :     CATCH_REQUIRE_FLOATING_POINT(t->get_decimal_version(), expected);
      63       20034 :     return v;
      64             : }
      65             : 
      66             : 
      67          22 : void invalid_version(char const * version, char const * errmsg)
      68             : {
      69          44 :     versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
      70          44 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      71          22 :     CATCH_REQUIRE_FALSE(v->is_valid());
      72          22 :     if(v->get_last_error(false) != errmsg)
      73             :     {
      74           0 :         std::cerr << "--- verifying invalid version [" << version << "]\n";
      75             :     }
      76          22 :     CATCH_REQUIRE(v->get_last_error(false) == errmsg);
      77          22 :     CATCH_REQUIRE(v->get_last_error() == errmsg);
      78          22 :     CATCH_REQUIRE(v->get_last_error().empty());
      79          22 : }
      80             : 
      81             : 
      82       15000 : std::string generate_number()
      83             : {
      84       15000 :     versiontheca::part_integer_t value;
      85       15000 :     SNAP_CATCH2_NAMESPACE::random(value);
      86       15000 :     return std::to_string(value);
      87             : }
      88             : 
      89             : 
      90       10000 : std::string generate_version(std::size_t max)
      91             : {
      92       10000 :     std::string v;
      93       25000 :     for(std::size_t i(0); i < max; ++i)
      94             :     {
      95       15000 :         if(!v.empty())
      96             :         {
      97        5000 :             v += '.';
      98             :         }
      99       15000 :         v += generate_number();
     100             :     }
     101       10000 :     return v;
     102             : }
     103             : 
     104             : 
     105             : 
     106             : }
     107             : // no name namespace
     108             : 
     109             : 
     110           5 : CATCH_TEST_CASE("decimal_versions", "[valid]")
     111             : {
     112           6 :     CATCH_START_SECTION("decimal_versions: verify test checker for version 1.0")
     113             :     {
     114           1 :         create("1.0", "1.0");
     115             :     }
     116             :     CATCH_END_SECTION()
     117             : 
     118           6 :     CATCH_START_SECTION("decimal_versions: verify that decimal canonicalization happens")
     119             :     {
     120             :         {
     121           2 :             versiontheca::versiontheca::pointer_t v(create("3", "3.0"));
     122           1 :             CATCH_REQUIRE(v->get_major() == 3);
     123           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     124           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     125           1 :             CATCH_REQUIRE(v->get_build() == 0);
     126             :         }
     127             :         {
     128           2 :             versiontheca::versiontheca::pointer_t v(create("3.000", "3.000"));
     129           1 :             CATCH_REQUIRE(v->get_major() == 3);
     130           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     131           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     132           1 :             CATCH_REQUIRE(v->get_build() == 0);
     133             :         }
     134             :         {
     135           2 :             versiontheca::versiontheca::pointer_t v(create("3.001"));
     136           1 :             CATCH_REQUIRE(v->get_major() == 3);
     137           1 :             CATCH_REQUIRE(v->get_minor() == 1);
     138           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     139           1 :             CATCH_REQUIRE(v->get_build() == 0);
     140             :         }
     141             :     }
     142             :     CATCH_END_SECTION()
     143             : 
     144           6 :     CATCH_START_SECTION("decimal_versions: many valid versions")
     145             :     {
     146             :         // many valid versions generated randomly to increase the likelyhood
     147             :         // of things I would otherwise not think of
     148             :         //
     149       10001 :         for(int i(0); i < 10'000; ++i)
     150             :         {
     151       10000 :             int const parts(i % 2 + 1);
     152       10000 :             if(parts == 1)
     153             :             {
     154       10000 :                 std::string v(generate_version(parts));
     155        5000 :                 create(v.c_str(), (v + ".0").c_str());
     156             :             }
     157             :             else
     158             :             {
     159       10000 :                 std::string v(generate_version(parts));
     160        5000 :                 create(v.c_str());
     161             :             }
     162             :         }
     163             :     }
     164             :     CATCH_END_SECTION()
     165           3 : }
     166             : 
     167             : 
     168           3 : CATCH_TEST_CASE("next_previous_decimal_versions", "[valid][next][previous]")
     169             : {
     170           2 :     CATCH_START_SECTION("next_previous_decimal_versions: next/previous at level 1, 0")
     171             :     {
     172             :         {
     173           2 :             versiontheca::versiontheca::pointer_t a(create("1.3"));
     174           1 :             CATCH_REQUIRE(a->next(1));
     175           1 :             CATCH_REQUIRE(a->get_version() == "1.4");
     176           1 :             CATCH_REQUIRE(a->previous(1));
     177           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     178           1 :             CATCH_REQUIRE(a->previous(1));
     179           1 :             CATCH_REQUIRE(a->get_version() == "1.2");
     180           1 :             CATCH_REQUIRE(a->next(1));
     181           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     182             :         }
     183             : 
     184             :         {
     185           2 :             versiontheca::versiontheca::pointer_t a(create("1.3"));
     186           1 :             CATCH_REQUIRE(a->next(0));
     187           1 :             CATCH_REQUIRE(a->get_version() == "2.0");
     188           1 :             CATCH_REQUIRE(a->previous(0));
     189           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     190           1 :             CATCH_REQUIRE(a->previous(0));
     191           1 :             CATCH_REQUIRE(a->get_version() == "0.0");
     192           1 :             CATCH_REQUIRE(a->next(0));
     193           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     194             :         }
     195             :     }
     196             :     CATCH_END_SECTION()
     197           1 : }
     198             : 
     199             : 
     200           3 : CATCH_TEST_CASE("compare_decimal_versions", "[valid][compare]")
     201             : {
     202           2 :     CATCH_START_SECTION("compare_decimal_versions: compare many versions")
     203             :     {
     204           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
     205           2 :         versiontheca::versiontheca::pointer_t b(create("1.1"));
     206           2 :         versiontheca::versiontheca::pointer_t c(create("1.2"));
     207             : 
     208           1 :         CATCH_REQUIRE(a->is_valid());
     209           1 :         CATCH_REQUIRE(b->is_valid());
     210           1 :         CATCH_REQUIRE(c->is_valid());
     211             : 
     212           1 :         CATCH_REQUIRE(*a == *a);
     213           1 :         CATCH_REQUIRE_FALSE(*a != *a);
     214           1 :         CATCH_REQUIRE_FALSE(*a > *a);
     215           1 :         CATCH_REQUIRE(*a >= *a);
     216           1 :         CATCH_REQUIRE_FALSE(*a < *a);
     217           1 :         CATCH_REQUIRE(*a <= *a);
     218             : 
     219           1 :         CATCH_REQUIRE_FALSE(*a == *b);
     220           1 :         CATCH_REQUIRE(*a != *b);
     221           1 :         CATCH_REQUIRE(*a > *b);
     222           1 :         CATCH_REQUIRE(*a >= *b);
     223           1 :         CATCH_REQUIRE_FALSE(*a < *b);
     224           1 :         CATCH_REQUIRE_FALSE(*a <= *b);
     225             : 
     226           1 :         CATCH_REQUIRE_FALSE(*b == *a);
     227           1 :         CATCH_REQUIRE(*b != *a);
     228           1 :         CATCH_REQUIRE_FALSE(*b > *a);
     229           1 :         CATCH_REQUIRE_FALSE(*b >= *a);
     230           1 :         CATCH_REQUIRE(*b < *a);
     231           1 :         CATCH_REQUIRE(*b <= *a);
     232             : 
     233           1 :         CATCH_REQUIRE(*a == *c);
     234           1 :         CATCH_REQUIRE_FALSE(*a != *c);
     235           1 :         CATCH_REQUIRE_FALSE(*a > *c);
     236           1 :         CATCH_REQUIRE(*a >= *c);
     237           1 :         CATCH_REQUIRE_FALSE(*a < *c);
     238           1 :         CATCH_REQUIRE(*a <= *c);
     239             : 
     240           1 :         CATCH_REQUIRE(*c == *a);
     241           1 :         CATCH_REQUIRE_FALSE(*c != *a);
     242           1 :         CATCH_REQUIRE_FALSE(*c > *a);
     243           1 :         CATCH_REQUIRE(*c >= *a);
     244           1 :         CATCH_REQUIRE_FALSE(*c < *a);
     245           1 :         CATCH_REQUIRE(*c <= *a);
     246             : 
     247             :         {
     248           2 :             std::stringstream ss;
     249           1 :             ss << *a;
     250           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     251             :         }
     252             :         {
     253           2 :             std::stringstream ss;
     254           1 :             ss << *b;
     255           1 :             CATCH_REQUIRE(ss.str() == "1.1");
     256             :         }
     257             :         {
     258           2 :             std::stringstream ss;
     259           1 :             ss << *c;
     260           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     261             :         }
     262             :     }
     263             :     CATCH_END_SECTION()
     264           1 : }
     265             : 
     266             : 
     267           7 : CATCH_TEST_CASE("invalid_decimal_versions", "[invalid]")
     268             : {
     269          10 :     CATCH_START_SECTION("invalid_debian_versions: empty")
     270             :     {
     271             :         // empty
     272             :         //
     273             :         // note: the empty version is "invalid" as far as versions go,
     274             :         //       but it does not generetate an error message
     275             :         //
     276           2 :         versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
     277           2 :         versiontheca::versiontheca v(t, "");
     278           1 :         CATCH_REQUIRE_FALSE(v.is_valid());
     279           1 :         CATCH_REQUIRE(v.get_last_error().empty());
     280             : 
     281           1 :         CATCH_REQUIRE(v.get_version().empty());
     282           1 :         CATCH_REQUIRE(v.get_last_error() == "no parts to output.");
     283             :     }
     284             :     CATCH_END_SECTION()
     285             : 
     286          10 :     CATCH_START_SECTION("invalid_decimal_versions: too many periods")
     287             :     {
     288             :         {
     289           2 :             versiontheca::versiontheca::pointer_t v(create("1.0.0", ""));
     290           1 :             CATCH_REQUIRE_FALSE(v->is_valid());
     291           1 :             CATCH_REQUIRE(v->get_major() == 0);
     292           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     293           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     294           1 :             CATCH_REQUIRE(v->get_build() == 0);
     295             :         }
     296             :         {
     297           2 :             versiontheca::versiontheca::pointer_t v(create("11.0.0.0", ""));
     298           1 :             CATCH_REQUIRE_FALSE(v->is_valid());
     299           1 :             CATCH_REQUIRE(v->get_major() == 0);
     300           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     301           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     302           1 :             CATCH_REQUIRE(v->get_build() == 0);
     303             :         }
     304             :     }
     305             :     CATCH_END_SECTION()
     306             : 
     307          10 :     CATCH_START_SECTION("invalid_decimal_versions: no support for ':' or '-' or '#' or '$'...")
     308             :     {
     309           1 :         invalid_version("3A3:1.2.3-pre55", "found unexpected character: \\U000041 in input.");
     310           1 :         invalid_version("33:-55", "found unexpected character: \\U00003A in input.");
     311           1 :         invalid_version(":", "found unexpected character: \\U00003A in input.");
     312           1 :         invalid_version("a:", "found unexpected character: \\U000061 in input.");
     313           1 :         invalid_version("-10:", "found unexpected character: \\U00002D in input.");
     314           1 :         invalid_version("99999999999999999:", "integer too large for a valid version.");
     315           1 :         invalid_version("3:", "found unexpected character: \\U00003A in input.");
     316           1 :         invalid_version("-751", "found unexpected character: \\U00002D in input.");
     317           1 :         invalid_version("-", "found unexpected character: \\U00002D in input.");
     318           1 :         invalid_version("--", "found unexpected character: \\U00002D in input.");
     319           1 :         invalid_version("+-", "found unexpected character: \\U00002B in input.");
     320           1 :         invalid_version("#-", "found unexpected character: \\U000023 in input.");
     321           1 :         invalid_version("55:435123-", "found unexpected character: \\U00003A in input.");
     322           1 :         invalid_version("-a", "found unexpected character: \\U00002D in input.");
     323           1 :         invalid_version("-0", "found unexpected character: \\U00002D in input.");
     324           1 :         invalid_version("-+", "found unexpected character: \\U00002D in input.");
     325           1 :         invalid_version("-3$7", "found unexpected character: \\U00002D in input.");
     326           1 :         invalid_version("32:1.2.55-3:7", "found unexpected character: \\U00003A in input.");
     327           1 :         invalid_version("-3.7", "found unexpected character: \\U00002D in input.");
     328           1 :         invalid_version("3.7#", "found unexpected character: \\U000023 in input.");
     329           1 :         invalid_version("3$7", "found unexpected character: \\U000024 in input.");
     330           1 :         invalid_version("3;7", "found unexpected character: \\U00003B in input.");
     331             :     }
     332             :     CATCH_END_SECTION()
     333             : 
     334          10 :     CATCH_START_SECTION("invalid_decimal_versions: max + 1 fails")
     335             :     {
     336           2 :         versiontheca::versiontheca::pointer_t a(create("4294967295.4294967295"));
     337           1 :         CATCH_REQUIRE(a->is_valid());
     338           1 :         CATCH_REQUIRE_FALSE(a->next(1));
     339           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     340           1 :         CATCH_REQUIRE(a->get_last_error() == "maximum limit reached; cannot increment version any further.");
     341             :     }
     342             :     CATCH_END_SECTION()
     343             : 
     344          10 :     CATCH_START_SECTION("invalid_decimal_versions: min - 1 fails")
     345             :     {
     346           2 :         versiontheca::versiontheca::pointer_t a(create("0.0"));
     347           1 :         CATCH_REQUIRE(a->is_valid());
     348           1 :         CATCH_REQUIRE_FALSE(a->previous(1));
     349           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     350           1 :         CATCH_REQUIRE(a->get_last_error() == "minimum limit reached; cannot decrement version any further.");
     351             :     }
     352             :     CATCH_END_SECTION()
     353           5 : }
     354             : 
     355             : 
     356           8 : CATCH_TEST_CASE("bad_decimal_calls", "[invalid]")
     357             : {
     358          12 :     CATCH_START_SECTION("bad_decimal_calls: next without a version")
     359             :     {
     360           2 :         versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
     361           2 :         versiontheca::versiontheca v(t);
     362           1 :         CATCH_REQUIRE(v.next(0));
     363           1 :         CATCH_REQUIRE(v.get_last_error() == "");
     364           1 :         CATCH_REQUIRE(v.get_version() == "1.0");
     365             :     }
     366             :     CATCH_END_SECTION()
     367             : 
     368          12 :     CATCH_START_SECTION("bad_decimal_calls: previous without a version")
     369             :     {
     370           2 :         versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
     371           2 :         versiontheca::versiontheca v(t);
     372           1 :         CATCH_REQUIRE_FALSE(v.previous(0));
     373           1 :         CATCH_REQUIRE(v.get_last_error() == "minimum limit reached; cannot decrement version any further.");
     374             :     }
     375             :     CATCH_END_SECTION()
     376             : 
     377          12 :     CATCH_START_SECTION("bad_decimal_calls: next out of bounds")
     378             :     {
     379           2 :         versiontheca::versiontheca::pointer_t a(create("1.5"));
     380         101 :         for(int p(-100); p < 0; ++p)
     381             :         {
     382         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     383             :                   a->next(p)
     384             :                 , versiontheca::invalid_parameter
     385             :                 , Catch::Matchers::ExceptionMessage(
     386             :                           "versiontheca_exception: position calling next() cannot be a negative number."));
     387             :         }
     388         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
     389             :         {
     390         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     391             :                   a->next(p)
     392             :                 , versiontheca::invalid_parameter
     393             :                 , Catch::Matchers::ExceptionMessage(
     394             :                           "versiontheca_exception: position calling next() cannot be more than "
     395             :                         + std::to_string(versiontheca::MAX_PARTS)
     396             :                         + "."));
     397             :         }
     398             :     }
     399             :     CATCH_END_SECTION()
     400             : 
     401          12 :     CATCH_START_SECTION("bad_decimal_calls: previous out of bounds")
     402             :     {
     403           2 :         versiontheca::versiontheca::pointer_t a(create("1.5"));
     404         101 :         for(int p(-100); p < 0; ++p)
     405             :         {
     406         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     407             :                   a->previous(p)
     408             :                 , versiontheca::invalid_parameter
     409             :                 , Catch::Matchers::ExceptionMessage(
     410             :                           "versiontheca_exception: position calling previous() cannot be a negative number."));
     411             :         }
     412         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
     413             :         {
     414         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     415             :                   a->previous(p)
     416             :                 , versiontheca::invalid_parameter
     417             :                 , Catch::Matchers::ExceptionMessage(
     418             :                           "versiontheca_exception: position calling previous() cannot be more than "
     419             :                         + std::to_string(versiontheca::MAX_PARTS)
     420             :                         + "."));
     421             :         }
     422             :     }
     423             :     CATCH_END_SECTION()
     424             : 
     425          12 :     CATCH_START_SECTION("bad_decimal_calls: compare against an empty (invalid) version")
     426             :     {
     427           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
     428           2 :         versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
     429           2 :         versiontheca::versiontheca empty(t, "");
     430             : 
     431           1 :         CATCH_REQUIRE(a->is_valid());
     432           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
     433             : 
     434           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     435             :               a->compare(empty)
     436             :             , versiontheca::invalid_version
     437             :             , Catch::Matchers::ExceptionMessage(
     438             :                       "versiontheca_exception: one or both of the input versions are not valid."));
     439             : 
     440           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     441             :               a->get_trait()->compare(t)
     442             :             , versiontheca::empty_version
     443             :             , Catch::Matchers::ExceptionMessage(
     444             :                       "versiontheca_exception: one or both of the input versions are empty."));
     445             :     }
     446             :     CATCH_END_SECTION()
     447             : 
     448          12 :     CATCH_START_SECTION("bad_decimal_calls: compare using an empty (invalid) version")
     449             :     {
     450           2 :         versiontheca::decimal::pointer_t t(std::make_shared<versiontheca::decimal>());
     451           2 :         versiontheca::versiontheca empty(t, "");
     452           2 :         versiontheca::versiontheca::pointer_t b(create("5.3"));
     453             : 
     454           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
     455           1 :         CATCH_REQUIRE(b->is_valid());
     456             : 
     457           1 :         CATCH_REQUIRE(empty.get_major() == 0);
     458           1 :         CATCH_REQUIRE(empty.get_minor() == 0);
     459           1 :         CATCH_REQUIRE(empty.get_patch() == 0);
     460           1 :         CATCH_REQUIRE(empty.get_build() == 0);
     461             : 
     462           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     463             :               empty.compare(*b)
     464             :             , versiontheca::invalid_version
     465             :             , Catch::Matchers::ExceptionMessage(
     466             :                       "versiontheca_exception: one or both of the input versions are not valid."));
     467             : 
     468           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     469             :               t->compare(b->get_trait())
     470             :             , versiontheca::empty_version
     471             :             , Catch::Matchers::ExceptionMessage(
     472             :                       "versiontheca_exception: one or both of the input versions are empty."));
     473             :     }
     474             :     CATCH_END_SECTION()
     475          12 : }
     476             : 
     477             : 
     478             : 
     479             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13