LCOV - code coverage report
Current view: top level - tests - catch_unicode.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 319 325 98.2 %
Date: 2023-01-24 22:36:19 Functions: 12 12 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             : #define protected public
      20             : 
      21             : // tested file
      22             : //
      23             : #include    "versiontheca/unicode.h"
      24             : 
      25             : 
      26             : // self
      27             : //
      28             : #include    "catch_main.h"
      29             : 
      30             : 
      31             : 
      32             : // versiontheca
      33             : //
      34             : #include    "versiontheca/exception.h"
      35             : #include    "versiontheca/versiontheca.h"
      36             : 
      37             : 
      38             : // libutf8
      39             : //
      40             : #include    "libutf8/libutf8.h"
      41             : 
      42             : 
      43             : // C++
      44             : //
      45             : #include    <cstring>
      46             : #include    <iomanip>
      47             : #include    <stdexcept>
      48             : 
      49             : 
      50             : 
      51             : 
      52             : namespace
      53             : {
      54             : 
      55             : 
      56             : 
      57       10147 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
      58             : {
      59       20294 :     versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
      60       10147 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      61       10048 :     if(verify == nullptr)
      62             :     {
      63        9641 :         verify = version;
      64             :     }
      65       10048 :     CATCH_REQUIRE(v->get_version() == verify);
      66       20096 :     return v;
      67             : }
      68             : 
      69             : 
      70        2115 : versiontheca::versiontheca::pointer_t invalid_version(char const * version, char const * errmsg)
      71             : {
      72        4230 :     versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
      73        2115 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      74        2115 : if(v->is_valid())
      75             : {
      76           0 : std::cerr << "--- testing invalid versions, but [" << version << "] is considered valid!\n";
      77             : }
      78             : 
      79        2115 :     CATCH_REQUIRE_FALSE(v->is_valid());
      80        2115 :     CATCH_REQUIRE(v->get_last_error(false) == errmsg);
      81        2115 :     CATCH_REQUIRE(v->get_last_error() == errmsg);
      82        2115 :     CATCH_REQUIRE(v->get_last_error().empty());
      83             : 
      84        4230 :     return v;
      85             : }
      86             : 
      87             : 
      88      130000 : std::string generate_number()
      89             : {
      90      130000 :     versiontheca::part_integer_t value;
      91      130000 :     SNAP_CATCH2_NAMESPACE::random(value);
      92      130000 :     return std::to_string(value);
      93             : }
      94             : 
      95             : 
      96       10000 : std::string generate_version(std::size_t max)
      97             : {
      98       10000 :     std::string v;
      99      140000 :     for(std::size_t i(0); i < max; ++i)
     100             :     {
     101      130000 :         if(!v.empty())
     102             :         {
     103      120000 :             v += '.';
     104             :         }
     105      130000 :         v += generate_number();
     106             :     }
     107       10000 :     return v;
     108             : }
     109             : 
     110             : 
     111             : // a version of wctombs which encodes anything including invalid unicode
     112             : // characters to make sure the library detects such as expected
     113             : //
     114        2134 : std::string wctombs(char32_t wc)
     115             : {
     116        2134 :     char mb[4];
     117        2134 :     if(wc < 0x80)
     118             :     {
     119             :         /* this will also encode '\0'... */
     120          32 :         mb[0] = static_cast<char>(wc);
     121          32 :         return std::string(mb, 1);
     122             :     }
     123        2102 :     if(wc < 0x800)
     124             :     {
     125          32 :         mb[0] = static_cast<char>((wc >> 6) | 0xC0);
     126          32 :         mb[1] = (wc & 0x3F) | 0x80;
     127          32 :         return std::string(mb, 2);
     128             :     }
     129        2070 :     if(wc < 0x10000)
     130             :     {
     131        2070 :         mb[0] = static_cast<char>((wc >> 12) | 0xE0);
     132        2070 :         mb[1] = ((wc >> 6) & 0x3F) | 0x80;
     133        2070 :         mb[2] = (wc & 0x3F) | 0x80;
     134        2070 :         return std::string(mb, 3);
     135             :     }
     136           0 :     mb[0] = static_cast<char>((wc >> 18) | 0xF0);
     137           0 :     mb[1] = ((wc >> 12) & 0x3F) | 0x80;
     138           0 :     mb[2] = ((wc >> 6) & 0x3F) | 0x80;
     139           0 :     mb[3] = (wc & 0x3F) | 0x80;
     140           0 :     return std::string(mb, 4);
     141             : }
     142             : 
     143             : }
     144             : // no name namespace
     145             : 
     146             : 
     147           5 : CATCH_TEST_CASE("unicode_versions", "[valid]")
     148             : {
     149           6 :     CATCH_START_SECTION("unicode_versions: verify test checker for version 1.0")
     150             :     {
     151           1 :         create("1.0");
     152             :     }
     153             :     CATCH_END_SECTION()
     154             : 
     155           6 :     CATCH_START_SECTION("unicode_versions: verify that canonicalization happens")
     156             :     {
     157             :         {
     158           2 :             versiontheca::versiontheca::pointer_t v(create("3", "3.0"));
     159           1 :             CATCH_REQUIRE(v->get_major() == 3);
     160           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     161           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     162           1 :             CATCH_REQUIRE(v->get_build() == 0);
     163             :         }
     164             :         {
     165           2 :             versiontheca::versiontheca::pointer_t v(create("1.0.0", "1.0"));
     166           1 :             CATCH_REQUIRE(v->get_major() == 1);
     167           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     168           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     169           1 :             CATCH_REQUIRE(v->get_build() == 0);
     170             :         }
     171             :         {
     172           2 :             versiontheca::versiontheca::pointer_t v(create("1.0.0.0", "1.0"));
     173           1 :             CATCH_REQUIRE(v->get_major() == 1);
     174           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     175           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     176           1 :             CATCH_REQUIRE(v->get_build() == 0);
     177             :         }
     178             :         {
     179           2 :             versiontheca::versiontheca::pointer_t v(create("A.A.A", "A.A"));
     180           1 :             CATCH_REQUIRE(v->get_major() == 0);
     181           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     182           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     183           1 :             CATCH_REQUIRE(v->get_build() == 0);
     184             :         }
     185             :         {
     186           2 :             versiontheca::versiontheca::pointer_t v(create("C.A.I"));
     187           1 :             CATCH_REQUIRE(v->get_major() == 0);
     188           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     189           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     190           1 :             CATCH_REQUIRE(v->get_build() == 0);
     191             :         }
     192             :     }
     193             :     CATCH_END_SECTION()
     194             : 
     195           6 :     CATCH_START_SECTION("unicode_versions: many valid versions")
     196             :     {
     197             :         // many valid versions generated randomly to increase the likelyhood
     198             :         // of things I would otherwise not think of
     199             :         //
     200       10001 :         for(int i(0); i < 10'000; ++i)
     201             :         {
     202       10000 :             int const parts(i % 25 + 1);
     203       10000 :             if(parts == 1)
     204             :             {
     205         800 :                 std::string v(generate_version(parts));
     206         400 :                 create(v.c_str(), (v + ".0").c_str());
     207             :             }
     208             :             else
     209             :             {
     210       19200 :                 std::string v(generate_version(parts));
     211        9600 :                 create(v.c_str());
     212             :             }
     213             :         }
     214             :     }
     215             :     CATCH_END_SECTION()
     216           3 : }
     217             : 
     218             : 
     219           3 : CATCH_TEST_CASE("next_previous_unicode_versions", "[valid][next][previous]")
     220             : {
     221           2 :     CATCH_START_SECTION("next_previous_unicode_versions: next/previous at level 4, 3, 2, 1, 0")
     222             :     {
     223             :         {
     224           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     225           1 :             CATCH_REQUIRE(a->next(4));
     226           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2.0.1");
     227           1 :             CATCH_REQUIRE(a->previous(4));
     228           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1, back to original
     229           1 :             CATCH_REQUIRE(a->previous(4));
     230           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295.4294967295");
     231           1 :             CATCH_REQUIRE(a->get_major() == 1);
     232           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     233           1 :             CATCH_REQUIRE(a->get_patch() == 1);
     234           1 :             CATCH_REQUIRE(a->get_build() == 4294967295);
     235           1 :             CATCH_REQUIRE(a->next(4));
     236           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1 -1 +1, back to original
     237             :         }
     238             : 
     239             :         {
     240           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     241           1 :             CATCH_REQUIRE(a->next(3));
     242           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2.1");
     243           1 :             CATCH_REQUIRE(a->previous(3));
     244           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     245           1 :             CATCH_REQUIRE(a->previous(3));
     246           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295");
     247           1 :             CATCH_REQUIRE(a->next(3));
     248           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     249             :         }
     250             : 
     251             :         {
     252           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     253           1 :             CATCH_REQUIRE(a->next(2));
     254           1 :             CATCH_REQUIRE(a->get_version() == "1.3.3");
     255           1 :             CATCH_REQUIRE(a->previous(2));
     256           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     257           1 :             CATCH_REQUIRE(a->previous(2));
     258           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1");
     259           1 :             CATCH_REQUIRE(a->next(2));
     260           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     261             :         }
     262             : 
     263             :         {
     264           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     265           1 :             CATCH_REQUIRE(a->next(1));
     266           1 :             CATCH_REQUIRE(a->get_version() == "1.4");
     267           1 :             CATCH_REQUIRE(a->previous(1));
     268           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     269           1 :             CATCH_REQUIRE(a->previous(1));
     270           1 :             CATCH_REQUIRE(a->get_version() == "1.2");
     271           1 :             CATCH_REQUIRE(a->next(1));
     272           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     273             :         }
     274             : 
     275             :         {
     276           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     277           1 :             CATCH_REQUIRE(a->next(0));
     278           1 :             CATCH_REQUIRE(a->get_version() == "2.0");
     279           1 :             CATCH_REQUIRE(a->previous(0));
     280           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     281           1 :             CATCH_REQUIRE(a->previous(0));
     282           1 :             CATCH_REQUIRE(a->get_version() == "0.0");
     283           1 :             CATCH_REQUIRE(a->next(0));
     284           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     285             :         }
     286             : 
     287             :         {
     288           2 :             versiontheca::versiontheca::pointer_t a(create("1.3"));
     289           2 :             versiontheca::versiontheca::pointer_t f(create("9.9z"));
     290           1 :             a->set_format(*f);
     291           1 :             CATCH_REQUIRE(a->next(2));
     292           1 :             CATCH_REQUIRE(a->get_version() == "1.3B");
     293             :         }
     294             :     }
     295             :     CATCH_END_SECTION()
     296           1 : }
     297             : 
     298             : 
     299           3 : CATCH_TEST_CASE("compare_unicode_versions", "[valid][compare]")
     300             : {
     301           2 :     CATCH_START_SECTION("compare_unicode_versions: compare many versions")
     302             :     {
     303           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
     304           2 :         versiontheca::versiontheca::pointer_t b(create("1.1"));
     305           2 :         versiontheca::versiontheca::pointer_t c(create("1.2.0.0", "1.2"));  // the zero are ignored by the compare
     306           2 :         versiontheca::versiontheca::pointer_t d(create("1.2.5", "1.2.5"));
     307             : 
     308           1 :         CATCH_REQUIRE(a->is_valid());
     309           1 :         CATCH_REQUIRE(b->is_valid());
     310           1 :         CATCH_REQUIRE(c->is_valid());
     311             : 
     312           1 :         CATCH_REQUIRE(*a == *a);
     313           1 :         CATCH_REQUIRE_FALSE(*a != *a);
     314           1 :         CATCH_REQUIRE_FALSE(*a > *a);
     315           1 :         CATCH_REQUIRE(*a >= *a);
     316           1 :         CATCH_REQUIRE_FALSE(*a < *a);
     317           1 :         CATCH_REQUIRE(*a <= *a);
     318             : 
     319           1 :         CATCH_REQUIRE_FALSE(*a == *b);
     320           1 :         CATCH_REQUIRE(*a != *b);
     321           1 :         CATCH_REQUIRE(*a > *b);
     322           1 :         CATCH_REQUIRE(*a >= *b);
     323           1 :         CATCH_REQUIRE_FALSE(*a < *b);
     324           1 :         CATCH_REQUIRE_FALSE(*a <= *b);
     325             : 
     326           1 :         CATCH_REQUIRE_FALSE(*b == *a);
     327           1 :         CATCH_REQUIRE(*b != *a);
     328           1 :         CATCH_REQUIRE_FALSE(*b > *a);
     329           1 :         CATCH_REQUIRE_FALSE(*b >= *a);
     330           1 :         CATCH_REQUIRE(*b < *a);
     331           1 :         CATCH_REQUIRE(*b <= *a);
     332             : 
     333           1 :         CATCH_REQUIRE(*a == *c);
     334           1 :         CATCH_REQUIRE_FALSE(*a != *c);
     335           1 :         CATCH_REQUIRE_FALSE(*a > *c);
     336           1 :         CATCH_REQUIRE(*a >= *c);
     337           1 :         CATCH_REQUIRE_FALSE(*a < *c);
     338           1 :         CATCH_REQUIRE(*a <= *c);
     339             : 
     340           1 :         CATCH_REQUIRE(*c == *a);
     341           1 :         CATCH_REQUIRE_FALSE(*c != *a);
     342           1 :         CATCH_REQUIRE_FALSE(*c > *a);
     343           1 :         CATCH_REQUIRE(*c >= *a);
     344           1 :         CATCH_REQUIRE_FALSE(*c < *a);
     345           1 :         CATCH_REQUIRE(*c <= *a);
     346             : 
     347           1 :         CATCH_REQUIRE_FALSE(*a == *d);
     348           1 :         CATCH_REQUIRE(*a != *d);
     349           1 :         CATCH_REQUIRE_FALSE(*a > *d);
     350           1 :         CATCH_REQUIRE_FALSE(*a >= *d);
     351           1 :         CATCH_REQUIRE(*a < *d);
     352           1 :         CATCH_REQUIRE(*a <= *d);
     353             : 
     354           1 :         CATCH_REQUIRE_FALSE(*d == *a);
     355           1 :         CATCH_REQUIRE(*d != *a);
     356           1 :         CATCH_REQUIRE(*d > *a);
     357           1 :         CATCH_REQUIRE(*d >= *a);
     358           1 :         CATCH_REQUIRE_FALSE(*d < *a);
     359           1 :         CATCH_REQUIRE_FALSE(*d <= *a);
     360             : 
     361             :         {
     362           2 :             std::stringstream ss;
     363           1 :             ss << *a;
     364           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     365             :         }
     366             :         {
     367           2 :             std::stringstream ss;
     368           1 :             ss << *b;
     369           1 :             CATCH_REQUIRE(ss.str() == "1.1");
     370             :         }
     371             :         {
     372           2 :             std::stringstream ss;
     373           1 :             ss << *c;
     374           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     375             :         }
     376             :     }
     377             :     CATCH_END_SECTION()
     378           1 : }
     379             : 
     380             : 
     381           7 : CATCH_TEST_CASE("invalid_unicode_versions", "[invalid]")
     382             : {
     383          10 :     CATCH_START_SECTION("invalid_unicode_versions: empty")
     384             :     {
     385             :         // empty
     386             :         //
     387             :         // note: the empty version is "invalid" as far as versions go,
     388             :         //       but it does not generetate an error message
     389             :         //
     390           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     391           2 :         versiontheca::versiontheca v(t, "");
     392           1 :         CATCH_REQUIRE_FALSE(v.is_valid());
     393           1 :         CATCH_REQUIRE(v.get_last_error().empty());
     394             : 
     395           1 :         CATCH_REQUIRE(v.get_version().empty());
     396           1 :         CATCH_REQUIRE(v.get_last_error() == "no parts to output.");
     397             :     }
     398             :     CATCH_END_SECTION()
     399             : 
     400          10 :     CATCH_START_SECTION("invalid_unicode_versions: two periods, period at the start or end")
     401             :     {
     402             :         // most characters are valid in Unicode versions
     403             :         // we still have a few cases of invalid entry
     404             :         //
     405           1 :         invalid_version("3A3:1.2..3-pre55", "a version value cannot be an empty string.");
     406           1 :         invalid_version(".33:-55", "a version value cannot be an empty string.");
     407           1 :         invalid_version(":.", "a version value cannot be an empty string.");
     408             :     }
     409             :     CATCH_END_SECTION()
     410             : 
     411          10 :     CATCH_START_SECTION("invalid_unicode_versions: randomized")
     412             :     {
     413             :         // check all invalid unicode characters
     414             :         //
     415     1114112 :         for(char32_t c(1); c < 0x110000; ++c)
     416             :         {
     417     1114111 :             if(libutf8::is_valid_unicode(c, false))
     418             :             {
     419             :                 // skip valid characters
     420             :                 //
     421     1111999 :                 continue;
     422             :             }
     423        2112 :             std::size_t const max_parts(rand() % (versiontheca::MAX_PARTS - 1) + 1);
     424        2112 :             std::size_t bad_pos(rand() % (max_parts * 5));
     425        4224 :             std::string v;
     426        2112 :             std::size_t count(0);
     427        2112 :             if(count == bad_pos)
     428             :             {
     429          64 :                 v += wctombs(c);
     430             :             }
     431       28892 :             for(std::size_t part_no(0); part_no < max_parts; ++part_no)
     432             :             {
     433       26780 :                 if(part_no != 0)
     434             :                 {
     435       24668 :                     v += '.';
     436             :                 }
     437       26780 :                 std::size_t const length(rand() % 10 + 1);
     438      174131 :                 for(std::size_t l(0); l < length; ++l)
     439             :                 {
     440      147351 :                     char32_t wc(U'\0');
     441           8 :                     do
     442             :                     {
     443      147359 :                         wc = SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE);
     444             :                     }
     445      147359 :                     while(wc == '.' || wc < 0x20 || (wc >= 0x7F && wc <= 0x9F));
     446      147351 :                     v += libutf8::to_u8string(wc);
     447      147351 :                     ++count;
     448      147351 :                     if(count == bad_pos)
     449             :                     {
     450        1977 :                         v += wctombs(c);
     451             :                     }
     452             :                 }
     453             :             }
     454        2112 :             if(bad_pos >= count)
     455             :             {
     456          93 :                 v += wctombs(c);
     457             :             }
     458        4224 :             std::stringstream last_error;
     459        2112 :             if(c >= 0xD800 && c <= 0xDFFF)
     460             :             {
     461        2048 :                 last_error << "input string includes an invalid code not representing a valid UTF-8 character.";
     462             :             }
     463             :             else
     464             :             {
     465          64 :                 last_error << "found unexpected character: \\U"
     466          64 :                            << std::hex << std::uppercase << std::setfill('0')
     467          64 :                                        << std::setw(6) << static_cast<int>(c)
     468          64 :                            << " in input.";
     469             :             }
     470        4224 :             versiontheca::versiontheca::pointer_t a(invalid_version(v.c_str(), last_error.str().c_str()));
     471        2112 :             if(max_parts < versiontheca::MAX_PARTS - 1)
     472             :             {
     473             :                 // make sure the parse_value() function catches invalid Unicode
     474             :                 //
     475        2026 :                 CATCH_REQUIRE_FALSE(a->get_trait()->parse_value(v, U'.'));
     476             :             }
     477             :         }
     478             :     }
     479             :     CATCH_END_SECTION()
     480             : 
     481          10 :     CATCH_START_SECTION("invalid_unicode_versions: max + 1 fails")
     482             :     {
     483           2 :         versiontheca::versiontheca::pointer_t a(create("4294967295.4294967295.4294967295"));
     484           1 :         CATCH_REQUIRE(a->is_valid());
     485           1 :         CATCH_REQUIRE_FALSE(a->next(2));
     486           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     487           1 :         CATCH_REQUIRE(a->get_last_error() == "maximum limit reached; cannot increment version any further.");
     488             :     }
     489             :     CATCH_END_SECTION()
     490             : 
     491          10 :     CATCH_START_SECTION("invalid_unicode_versions: min - 1 fails")
     492             :     {
     493           2 :         versiontheca::versiontheca::pointer_t a(create("0.0"));
     494           1 :         CATCH_REQUIRE(a->is_valid());
     495           1 :         CATCH_REQUIRE_FALSE(a->previous(2));
     496           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     497           1 :         CATCH_REQUIRE(a->get_last_error() == "minimum limit reached; cannot decrement version any further.");
     498             :     }
     499             :     CATCH_END_SECTION()
     500           5 : }
     501             : 
     502             : 
     503          10 : CATCH_TEST_CASE("bad_unicode_calls", "[invalid]")
     504             : {
     505          16 :     CATCH_START_SECTION("bad_unicode_calls: next without a version")
     506             :     {
     507           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     508           2 :         versiontheca::versiontheca v(t);
     509           1 :         CATCH_REQUIRE(v.next(0));
     510           1 :         CATCH_REQUIRE(v.get_last_error() == "");
     511           1 :         CATCH_REQUIRE(v.get_version() == "1.0");
     512             :     }
     513             :     CATCH_END_SECTION()
     514             : 
     515          16 :     CATCH_START_SECTION("bad_unicode_calls: previous without a version")
     516             :     {
     517           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     518           2 :         versiontheca::versiontheca v(t);
     519           1 :         CATCH_REQUIRE_FALSE(v.previous(0));
     520           1 :         CATCH_REQUIRE(v.get_last_error() == "minimum limit reached; cannot decrement version any further.");
     521             :     }
     522             :     CATCH_END_SECTION()
     523             : 
     524          16 :     CATCH_START_SECTION("bad_unicode_calls: next out of bounds")
     525             :     {
     526           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3"));
     527         101 :         for(int p(-100); p < 0; ++p)
     528             :         {
     529         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     530             :                   a->next(p)
     531             :                 , versiontheca::invalid_parameter
     532             :                 , Catch::Matchers::ExceptionMessage(
     533             :                           "versiontheca_exception: position calling next() cannot be a negative number."));
     534             :         }
     535         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
     536             :         {
     537         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     538             :                   a->next(p)
     539             :                 , versiontheca::invalid_parameter
     540             :                 , Catch::Matchers::ExceptionMessage(
     541             :                           "versiontheca_exception: position calling next() cannot be more than "
     542             :                         + std::to_string(versiontheca::MAX_PARTS)
     543             :                         + "."));
     544             :         }
     545             :     }
     546             :     CATCH_END_SECTION()
     547             : 
     548          16 :     CATCH_START_SECTION("bad_unicode_calls: previous out of bounds")
     549             :     {
     550           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3"));
     551         101 :         for(int p(-100); p < 0; ++p)
     552             :         {
     553         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     554             :                   a->previous(p)
     555             :                 , versiontheca::invalid_parameter
     556             :                 , Catch::Matchers::ExceptionMessage(
     557             :                           "versiontheca_exception: position calling previous() cannot be a negative number."));
     558             :         }
     559         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
     560             :         {
     561         100 :             CATCH_REQUIRE_THROWS_MATCHES(
     562             :                   a->previous(p)
     563             :                 , versiontheca::invalid_parameter
     564             :                 , Catch::Matchers::ExceptionMessage(
     565             :                           "versiontheca_exception: position calling previous() cannot be more than "
     566             :                         + std::to_string(versiontheca::MAX_PARTS)
     567             :                         + "."));
     568             :         }
     569             :     }
     570             :     CATCH_END_SECTION()
     571             : 
     572          16 :     CATCH_START_SECTION("bad_unicode_calls: compare against an empty (invalid) version")
     573             :     {
     574           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
     575           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     576           2 :         versiontheca::versiontheca empty(t, "");
     577             : 
     578           1 :         CATCH_REQUIRE(a->is_valid());
     579           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
     580             : 
     581           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     582             :               a->compare(empty)
     583             :             , versiontheca::invalid_version
     584             :             , Catch::Matchers::ExceptionMessage(
     585             :                       "versiontheca_exception: one or both of the input versions are not valid."));
     586             : 
     587           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     588             :               a->get_trait()->compare(t)
     589             :             , versiontheca::empty_version
     590             :             , Catch::Matchers::ExceptionMessage(
     591             :                       "versiontheca_exception: one or both of the input versions are empty."));
     592             :     }
     593             :     CATCH_END_SECTION()
     594             : 
     595          16 :     CATCH_START_SECTION("bad_unicode_calls: compare using an empty (invalid) version")
     596             :     {
     597           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     598           2 :         versiontheca::versiontheca empty(t, "");
     599           2 :         versiontheca::versiontheca::pointer_t b(create("5.3"));
     600             : 
     601           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
     602           1 :         CATCH_REQUIRE(b->is_valid());
     603             : 
     604           1 :         CATCH_REQUIRE(empty.get_major() == 0);
     605           1 :         CATCH_REQUIRE(empty.get_minor() == 0);
     606           1 :         CATCH_REQUIRE(empty.get_patch() == 0);
     607           1 :         CATCH_REQUIRE(empty.get_build() == 0);
     608             : 
     609           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     610             :               empty.compare(*b)
     611             :             , versiontheca::invalid_version
     612             :             , Catch::Matchers::ExceptionMessage(
     613             :                       "versiontheca_exception: one or both of the input versions are not valid."));
     614             : 
     615           1 :         CATCH_REQUIRE_THROWS_MATCHES(
     616             :               t->compare(b->get_trait())
     617             :             , versiontheca::empty_version
     618             :             , Catch::Matchers::ExceptionMessage(
     619             :                       "versiontheca_exception: one or both of the input versions are empty."));
     620             :     }
     621             :     CATCH_END_SECTION()
     622             : 
     623          16 :     CATCH_START_SECTION("bad_unicode_calls: too many parts")
     624             :     {
     625             :         // up to MAX_PARTS, it works
     626             :         //
     627           2 :         std::string version("1");  // version "1" is a special case
     628           1 :         create(version.c_str(), "1.0");
     629           1 :         std::size_t idx(2);
     630          49 :         for(; idx <= versiontheca::MAX_PARTS; ++idx)
     631             :         {
     632          24 :             version += '.';
     633          24 :             version += std::to_string(idx);
     634          24 :             create(version.c_str());
     635             :         }
     636             : 
     637             :         // when more than MAX_PARTS, it throws
     638             :         //
     639         199 :         for(; idx < versiontheca::MAX_PARTS + 100; ++idx)
     640             :         {
     641          99 :             version += '.';
     642          99 :             version += std::to_string(idx);
     643          99 :             CATCH_REQUIRE_THROWS_MATCHES(
     644             :                   create(version.c_str())
     645             :                 , versiontheca::invalid_parameter
     646             :                 , Catch::Matchers::ExceptionMessage(
     647             :                           "versiontheca_exception: trying to append more parts when maximum was already reached."));
     648             :         }
     649             :     }
     650             :     CATCH_END_SECTION()
     651             : 
     652          16 :     CATCH_START_SECTION("bad_unicode_calls: next/erase out of bounds")
     653             :     {
     654           2 :         versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
     655           1 :         CATCH_REQUIRE_FALSE(t->parse(std::string()));
     656           1 :         CATCH_REQUIRE(t->get_last_error() == "an empty input string cannot represent a valid version.");
     657           1 :         CATCH_REQUIRE(t->get_last_error().empty());
     658             :     }
     659             :     CATCH_END_SECTION()
     660          14 : }
     661             : 
     662             : 
     663             : 
     664             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13