LCOV - code coverage report
Current view: top level - tests - catch_hexadecimal_string.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 150 160 93.8 %
Date: 2023-05-29 16:11:08 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2021-2023  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 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             : /** \file
      20             :  * \brief Verify that the hexadecimal convertions work.
      21             :  *
      22             :  * This file implements tests for the hexadecimal to binary and vice
      23             :  * versa functions.
      24             :  */
      25             : 
      26             : // self
      27             : //
      28             : #include    <snapdev/hexadecimal_string.h>
      29             : 
      30             : #include    "catch_main.h"
      31             : 
      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         221 : int wctombs(char * mb, char32_t wc, size_t len)
      54             : {
      55         221 :     auto verify_length = [&len](size_t required_len)
      56             :     {
      57         221 :         if(len < required_len)
      58             :         {
      59           0 :             throw std::logic_error("wctombs() called with an output buffer which is too small.");
      60             :         }
      61         221 :     };
      62             : 
      63         221 :     if(wc < 0x80)
      64             :     {
      65           1 :         verify_length(2);
      66             : 
      67             :         /* this will also encode '\0'... */
      68           1 :         mb[0] = static_cast<char>(wc);
      69           1 :         mb[1] = '\0';
      70           1 :         return 1;
      71             :     }
      72         220 :     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         220 :     if(wc < 0xD800 || wc > 0xDFFF)
      86             :     {
      87         220 :         if(wc < 0x10000)
      88             :         {
      89          21 :             verify_length(4);
      90             : 
      91          21 :             mb[0] = static_cast<char>((wc >> 12) | 0xE0);
      92          21 :             mb[1] = ((wc >> 6) & 0x3F) | 0x80;
      93          21 :             mb[2] = (wc & 0x3F) | 0x80;
      94          21 :             mb[3] = '\0';
      95          21 :             return 3;
      96             :         }
      97         199 :         if(wc < 0x110000)
      98             :         {
      99         199 :             verify_length(5);
     100             : 
     101         199 :             mb[0] = static_cast<char>((wc >> 18) | 0xF0);
     102         199 :             mb[1] = ((wc >> 12) & 0x3F) | 0x80;
     103         199 :             mb[2] = ((wc >> 6) & 0x3F) | 0x80;
     104         199 :             mb[3] = (wc & 0x3F) | 0x80;
     105         199 :             mb[4] = '\0';
     106         199 :             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           1 : CATCH_TEST_CASE("hexadecimal_string_hex_digits", "[hexadecimal_string]")
     123             : {
     124           1 :     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          22 :             }
     134             :             else
     135             :             {
     136     1114090 :                 CATCH_REQUIRE_FALSE(snapdev::is_hexdigit(i));
     137             :             }
     138             :         }
     139             :     }
     140           1 :     CATCH_END_SECTION()
     141           1 : }
     142             : 
     143             : 
     144           4 : CATCH_TEST_CASE("hexadecimal_string_16_bit_values", "[hexadecimal_string]")
     145             : {
     146           4 :     CATCH_START_SECTION("hexadecimal_string: all 16 bit values")
     147             :     {
     148       65537 :         for(int i(0); i < 65'536; ++i)
     149             :         {
     150       65536 :             std::stringstream ss;
     151       65536 :             ss << std::hex << i;
     152       65536 :             std::string value(ss.str());
     153       65536 :             if((value.length() & 1) != 0)
     154             :             {
     155        3856 :                 value = "0" + value;
     156             :             }
     157       65536 :             std::string bin(snapdev::hex_to_bin(value));
     158       65536 :             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       65536 :             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       65536 :         }
     201             :     }
     202           4 :     CATCH_END_SECTION()
     203             : 
     204           4 :     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        1000 :             std::stringstream ss;
     209        1000 :             std::string result;
     210        1000 :             int const length((rand() % 16 + 2) & -2);
     211       10132 :             for(int j(0); j < length; ++j)
     212             :             {
     213        9132 :                 int value(rand() % 256);
     214        9132 :                 ss << std::setw(2) << std::setfill('0') << std::hex << value;
     215        9132 :                 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        1000 :         }
     220             :     }
     221           4 :     CATCH_END_SECTION()
     222             : 
     223           4 :     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           4 :     CATCH_END_SECTION()
     229             : 
     230           4 :     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         256 :             std::string const value(snapdev::int_to_hex(c, true, 4));  // 4 when type is at most 2 digits, return 2 digits...
     236         256 :             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         256 :         }
     240             :     }
     241           4 :     CATCH_END_SECTION()
     242           4 : }
     243             : 
     244             : 
     245           3 : CATCH_TEST_CASE("hexadecimal_string_invalid_input", "[hexadecimal_string][error]")
     246             : {
     247           3 :     CATCH_START_SECTION("hexadecimal_string: invalid length")
     248             :     {
     249          17 :         for(int i(1); i <= 31; i += 2)
     250             :         {
     251          16 :             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          48 :             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          16 :         }
     263             :     }
     264           3 :     CATCH_END_SECTION()
     265             : 
     266           3 :     CATCH_START_SECTION("hexadecimal_string: invalid digits")
     267             :     {
     268           1 :         int left(0);
     269           1 :         int right(0);
     270           1 :         bool small_char(false);
     271         222 :         while(left < 25 || right < 25 || !small_char)
     272             :         {
     273         221 :             std::stringstream ss;
     274         221 :             ss << std::hex << rand();
     275         221 :             if((ss.str().length() & 1) != 0)
     276             :             {
     277          25 :                 ++right;
     278             :             }
     279             :             else
     280             :             {
     281         196 :                 ++left;
     282             :             }
     283             : 
     284             :             // all of our std::string are considered UTF-8 so add a Unicode
     285             :             // character even though the hex_to_bin() function really
     286             :             // only expects ASCII
     287             :             //
     288         221 :             char32_t invalid(U'\0');
     289             :             for(;;)
     290             :             {
     291         222 :                 invalid = rand() % (0x110000 - 1) + 1;
     292         222 :                 if(!small_char && left > 10 && right > 10)
     293             :                 {
     294           1 :                     invalid = (invalid - 1) % 0x7F + 1;
     295             :                 }
     296         202 :                 if((invalid < 0xD800 || invalid >= 0xE000)  // don't try with UTF-16 surrogates
     297         424 :                 && !snapdev::is_hexdigit(invalid))
     298             :                 {
     299         221 :                     if(invalid < 0x80)
     300             :                     {
     301           1 :                         small_char = true;
     302             :                     }
     303         221 :                     char buf[16];
     304         221 :                     wctombs(buf, invalid, sizeof(buf));
     305         221 :                     ss << buf;
     306         221 :                     break;
     307             :                 }
     308           1 :             }
     309             : 
     310             :             // make sure the length is even
     311             :             //
     312         221 :             if((ss.str().length() & 1) != 0)
     313             :             {
     314          41 :                 ss << std::hex << rand() % 16;
     315             :             }
     316             : 
     317         221 :             if(invalid >= 0x80)
     318             :             {
     319             :                 // in this case the character is not available so we cannot
     320             :                 // just add it cleanly to the exception message
     321             :                 //
     322         660 :                 CATCH_REQUIRE_THROWS_MATCHES(
     323             :                           snapdev::hex_to_bin(ss.str())
     324             :                         , snapdev::hexadecimal_string_invalid_parameter
     325             :                         , Catch::Matchers::ExceptionMessage(
     326             :                                     "hexadecimal_string_exception: input character is not an hexadecimal digit."));
     327             :             }
     328             :             else
     329             :             {
     330           3 :                 CATCH_REQUIRE_THROWS_MATCHES(
     331             :                           snapdev::hex_to_bin(ss.str())
     332             :                         , snapdev::hexadecimal_string_invalid_parameter
     333             :                         , Catch::Matchers::ExceptionMessage(
     334             :                                     std::string("hexadecimal_string_exception: input character '")
     335             :                                   + static_cast<char>(invalid)
     336             :                                   + "' is not an hexadecimal digit."));
     337             :             }
     338         221 :         }
     339             :     }
     340           3 :     CATCH_END_SECTION()
     341             : 
     342           3 :     CATCH_START_SECTION("hexadecimal_string: integer too small (overflow)")
     343             :     {
     344        1002 :         for(int i(0); i <= 1'000; ++i)
     345             :         {
     346        1001 :             std::uint32_t value(rand());
     347        1001 :             while(value < 256)
     348             :             {
     349           0 :                 value = rand();
     350             :             }
     351        1001 :             std::stringstream ss;
     352        1001 :             ss << value;
     353        3003 :             CATCH_REQUIRE_THROWS_MATCHES(
     354             :                       snapdev::hex_to_int<std::uint8_t>(ss.str())
     355             :                     , snapdev::hexadecimal_string_out_of_range
     356             :                     , Catch::Matchers::ExceptionMessage(
     357             :                               "hexadecimal_string_out_of_range: input string has an hexadecimal number which is too large for the output integer type."));
     358        1001 :         }
     359             :     }
     360           3 :     CATCH_END_SECTION()
     361           3 : }
     362             : 
     363             : 
     364             : 
     365             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14