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

          Line data    Source code
       1             : // Copyright (c) 2023  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/versiontheca
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software: you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation, either version 3 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program.  If not, see <https://www.gnu.org/licenses/>.
      18             : 
      19             : // tested file
      20             : //
      21             : #include    "versiontheca/roman.h"
      22             : 
      23             : 
      24             : // self
      25             : //
      26             : #include    "catch_main.h"
      27             : 
      28             : 
      29             : 
      30             : // versiontheca
      31             : //
      32             : #include    "versiontheca/exception.h"
      33             : #include    "versiontheca/versiontheca.h"
      34             : 
      35             : 
      36             : // libutf8
      37             : //
      38             : #include    "libutf8/libutf8.h"
      39             : 
      40             : 
      41             : // C++
      42             : //
      43             : #include    <cstring>
      44             : #include    <iomanip>
      45             : #include    <stdexcept>
      46             : 
      47             : 
      48             : 
      49             : 
      50             : namespace
      51             : {
      52             : 
      53             : 
      54             : 
      55        4013 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
      56             : {
      57        8026 :     versiontheca::roman::pointer_t t(std::make_shared<versiontheca::roman>());
      58        4013 :     versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
      59        4013 :     if(verify == nullptr)
      60             :     {
      61        4001 :         verify = version;
      62             :     }
      63        4013 :     CATCH_REQUIRE(v->get_version() == verify);
      64        8026 :     return v;
      65             : }
      66             : 
      67             : 
      68       11997 : std::string value_to_roman(int v)
      69             : {
      70       11997 :     std::string result;
      71             : 
      72       47999 :     for(; v >= 1'000; v -= 1'000)
      73             :     {
      74       18001 :         result += 'M';
      75             :     }
      76             : 
      77       11997 :     if(v >= 900)
      78             :     {
      79        1175 :         result += "CM";
      80        1175 :         v -= 900;
      81             :     }
      82             :     else
      83             :     {
      84       10822 :         if(v >= 500)
      85             :         {
      86        4751 :             result += "D";
      87        4751 :             v -= 500;
      88             :         }
      89        6071 :         else if(v >= 400) // v E [400 .. 500)
      90             :         {
      91        1192 :             result += "CD";
      92        1192 :             v -= 400;
      93             :         }
      94             : 
      95       39780 :         for(; v >= 100; v -= 100)
      96             :         {
      97       14479 :             result += 'C';
      98             :         }
      99             :     }
     100             : 
     101       11997 :     if(v >= 90)
     102             :     {
     103        1230 :         result += "XC";
     104        1230 :         v -= 90;
     105             :     }
     106             :     else
     107             :     {
     108       10767 :         if(v >= 50)
     109             :         {
     110        4844 :             result += 'L';
     111        4844 :             v -= 50;
     112             :         }
     113        5923 :         else if(v >= 40)
     114             :         {
     115        1178 :             result += "XL";
     116        1178 :             v -= 40;
     117             :         }
     118             : 
     119       39493 :         for(; v >= 10; v -= 10)
     120             :         {
     121       14363 :             result += 'X';
     122             :         }
     123             :     }
     124             : 
     125       11997 :     if(v == 9)
     126             :     {
     127        1212 :         result += "IX";
     128             :     }
     129             :     else
     130             :     {
     131       10785 :         if(v >= 5)
     132             :         {
     133        4819 :             result += 'V';
     134        4819 :             v -= 5;
     135             :         }
     136        5966 :         else if(v == 4)
     137             :         {
     138        1177 :             result += "IV";
     139        1177 :             v = 0;
     140             :         }
     141             : 
     142       39735 :         for(; v > 0; --v)
     143             :         {
     144       14475 :             result += 'I';
     145             :         }
     146             :     }
     147             : 
     148       11997 :     return result;
     149             : }
     150             : 
     151             : 
     152             : 
     153             : }
     154             : // no name namespace
     155             : 
     156             : 
     157           3 : CATCH_TEST_CASE("roman_numerals", "[roman][valid]")
     158             : {
     159           2 :     CATCH_START_SECTION("roman_numerals: verify roman number conversions")
     160             :     {
     161        4000 :         for(int i(1); i <= 3999; ++i)
     162             :         {
     163        7998 :             std::string const roman_number(value_to_roman(i));
     164        3999 :             CATCH_REQUIRE(versiontheca::from_roman_number(roman_number) == static_cast<versiontheca::part_integer_t>(i));
     165        3999 :             CATCH_REQUIRE(versiontheca::to_roman_number(i) == roman_number);
     166             :         }
     167             :     }
     168             :     CATCH_END_SECTION()
     169           1 : }
     170             : 
     171             : 
     172           6 : CATCH_TEST_CASE("roman_versions", "[roman][valid]")
     173             : {
     174           8 :     CATCH_START_SECTION("roman_versions: verify test checker for version 1.0 and i.0 and I.0")
     175             :     {
     176           1 :         create("1.0");
     177           1 :         create("i.0", "I.0");
     178           1 :         create("I.0");
     179             :     }
     180             :     CATCH_END_SECTION()
     181             : 
     182           8 :     CATCH_START_SECTION("roman_versions: verify numbers from 1 to 3999")
     183             :     {
     184        4000 :         for(int major_version(1); major_version <= 3999; ++major_version)
     185             :         {
     186             :             // use a random number for the minor
     187             :             //
     188        3999 :             int const minor_version(rand() % 3999 + 1);
     189        7998 :             std::string const major_number(value_to_roman(major_version));
     190        7998 :             std::string const minor_number(value_to_roman(minor_version));
     191        7998 :             std::string version(major_number + '.' + minor_number);
     192        7998 :             versiontheca::versiontheca::pointer_t v(create(version.c_str()));
     193        3999 :             CATCH_REQUIRE(v->get_major() == static_cast<versiontheca::part_integer_t>(major_version));
     194        3999 :             CATCH_REQUIRE(v->get_minor() == static_cast<versiontheca::part_integer_t>(minor_version));
     195             :         }
     196             :     }
     197             :     CATCH_END_SECTION()
     198             : 
     199           8 :     CATCH_START_SECTION("roman_versions: funny roman numerals")
     200             :     {
     201             :         // the numerals on the left are _possible_ but should not be used
     202             :         //
     203           1 :         create("I.IL", "I.XLIX");
     204           1 :         create("I.IC", "I.XCIX");
     205           1 :         create("I.vc", "I.XCV");
     206           1 :         create("I.ID", "I.CDXCIX");
     207           1 :         create("i.vd", "I.CDXCV");
     208           1 :         create("I.IM", "I.CMXCIX");
     209           1 :         create("IIII.A", "IV.A");
     210             : 
     211             :         // the following lot is really just circumstancial
     212             :         //
     213           1 :         create("I.LC", "I.L");
     214           1 :         create("i.llci", "I.I");
     215             :     }
     216             :     CATCH_END_SECTION()
     217             : 
     218           8 :     CATCH_START_SECTION("roman_versions: including other things")
     219             :     {
     220           2 :         versiontheca::versiontheca::pointer_t a(create("3.L.rc5", "3.L.rc5"));
     221           2 :         versiontheca::versiontheca::pointer_t b(create("3.XI.rc6", "3.XI.rc6"));
     222             : 
     223           1 :         CATCH_REQUIRE(*a > *b); // 50 > 11
     224             :     }
     225             :     CATCH_END_SECTION()
     226           4 : }
     227             : 
     228             : 
     229           5 : CATCH_TEST_CASE("roman_invalid", "[roman][invalid]")
     230             : {
     231           6 :     CATCH_START_SECTION("roman_invalid: verify test checker for version 1.0 and i.0 and I.0")
     232             :     {
     233           2 :         versiontheca::roman::pointer_t t(std::make_shared<versiontheca::roman>());
     234           2 :         versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, ""));
     235           1 :         CATCH_REQUIRE(v->get_last_error().empty());
     236           1 :         CATCH_REQUIRE(v->get_version().empty());
     237           1 :         CATCH_REQUIRE(v->get_last_error() == "no parts to output.");
     238           1 :         CATCH_REQUIRE(v->get_last_error().empty());
     239             :     }
     240             :     CATCH_END_SECTION()
     241             : 
     242           6 :     CATCH_START_SECTION("roman_invalid: two periods one after the other is not valid")
     243             :     {
     244           2 :         versiontheca::roman::pointer_t t(std::make_shared<versiontheca::roman>());
     245           2 :         versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, ""));
     246           1 :         CATCH_REQUIRE_FALSE(v->set_version("1..2"));
     247           1 :         CATCH_REQUIRE(v->get_last_error() == "a version value cannot be an empty string.");
     248           1 :         CATCH_REQUIRE(v->get_last_error().empty());
     249             :     }
     250             :     CATCH_END_SECTION()
     251             : 
     252           6 :     CATCH_START_SECTION("roman_invalid: invalid roman numbers")
     253             :     {
     254           1 :         CATCH_REQUIRE(versiontheca::to_roman_number(0).empty());
     255        1002 :         for(int n(4000); n <= 5000; ++n)
     256             :         {
     257        1001 :             CATCH_REQUIRE(versiontheca::to_roman_number(n).empty());
     258             :         }
     259             : 
     260           1 :         CATCH_REQUIRE(versiontheca::from_roman_number("") == 0);
     261             :     }
     262             :     CATCH_END_SECTION()
     263           9 : }
     264             : 
     265             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13