LCOV - code coverage report
Current view: top level - tests - catch_rpm.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 620 642 96.6 %
Date: 2023-01-24 22:36:19 Functions: 12 13 92.3 %
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/rpm.h"
      22             : 
      23             : 
      24             : // self
      25             : //
      26             : #include    "catch_main.h"
      27             : 
      28             : 
      29             : 
      30             : // versiontheca
      31             : //
      32             : #include    "versiontheca/basic.h"
      33             : #include    "versiontheca/exception.h"
      34             : #include    "versiontheca/versiontheca.h"
      35             : 
      36             : 
      37             : // C++
      38             : //
      39             : #include    <cstring>
      40             : #include    <iomanip>
      41             : #include    <stdexcept>
      42             : 
      43             : 
      44             : 
      45             : 
      46             : namespace
      47             : {
      48             : 
      49             : 
      50             : 
      51          39 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
      52             : {
      53          78 :     versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
      54          39 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      55          39 :     CATCH_REQUIRE(v->get_version() == (verify == nullptr ? version : verify));
      56          78 :     return v;
      57             : }
      58             : 
      59             : 
      60             : 
      61           0 : std::string print_version(std::string const & version)
      62             : {
      63           0 :     std::stringstream result;
      64           0 :     for(char const * s(version.c_str()); *s != '\0'; ++s)
      65             :     {
      66           0 :         if(static_cast<unsigned char>(*s) < ' ')
      67             :         {
      68           0 :             result << "^" << static_cast<char>(*s + '@');
      69             :         }
      70           0 :         else if(static_cast<unsigned char>(*s) >= 0x80)
      71             :         {
      72           0 :             result << "\\x" << std::hex << static_cast<int>(static_cast<unsigned char>(*s));
      73             :         }
      74           0 :         else if(*s == 0x7F)
      75             :         {
      76           0 :             result << "<DEL>";
      77             :         }
      78           0 :         else if(*s == '^')
      79             :         {
      80           0 :             result << "^^";
      81             :         }
      82           0 :         else if(*s == '@')
      83             :         {
      84           0 :             result << "@@";
      85             :         }
      86             :         else
      87             :         {
      88           0 :             result << *s;
      89             :         }
      90             :     }
      91             : 
      92           0 :     return result.str();
      93             : }
      94             : 
      95             : 
      96       38481 : void check_version(std::string const & version, std::string const & error_msg)
      97             : {
      98             :     // validate_rpm_version()
      99             :     {
     100             :         //char error_string[256];
     101             :         //strcpy(error_string, "no errors");
     102       76962 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     103       76962 :         versiontheca::versiontheca v(t, version);
     104             :         //int valid(validate_rpm_version(version, error_string, sizeof(error_string) / sizeof(error_string[0])));
     105             : //printf("from {%s} result = %d [%s] [%s]\n", print_version(version).c_str(), valid, error_string, error_msg);
     106       38481 :         if(error_msg.empty())
     107             :         {
     108       38401 : if(!v.is_valid())
     109           0 : std::cerr << "--- BAD: checked version [" << version << "], expected to be valid; err = [" << v.get_last_error(false) << "]\n";
     110             :             // in this case it must be valid
     111       38401 :             CATCH_REQUIRE(v.is_valid());
     112       38401 :             CATCH_REQUIRE(v.get_last_error().empty());
     113             :         }
     114             :         else
     115             :         {
     116          80 : if(v.is_valid())
     117           0 : std::cerr << "--- BAD: checked version [" << version << "], expected to be invalid; message: [" << error_msg << "]\n";
     118          80 : else if(v.get_last_error(false) != error_msg)
     119           0 : std::cerr << "--- BAD: checked version [" << version << "] invalid as expected, error message do not match, however: [" << v.get_last_error(false) << "] instead of [" << error_msg << "]\n";
     120          80 :             CATCH_REQUIRE_FALSE(v.is_valid());
     121          80 :             CATCH_REQUIRE(error_msg == v.get_last_error());
     122             :         }
     123             :     }
     124       38481 : }
     125             : 
     126             : 
     127             : //constexpr char const g_valid_digits[]    = "0123456789";
     128             : //constexpr std::size_t const g_valid_digits_length = std::size(g_valid_digits) - 1;
     129             : 
     130             : // WARNING: the alphanum is used by "invalid tests" and it includes a '.'
     131             : //          which will cause issues in valid tests (i.e. two periods one after
     132             : //          the other)
     133             : //
     134             : constexpr char const g_valid_alphanum[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz:-.~^_";
     135             : constexpr std::size_t const g_valid_alphanum_length = std::size(g_valid_alphanum) - 1;
     136             : 
     137             : // all of the following support a '.' but we handle it specially to avoid
     138             : //
     139             : //     1. periods at the end
     140             : //     2. two periods in a row
     141             : //
     142             : constexpr char const g_valid_letters[]   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~^_";
     143             : constexpr std::size_t const g_valid_letters_length = std::size(g_valid_letters) - 1;
     144             : 
     145             : constexpr char const g_valid_letters_colon[]   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~^_:";
     146             : constexpr std::size_t const g_valid_letters_colon_length = std::size(g_valid_letters_colon) - 1;
     147             : 
     148             : constexpr char const g_valid_letters_dash[]   = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~^_-";
     149             : constexpr std::size_t const g_valid_letters_dash_length = std::size(g_valid_letters_dash) - 1;
     150             : 
     151             : constexpr char const g_valid_all_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~^_:-";
     152             : constexpr std::size_t const g_valid_all_chars_length = std::size(g_valid_all_chars) - 1;
     153             : 
     154             : 
     155       57200 : std::string generate_number()
     156             : {
     157       57200 :     versiontheca::part_integer_t value;
     158       57200 :     SNAP_CATCH2_NAMESPACE::random(value);
     159       57200 :     return std::to_string(value);
     160             : }
     161             : 
     162             : 
     163      460400 : std::string generate_word(char const * valid_chars, std::size_t length)
     164             : {
     165      460400 :     std::string v;
     166      460400 :     int const size(rand() % 10 + 1);
     167     2992593 :     for(int j(0); j < size; ++j)
     168             :     {
     169     2532193 :         v += valid_chars[rand() % length];
     170             :     }
     171      460400 :     while(!v.empty()
     172      460400 :        && v.back() == '-')
     173             :     {
     174           0 :         v.back() = valid_chars[rand() % length];
     175             :     }
     176      460400 :     return v;
     177             : }
     178             : 
     179             : 
     180       57200 : std::string generate_version(
     181             :       std::size_t max
     182             :     , char const * valid_chars
     183             :     , std::size_t length
     184             :     , bool prepend_number = true)
     185             : {
     186       57200 :     std::string v;
     187       57200 :     std::size_t i(0);
     188       57200 :     if(prepend_number)
     189             :     {
     190       38400 :         v += generate_number();
     191       38400 :         ++i;
     192       38400 :         prepend_number = false;
     193             :     }
     194      978000 :     for(; i < max; ++i)
     195             :     {
     196      920800 :         if(!v.empty()
     197      460400 :         && rand() % 100 < 10)
     198             :         {
     199       44119 :             v += '.';
     200             :         }
     201      460400 :         if(prepend_number)
     202             :         {
     203           0 :             v += generate_number();
     204             :         }
     205             :         else
     206             :         {
     207      460400 :             v += generate_word(valid_chars, length);
     208             :         }
     209             :     }
     210       57200 :     return v;
     211             : }
     212             : 
     213             : 
     214             : }
     215             : // no name namespace
     216             : 
     217             : 
     218           5 : CATCH_TEST_CASE("rpm_versions", "[valid]")
     219             : {
     220           6 :     CATCH_START_SECTION("rpm_versions: verify test checker for version 1.0")
     221             :     {
     222           1 :         check_version("1.0", std::string());
     223             :     }
     224             :     CATCH_END_SECTION()
     225             : 
     226           6 :     CATCH_START_SECTION("rpm_versions: verify that canonicalization happens")
     227             :     {
     228             :         {
     229           2 :             versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     230           2 :             versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, "3"));
     231           1 :             CATCH_REQUIRE(v->get_version() == "3.0");
     232           1 :             CATCH_REQUIRE(v->get_major() == 3);
     233           1 :             CATCH_REQUIRE(v->get_minor() == 0);
     234           1 :             CATCH_REQUIRE(v->get_patch() == 0);
     235           1 :             CATCH_REQUIRE(v->get_build() == 0);
     236             :         }
     237             :         {
     238           2 :             versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     239           2 :             versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, "1.0.0"));
     240           1 :             CATCH_REQUIRE(v->get_version() == "1.0");
     241             :         }
     242             :         {
     243           2 :             versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     244           2 :             versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, "1.0.0.0"));
     245           1 :             CATCH_REQUIRE(v->get_version() == "1.0");
     246             :         }
     247             :         {
     248           2 :             versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     249           2 :             versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, "0:q2.71-z3"));
     250           1 :             CATCH_REQUIRE(v->get_version() == "q2.71-z3");
     251             :         }
     252             :         {
     253           2 :             versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     254           2 :             versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, "0:2.71.3z-rc32.5"));
     255           1 :             CATCH_REQUIRE(v->get_version() == "2.71.3z-rc32.5");
     256             :         }
     257             :     }
     258             :     CATCH_END_SECTION()
     259             : 
     260           6 :     CATCH_START_SECTION("rpm_versions: many valid versions")
     261             :     {
     262             :         // many valid versions generated randomly to increase the likelyhood
     263             :         // of things I would otherwise not think of
     264             :         //
     265       10001 :         for(int i(0); i < 10'000; ++i)
     266             :         {
     267       10000 :             int const parts(i % 25 + 1);
     268             : 
     269             :             // simple version (no epoch/revision)
     270             :             {
     271       20000 :                 std::string v(generate_version(parts, g_valid_letters, g_valid_letters_length));
     272       10000 :                 check_version(v.c_str(), std::string());
     273             :             }
     274             : 
     275             :             // epoch + version
     276       10000 :             if(parts > 1)
     277             :             {
     278       19200 :                 std::stringstream ss;
     279        9600 :                 ss << generate_number() << ':';
     280        9600 :                 ss << generate_version(parts - 1, g_valid_letters, g_valid_letters_length);
     281        9600 :                 check_version(ss.str(), std::string());
     282             :             }
     283             : 
     284             :             // version + revision
     285       10000 :             if(parts > 1)
     286             :             {
     287       19200 :                 std::string v(generate_version(std::max(1UL, parts / 2UL), g_valid_letters, g_valid_letters_length));
     288        9600 :                 v += '-';
     289        9600 :                 v += generate_version(std::max(1UL, parts / 2UL), g_valid_letters, g_valid_letters_length, false);
     290        9600 :                 check_version(v.c_str(), std::string());
     291             :             }
     292             : 
     293             :             // epoch + version + revision
     294       10000 :             if(parts > 2)
     295             :             {
     296       18400 :                 std::stringstream ss;
     297       18400 :                 ss << generate_number() << ':'
     298             :                       // anything can appear in upstream version when we have an epoch & revision
     299       18400 :                    << generate_version(std::max(1UL, parts / 2UL), g_valid_letters, g_valid_letters_length)
     300             :                    << '-'
     301             :                       // no dashes, no colons in revisions
     302       27600 :                    << generate_version(std::max(1UL, parts / 2UL), g_valid_letters, g_valid_letters_length, false);
     303        9200 :                 check_version(ss.str(), std::string());
     304             :             }
     305             :         }
     306             :     }
     307             :     CATCH_END_SECTION()
     308           3 : }
     309             : 
     310             : 
     311           7 : CATCH_TEST_CASE("next_previous_rpm_versions", "[valid][next][previous]")
     312             : {
     313          10 :     CATCH_START_SECTION("next_previous_rpm_versions: next/previous at level 4, 3, 2, 1, 0")
     314             :     {
     315             :         {
     316           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     317           1 :             CATCH_REQUIRE(a->next(4));
     318           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2.0.1");
     319           1 :             CATCH_REQUIRE(a->previous(4));
     320           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1, back to original
     321           1 :             CATCH_REQUIRE(a->previous(4));
     322           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295.4294967295");
     323           1 :             CATCH_REQUIRE(a->get_major() == 1);
     324           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     325           1 :             CATCH_REQUIRE(a->get_patch() == 1);
     326           1 :             CATCH_REQUIRE(a->get_build() == 4294967295);
     327           1 :             CATCH_REQUIRE(a->next(4));
     328           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1 -1 +1, back to original
     329             :         }
     330             : 
     331             :         {
     332           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     333           1 :             CATCH_REQUIRE(a->next(3));
     334           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2.1");
     335           1 :             CATCH_REQUIRE(a->previous(3));
     336           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     337           1 :             CATCH_REQUIRE(a->previous(3));
     338           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295");
     339           1 :             CATCH_REQUIRE(a->next(3));
     340           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     341             :         }
     342             : 
     343             :         {
     344           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     345           1 :             CATCH_REQUIRE(a->next(2));
     346           1 :             CATCH_REQUIRE(a->get_version() == "1.3.3");
     347           1 :             CATCH_REQUIRE(a->previous(2));
     348           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     349           1 :             CATCH_REQUIRE(a->previous(2));
     350           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1");
     351           1 :             CATCH_REQUIRE(a->next(2));
     352           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     353             :         }
     354             : 
     355             :         {
     356           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     357           1 :             CATCH_REQUIRE(a->next(1));
     358           1 :             CATCH_REQUIRE(a->get_version() == "1.4");
     359           1 :             CATCH_REQUIRE(a->previous(1));
     360           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     361           1 :             CATCH_REQUIRE(a->previous(1));
     362           1 :             CATCH_REQUIRE(a->get_version() == "1.2");
     363           1 :             CATCH_REQUIRE(a->next(1));
     364           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     365             :         }
     366             : 
     367             :         {
     368           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     369           1 :             CATCH_REQUIRE(a->next(0));
     370           1 :             CATCH_REQUIRE(a->get_version() == "2.0");
     371           1 :             CATCH_REQUIRE(a->previous(0));
     372           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     373           1 :             CATCH_REQUIRE(a->previous(0));
     374           1 :             CATCH_REQUIRE(a->get_version() == "0.0");
     375           1 :             CATCH_REQUIRE(a->next(0));
     376           1 :             CATCH_REQUIRE(a->get_version() == "1.0");
     377             :         }
     378             :     }
     379             :     CATCH_END_SECTION()
     380             : 
     381          10 :     CATCH_START_SECTION("next_previous_rpm_versions: next/previous with letters")
     382             :     {
     383             :         {
     384           2 :             versiontheca::versiontheca::pointer_t a(create("1.3.2"));
     385           2 :             versiontheca::versiontheca::pointer_t f(create("9.9.9z.9"));
     386           1 :             CATCH_REQUIRE(a->size() == 3); // 3 on creation
     387           1 :             CATCH_REQUIRE(a->get_major() == 1);
     388           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     389           1 :             CATCH_REQUIRE(a->get_patch() == 2);
     390           1 :             CATCH_REQUIRE(a->get_build() == 0);
     391           1 :             CATCH_REQUIRE(f->size() == 5); // 5 on creation (and we do not chnage that one)
     392           1 :             a->set_format(*f);
     393           1 :             CATCH_REQUIRE(a->next(4));
     394           1 :             CATCH_REQUIRE(a->size() == 5); // now it's 5
     395           1 :             CATCH_REQUIRE(a->get_major() == 1);
     396           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     397           1 :             CATCH_REQUIRE(a->get_patch() == 2);
     398           1 :             CATCH_REQUIRE(a->get_build() == 0); // strings returned as 0
     399           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.1");
     400           1 :             CATCH_REQUIRE(a->next(4));
     401           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.2");
     402           1 :             CATCH_REQUIRE(a->next(4));
     403           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.3");
     404           1 :             CATCH_REQUIRE(a->next(4));
     405           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.4");
     406           1 :             CATCH_REQUIRE(a->next(4));
     407           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.5");
     408           1 :             CATCH_REQUIRE(a->next(4));
     409           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.6");
     410           1 :             CATCH_REQUIRE(a->next(4));
     411           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.7");
     412           1 :             CATCH_REQUIRE(a->next(4));
     413           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.8");
     414           1 :             CATCH_REQUIRE(a->next(4));
     415           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.9");
     416           1 :             CATCH_REQUIRE(a->next(4));
     417           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2B");
     418           1 :             CATCH_REQUIRE(a->size() == 4); // we delete zeroes
     419           1 :             CATCH_REQUIRE(a->previous(4));
     420           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.9");
     421           1 :             CATCH_REQUIRE(a->previous(4));
     422           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.8");
     423           1 :             CATCH_REQUIRE(a->previous(4));
     424           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.7");
     425           1 :             CATCH_REQUIRE(a->previous(4));
     426           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.6");
     427           1 :             CATCH_REQUIRE(a->previous(4));
     428           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.5");
     429           1 :             CATCH_REQUIRE(a->previous(4));
     430           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.4");
     431           1 :             CATCH_REQUIRE(a->previous(4));
     432           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.3");
     433           1 :             CATCH_REQUIRE(a->previous(4));
     434           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.2");
     435           1 :             CATCH_REQUIRE(a->previous(4));
     436           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2A.1");
     437           1 :             CATCH_REQUIRE(a->previous(4));
     438           1 :             CATCH_REQUIRE(a->get_version() == "1.3.2");
     439           1 :             CATCH_REQUIRE(a->size() == 3); // we delete zeroes
     440           1 :             CATCH_REQUIRE(a->previous(4));
     441           1 :             CATCH_REQUIRE(a->size() == 5);
     442           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1z.9");
     443           1 :             CATCH_REQUIRE(a->previous(4));
     444           1 :             CATCH_REQUIRE(a->get_version() == "1.3.1z.8");
     445           1 :             CATCH_REQUIRE(a->get_major() == 1);
     446           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     447           1 :             CATCH_REQUIRE(a->get_patch() == 1);
     448           1 :             CATCH_REQUIRE(a->get_build() == 0); // strings returned as 0
     449             :         }
     450             : 
     451             :         {
     452           2 :             versiontheca::versiontheca::pointer_t a(create("1.3C"));
     453           2 :             versiontheca::versiontheca::pointer_t f(create("9.9"));
     454           1 :             CATCH_REQUIRE(a->size() == 3); // 3 on creation
     455           1 :             CATCH_REQUIRE(a->get_major() == 1);
     456           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     457           1 :             CATCH_REQUIRE(a->get_patch() == 0);
     458           1 :             CATCH_REQUIRE(a->get_build() == 0);
     459           1 :             CATCH_REQUIRE(f->size() == 2);
     460           1 :             a->set_format(*f);
     461           1 :             CATCH_REQUIRE(a->previous(2));
     462           1 :             CATCH_REQUIRE(a->get_version() == "1.3B");
     463           1 :             CATCH_REQUIRE(a->previous(2));
     464           1 :             CATCH_REQUIRE(a->get_version() == "1.3");
     465           1 :             CATCH_REQUIRE(a->size() == 2);
     466           1 :             CATCH_REQUIRE(a->previous(2));
     467           1 :             CATCH_REQUIRE(a->get_version() == "1.2.4294967295");
     468             :         }
     469             : 
     470             :         {
     471           2 :             versiontheca::versiontheca::pointer_t a(create("1.3A", "1.3"));
     472           2 :             versiontheca::versiontheca::pointer_t f(create("9.9"));
     473           1 :             CATCH_REQUIRE(a->size() == 3); // 3 on creation
     474           1 :             CATCH_REQUIRE(a->get_major() == 1);
     475           1 :             CATCH_REQUIRE(a->get_minor() == 3);
     476           1 :             CATCH_REQUIRE(a->get_patch() == 0);
     477           1 :             CATCH_REQUIRE(a->get_build() == 0);
     478           1 :             CATCH_REQUIRE(f->size() == 2);
     479           1 :             a->set_format(*f);
     480           1 :             CATCH_REQUIRE(a->previous(2));
     481           1 :             CATCH_REQUIRE(a->get_version() == "1.2z");
     482           1 :             CATCH_REQUIRE(a->previous(2));
     483           1 :             CATCH_REQUIRE(a->get_version() == "1.2y");
     484             :         }
     485             :     }
     486             :     CATCH_END_SECTION()
     487             : 
     488          10 :     CATCH_START_SECTION("next_previous_rpm_versions: next/previous with epoch")
     489             :     {
     490           2 :         versiontheca::versiontheca::pointer_t a(create("75:1.5.3"));
     491           1 :         CATCH_REQUIRE(a->size() == 4);
     492           1 :         CATCH_REQUIRE(a->next(2));
     493           1 :         CATCH_REQUIRE(a->get_version() == "75:1.5.4");
     494           1 :         CATCH_REQUIRE(a->previous(2));
     495           1 :         CATCH_REQUIRE(a->get_version() == "75:1.5.3");
     496           1 :         CATCH_REQUIRE(a->previous(2));
     497           1 :         CATCH_REQUIRE(a->get_version() == "75:1.5.2");
     498           1 :         CATCH_REQUIRE(a->next(2));
     499           1 :         CATCH_REQUIRE(a->get_version() == "75:1.5.3");
     500             :     }
     501             :     CATCH_END_SECTION()
     502             : 
     503          10 :     CATCH_START_SECTION("next_previous_rpm_versions: next/previous with release")
     504             :     {
     505           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3-r5"));
     506           1 :         CATCH_REQUIRE(a->next(2));
     507           1 :         CATCH_REQUIRE(a->get_version() == "1.5.4-r5");
     508           1 :         CATCH_REQUIRE(a->previous(2));
     509           1 :         CATCH_REQUIRE(a->get_version() == "1.5.3-r5");
     510           1 :         CATCH_REQUIRE(a->previous(2));
     511           1 :         CATCH_REQUIRE(a->get_version() == "1.5.2-r5");
     512           1 :         CATCH_REQUIRE(a->next(2));
     513           1 :         CATCH_REQUIRE(a->get_version() == "1.5.3-r5");
     514             :     }
     515             :     CATCH_END_SECTION()
     516             : 
     517          10 :     CATCH_START_SECTION("next_previous_rpm_versions: previous/next with release")
     518             :     {
     519           2 :         versiontheca::versiontheca::pointer_t a(create("5:1.5.3-r5"));
     520           1 :         CATCH_REQUIRE(a->previous(4));
     521           1 :         CATCH_REQUIRE(a->get_version() == "5:1.5.2.4294967295.4294967295-r5");
     522           1 :         CATCH_REQUIRE(a->next(4));
     523           1 :         CATCH_REQUIRE(a->get_version() == "5:1.5.3-r5");
     524           1 :         CATCH_REQUIRE(a->next(4));
     525           1 :         CATCH_REQUIRE(a->get_version() == "5:1.5.3.0.1-r5");
     526           1 :         CATCH_REQUIRE(a->previous(4));
     527           1 :         CATCH_REQUIRE(a->get_version() == "5:1.5.3-r5");
     528             :     }
     529             :     CATCH_END_SECTION()
     530           5 : }
     531             : 
     532             : 
     533           5 : CATCH_TEST_CASE("compare_rpm_versions", "[valid][compare]")
     534             : {
     535           6 :     CATCH_START_SECTION("compare_rpm_versions: compare many versions")
     536             :     {
     537           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
     538           2 :         versiontheca::versiontheca::pointer_t b(create("1.1"));
     539           2 :         versiontheca::versiontheca::pointer_t c(create("1.2.0.0", "1.2"));  // the zero are ignored by the compare
     540           2 :         versiontheca::versiontheca::pointer_t d(create("1:1.1"));
     541           2 :         versiontheca::versiontheca::pointer_t e(create("1.1-rc1"));
     542           2 :         versiontheca::versiontheca::pointer_t f(create("1.1-rc2"));
     543           2 :         versiontheca::versiontheca::pointer_t g(create("1.1-alpha"));
     544           2 :         versiontheca::versiontheca::pointer_t h(create("1.1~before"));
     545           2 :         versiontheca::versiontheca::pointer_t i(create("1.1-_rc1"));
     546           2 :         versiontheca::versiontheca::pointer_t j(create("1.1-rc1_"));
     547           2 :         versiontheca::versiontheca::pointer_t k(create("1.1q"));
     548           2 :         versiontheca::versiontheca::pointer_t l(create("1.1f"));
     549           2 :         versiontheca::versiontheca::pointer_t m(create("1.1.5"));
     550             : 
     551           1 :         CATCH_REQUIRE(a->is_valid());
     552           1 :         CATCH_REQUIRE(b->is_valid());
     553           1 :         CATCH_REQUIRE(c->is_valid());
     554           1 :         CATCH_REQUIRE(d->is_valid());
     555           1 :         CATCH_REQUIRE(e->is_valid());
     556           1 :         CATCH_REQUIRE(f->is_valid());
     557           1 :         CATCH_REQUIRE(g->is_valid());
     558           1 :         CATCH_REQUIRE(h->is_valid());
     559           1 :         CATCH_REQUIRE(i->is_valid());
     560           1 :         CATCH_REQUIRE(j->is_valid());
     561           1 :         CATCH_REQUIRE(k->is_valid());
     562           1 :         CATCH_REQUIRE(l->is_valid());
     563           1 :         CATCH_REQUIRE(m->is_valid());
     564             : 
     565           1 :         CATCH_REQUIRE(*a == *a);
     566           1 :         CATCH_REQUIRE_FALSE(*a != *a);
     567           1 :         CATCH_REQUIRE_FALSE(*a > *a);
     568           1 :         CATCH_REQUIRE(*a >= *a);
     569           1 :         CATCH_REQUIRE_FALSE(*a < *a);
     570           1 :         CATCH_REQUIRE(*a <= *a);
     571             : 
     572           1 :         CATCH_REQUIRE_FALSE(*a == *b);
     573           1 :         CATCH_REQUIRE(*a != *b);
     574           1 :         CATCH_REQUIRE(*a > *b);
     575           1 :         CATCH_REQUIRE(*a >= *b);
     576           1 :         CATCH_REQUIRE_FALSE(*a < *b);
     577           1 :         CATCH_REQUIRE_FALSE(*a <= *b);
     578             : 
     579           1 :         CATCH_REQUIRE_FALSE(*b == *a);
     580           1 :         CATCH_REQUIRE(*b != *a);
     581           1 :         CATCH_REQUIRE_FALSE(*b > *a);
     582           1 :         CATCH_REQUIRE_FALSE(*b >= *a);
     583           1 :         CATCH_REQUIRE(*b < *a);
     584           1 :         CATCH_REQUIRE(*b <= *a);
     585             : 
     586           1 :         CATCH_REQUIRE(*a == *c);
     587           1 :         CATCH_REQUIRE_FALSE(*a != *c);
     588           1 :         CATCH_REQUIRE_FALSE(*a > *c);
     589           1 :         CATCH_REQUIRE(*a >= *c);
     590           1 :         CATCH_REQUIRE_FALSE(*a < *c);
     591           1 :         CATCH_REQUIRE(*a <= *c);
     592             : 
     593           1 :         CATCH_REQUIRE(*c == *a);
     594           1 :         CATCH_REQUIRE_FALSE(*c != *a);
     595           1 :         CATCH_REQUIRE_FALSE(*c > *a);
     596           1 :         CATCH_REQUIRE(*c >= *a);
     597           1 :         CATCH_REQUIRE_FALSE(*c < *a);
     598           1 :         CATCH_REQUIRE(*c <= *a);
     599             : 
     600           1 :         CATCH_REQUIRE_FALSE(*a == *d);
     601           1 :         CATCH_REQUIRE(*a != *d);
     602           1 :         CATCH_REQUIRE_FALSE(*a > *d);
     603           1 :         CATCH_REQUIRE_FALSE(*a >= *d);
     604           1 :         CATCH_REQUIRE(*a < *d);
     605           1 :         CATCH_REQUIRE(*a <= *d);
     606             : 
     607           1 :         CATCH_REQUIRE_FALSE(*d == *a);
     608           1 :         CATCH_REQUIRE(*d != *a);
     609           1 :         CATCH_REQUIRE(*d > *a);
     610           1 :         CATCH_REQUIRE(*d >= *a);
     611           1 :         CATCH_REQUIRE_FALSE(*d < *a);
     612           1 :         CATCH_REQUIRE_FALSE(*d <= *a);
     613             : 
     614           1 :         CATCH_REQUIRE_FALSE(*b == *d);
     615           1 :         CATCH_REQUIRE(*b != *d);
     616           1 :         CATCH_REQUIRE_FALSE(*b > *d);
     617           1 :         CATCH_REQUIRE_FALSE(*b >= *d);
     618           1 :         CATCH_REQUIRE(*b < *d);
     619           1 :         CATCH_REQUIRE(*b <= *d);
     620             : 
     621           1 :         CATCH_REQUIRE(*e == *e);
     622           1 :         CATCH_REQUIRE_FALSE(*e != *e);
     623           1 :         CATCH_REQUIRE_FALSE(*e > *e);
     624           1 :         CATCH_REQUIRE(*e >= *e);
     625           1 :         CATCH_REQUIRE_FALSE(*e < *e);
     626           1 :         CATCH_REQUIRE(*e <= *e);
     627             : 
     628           1 :         CATCH_REQUIRE_FALSE(*b == *e);
     629           1 :         CATCH_REQUIRE(*b != *e);
     630           1 :         CATCH_REQUIRE_FALSE(*b > *e);
     631           1 :         CATCH_REQUIRE_FALSE(*b >= *e);
     632           1 :         CATCH_REQUIRE(*b < *e);
     633           1 :         CATCH_REQUIRE(*b <= *e);
     634             : 
     635           1 :         CATCH_REQUIRE_FALSE(*e == *f);
     636           1 :         CATCH_REQUIRE(*e != *f);
     637           1 :         CATCH_REQUIRE_FALSE(*e > *f);
     638           1 :         CATCH_REQUIRE_FALSE(*e >= *f);
     639           1 :         CATCH_REQUIRE(*e < *f);
     640           1 :         CATCH_REQUIRE(*e <= *f);
     641             : 
     642           1 :         CATCH_REQUIRE(*g < *e);
     643           1 :         CATCH_REQUIRE(*g < *f);
     644             : 
     645           1 :         CATCH_REQUIRE_FALSE(*b == *h);
     646           1 :         CATCH_REQUIRE(*b != *h);
     647           1 :         CATCH_REQUIRE(*b > *h);
     648           1 :         CATCH_REQUIRE(*b >= *h);
     649           1 :         CATCH_REQUIRE_FALSE(*b < *h);
     650           1 :         CATCH_REQUIRE_FALSE(*b <= *h);
     651             : 
     652           1 :         CATCH_REQUIRE(*e == *i);
     653           1 :         CATCH_REQUIRE_FALSE(*e != *i);
     654           1 :         CATCH_REQUIRE_FALSE(*e > *i);
     655           1 :         CATCH_REQUIRE(*e >= *i);
     656           1 :         CATCH_REQUIRE_FALSE(*e < *i);
     657           1 :         CATCH_REQUIRE(*e <= *i);
     658             : 
     659           1 :         CATCH_REQUIRE(*i == *e);
     660           1 :         CATCH_REQUIRE_FALSE(*i != *e);
     661           1 :         CATCH_REQUIRE_FALSE(*i > *e);
     662           1 :         CATCH_REQUIRE(*i >= *e);
     663           1 :         CATCH_REQUIRE_FALSE(*i < *e);
     664           1 :         CATCH_REQUIRE(*i <= *e);
     665             : 
     666           1 :         CATCH_REQUIRE(*e == *j);
     667           1 :         CATCH_REQUIRE_FALSE(*e != *j);
     668           1 :         CATCH_REQUIRE_FALSE(*e > *j);
     669           1 :         CATCH_REQUIRE(*e >= *j);
     670           1 :         CATCH_REQUIRE_FALSE(*e < *j);
     671           1 :         CATCH_REQUIRE(*e <= *j);
     672             : 
     673           1 :         CATCH_REQUIRE(*j == *e);
     674           1 :         CATCH_REQUIRE_FALSE(*j != *e);
     675           1 :         CATCH_REQUIRE_FALSE(*j > *e);
     676           1 :         CATCH_REQUIRE(*j >= *e);
     677           1 :         CATCH_REQUIRE_FALSE(*j < *e);
     678           1 :         CATCH_REQUIRE(*j <= *e);
     679             : 
     680           1 :         CATCH_REQUIRE(*i == *j);
     681           1 :         CATCH_REQUIRE_FALSE(*i != *j);
     682           1 :         CATCH_REQUIRE_FALSE(*i > *j);
     683           1 :         CATCH_REQUIRE(*i >= *j);
     684           1 :         CATCH_REQUIRE_FALSE(*i < *j);
     685           1 :         CATCH_REQUIRE(*i <= *j);
     686             : 
     687           1 :         CATCH_REQUIRE(*j == *i);
     688           1 :         CATCH_REQUIRE_FALSE(*j != *i);
     689           1 :         CATCH_REQUIRE_FALSE(*j > *i);
     690           1 :         CATCH_REQUIRE(*j >= *i);
     691           1 :         CATCH_REQUIRE_FALSE(*j < *i);
     692           1 :         CATCH_REQUIRE(*j <= *i);
     693             : 
     694           1 :         CATCH_REQUIRE(*k > *l);
     695           1 :         CATCH_REQUIRE(*l < *k);
     696           1 :         CATCH_REQUIRE(*c > *k);
     697           1 :         CATCH_REQUIRE(*c > *l);
     698           1 :         CATCH_REQUIRE(*k < *c);
     699           1 :         CATCH_REQUIRE(*l < *c);
     700           1 :         CATCH_REQUIRE(*m > *k);
     701           1 :         CATCH_REQUIRE(*m > *l);
     702           1 :         CATCH_REQUIRE(*k < *m);
     703           1 :         CATCH_REQUIRE(*l < *m);
     704             : 
     705             :         {
     706           2 :             std::stringstream ss;
     707           1 :             ss << *a;
     708           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     709             :         }
     710             :         {
     711           2 :             std::stringstream ss;
     712           1 :             ss << *b;
     713           1 :             CATCH_REQUIRE(ss.str() == "1.1");
     714             :         }
     715             :         {
     716           2 :             std::stringstream ss;
     717           1 :             ss << *c;
     718           1 :             CATCH_REQUIRE(ss.str() == "1.2");
     719             :         }
     720             :         {
     721           2 :             std::stringstream ss;
     722           1 :             ss << *d;
     723           1 :             CATCH_REQUIRE(ss.str() == "1:1.1");
     724             :         }
     725             :         {
     726           2 :             std::stringstream ss;
     727           1 :             ss << *e;
     728           1 :             CATCH_REQUIRE(ss.str() == "1.1-rc1");
     729             :         }
     730             :         {
     731           2 :             std::stringstream ss;
     732           1 :             ss << *f;
     733           1 :             CATCH_REQUIRE(ss.str() == "1.1-rc2");
     734             :         }
     735             :         {
     736           2 :             std::stringstream ss;
     737           1 :             ss << *g;
     738           1 :             CATCH_REQUIRE(ss.str() == "1.1-alpha");
     739             :         }
     740             :         {
     741           2 :             std::stringstream ss;
     742           1 :             ss << *h;
     743           1 :             CATCH_REQUIRE(ss.str() == "1.1~before");
     744             :         }
     745             :         {
     746           2 :             std::stringstream ss;
     747           1 :             ss << *i;
     748           1 :             CATCH_REQUIRE(ss.str() == "1.1-_rc1");
     749             :         }
     750             :     }
     751             :     CATCH_END_SECTION()
     752             : 
     753           6 :     CATCH_START_SECTION("compare_rpm_versions: compare rpm vs basic versions")
     754             :     {
     755           2 :         versiontheca::rpm::pointer_t d(std::make_shared<versiontheca::rpm>());
     756           2 :         versiontheca::versiontheca dv(d, "1.2.5");
     757           2 :         versiontheca::basic::pointer_t b(std::make_shared<versiontheca::basic>());
     758           2 :         versiontheca::versiontheca bv(b, "1.2.4");
     759             : 
     760           1 :         CATCH_REQUIRE(dv.is_valid());
     761           1 :         CATCH_REQUIRE(bv.is_valid());
     762             : 
     763           1 :         CATCH_REQUIRE_FALSE(dv == bv);
     764           1 :         CATCH_REQUIRE(dv != bv);
     765           1 :         CATCH_REQUIRE(dv > bv);
     766           1 :         CATCH_REQUIRE(dv >= bv);
     767           1 :         CATCH_REQUIRE_FALSE(dv < bv);
     768           1 :         CATCH_REQUIRE_FALSE(dv <= bv);
     769             :     }
     770             :     CATCH_END_SECTION()
     771             : 
     772           6 :     CATCH_START_SECTION("compare_rpm_versions: verify case sensitivity")
     773             :     {
     774           2 :         versiontheca::versiontheca::pointer_t a(create("53A2z"));
     775           2 :         versiontheca::versiontheca::pointer_t b(create("53a2z"));
     776             : 
     777           1 :         CATCH_REQUIRE(*a < *b);
     778             : 
     779           1 :         CATCH_REQUIRE(a->get_major() == 53);
     780           1 :         CATCH_REQUIRE(a->get_minor() == 0);
     781           1 :         CATCH_REQUIRE(a->get_patch() == 2);
     782           1 :         CATCH_REQUIRE(a->get_build() == 0);
     783             : 
     784           1 :         a = create("53.2z");
     785           1 :         b = create("53.2Z");
     786             : 
     787           1 :         CATCH_REQUIRE(*a > *b);
     788             : 
     789           1 :         CATCH_REQUIRE(a->get_major() == 53);
     790           1 :         CATCH_REQUIRE(a->get_minor() == 2);
     791           1 :         CATCH_REQUIRE(a->get_patch() == 0);
     792           1 :         CATCH_REQUIRE(a->get_build() == 0);
     793             :     }
     794             :     CATCH_END_SECTION()
     795           3 : }
     796             : 
     797             : 
     798           9 : CATCH_TEST_CASE("invalid_rpm_versions", "[invalid]")
     799             : {
     800          14 :     CATCH_START_SECTION("invalid_rpm_versions: empty")
     801             :     {
     802             :         // empty
     803             :         //
     804             :         // note: the empty version is "invalid" as far as versions go,
     805             :         //       but it does not generetate an error message
     806             :         //
     807             :         //       -- the check_version() cannot be used here because ""
     808             :         //          is the empty string and that means a valid version
     809             :         //
     810           2 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     811           2 :         versiontheca::versiontheca v(t, "");
     812           1 :         CATCH_REQUIRE_FALSE(v.is_valid());
     813           1 :         CATCH_REQUIRE(v.get_last_error().empty());
     814             : 
     815           1 :         CATCH_REQUIRE(v.get_version().empty());
     816           1 :         CATCH_REQUIRE(v.get_last_error() == "no parts to output.");
     817             :     }
     818             :     CATCH_END_SECTION()
     819             : 
     820          14 :     CATCH_START_SECTION("invalid_rpm_versions: various invalid epoch")
     821             :     {
     822             :         // epoch
     823             :         //
     824           1 :         check_version("3A3:1.2.3-pre55", "epoch must be a valid integer.");
     825           1 :         check_version("33:-55", "a version value cannot be an empty string.");
     826           1 :         check_version(":", "position of ':' and/or '-' is invalid in \":\".");
     827           1 :         check_version("a:", "epoch must be a valid integer.");
     828           1 :         check_version("-10:", "position of ':' and/or '-' is invalid in \"-10:\".");
     829           1 :         check_version("99999999999999999:", "integer too large for a valid version.");
     830           1 :         check_version("3:", "a version value cannot be an empty string.");
     831           1 :         check_version("-751", "position of ':' and/or '-' is invalid in \"-751\".");
     832             :     }
     833             :     CATCH_END_SECTION()
     834             : 
     835          14 :     CATCH_START_SECTION("invalid_rpm_versions: revision")
     836             :     {
     837             :         // revision
     838           1 :         check_version("-", "position of ':' and/or '-' is invalid in \"-\".");
     839           1 :         check_version("--", "found unexpected character: \\U00002D in input.");
     840           1 :         check_version("+-", "a version value cannot be an empty string.");
     841           1 :         check_version("#-", "found unexpected character: \\U000023 in input.");
     842           1 :         check_version("55:435123-", "a version value cannot be an empty string.");
     843           1 :         check_version("-a", "position of ':' and/or '-' is invalid in \"-a\".");
     844           1 :         check_version("-0", "position of ':' and/or '-' is invalid in \"-0\".");
     845           1 :         check_version("-+", "position of ':' and/or '-' is invalid in \"-+\".");
     846           1 :         check_version("-3$7", "position of ':' and/or '-' is invalid in \"-3$7\".");
     847           1 :         check_version("32:1.2.55-3:7", "found unexpected character: \\U00003A in input.");
     848           1 :         check_version("-3.7", "position of ':' and/or '-' is invalid in \"-3.7\".");
     849             :     }
     850             :     CATCH_END_SECTION()
     851             : 
     852          14 :     CATCH_START_SECTION("invalid_rpm_versions: version")
     853             :     {
     854             :         // version
     855             :         //
     856           1 :         check_version("3.7#", "found unexpected character: \\U000023 in input.");
     857           1 :         check_version("3$7", "found unexpected character: \\U000024 in input.");
     858           1 :         check_version("3;7", "found unexpected character: \\U00003B in input.");
     859             :     }
     860             :     CATCH_END_SECTION()
     861             : 
     862          14 :     CATCH_START_SECTION("invalid_rpm_versions: randomized invalid characters")
     863             :     {
     864             :         // do another loop for some random unicode characters
     865             :         //
     866         128 :         for(int i(1); i < 128; ++i)
     867             :         {
     868         127 :             char c(static_cast<char>(i));
     869         127 :             if(strchr(g_valid_alphanum, c) != nullptr || c == '+')
     870             :             {
     871             :                 // skip all valid characters
     872             :                 //
     873          69 :                 continue;
     874             :             }
     875             :             // TODO: the following loop is really complex
     876             :             //       I want to revamp with (1) a rand() that defines which
     877             :             //       parts to generate (0, 1, 2, or 3 -- if bit 0, add epoch,
     878             :             //       if bit 1, add a release -- always have an upstream version)
     879             :             //
     880         116 :             std::string v;
     881          58 :             std::size_t bad_at(1000);
     882          58 :             bool has_release(false);
     883          58 :             if(rand() % 10 == 0)
     884             :             {
     885          20 :                 std::stringstream ss;
     886          10 :                 ss << rand() << ':';
     887          10 :                 v += ss.str();
     888             :             }
     889         754 :             for(int j(0); j < 12; ++j)
     890             :             {
     891         696 :                 if(j == 6)
     892             :                 {
     893             :                     // add the spurious character now
     894             :                     //
     895          58 :                     bad_at = v.length();
     896          58 :                     v += c;
     897             :                 }
     898             :                 char vc;
     899          33 :                 do
     900             :                 {
     901         729 :                     vc = g_valid_alphanum[rand() % g_valid_alphanum_length];
     902             :                 }
     903         676 :                 while(((has_release || v.empty() || v.back() == ':') && vc == '-')
     904         728 :                    || (v.empty() && vc == '.')
     905         727 :                    || vc == ':'
     906         719 :                    || vc == '^'
     907        1436 :                    || vc == '~');
     908         696 :                 if(vc == '-')
     909             :                 {
     910          11 :                     has_release = true;
     911             :                 }
     912        1392 :                 if(!v.empty()
     913         648 :                 && v.back() == '.'
     914         707 :                 && (vc == '-' || vc == '.'))
     915             :                 {
     916           0 :                     v += 'N'; // add a nugget between '.' and '-'/'.'
     917             :                 }
     918         696 :                 v += vc;
     919             :             }
     920             : //std::cerr << "--- bad character is 0x" << static_cast<int>(c) << "\n";
     921         116 :             std::stringstream last_error;
     922          58 :             last_error << "found unexpected character: \\U"
     923          58 :                        << std::hex << std::uppercase << std::setfill('0')
     924          58 :                                    << std::setw(6) << static_cast<int>(c)
     925          58 :                        << " in input.";
     926             :             // check whether the bad character was inserted after the last dash
     927             :             {
     928          58 :                 std::string::size_type const p(v.find_last_of("-"));
     929          58 :                 if(p == std::string::npos)
     930             :                 {
     931          47 :                     check_version(v.c_str(), last_error.str());
     932             :                 }
     933             :                 else
     934             :                 {
     935          11 :                     if(p == v.length() - 1)
     936             :                     {
     937             :                         // avoid invalid (empty) revisions because that's not
     938             :                         // the purpose of this test
     939           2 :                         std::stringstream ss;
     940           1 :                         ss << rand() % 10;
     941           1 :                         v += ss.str();
     942             :                     }
     943          11 :                     if(p < bad_at)
     944             :                     {
     945             :                         // bad character ended up in the revision
     946           4 :                         check_version(v.c_str(), last_error.str());
     947             :                     }
     948             :                     else
     949             :                     {
     950           7 :                         if(v.find_first_of(':', p + 1) == std::string::npos)
     951             :                         {
     952           7 :                             check_version(v.c_str(), last_error.str());
     953             :                         }
     954             :                         else
     955             :                         {
     956             :                             // a revision does not accept a ':' character and since
     957             :                             // it is checked before the version we get that error
     958             :                             // instead instead of the version error...
     959           0 :                             check_version(v.c_str(), last_error.str());
     960             :                         }
     961             :                     }
     962             :                 }
     963             :             }
     964             :         }
     965             :     }
     966             :     CATCH_END_SECTION()
     967             : 
     968          14 :     CATCH_START_SECTION("invalid_rpm_versions: max + 1 fails")
     969             :     {
     970           2 :         versiontheca::versiontheca::pointer_t a(create("4294967295.4294967295.4294967295"));
     971           1 :         CATCH_REQUIRE(a->is_valid());
     972           1 :         CATCH_REQUIRE_FALSE(a->next(2));
     973           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     974           1 :         CATCH_REQUIRE(a->get_last_error() == "maximum limit reached; cannot increment version any further.");
     975             :     }
     976             :     CATCH_END_SECTION()
     977             : 
     978          14 :     CATCH_START_SECTION("invalid_rpm_versions: min - 1 fails")
     979             :     {
     980           2 :         versiontheca::versiontheca::pointer_t a(create("0.0"));
     981           1 :         CATCH_REQUIRE(a->is_valid());
     982           1 :         CATCH_REQUIRE_FALSE(a->previous(2));
     983           1 :         CATCH_REQUIRE_FALSE(a->is_valid());
     984           1 :         CATCH_REQUIRE(a->get_last_error() == "minimum limit reached; cannot decrement version any further.");
     985             :     }
     986             :     CATCH_END_SECTION()
     987           7 : }
     988             : 
     989             : 
     990          10 : CATCH_TEST_CASE("bad_rpm_calls", "[invalid]")
     991             : {
     992          16 :     CATCH_START_SECTION("bad_rpm_calls: next without a version")
     993             :     {
     994           2 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
     995           2 :         versiontheca::versiontheca v(t);
     996           1 :         CATCH_REQUIRE_FALSE(v.next(0));
     997           1 :         CATCH_REQUIRE(v.get_last_error() == "no parts in this RPM version; cannot compute upstream start/end.");
     998             :     }
     999             :     CATCH_END_SECTION()
    1000             : 
    1001          16 :     CATCH_START_SECTION("bad_rpm_calls: previous without a version")
    1002             :     {
    1003           2 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
    1004           2 :         versiontheca::versiontheca v(t);
    1005           1 :         CATCH_REQUIRE_FALSE(v.previous(0));
    1006           1 :         CATCH_REQUIRE(v.get_last_error() == "no parts in this RPM version; cannot compute upstream start/end.");
    1007             :     }
    1008             :     CATCH_END_SECTION()
    1009             : 
    1010          16 :     CATCH_START_SECTION("bad_rpm_calls: next out of bounds")
    1011             :     {
    1012           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3-r5"));
    1013         101 :         for(int p(-100); p < 0; ++p)
    1014             :         {
    1015         100 :             CATCH_REQUIRE_THROWS_MATCHES(
    1016             :                   a->next(p)
    1017             :                 , versiontheca::invalid_parameter
    1018             :                 , Catch::Matchers::ExceptionMessage(
    1019             :                           "versiontheca_exception: position calling next() cannot be a negative number."));
    1020             :         }
    1021         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
    1022             :         {
    1023         100 :             CATCH_REQUIRE_THROWS_MATCHES(
    1024             :                   a->next(p)
    1025             :                 , versiontheca::invalid_parameter
    1026             :                 , Catch::Matchers::ExceptionMessage(
    1027             :                           "versiontheca_exception: position calling next() cannot be more than "
    1028             :                         + std::to_string(versiontheca::MAX_PARTS)
    1029             :                         + "."));
    1030             :         }
    1031             :     }
    1032             :     CATCH_END_SECTION()
    1033             : 
    1034          16 :     CATCH_START_SECTION("bad_rpm_calls: previous out of bounds")
    1035             :     {
    1036           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3-r5"));
    1037         101 :         for(int p(-100); p < 0; ++p)
    1038             :         {
    1039         100 :             CATCH_REQUIRE_THROWS_MATCHES(
    1040             :                   a->previous(p)
    1041             :                 , versiontheca::invalid_parameter
    1042             :                 , Catch::Matchers::ExceptionMessage(
    1043             :                           "versiontheca_exception: position calling previous() cannot be a negative number."));
    1044             :         }
    1045         101 :         for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
    1046             :         {
    1047         100 :             CATCH_REQUIRE_THROWS_MATCHES(
    1048             :                   a->previous(p)
    1049             :                 , versiontheca::invalid_parameter
    1050             :                 , Catch::Matchers::ExceptionMessage(
    1051             :                           "versiontheca_exception: position calling previous() cannot be more than "
    1052             :                         + std::to_string(versiontheca::MAX_PARTS)
    1053             :                         + "."));
    1054             :         }
    1055             :     }
    1056             :     CATCH_END_SECTION()
    1057             : 
    1058          16 :     CATCH_START_SECTION("bad_rpm_calls: resize out of bounds")
    1059             :     {
    1060           2 :         versiontheca::versiontheca::pointer_t a(create("1.5.3-r5"));
    1061         100 :         for(int p(versiontheca::MAX_PARTS + 1); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
    1062             :         {
    1063          99 :             CATCH_REQUIRE_THROWS_MATCHES(
    1064             :                   a->get_trait()->resize(p)
    1065             :                 , versiontheca::invalid_parameter
    1066             :                 , Catch::Matchers::ExceptionMessage(
    1067             :                           "versiontheca_exception: requested too many parts."));
    1068             :         }
    1069             :     }
    1070             :     CATCH_END_SECTION()
    1071             : 
    1072          16 :     CATCH_START_SECTION("bad_rpm_calls: next/erase out of bounds")
    1073             :     {
    1074           2 :         versiontheca::versiontheca::pointer_t a(create("103:1.2.3.4.5-r5with6many8release9parts"));
    1075           1 :         CATCH_REQUIRE(a->size() == 15);
    1076           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1077             :               a->next(15)  // too many because this checks total number while insert()-ing
    1078             :             , versiontheca::invalid_parameter
    1079             :             , Catch::Matchers::ExceptionMessage(
    1080             :                       "versiontheca_exception: trying to insert more parts when maximum was already reached."));
    1081           1 :         CATCH_REQUIRE(a->size() == 25);
    1082          11 :         for(int i(0); i < 10; ++i)
    1083             :         {
    1084          10 :             a->get_trait()->erase(15);
    1085             :         }
    1086           1 :         CATCH_REQUIRE(a->size() == 15);
    1087           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1088             :               a->get_trait()->erase(15)  // we have 15 left, trying to delete more will fail
    1089             :             , versiontheca::invalid_parameter
    1090             :             , Catch::Matchers::ExceptionMessage(
    1091             :                       "versiontheca_exception: trying to erase a non-existant part."));
    1092          31 :         while(a->size() > 0)
    1093             :         {
    1094          15 :             a->get_trait()->resize(a->size() - 1);
    1095             :         }
    1096             :     }
    1097             :     CATCH_END_SECTION()
    1098             : 
    1099          16 :     CATCH_START_SECTION("bad_rpm_calls: compare against an empty (invalid) version")
    1100             :     {
    1101           2 :         versiontheca::versiontheca::pointer_t a(create("1.2"));
    1102           2 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
    1103           2 :         versiontheca::versiontheca empty(t, "");
    1104             : 
    1105           1 :         CATCH_REQUIRE(a->is_valid());
    1106           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
    1107             : 
    1108           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1109             :               a->compare(empty)
    1110             :             , versiontheca::invalid_version
    1111             :             , Catch::Matchers::ExceptionMessage(
    1112             :                       "versiontheca_exception: one or both of the input versions are not valid."));
    1113             : 
    1114           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1115             :               a->get_trait()->compare(t)
    1116             :             , versiontheca::empty_version
    1117             :             , Catch::Matchers::ExceptionMessage(
    1118             :                       "versiontheca_exception: one or both of the input versions are empty."));
    1119             :     }
    1120             :     CATCH_END_SECTION()
    1121             : 
    1122          16 :     CATCH_START_SECTION("bad_rpm_calls: compare using an empty (invalid) version")
    1123             :     {
    1124           2 :         versiontheca::rpm::pointer_t t(std::make_shared<versiontheca::rpm>());
    1125           2 :         versiontheca::versiontheca empty(t, "");
    1126           2 :         versiontheca::versiontheca::pointer_t b(create("5.3"));
    1127             : 
    1128           1 :         CATCH_REQUIRE_FALSE(empty.is_valid());
    1129           1 :         CATCH_REQUIRE(b->is_valid());
    1130             : 
    1131           1 :         CATCH_REQUIRE(empty.get_major() == 0);
    1132           1 :         CATCH_REQUIRE(empty.get_minor() == 0);
    1133           1 :         CATCH_REQUIRE(empty.get_patch() == 0);
    1134           1 :         CATCH_REQUIRE(empty.get_build() == 0);
    1135             : 
    1136           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1137             :               empty.compare(*b)
    1138             :             , versiontheca::invalid_version
    1139             :             , Catch::Matchers::ExceptionMessage(
    1140             :                       "versiontheca_exception: one or both of the input versions are not valid."));
    1141             : 
    1142           1 :         CATCH_REQUIRE_THROWS_MATCHES(
    1143             :               t->compare(b->get_trait())
    1144             :             , versiontheca::empty_version
    1145             :             , Catch::Matchers::ExceptionMessage(
    1146             :                       "versiontheca_exception: one or both of the input versions are empty."));
    1147             :     }
    1148             :     CATCH_END_SECTION()
    1149          14 : }
    1150             : 
    1151             : 
    1152             : 
    1153             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13