LCOV - code coverage report
Current view: top level - tests - catch_hexadecimal_string.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 123 138 89.1 %
Date: 2022-02-13 10:56:14 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2021-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/snapdev
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             : 
      20             : /** \file
      21             :  * \brief Verify that the hexadecimal convertions work.
      22             :  *
      23             :  * This file implements tests for the hexadecimal to binary and vice
      24             :  * versa functions.
      25             :  */
      26             : 
      27             : // self
      28             : //
      29             : #include    <snapdev/hexadecimal_string.h>
      30             : 
      31             : #include    "catch_main.h"
      32             : 
      33             : 
      34             : // C++ lib
      35             : //
      36             : #include    <iomanip>
      37             : #include    <set>
      38             : 
      39             : 
      40             : // last include
      41             : //
      42             : #include    <snapdev/poison.h>
      43             : 
      44             : 
      45             : 
      46             : namespace
      47             : {
      48             : 
      49             : 
      50             : 
      51             : // function stolen from the libutf8 library
      52             : //   see https://snapwebsites.org/project/libutf8
      53          26 : int wctombs(char * mb, char32_t wc, size_t len)
      54             : {
      55          52 :     auto verify_length = [&len](size_t required_len)
      56          26 :     {
      57          26 :         if(len < required_len)
      58             :         {
      59           0 :             throw std::logic_error("wctombs() called with an output buffer which is too small.");
      60             :         }
      61          52 :     };
      62             : 
      63          26 :     if(wc < 0x80)
      64             :     {
      65           0 :         verify_length(2);
      66             : 
      67             :         /* this will also encode '\0'... */
      68           0 :         mb[0] = static_cast<char>(wc);
      69           0 :         mb[1] = '\0';
      70           0 :         return 1;
      71             :     }
      72          26 :     if(wc < 0x800)
      73             :     {
      74           0 :         verify_length(3);
      75             : 
      76           0 :         mb[0] = static_cast<char>((wc >> 6) | 0xC0);
      77           0 :         mb[1] = (wc & 0x3F) | 0x80;
      78           0 :         mb[2] = '\0';
      79           0 :         return 2;
      80             :     }
      81             : 
      82             :     // avoid encoding the UTF-16 surrogate because those code points do not
      83             :     // represent characters
      84             :     //
      85          26 :     if(wc < 0xD800 || wc > 0xDFFF)
      86             :     {
      87          26 :         if(wc < 0x10000)
      88             :         {
      89           1 :             verify_length(4);
      90             : 
      91           1 :             mb[0] = static_cast<char>((wc >> 12) | 0xE0);
      92           1 :             mb[1] = ((wc >> 6) & 0x3F) | 0x80;
      93           1 :             mb[2] = (wc & 0x3F) | 0x80;
      94           1 :             mb[3] = '\0';
      95           1 :             return 3;
      96             :         }
      97          25 :         if(wc < 0x110000)
      98             :         {
      99          25 :             verify_length(5);
     100             : 
     101          25 :             mb[0] = static_cast<char>((wc >> 18) | 0xF0);
     102          25 :             mb[1] = ((wc >> 12) & 0x3F) | 0x80;
     103          25 :             mb[2] = ((wc >> 6) & 0x3F) | 0x80;
     104          25 :             mb[3] = (wc & 0x3F) | 0x80;
     105          25 :             mb[4] = '\0';
     106          25 :             return 4;
     107             :         }
     108             :     }
     109             : 
     110           0 :     verify_length(1);
     111             : 
     112             :     /* an invalid wide character */
     113           0 :     mb[0] = '\0';
     114           0 :     return -1;
     115             : }
     116             : 
     117             : 
     118             : } // no name namespace
     119             : 
     120             : 
     121             : 
     122           3 : CATCH_TEST_CASE("hexadecimal_string_hex_digits", "[hexadecimal_string]")
     123             : {
     124           2 :     CATCH_START_SECTION("hexadecimal_string: verify hexadecimal digit detection")
     125             :     {
     126     1114113 :         for(int i(0); i < 0x110000; ++i) // all of Unicode
     127             :         {
     128     1114112 :             if((i >= '0' && i <= '9')
     129     1114102 :             || (i >= 'a' && i <= 'f')
     130     1114096 :             || (i >= 'A' && i <= 'F'))
     131             :             {
     132          22 :                 CATCH_REQUIRE(snapdev::is_hexdigit(i));
     133             :             }
     134             :             else
     135             :             {
     136     1114090 :                 CATCH_REQUIRE_FALSE(snapdev::is_hexdigit(i));
     137             :             }
     138             :         }
     139             :     }
     140             :     CATCH_END_SECTION()
     141           1 : }
     142             : 
     143             : 
     144           6 : CATCH_TEST_CASE("hexadecimal_string_16_bit_values", "[hexadecimal_string]")
     145             : {
     146           8 :     CATCH_START_SECTION("hexadecimal_string: all 16 bit values")
     147             :     {
     148       65537 :         for(int i(0); i < 65'536; ++i)
     149             :         {
     150      131072 :             std::stringstream ss;
     151       65536 :             ss << std::hex << i;
     152      131072 :             std::string value(ss.str());
     153       65536 :             if((value.length() & 1) != 0)
     154             :             {
     155        3856 :                 value = "0" + value;
     156             :             }
     157      131072 :             std::string bin(snapdev::hex_to_bin(value));
     158      131072 :             std::string upper;
     159       65536 :             if(bin.length() == 1)
     160             :             {
     161         256 :                 CATCH_REQUIRE(i < 256);
     162         256 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i));
     163             : 
     164         256 :                 CATCH_REQUIRE(snapdev::int_to_hex(i, false) == ss.str());
     165         256 :                 CATCH_REQUIRE(snapdev::int_to_hex(i, false, 2) == value);
     166             : 
     167         256 :                 CATCH_REQUIRE(snapdev::hex_to_int<int>(ss.str()) == i);
     168         256 :                 CATCH_REQUIRE(snapdev::hex_to_int<int>(value) == i);
     169             : 
     170         256 :                 upper = snapdev::int_to_hex(i, true, 2);
     171             :             }
     172             :             else
     173             :             {
     174       65280 :                 CATCH_REQUIRE(bin.length() == 2);
     175       65280 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i >> 8));
     176       65280 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[1]) == static_cast<unsigned char>(i));
     177             : 
     178       65280 :                 CATCH_REQUIRE(snapdev::int_to_hex(i, false) == ss.str());
     179       65280 :                 CATCH_REQUIRE(snapdev::int_to_hex(i, false, 4) == value);
     180       65280 :                 CATCH_REQUIRE(snapdev::hex_to_int<int>(ss.str()) == i);
     181       65280 :                 CATCH_REQUIRE(snapdev::hex_to_int<int>(value) == i);
     182             : 
     183       65280 :                 upper = snapdev::int_to_hex(i, true, 4);
     184             :             }
     185             : 
     186       65536 :             CATCH_REQUIRE(snapdev::bin_to_hex(bin) == value);
     187             : 
     188      131072 :             std::string bin_from_upper(snapdev::hex_to_bin(upper));
     189       65536 :             if(bin_from_upper.length() == 1)
     190             :             {
     191         256 :                 CATCH_REQUIRE(i < 256);
     192         256 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i));
     193             :             }
     194             :             else
     195             :             {
     196       65280 :                 CATCH_REQUIRE(bin.length() == 2);
     197       65280 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[0]) == static_cast<unsigned char>(i >> 8));
     198       65280 :                 CATCH_REQUIRE(static_cast<unsigned char>(bin[1]) == static_cast<unsigned char>(i));
     199             :             }
     200             :         }
     201             :     }
     202             :     CATCH_END_SECTION()
     203             : 
     204           8 :     CATCH_START_SECTION("hexadecimal_string: hex_to_bin & bin_to_hex, large (and small) random numbers")
     205             :     {
     206        1001 :         for(int i(0); i < 1'000; ++i)
     207             :         {
     208        2000 :             std::stringstream ss;
     209        2000 :             std::string result;
     210        1000 :             int const length((rand() % 16 + 2) & -2);
     211       10208 :             for(int j(0); j < length; ++j)
     212             :             {
     213        9208 :                 int value(rand() % 256);
     214        9208 :                 ss << std::setw(2) << std::setfill('0') << std::hex << value;
     215        9208 :                 result.push_back(value);
     216             :             }
     217        1000 :             CATCH_REQUIRE(snapdev::hex_to_bin(ss.str()) == result);
     218        1000 :             CATCH_REQUIRE(snapdev::bin_to_hex(result) == ss.str());
     219             :         }
     220             :     }
     221             :     CATCH_END_SECTION()
     222             : 
     223           8 :     CATCH_START_SECTION("hexadecimal_string: bin_to_hex with empty")
     224             :     {
     225           1 :         CATCH_REQUIRE(snapdev::bin_to_hex(std::string()) == std::string());
     226           1 :         CATCH_REQUIRE(snapdev::bin_to_hex("") == "");
     227             :     }
     228             :     CATCH_END_SECTION()
     229             : 
     230           8 :     CATCH_START_SECTION("hexadecimal_string: large width for int_to_hex()")
     231             :     {
     232         257 :         for(int i(0); i < 256; ++i)
     233             :         {
     234         256 :             std::uint8_t c(i & 255);
     235         512 :             std::string const value(snapdev::int_to_hex(c, true, 4));  // 4 when type is at most 2 digits, return 2 digits...
     236         512 :             std::stringstream ss;
     237         256 :             ss << std::setw(2) << std::setfill('0') << std::hex << std::uppercase << i;
     238         256 :             CATCH_REQUIRE(ss.str() == value);
     239             :         }
     240             :     }
     241             :     CATCH_END_SECTION()
     242           4 : }
     243             : 
     244             : 
     245           5 : CATCH_TEST_CASE("hexadecimal_string_invalid_input", "[hexadecimal_string][error]")
     246             : {
     247           6 :     CATCH_START_SECTION("hexadecimal_string: invalid length")
     248             :     {
     249          17 :         for(int i(1); i <= 31; i += 2)
     250             :         {
     251          32 :             std::stringstream ss;
     252         272 :             for(int j(0); j < i; ++j)
     253             :             {
     254         256 :                 int const value(rand() % 16);
     255         256 :                 ss << std::hex << value;
     256             :             }
     257          16 :             CATCH_REQUIRE_THROWS_MATCHES(
     258             :                       snapdev::hex_to_bin(ss.str())
     259             :                     , snapdev::hexadecimal_string_invalid_parameter
     260             :                     , Catch::Matchers::ExceptionMessage(
     261             :                               "hexadecimal_string_exception: the hex parameter must have an even size."));
     262             :         }
     263             :     }
     264             :     CATCH_END_SECTION()
     265             : 
     266           6 :     CATCH_START_SECTION("hexadecimal_string: invalid digits")
     267             :     {
     268           1 :         int left(0);
     269           1 :         int right(0);
     270          27 :         while(left < 25 && right < 25)
     271             :         {
     272          52 :             std::stringstream ss;
     273          26 :             ss << std::hex << rand();
     274          26 :             if((ss.str().length() & 1) != 0)
     275             :             {
     276           1 :                 ++right;
     277             :             }
     278             :             else
     279             :             {
     280          25 :                 ++left;
     281             :             }
     282             : 
     283             :             // all of our std::string are considered UTF-8 so add a Unicode
     284             :             // character even though the hex_to_bin() function really
     285             :             // only expects ASCII
     286             :             //
     287             :             for(;;)
     288             :             {
     289          26 :                 char32_t const invalid(rand() % (0x110000 - 1) + 1);
     290          51 :                 if((invalid < 0xD800 || invalid >= 0xE000)  // don't try with UTF-16 surrogates
     291          52 :                 && !snapdev::is_hexdigit(invalid))
     292             :                 {
     293          26 :                     char buf[16];
     294          26 :                     wctombs(buf, invalid, sizeof(buf));
     295          26 :                     ss << buf;
     296          26 :                     break;
     297             :                 }
     298           0 :             }
     299             : 
     300             :             // make sure the length is even
     301             :             //
     302          52 :             if((ss.str().length() & 1) != 0)
     303             :             {
     304           2 :                 ss << std::hex << rand() % 16;
     305             :             }
     306             : 
     307          26 :             CATCH_REQUIRE_THROWS_MATCHES(
     308             :                       snapdev::hex_to_bin(ss.str())
     309             :                     , snapdev::hexadecimal_string_invalid_parameter
     310             :                     , Catch::Matchers::ExceptionMessage(
     311             :                               "hexadecimal_string_exception: the input character is not an hexadecimal digit."));
     312             :         }
     313             :     }
     314             :     CATCH_END_SECTION()
     315             : 
     316           6 :     CATCH_START_SECTION("hexadecimal_string: integer too small (overflow)")
     317             :     {
     318        1002 :         for(int i(0); i <= 1'000; ++i)
     319             :         {
     320        1001 :             std::uint32_t value(rand());
     321        1001 :             while(value < 256)
     322             :             {
     323           0 :                 value = rand();
     324             :             }
     325        2002 :             std::stringstream ss;
     326        1001 :             ss << value;
     327        1001 :             CATCH_REQUIRE_THROWS_MATCHES(
     328             :                       snapdev::hex_to_int<std::uint8_t>(ss.str())
     329             :                     , snapdev::hexadecimal_string_out_of_range
     330             :                     , Catch::Matchers::ExceptionMessage(
     331             :                               "hexadecimal_string_out_of_range: input string has an hexadecimal number which is too large for the output integer type."));
     332             :         }
     333             :     }
     334             :     CATCH_END_SECTION()
     335           9 : }
     336             : 
     337             : 
     338             : 
     339             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13