LCOV - code coverage report
Current view: top level - tests - catch_brs.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.0 % 1215 1167
Test Date: 2025-08-31 07:54:46 Functions: 100.0 % 28 28
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2011-2025  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 the BRS functions.
      21              :  *
      22              :  * This file implements tests to verify that the BRS functions do what
      23              :  * they are expected to do.
      24              :  */
      25              : 
      26              : // snapdev
      27              : //
      28              : #include    <snapdev/brs.h>
      29              : 
      30              : #include    <snapdev/timespec_ex.h>
      31              : 
      32              : 
      33              : // self
      34              : //
      35              : #include    "catch_main.h"
      36              : 
      37              : 
      38              : // C++
      39              : //
      40              : #include    <fstream>
      41              : 
      42              : 
      43              : 
      44              : 
      45            1 : CATCH_TEST_CASE("brs_bitfield_size", "[serialization][math]")
      46              : {
      47            1 :     CATCH_START_SECTION("brs_bitfield_size: bitfield_size")
      48              :     {
      49            1 :         constexpr std::size_t const sizeof_type(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_type));
      50            1 :         CATCH_REQUIRE(sizeof_type == 2);
      51              : 
      52            1 :         constexpr std::size_t const sizeof_name(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_name));
      53            1 :         CATCH_REQUIRE(sizeof_name == 7);
      54              : 
      55            1 :         constexpr std::size_t const sizeof_hunk(SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk));
      56            1 :         CATCH_REQUIRE(sizeof_hunk == 23);
      57              :     }
      58            1 :     CATCH_END_SECTION()
      59            1 : }
      60              : 
      61              : 
      62           14 : CATCH_TEST_CASE("brs_basic_types", "[serialization]")
      63              : {
      64           14 :     CATCH_START_SECTION("brs_basic_types: push/restore char")
      65              :     {
      66            1 :         std::stringstream buffer;
      67            1 :         snapdev::serializer out(buffer);
      68              : 
      69            1 :         std::string data(buffer.str());
      70            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
      71              : 
      72            1 :         CATCH_REQUIRE(data[0] == 'B');
      73            1 :         CATCH_REQUIRE(data[1] == 'R');
      74            1 :         CATCH_REQUIRE(data[2] == 'L');
      75            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
      76              : 
      77            1 :         char value = 33;
      78            3 :         out.add_value("orange", value);
      79              : 
      80            5 :         CATCH_REQUIRE_THROWS_MATCHES(
      81              :                   out.add_value(std::string(), &value, sizeof(value))
      82              :                 , snapdev::brs_cannot_be_empty
      83              :                 , Catch::Matchers::ExceptionMessage(
      84              :                           "brs_error: name cannot be an empty string."));
      85              : 
      86              :         // make sure it did not get smashed
      87            1 :         data = buffer.str();
      88            1 :         CATCH_REQUIRE(data[0] == 'B');
      89            1 :         CATCH_REQUIRE(data[1] == 'R');
      90            1 :         CATCH_REQUIRE(data[2] == 'L');
      91            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
      92              : 
      93            1 :         CATCH_REQUIRE(data[4] == 6 << 2);  // hunk_sizes_t
      94            1 :         CATCH_REQUIRE(data[5] == 1 << 1);
      95            1 :         CATCH_REQUIRE(data[6] == 0);
      96            1 :         CATCH_REQUIRE(data[7] == 0);
      97              : 
      98            1 :         CATCH_REQUIRE(data[8] == 'o');    // name
      99            1 :         CATCH_REQUIRE(data[9] == 'r');
     100            1 :         CATCH_REQUIRE(data[10] == 'a');
     101            1 :         CATCH_REQUIRE(data[11] == 'n');
     102            1 :         CATCH_REQUIRE(data[12] == 'g');
     103            1 :         CATCH_REQUIRE(data[13] == 'e');
     104              : 
     105            1 :         CATCH_REQUIRE(data[14] == 33);   // value
     106              : 
     107              :         struct processor
     108              :         {
     109            1 :             static bool process_hunk(
     110              :                           snapdev::deserializer<std::stringstream> & in
     111              :                         , snapdev::field_t const & field)
     112              :             {
     113            1 :                 CATCH_REQUIRE(field.f_name == "orange");
     114            1 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     115            1 :                 CATCH_REQUIRE(field.f_index == -1);
     116            1 :                 CATCH_REQUIRE(field.f_size == 1);
     117            1 :                 char c;
     118            1 :                 in.read_data(c);
     119            1 :                 CATCH_REQUIRE(c == 33);
     120            1 :                 return true;
     121              :             }
     122              :         };
     123              : 
     124              :         // Note: you don't usually end up re-using the serializer buffer
     125              :         //       but here it's practical
     126              :         //
     127            1 :         buffer.clear();
     128              : 
     129            1 :         snapdev::deserializer in(buffer);
     130              : 
     131              :         // WARNING: we want to use CATCH_...() macros inside the callback
     132              :         //          so make sure not to use one around unserialize_buffer().
     133              :         //
     134            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     135            2 :                       &processor::process_hunk
     136              :                     , std::placeholders::_1
     137            2 :                     , std::placeholders::_2));
     138            1 :         bool const r(in.deserialize(func));
     139            1 :         CATCH_REQUIRE(r);
     140            1 :     }
     141           14 :     CATCH_END_SECTION()
     142              : 
     143           14 :     CATCH_START_SECTION("brs_basic_types: push/restore signed char")
     144              :     {
     145            1 :         std::stringstream buffer;
     146            1 :         snapdev::serializer out(buffer);
     147              : 
     148            1 :         std::string data(buffer.str());
     149            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     150              : 
     151            1 :         CATCH_REQUIRE(data[0] == 'B');
     152            1 :         CATCH_REQUIRE(data[1] == 'R');
     153            1 :         CATCH_REQUIRE(data[2] == 'L');
     154            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     155              : 
     156            1 :         signed char value = -43;
     157            3 :         out.add_value("orange", value);
     158              : 
     159              :         // make sure it did not get smashed
     160            1 :         data = buffer.str();
     161            1 :         CATCH_REQUIRE(data[0] == 'B');
     162            1 :         CATCH_REQUIRE(data[1] == 'R');
     163            1 :         CATCH_REQUIRE(data[2] == 'L');
     164            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     165              : 
     166            1 :         CATCH_REQUIRE(data[4] == 6 << 2);  // hunk_sizes_t
     167            1 :         CATCH_REQUIRE(data[5] == 1 << 1);
     168            1 :         CATCH_REQUIRE(data[6] == 0);
     169            1 :         CATCH_REQUIRE(data[7] == 0);
     170              : 
     171            1 :         CATCH_REQUIRE(data[8] == 'o');    // name
     172            1 :         CATCH_REQUIRE(data[9] == 'r');
     173            1 :         CATCH_REQUIRE(data[10] == 'a');
     174            1 :         CATCH_REQUIRE(data[11] == 'n');
     175            1 :         CATCH_REQUIRE(data[12] == 'g');
     176            1 :         CATCH_REQUIRE(data[13] == 'e');
     177              : 
     178            1 :         CATCH_REQUIRE(static_cast<signed char>(data[14]) == -43);   // value
     179              : 
     180              :         struct processor
     181              :         {
     182            1 :             static bool process_hunk(
     183              :                           snapdev::deserializer<std::stringstream> & in
     184              :                         , snapdev::field_t const & field)
     185              :             {
     186            1 :                 CATCH_REQUIRE(field.f_name == "orange");
     187            1 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     188            1 :                 CATCH_REQUIRE(field.f_index == -1);
     189            1 :                 CATCH_REQUIRE(field.f_size == 1);
     190            1 :                 signed char c;
     191            1 :                 in.read_data(c);
     192            1 :                 CATCH_REQUIRE(c == -43);
     193            1 :                 return true;
     194              :             }
     195              :         };
     196              : 
     197              :         // Note: you don't usually end up re-using the serializer buffer
     198              :         //       but here it's practical
     199              :         //
     200            1 :         buffer.clear();
     201              : 
     202            1 :         snapdev::deserializer in(buffer);
     203              : 
     204              :         // WARNING: we want to use CATCH_...() macros inside the callback
     205              :         //          so make sure not to use one around unserialize_buffer().
     206              :         //
     207            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     208            2 :                       &processor::process_hunk
     209              :                     , std::placeholders::_1
     210            2 :                     , std::placeholders::_2));
     211            1 :         bool const r(in.deserialize(func));
     212            1 :         CATCH_REQUIRE(r);
     213            1 :     }
     214           14 :     CATCH_END_SECTION()
     215              : 
     216           14 :     CATCH_START_SECTION("brs_basic_types: push/restore unsigned char")
     217              :     {
     218            1 :         std::stringstream buffer;
     219            1 :         snapdev::serializer out(buffer);
     220              : 
     221            1 :         std::string data(buffer.str());
     222            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     223              : 
     224            1 :         CATCH_REQUIRE(data[0] == 'B');
     225            1 :         CATCH_REQUIRE(data[1] == 'R');
     226            1 :         CATCH_REQUIRE(data[2] == 'L');
     227            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     228              : 
     229            1 :         unsigned char value = 200;
     230            3 :         out.add_value("orange", value);
     231              : 
     232              :         // make sure it did not get smashed
     233            1 :         data = buffer.str();
     234            1 :         CATCH_REQUIRE(data[0] == 'B');
     235            1 :         CATCH_REQUIRE(data[1] == 'R');
     236            1 :         CATCH_REQUIRE(data[2] == 'L');
     237            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     238              : 
     239            1 :         CATCH_REQUIRE(data[4] == 6 << 2);  // hunk_sizes_t
     240            1 :         CATCH_REQUIRE(data[5] == 1 << 1);
     241            1 :         CATCH_REQUIRE(data[6] == 0);
     242            1 :         CATCH_REQUIRE(data[7] == 0);
     243              : 
     244            1 :         CATCH_REQUIRE(data[8] == 'o');    // name
     245            1 :         CATCH_REQUIRE(data[9] == 'r');
     246            1 :         CATCH_REQUIRE(data[10] == 'a');
     247            1 :         CATCH_REQUIRE(data[11] == 'n');
     248            1 :         CATCH_REQUIRE(data[12] == 'g');
     249            1 :         CATCH_REQUIRE(data[13] == 'e');
     250              : 
     251            1 :         CATCH_REQUIRE(static_cast<unsigned char>(data[14]) == 200);   // value
     252              : 
     253              :         struct processor
     254              :         {
     255            1 :             static bool process_hunk(
     256              :                           snapdev::deserializer<std::stringstream> & in
     257              :                         , snapdev::field_t const & field)
     258              :             {
     259            1 :                 CATCH_REQUIRE(field.f_name == "orange");
     260            1 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     261            1 :                 CATCH_REQUIRE(field.f_index == -1);
     262            1 :                 CATCH_REQUIRE(field.f_size == 1);
     263            1 :                 unsigned char c;
     264            1 :                 in.read_data(c);
     265            1 :                 CATCH_REQUIRE(c == 200);
     266            1 :                 return true;
     267              :             }
     268              :         };
     269              : 
     270              :         // Note: you don't usually end up re-using the serializer buffer
     271              :         //       but here it's practical
     272              :         //
     273            1 :         buffer.clear();
     274              : 
     275            1 :         snapdev::deserializer in(buffer);
     276              : 
     277              :         // WARNING: we want to use CATCH_...() macros inside the callback
     278              :         //          so make sure not to use one around unserialize_buffer().
     279              :         //
     280            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     281            2 :                       &processor::process_hunk
     282              :                     , std::placeholders::_1
     283            2 :                     , std::placeholders::_2));
     284            1 :         bool const r(in.deserialize(func));
     285            1 :         CATCH_REQUIRE(r);
     286            1 :     }
     287           14 :     CATCH_END_SECTION()
     288              : 
     289           14 :     CATCH_START_SECTION("brs_basic_types: push/restore shorts (16 bits)")
     290              :     {
     291            1 :         std::stringstream buffer;
     292            1 :         snapdev::serializer out(buffer);
     293              : 
     294            1 :         std::string data(buffer.str());
     295            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     296              : 
     297            1 :         CATCH_REQUIRE(data[0] == 'B');
     298            1 :         CATCH_REQUIRE(data[1] == 'R');
     299            1 :         CATCH_REQUIRE(data[2] == 'L');
     300            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     301              : 
     302            1 :         std::int16_t const purple = 3003;
     303            3 :         out.add_value("purple", purple);
     304              : 
     305            1 :         std::uint16_t const black = 65001;
     306            3 :         out.add_value("black", black);
     307              : 
     308              :         // make sure it did not get smashed
     309            1 :         data = buffer.str();
     310            1 :         CATCH_REQUIRE(data[0] == 'B');
     311            1 :         CATCH_REQUIRE(data[1] == 'R');
     312            1 :         CATCH_REQUIRE(data[2] == 'L');
     313            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     314              : 
     315            1 :         CATCH_REQUIRE(data[4] == 6 << 2);  // hunk_sizes_t
     316            1 :         CATCH_REQUIRE(data[5] == 2 << 1);
     317            1 :         CATCH_REQUIRE(data[6] == 0);
     318            1 :         CATCH_REQUIRE(data[7] == 0);
     319              : 
     320            1 :         CATCH_REQUIRE(data[8] == 'p');    // name
     321            1 :         CATCH_REQUIRE(data[9] == 'u');
     322            1 :         CATCH_REQUIRE(data[10] == 'r');
     323            1 :         CATCH_REQUIRE(data[11] == 'p');
     324            1 :         CATCH_REQUIRE(data[12] == 'l');
     325            1 :         CATCH_REQUIRE(data[13] == 'e');
     326              : 
     327              : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
     328              :         CATCH_REQUIRE(static_cast<std::uint8_t>(data[14]) * 256 + static_cast<std::uint8_t>(data[15]) == 3003);   // value
     329              : #else
     330            1 :         CATCH_REQUIRE(static_cast<std::uint8_t>(data[14]) + static_cast<std::uint8_t>(data[15]) * 256 == 3003);   // value
     331              : #endif
     332              : 
     333              :         struct processor
     334              :         {
     335            2 :             static bool process_hunk(
     336              :                           snapdev::deserializer<std::stringstream> & in
     337              :                         , snapdev::field_t const & field
     338              :                         , std::int16_t purple
     339              :                         , std::uint16_t black)
     340              :             {
     341            2 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     342            2 :                 CATCH_REQUIRE(field.f_index == -1);
     343            2 :                 CATCH_REQUIRE(field.f_size == 2);
     344            2 :                 if(field.f_name == "purple")
     345              :                 {
     346            1 :                     std::int16_t value;
     347            1 :                     in.read_data(value);
     348            1 :                     CATCH_REQUIRE(value == purple);
     349              :                 }
     350            1 :                 else if(field.f_name == "black")
     351              :                 {
     352            1 :                     std::uint16_t value;
     353            1 :                     in.read_data(value);
     354            1 :                     CATCH_REQUIRE(value == black);
     355              :                 }
     356              :                 else
     357              :                 {
     358            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
     359              :                 }
     360            2 :                 return true;
     361              :             }
     362              :         };
     363              : 
     364              :         // Note: you don't usually end up re-using the serializer buffer
     365              :         //       but here it's practical
     366              :         //
     367            1 :         buffer.clear();
     368              : 
     369            1 :         snapdev::deserializer in(buffer);
     370              : 
     371              :         // WARNING: we want to use CATCH_...() macros inside the callback
     372              :         //          so make sure not to use one around unserialize_buffer().
     373              :         //
     374            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     375            2 :                       &processor::process_hunk
     376              :                     , std::placeholders::_1
     377              :                     , std::placeholders::_2
     378              :                     , purple
     379            2 :                     , black));
     380            1 :         bool const r(in.deserialize(func));
     381            1 :         CATCH_REQUIRE(r);
     382            1 :     }
     383           14 :     CATCH_END_SECTION()
     384              : 
     385           14 :     CATCH_START_SECTION("brs_basic_types: push/restore ints (32 bits)")
     386              :     {
     387            1 :         std::stringstream buffer;
     388            1 :         snapdev::serializer out(buffer);
     389              : 
     390            1 :         std::string data(buffer.str());
     391            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     392              : 
     393            1 :         CATCH_REQUIRE(data[0] == 'B');
     394            1 :         CATCH_REQUIRE(data[1] == 'R');
     395            1 :         CATCH_REQUIRE(data[2] == 'L');
     396            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     397              : 
     398            1 :         std::int32_t const red = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
     399            3 :         out.add_value("red", red);
     400              : 
     401            1 :         std::uint32_t const blue = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
     402            3 :         out.add_value("blue", blue);
     403              : 
     404              :         // make sure it did not get smashed
     405            1 :         data = buffer.str();
     406            1 :         CATCH_REQUIRE(data[0] == 'B');
     407            1 :         CATCH_REQUIRE(data[1] == 'R');
     408            1 :         CATCH_REQUIRE(data[2] == 'L');
     409            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     410              : 
     411            1 :         CATCH_REQUIRE(data[4] == 3 << 2);  // hunk_sizes_t
     412            1 :         CATCH_REQUIRE(data[5] == 4 << 1);
     413            1 :         CATCH_REQUIRE(data[6] == 0);
     414            1 :         CATCH_REQUIRE(data[7] == 0);
     415              : 
     416            1 :         CATCH_REQUIRE(data[8] == 'r');    // name
     417            1 :         CATCH_REQUIRE(data[9] == 'e');
     418            1 :         CATCH_REQUIRE(data[10] == 'd');
     419              : 
     420              : #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
     421              :         CATCH_REQUIRE(
     422              :                   static_cast<std::uint8_t>(data[11]) * 0x1000000
     423              :                 + static_cast<std::uint8_t>(data[12]) * 0x10000
     424              :                 + static_cast<std::uint8_t>(data[13]) * 0x100
     425              :                 + static_cast<std::uint8_t>(data[14]) * 0x1 == red);   // value
     426              : #else
     427            1 :         CATCH_REQUIRE(
     428              :                   static_cast<std::uint8_t>(data[14]) * 0x1000000
     429              :                 + static_cast<std::uint8_t>(data[13]) * 0x10000
     430              :                 + static_cast<std::uint8_t>(data[12]) * 0x100
     431              :                 + static_cast<std::uint8_t>(data[11]) * 0x1 == red);   // value
     432              : #endif
     433              : 
     434              :         struct processor
     435              :         {
     436            2 :             static bool process_hunk(
     437              :                           snapdev::deserializer<std::stringstream> & in
     438              :                         , snapdev::field_t const & field
     439              :                         , std::int32_t red
     440              :                         , std::uint32_t blue)
     441              :             {
     442            2 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     443            2 :                 CATCH_REQUIRE(field.f_index == -1);
     444            2 :                 CATCH_REQUIRE(field.f_size == 4);
     445            2 :                 if(field.f_name == "red")
     446              :                 {
     447            1 :                     std::int32_t value;
     448            1 :                     in.read_data(value);
     449            1 :                     CATCH_REQUIRE(value == red);
     450              :                 }
     451            1 :                 else if(field.f_name == "blue")
     452              :                 {
     453            1 :                     std::uint32_t value;
     454            1 :                     in.read_data(value);
     455            1 :                     CATCH_REQUIRE(value == blue);
     456              :                 }
     457              :                 else
     458              :                 {
     459            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
     460              :                 }
     461            2 :                 return true;
     462              :             }
     463              :         };
     464              : 
     465              :         // Note: you don't usually end up re-using the serializer buffer
     466              :         //       but here it's practical
     467              :         //
     468            1 :         buffer.clear();
     469              : 
     470            1 :         snapdev::deserializer in(buffer);
     471              : 
     472              :         // WARNING: we want to use CATCH_...() macros inside the callback
     473              :         //          so make sure not to use one around unserialize_buffer().
     474              :         //
     475            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     476            2 :                       &processor::process_hunk
     477              :                     , std::placeholders::_1
     478              :                     , std::placeholders::_2
     479              :                     , red
     480            2 :                     , blue));
     481            1 :         bool const r(in.deserialize(func));
     482            1 :         CATCH_REQUIRE(r);
     483            1 :     }
     484           14 :     CATCH_END_SECTION()
     485              : 
     486           14 :     CATCH_START_SECTION("brs_basic_types: push/restore ints (64 bits)")
     487              :     {
     488            1 :         std::stringstream buffer;
     489            1 :         snapdev::serializer out(buffer);
     490              : 
     491            1 :         std::string data(buffer.str());
     492            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     493              : 
     494            1 :         CATCH_REQUIRE(data[0] == 'B');
     495            1 :         CATCH_REQUIRE(data[1] == 'R');
     496            1 :         CATCH_REQUIRE(data[2] == 'L');
     497            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     498              : 
     499            1 :         std::int64_t const white = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
     500            3 :         out.add_value("white", white);
     501              : 
     502            1 :         std::uint64_t const gray = static_cast<std::int32_t>(SNAP_CATCH2_NAMESPACE::rand_int64());
     503            3 :         out.add_value("gray", gray);
     504              : 
     505              :         // make sure it did not get smashed
     506            1 :         data = buffer.str();
     507            1 :         CATCH_REQUIRE(data[0] == 'B');
     508            1 :         CATCH_REQUIRE(data[1] == 'R');
     509            1 :         CATCH_REQUIRE(data[2] == 'L');
     510            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     511              : 
     512            1 :         CATCH_REQUIRE(data[4] == 5 << 2);  // hunk_sizes_t
     513            1 :         CATCH_REQUIRE(data[5] == 8 << 1);
     514            1 :         CATCH_REQUIRE(data[6] == 0);
     515            1 :         CATCH_REQUIRE(data[7] == 0);
     516              : 
     517            1 :         CATCH_REQUIRE(data[8] == 'w');    // name
     518            1 :         CATCH_REQUIRE(data[9] == 'h');
     519            1 :         CATCH_REQUIRE(data[10] == 'i');
     520            1 :         CATCH_REQUIRE(data[11] == 't');
     521            1 :         CATCH_REQUIRE(data[12] == 'e');
     522              : 
     523              :         struct processor
     524              :         {
     525            2 :             static bool process_hunk(
     526              :                           snapdev::deserializer<std::stringstream> & in
     527              :                         , snapdev::field_t const & field
     528              :                         , std::int64_t white
     529              :                         , std::uint64_t gray)
     530              :             {
     531            2 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     532            2 :                 CATCH_REQUIRE(field.f_index == -1);
     533            2 :                 CATCH_REQUIRE(field.f_size == 8);
     534            2 :                 if(field.f_name == "white")
     535              :                 {
     536            1 :                     std::int64_t value;
     537            1 :                     in.read_data(value);
     538            1 :                     CATCH_REQUIRE(value == white);
     539              :                 }
     540            1 :                 else if(field.f_name == "gray")
     541              :                 {
     542            1 :                     std::uint64_t value;
     543            1 :                     in.read_data(value);
     544            1 :                     CATCH_REQUIRE(value == gray);
     545              :                 }
     546              :                 else
     547              :                 {
     548            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
     549              :                 }
     550            2 :                 return true;
     551              :             }
     552              :         };
     553              : 
     554              :         // Note: you don't usually end up re-using the serializer buffer
     555              :         //       but here it's practical
     556              :         //
     557            1 :         buffer.clear();
     558              : 
     559            1 :         snapdev::deserializer in(buffer);
     560              : 
     561              :         // WARNING: we want to use CATCH_...() macros inside the callback
     562              :         //          so make sure not to use one around unserialize_buffer().
     563              :         //
     564            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     565            2 :                       &processor::process_hunk
     566              :                     , std::placeholders::_1
     567              :                     , std::placeholders::_2
     568              :                     , white
     569            2 :                     , gray));
     570            1 :         bool const r(in.deserialize(func));
     571            1 :         CATCH_REQUIRE(r);
     572            1 :     }
     573           14 :     CATCH_END_SECTION()
     574              : 
     575           14 :     CATCH_START_SECTION("brs_basic_types: push/restore floats")
     576              :     {
     577            1 :         std::stringstream buffer;
     578            1 :         snapdev::serializer out(buffer);
     579              : 
     580            1 :         std::string data(buffer.str());
     581            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     582              : 
     583            1 :         CATCH_REQUIRE(data[0] == 'B');
     584            1 :         CATCH_REQUIRE(data[1] == 'R');
     585            1 :         CATCH_REQUIRE(data[2] == 'L');
     586            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     587              : 
     588            1 :         float const green = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
     589            1 :                           / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
     590            3 :         out.add_value("green", green);
     591              : 
     592            1 :         double const yellow = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
     593            1 :                             / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
     594            3 :         out.add_value("yellow", yellow);
     595              : 
     596            1 :         long double const fushia = static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64())
     597            1 :                                  / static_cast<float>(SNAP_CATCH2_NAMESPACE::rand_int64());
     598            3 :         out.add_value("fushia", fushia);
     599              : 
     600              :         // make sure it did not get smashed
     601            1 :         data = buffer.str();
     602            1 :         CATCH_REQUIRE(data[0] == 'B');
     603            1 :         CATCH_REQUIRE(data[1] == 'R');
     604            1 :         CATCH_REQUIRE(data[2] == 'L');
     605            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     606              : 
     607            1 :         CATCH_REQUIRE(data[4] == 5 << 2);  // hunk_sizes_t
     608            1 :         CATCH_REQUIRE(data[5] == 4 << 1);
     609            1 :         CATCH_REQUIRE(data[6] == 0);
     610            1 :         CATCH_REQUIRE(data[7] == 0);
     611              : 
     612            1 :         CATCH_REQUIRE(data[8] == 'g');    // name
     613            1 :         CATCH_REQUIRE(data[9] == 'r');
     614            1 :         CATCH_REQUIRE(data[10] == 'e');
     615            1 :         CATCH_REQUIRE(data[11] == 'e');
     616            1 :         CATCH_REQUIRE(data[12] == 'n');
     617              : 
     618              :         struct processor
     619              :         {
     620            3 :             static bool process_hunk(
     621              :                           snapdev::deserializer<std::stringstream> & in
     622              :                         , snapdev::field_t const & field
     623              :                         , float green
     624              :                         , double yellow
     625              :                         , long double fushia)
     626              :             {
     627            3 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     628            3 :                 CATCH_REQUIRE(field.f_index == -1);
     629            3 :                 if(field.f_name == "green")
     630              :                 {
     631            1 :                     CATCH_REQUIRE(field.f_size == 4);
     632              : 
     633            1 :                     float value;
     634            1 :                     in.read_data(value);
     635            1 :                     CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, green, 0.0f));
     636              :                 }
     637            2 :                 else if(field.f_name == "yellow")
     638              :                 {
     639            1 :                     CATCH_REQUIRE(field.f_size == 8);
     640              : 
     641            1 :                     double value;
     642            1 :                     in.read_data(value);
     643            1 :                     CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, yellow, 0.0));
     644              :                 }
     645            1 :                 else if(field.f_name == "fushia")
     646              :                 {
     647            1 :                     CATCH_REQUIRE(field.f_size == 16);
     648              : 
     649            1 :                     long double value;
     650            1 :                     in.read_data(value);
     651            1 :                     CATCH_REQUIRE(SNAP_CATCH2_NAMESPACE::nearly_equal(value, fushia, 0.0L));
     652              :                 }
     653              :                 else
     654              :                 {
     655            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
     656              :                 }
     657            3 :                 return true;
     658              :             }
     659              :         };
     660              : 
     661              :         // Note: you don't usually end up re-using the serializer buffer
     662              :         //       but here it's practical
     663              :         //
     664            1 :         buffer.clear();
     665              : 
     666            1 :         snapdev::deserializer in(buffer);
     667              : 
     668              :         // WARNING: we want to use CATCH_...() macros inside the callback
     669              :         //          so make sure not to use one around unserialize_buffer().
     670              :         //
     671            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     672            2 :                       &processor::process_hunk
     673              :                     , std::placeholders::_1
     674              :                     , std::placeholders::_2
     675              :                     , green
     676              :                     , yellow
     677            2 :                     , fushia));
     678            1 :         bool const r(in.deserialize(func));
     679            1 :         CATCH_REQUIRE(r);
     680            1 :     }
     681           14 :     CATCH_END_SECTION()
     682              : 
     683           14 :     CATCH_START_SECTION("brs_basic_types: push/restore string")
     684              :     {
     685            1 :         std::stringstream buffer;
     686            1 :         snapdev::serializer out(buffer);
     687              : 
     688            1 :         std::string data(buffer.str());
     689            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     690              : 
     691            1 :         CATCH_REQUIRE(data[0] == 'B');
     692            1 :         CATCH_REQUIRE(data[1] == 'R');
     693            1 :         CATCH_REQUIRE(data[2] == 'L');
     694            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     695              : 
     696            3 :         std::string const message("this is the message we are going to serialize");
     697            3 :         out.add_value("message", message);
     698              : 
     699              :         // make sure it did not get smashed
     700            1 :         data = buffer.str();
     701            1 :         CATCH_REQUIRE(data[0] == 'B');
     702            1 :         CATCH_REQUIRE(data[1] == 'R');
     703            1 :         CATCH_REQUIRE(data[2] == 'L');
     704            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     705              : 
     706            1 :         CATCH_REQUIRE(data[4] == 7 << 2);  // hunk_sizes_t
     707            1 :         CATCH_REQUIRE(data[5] == 45 << 1);
     708            1 :         CATCH_REQUIRE(data[6] == 0);
     709            1 :         CATCH_REQUIRE(data[7] == 0);
     710              : 
     711            1 :         CATCH_REQUIRE(data[8] == 'm');    // name
     712            1 :         CATCH_REQUIRE(data[9] == 'e');
     713            1 :         CATCH_REQUIRE(data[10] == 's');
     714            1 :         CATCH_REQUIRE(data[11] == 's');
     715            1 :         CATCH_REQUIRE(data[12] == 'a');
     716            1 :         CATCH_REQUIRE(data[13] == 'g');
     717            1 :         CATCH_REQUIRE(data[14] == 'e');
     718              : 
     719              :         struct processor
     720              :         {
     721            1 :             static bool process_hunk(
     722              :                           snapdev::deserializer<std::stringstream> & in
     723              :                         , snapdev::field_t const & field
     724              :                         , std::string message)
     725              :             {
     726            1 :                 CATCH_REQUIRE(field.f_name == "message");
     727            1 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     728            1 :                 CATCH_REQUIRE(field.f_index == -1);
     729            1 :                 CATCH_REQUIRE(field.f_size == 45);
     730              : 
     731            1 :                 std::string value;
     732            1 :                 in.read_data(value);
     733            1 :                 CATCH_REQUIRE(value == message);
     734              : 
     735            1 :                 return true;
     736            1 :             }
     737              :         };
     738              : 
     739              :         // Note: you don't usually end up re-using the serializer buffer
     740              :         //       but here it's practical
     741              :         //
     742            1 :         buffer.clear();
     743              : 
     744            1 :         snapdev::deserializer in(buffer);
     745              : 
     746              :         // WARNING: we want to use CATCH_...() macros inside the callback
     747              :         //          so make sure not to use one around unserialize_buffer().
     748              :         //
     749            4 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     750            2 :                       &processor::process_hunk
     751              :                     , std::placeholders::_1
     752              :                     , std::placeholders::_2
     753            2 :                     , message));
     754            1 :         bool const r(in.deserialize(func));
     755            1 :         CATCH_REQUIRE(r);
     756            1 :     }
     757           14 :     CATCH_END_SECTION()
     758              : 
     759           14 :     CATCH_START_SECTION("brs_basic_types: push/restore timespec")
     760              :     {
     761            1 :         std::stringstream buffer;
     762            1 :         snapdev::serializer out(buffer);
     763              : 
     764            1 :         timespec const now(snapdev::now());
     765            1 :         std::string data(buffer.str());
     766            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
     767              : 
     768            1 :         CATCH_REQUIRE(data[0] == 'B');
     769            1 :         CATCH_REQUIRE(data[1] == 'R');
     770            1 :         CATCH_REQUIRE(data[2] == 'L');
     771            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     772              : 
     773            1 :         timespec bad_time(now);
     774            4 :         while(bad_time.tv_nsec < 1'000'000'000)
     775              :         {
     776            3 :             SNAP_CATCH2_NAMESPACE::random(bad_time.tv_nsec);
     777              :         }
     778            5 :         CATCH_REQUIRE_THROWS_MATCHES(
     779              :                   out.add_value("now", bad_time)
     780              :                 , snapdev::brs_invalid_value
     781              :                 , Catch::Matchers::ExceptionMessage(
     782              :                           "brs_error: nanoseconds are out of range: "
     783              :                         + std::to_string(bad_time.tv_nsec)
     784              :                         + "."));
     785              : 
     786            3 :         out.add_value("now", now);
     787              : 
     788              :         // make sure the header did not get smashed
     789              :         //
     790            1 :         data = buffer.str();
     791            1 :         CATCH_REQUIRE(data[0] == 'B');
     792            1 :         CATCH_REQUIRE(data[1] == 'R');
     793            1 :         CATCH_REQUIRE(data[2] == 'L');
     794            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
     795              : 
     796            1 :         CATCH_REQUIRE(data[4] == 3 << 2);  // hunk_sizes_t
     797            1 :         CATCH_REQUIRE(data[5] == (sizeof(now.tv_sec) + sizeof(std::uint32_t)) << 1);
     798            1 :         CATCH_REQUIRE(data[6] == 0);
     799            1 :         CATCH_REQUIRE(data[7] == 0);
     800              : 
     801            1 :         CATCH_REQUIRE(data[8] == 'n');    // name
     802            1 :         CATCH_REQUIRE(data[9] == 'o');
     803            1 :         CATCH_REQUIRE(data[10] == 'w');
     804              : 
     805              :         struct processor
     806              :         {
     807            5 :             static bool process_hunk(
     808              :                           snapdev::deserializer<std::stringstream> & in
     809              :                         , snapdev::field_t const & field
     810              :                         , timespec const & now
     811              :                         , bool & called_flag)
     812              :             {
     813            5 :                 CATCH_REQUIRE(field.f_name == "now");
     814            5 :                 CATCH_REQUIRE(field.f_sub_name.empty());
     815            5 :                 CATCH_REQUIRE(field.f_index == -1);
     816            5 :                 CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
     817              : 
     818            5 :                 timespec value;
     819            5 :                 CATCH_REQUIRE(in.read_data(value));
     820            5 :                 bool const result(memcmp(&value, &now, sizeof(now)) == 0);
     821            5 :                 if(!result)
     822              :                 {
     823              :                     std::cerr << "timespecs not equal: "
     824              :                         << value
     825              :                         << " (read back) != "
     826              :                         << now
     827            0 :                         << " (expected)\n";
     828              :                 }
     829            5 :                 CATCH_REQUIRE(result);
     830              : 
     831            5 :                 called_flag = true;
     832              : 
     833            5 :                 return true;
     834              :             }
     835              :         };
     836              : 
     837            6 :         for(int count(0); count < 5; ++count)
     838              :         {
     839              :             // Note: you don't usually end up re-using the serializer buffer
     840              :             //       but here it's practical; warning: this works because in
     841              :             //       a C++ stream the seek position is distinct between the
     842              :             //       write() and read() functions (tellp() vs tellg())
     843              :             //
     844            5 :             buffer.clear();
     845            5 :             if(count != 0)
     846              :             {
     847              :                 // the second time and further need a seek
     848              :                 //
     849            4 :                 buffer.seekg(0, std::ios_base::beg);
     850              :             }
     851              : 
     852            5 :             snapdev::deserializer in(buffer);
     853              : 
     854              :             // WARNING: we want to use CATCH_...() macros inside the callback
     855              :             //          so make sure not to use one around unserialize_buffer().
     856              :             //
     857            5 :             bool process_hunk_called(false);
     858           20 :             snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     859           10 :                           &processor::process_hunk
     860              :                         , std::placeholders::_1
     861              :                         , std::placeholders::_2
     862              :                         , now
     863           20 :                         , std::ref(process_hunk_called)));
     864            5 :             bool const r(in.deserialize(func));
     865            5 :             CATCH_REQUIRE(r);
     866            5 :             CATCH_REQUIRE(process_hunk_called);
     867            5 :         }
     868              : 
     869              :         {
     870              :             struct any_size_processor
     871              :             {
     872            1 :                 static bool any_size_process_hunk(
     873              :                               snapdev::deserializer<std::stringstream> & in
     874              :                             , snapdev::field_t const & field
     875              :                             , timespec const & now
     876              :                             , bool & called_flag)
     877              :                 {
     878            1 :                     CATCH_REQUIRE(field.f_name == "now");
     879            1 :                     CATCH_REQUIRE(field.f_sub_name.empty());
     880            1 :                     CATCH_REQUIRE(field.f_index == -1);
     881              :                     //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
     882              : 
     883            1 :                     timespec value;
     884            1 :                     in.read_data(value); // no CATCH_...() here we're expecting a throw;
     885            0 :                     bool const result(memcmp(&value, &now, sizeof(now)) == 0);
     886            0 :                     if(!result)
     887              :                     {
     888              :                         std::cerr << "timespecs not equal: "
     889              :                             << value
     890              :                             << " (read back) != "
     891              :                             << now
     892            0 :                             << " (expected)\n";
     893              :                     }
     894            0 :                     CATCH_REQUIRE(result);
     895              : 
     896            0 :                     called_flag = true;
     897              : 
     898            0 :                     return true;
     899              :                 }
     900              :             };
     901              : 
     902              :             // re-re-using
     903              :             //
     904            1 :             buffer.clear();
     905            1 :             buffer.seekg(0, std::ios_base::beg);
     906              : 
     907            1 :             snapdev::deserializer in(buffer);
     908              : 
     909              :             // the buffer should not be modified while deserializing,
     910              :             // but this is a test and we want to see that destroying
     911              :             // something in the buffer makes the deserialization fail
     912              :             //
     913            1 :             buffer.seekp(5, std::ios_base::beg);
     914            1 :             char const v((sizeof(now.tv_sec) + sizeof(std::uint32_t) - 1) << 1);
     915            1 :             buffer.write(&v, 1);
     916              : 
     917              :             // WARNING: we want to use CATCH_...() macros inside the callback
     918              :             //          so make sure not to use one around deserialize().
     919              :             //
     920            1 :             bool process_hunk_called(false);
     921            4 :             snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
     922            2 :                           &any_size_processor::any_size_process_hunk
     923              :                         , std::placeholders::_1
     924              :                         , std::placeholders::_2
     925              :                         , now
     926            4 :                         , std::ref(process_hunk_called)));
     927              : 
     928            3 :             CATCH_REQUIRE_THROWS_MATCHES(
     929              :                       in.deserialize(func)
     930              :                     , snapdev::brs_logic_error
     931              :                     , Catch::Matchers::ExceptionMessage(
     932              :                               "brs_logic_error: hunk size is 11, but you are trying to read 12."));
     933              : 
     934              :             // the read must happened before we set this variable to
     935              :             // true so if it all works as expected, this is still false
     936              :             //
     937            1 :             CATCH_REQUIRE_FALSE(process_hunk_called);
     938              : 
     939              :             // test passed, fix the size for the next tests
     940              :             //
     941            1 :             buffer.seekp(5, std::ios_base::beg);
     942            1 :             char const c((sizeof(now.tv_sec) + sizeof(std::uint32_t)) << 1);
     943            1 :             buffer.write(&c, 1);
     944            1 :         }
     945              : 
     946              :         {
     947              :             struct any_size_processor
     948              :             {
     949            1 :                 static bool any_size_process_hunk(
     950              :                               snapdev::deserializer<std::stringstream> & in
     951              :                             , snapdev::field_t const & field
     952              :                             , timespec const & now
     953              :                             , bool & called_flag)
     954              :                 {
     955            1 :                     CATCH_REQUIRE(field.f_name == "now");
     956            1 :                     CATCH_REQUIRE(field.f_sub_name.empty());
     957            1 :                     CATCH_REQUIRE(field.f_index == -1);
     958              :                     //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
     959              : 
     960            1 :                     timespec value;
     961            1 :                     in.read_data(value); // no CATCH_...() here we're expecting a throw;
     962            0 :                     bool const result(memcmp(&value, &now, sizeof(now)) == 0);
     963            0 :                     if(!result)
     964              :                     {
     965              :                         std::cerr << "timespecs not equal: "
     966              :                             << value
     967              :                             << " (read back) != "
     968              :                             << now
     969            0 :                             << " (expected)\n";
     970              :                     }
     971            0 :                     CATCH_REQUIRE(result);
     972              : 
     973            0 :                     called_flag = true;
     974              : 
     975            0 :                     return true;
     976              :                 }
     977              :             };
     978              : 
     979              :             // re-re-using
     980              :             //
     981            1 :             buffer.clear();
     982            1 :             buffer.seekg(0, std::ios_base::beg);
     983              : 
     984            1 :             snapdev::deserializer in(buffer);
     985              : 
     986              :             // the buffer should not be modified while deserializing,
     987              :             // but this is a test and we want to see that destroying
     988              :             // something in the buffer makes the deserialization fail
     989              :             //
     990            1 :             std::uint32_t bad_nsec(0);
     991            2 :             while(bad_nsec < 1'000'000'000)
     992              :             {
     993            1 :                 SNAP_CATCH2_NAMESPACE::random(bad_nsec);
     994              :             }
     995            1 :             buffer.seekp(19, std::ios_base::beg);
     996            1 :             buffer.write(reinterpret_cast<char const *>(&bad_nsec), sizeof(std::uint32_t));
     997              : 
     998              :             // WARNING: we want to use CATCH_...() macros inside the callback
     999              :             //          so make sure not to use one around deserialize().
    1000              :             //
    1001            1 :             bool process_hunk_called(false);
    1002            4 :             snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1003            2 :                           &any_size_processor::any_size_process_hunk
    1004              :                         , std::placeholders::_1
    1005              :                         , std::placeholders::_2
    1006              :                         , now
    1007            4 :                         , std::ref(process_hunk_called)));
    1008              : 
    1009            1 :             CATCH_REQUIRE_THROWS_MATCHES(
    1010              :                       in.deserialize(func)
    1011              :                     , snapdev::brs_invalid_value
    1012              :                     , Catch::Matchers::ExceptionMessage(
    1013              :                               "brs_error: nanoseconds are out of range: "
    1014              :                             + std::to_string(bad_nsec)
    1015              :                             + "."));
    1016              : 
    1017              :             // the read must happened before we set this variable to
    1018              :             // true so if it all works as expected, this is still false
    1019              :             //
    1020            1 :             CATCH_REQUIRE_FALSE(process_hunk_called);
    1021              : 
    1022              :             // test passed, fix tv_nsec for the next tests
    1023              :             //
    1024            1 :             buffer.seekp(19, std::ios_base::beg);
    1025            1 :             std::uint32_t const good_nsec(now.tv_nsec);
    1026            1 :             buffer.write(reinterpret_cast<char const *>(&good_nsec), sizeof(std::uint32_t));
    1027            1 :         }
    1028              : 
    1029            1 :         std::string short_value(data);
    1030           13 :         for(std::size_t size(data.length() - 1); size >= data.length() - sizeof(now.tv_sec) - sizeof(std::uint32_t); --size)
    1031              :         {
    1032              :             struct short_processor
    1033              :             {
    1034           12 :                 static bool short_process_hunk(
    1035              :                               snapdev::deserializer<std::stringstream> & in
    1036              :                             , snapdev::field_t const & field
    1037              :                             , bool & called_flag)
    1038              :                 {
    1039           12 :                     CATCH_REQUIRE(field.f_name == "now");
    1040           12 :                     CATCH_REQUIRE(field.f_sub_name.empty());
    1041           12 :                     CATCH_REQUIRE(field.f_index == -1);
    1042              :                     //CATCH_REQUIRE(field.f_size == sizeof(now.tv_sec) + sizeof(std::uint32_t));
    1043              : 
    1044           12 :                     timespec value;
    1045           12 :                     CATCH_REQUIRE_FALSE(in.read_data(value));
    1046              : 
    1047           12 :                     called_flag = true;
    1048              : 
    1049           12 :                     return false;
    1050              :                 }
    1051              :             };
    1052              : 
    1053              :             // re-re-using
    1054              :             //
    1055           12 :             short_value.resize(size);
    1056           12 :             buffer.str(short_value);
    1057           12 :             buffer.clear();
    1058           12 :             buffer.seekg(0, std::ios_base::beg);
    1059              : 
    1060           12 :             snapdev::deserializer in(buffer);
    1061              : 
    1062              :             // WARNING: we want to use CATCH_...() macros inside the callback
    1063              :             //          so make sure not to use one around deserialize().
    1064              :             //
    1065           12 :             bool process_hunk_called(false);
    1066           48 :             snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1067           24 :                           &short_processor::short_process_hunk
    1068              :                         , std::placeholders::_1
    1069              :                         , std::placeholders::_2
    1070           48 :                         , std::ref(process_hunk_called)));
    1071              : 
    1072           12 :             CATCH_REQUIRE_FALSE(in.deserialize(func));
    1073              : 
    1074              :             // even if we return false, we processed the hunk
    1075              :             //
    1076           12 :             CATCH_REQUIRE(process_hunk_called);
    1077           12 :         }
    1078            1 :     }
    1079           14 :     CATCH_END_SECTION()
    1080              : 
    1081           14 :     CATCH_START_SECTION("brs_basic_types: push/restore array (varying name)")
    1082              :     {
    1083            1 :         std::stringstream buffer;
    1084            1 :         snapdev::serializer out(buffer);
    1085              : 
    1086            1 :         std::string data(buffer.str());
    1087            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1088              : 
    1089            1 :         CATCH_REQUIRE(data[0] == 'B');
    1090            1 :         CATCH_REQUIRE(data[1] == 'R');
    1091            1 :         CATCH_REQUIRE(data[2] == 'L');
    1092            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1093              : 
    1094            1 :         std::vector<int> order;
    1095              :         typedef std::map<int, std::string> value_t;
    1096            1 :         value_t values;
    1097            1 :         int index(-1);
    1098           26 :         for(int i(0); i < 25; ++i)
    1099              :         {
    1100              :             for(;;)
    1101              :             {
    1102           26 :                 index = rand() % 256;
    1103           26 :                 if(values.find(index) == values.end())
    1104              :                 {
    1105           25 :                     break;
    1106              :                 }
    1107              :             }
    1108           25 :             std::string str;
    1109           25 :             int max(rand() % 25 + 1);
    1110          373 :             for(int j(0); j < max; ++j)
    1111              :             {
    1112          348 :                 str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
    1113              :             }
    1114           25 :             values[index] = str;
    1115           25 :             order.push_back(index);
    1116              : 
    1117           25 :             out.add_value("str" + std::to_string(index), index, str);
    1118           25 :         }
    1119              : 
    1120              :         // make sure it did not get smashed
    1121            1 :         data = buffer.str();
    1122            1 :         CATCH_REQUIRE(data[0] == 'B');
    1123            1 :         CATCH_REQUIRE(data[1] == 'R');
    1124            1 :         CATCH_REQUIRE(data[2] == 'L');
    1125            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1126              : 
    1127              :         struct processor
    1128              :         {
    1129           25 :             static bool process_hunk(
    1130              :                           snapdev::deserializer<std::stringstream> & in
    1131              :                         , snapdev::field_t const & field
    1132              :                         , value_t & values)
    1133              :             {
    1134           25 :                 CATCH_REQUIRE(field.f_sub_name.empty());
    1135           25 :                 CATCH_REQUIRE(field.f_index != -1);
    1136              : 
    1137           25 :                 std::string expected_name("str" + std::to_string(field.f_index));
    1138           25 :                 CATCH_REQUIRE(field.f_name == expected_name);
    1139              : 
    1140           25 :                 CATCH_REQUIRE(field.f_size == values[field.f_index].length());
    1141              : 
    1142           25 :                 std::string value;
    1143           25 :                 in.read_data(value);
    1144           25 :                 CATCH_REQUIRE(value == values[field.f_index]);
    1145              : 
    1146           25 :                 return true;
    1147           25 :             }
    1148              :         };
    1149              : 
    1150              :         // Note: you don't usually end up re-using the serializer buffer
    1151              :         //       but here it's practical; warning: this works because in
    1152              :         //       a C++ stream the seek position is distinct between the
    1153              :         //       write() and read() functions (tellp() vs tellg())
    1154              :         //
    1155            1 :         buffer.clear();
    1156              : 
    1157            1 :         snapdev::deserializer in(buffer);
    1158              : 
    1159              :         // WARNING: we want to use CATCH_...() macros inside the callback
    1160              :         //          so make sure not to use one around unserialize_buffer().
    1161              :         //
    1162            4 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1163            2 :                       &processor::process_hunk
    1164              :                     , std::placeholders::_1
    1165              :                     , std::placeholders::_2
    1166            2 :                     , values));
    1167            1 :         bool const r(in.deserialize(func));
    1168            1 :         CATCH_REQUIRE(r);
    1169            1 :     }
    1170           14 :     CATCH_END_SECTION()
    1171              : 
    1172           14 :     CATCH_START_SECTION("brs_basic_types: push/restore array (same name)")
    1173              :     {
    1174            1 :         std::stringstream buffer;
    1175            1 :         snapdev::serializer out(buffer);
    1176              : 
    1177            1 :         std::string data(buffer.str());
    1178            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1179              : 
    1180            1 :         CATCH_REQUIRE(data[0] == 'B');
    1181            1 :         CATCH_REQUIRE(data[1] == 'R');
    1182            1 :         CATCH_REQUIRE(data[2] == 'L');
    1183            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1184              : 
    1185              :         // order does not matter and we can have gaps, to test that, create
    1186              :         // a map of a few values and corresponding strings
    1187              :         //
    1188            1 :         std::vector<int> order;
    1189              :         typedef std::map<int, std::string> value_t;
    1190            1 :         value_t values;
    1191            1 :         int index(-1);
    1192            1 :         int const size(25);
    1193           26 :         for(int i(0); i < size; ++i)
    1194              :         {
    1195              :             for(;;)
    1196              :             {
    1197           26 :                 index = rand() % 256;
    1198           26 :                 if(values.find(index) == values.end())
    1199              :                 {
    1200           25 :                     break;
    1201              :                 }
    1202              :             }
    1203           25 :             std::string str;
    1204           25 :             int max(rand() % 25 + 1);
    1205          377 :             for(int j(0); j < max; ++j)
    1206              :             {
    1207          352 :                 str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
    1208              :             }
    1209           25 :             values[index] = str;
    1210           25 :             order.push_back(index);
    1211              : 
    1212           25 :             if((index & 1) != 0)
    1213              :             {
    1214              :                 // pass as a 'char const *'
    1215           51 :                 out.add_value("unique", index, str.c_str());
    1216              :             }
    1217              :             else
    1218              :             {
    1219              :                 // pass as an std::string
    1220           24 :                 out.add_value("unique", index, str);
    1221              :             }
    1222              : 
    1223              :             // nullptr does nothing
    1224           75 :             out.add_value("unique", index + size, nullptr);
    1225           25 :         }
    1226              : 
    1227              :         // make sure it did not get smashed
    1228            1 :         data = buffer.str();
    1229            1 :         CATCH_REQUIRE(data[0] == 'B');
    1230            1 :         CATCH_REQUIRE(data[1] == 'R');
    1231            1 :         CATCH_REQUIRE(data[2] == 'L');
    1232            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1233              : 
    1234              :         struct processor
    1235              :         {
    1236           25 :             static bool process_hunk(
    1237              :                           snapdev::deserializer<std::stringstream> & in
    1238              :                         , snapdev::field_t const & field
    1239              :                         , value_t & values)
    1240              :             {
    1241           25 :                 CATCH_REQUIRE(field.f_name == "unique");
    1242           25 :                 CATCH_REQUIRE(field.f_sub_name.empty());
    1243           25 :                 CATCH_REQUIRE(field.f_index != -1);
    1244              : 
    1245           25 :                 CATCH_REQUIRE(field.f_size == values[field.f_index].length());
    1246              : 
    1247           25 :                 std::string value;
    1248           25 :                 in.read_data(value);
    1249           25 :                 CATCH_REQUIRE(value == values[field.f_index]);
    1250              : 
    1251           25 :                 return true;
    1252           25 :             }
    1253              :         };
    1254              : 
    1255              :         // Note: you don't usually end up re-using the serializer buffer
    1256              :         //       but here it's practical; warning: this works because in
    1257              :         //       a C++ stream the seek position is distinct between the
    1258              :         //       write() and read() functions (tellp() vs tellg())
    1259              :         //
    1260            1 :         buffer.clear();
    1261              : 
    1262            1 :         snapdev::deserializer in(buffer);
    1263              : 
    1264              :         // WARNING: we want to use CATCH_...() macros inside the callback
    1265              :         //          so make sure not to use one around unserialize_buffer().
    1266              :         //
    1267            4 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1268            2 :                       &processor::process_hunk
    1269              :                     , std::placeholders::_1
    1270              :                     , std::placeholders::_2
    1271            2 :                     , values));
    1272            1 :         bool const r(in.deserialize(func));
    1273            1 :         CATCH_REQUIRE(r);
    1274            1 :     }
    1275           14 :     CATCH_END_SECTION()
    1276              : 
    1277           14 :     CATCH_START_SECTION("brs_basic_types: push/restore map of strings")
    1278              :     {
    1279            1 :         std::stringstream buffer;
    1280            1 :         snapdev::serializer out(buffer);
    1281              : 
    1282            1 :         std::string data(buffer.str());
    1283            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1284              : 
    1285            1 :         CATCH_REQUIRE(data[0] == 'B');
    1286            1 :         CATCH_REQUIRE(data[1] == 'R');
    1287            1 :         CATCH_REQUIRE(data[2] == 'L');
    1288            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1289              : 
    1290              :         // order does not matter and we can have gaps, to test that, create
    1291              :         // a map of a few values and corresponding strings
    1292              :         //
    1293            1 :         std::vector<std::string> order;
    1294              :         typedef std::map<std::string, std::string> value_t;
    1295            1 :         value_t values;
    1296            1 :         int empty(rand() % 25);
    1297           26 :         for(int i(0); i < 25; ++i)
    1298              :         {
    1299           25 :             int max(0);
    1300           25 :             std::string index;
    1301              :             for(;;)
    1302              :             {
    1303           25 :                 max = rand() % 25 + 1;
    1304          356 :                 for(int j(0); j < max; ++j)
    1305              :                 {
    1306          331 :                     index += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
    1307              :                 }
    1308           25 :                 if(values.find(index) == values.end())
    1309              :                 {
    1310              :                     // index is unique, use it
    1311           25 :                     break;
    1312              :                 }
    1313            0 :             }
    1314              : 
    1315           25 :             std::string str;
    1316           25 :             if(i != empty)
    1317              :             {
    1318           24 :                 max = rand() % 25;
    1319          293 :                 for(int j(0); j < max; ++j)
    1320              :                 {
    1321          269 :                     str += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
    1322              :                 }
    1323              :             }
    1324              : 
    1325           25 :             values[index] = str;
    1326           25 :             order.push_back(index);
    1327              : 
    1328           25 :             if((i & 1) == 0)
    1329              :             {
    1330           13 :                 if(str.empty()
    1331           13 :                 || (rand() & 1) != 0)
    1332              :                 {
    1333           18 :                     out.add_value("mapping", index, str);
    1334              :                 }
    1335              :                 else
    1336              :                 {
    1337           21 :                     out.add_value_if_not_empty("mapping", index, str);
    1338              :                 }
    1339              :             }
    1340              :             else
    1341              :             {
    1342              :                 // bare string
    1343              :                 //
    1344           36 :                 out.add_value("mapping", index, str.c_str());
    1345              :             }
    1346              : 
    1347           75 :             out.add_value("mapping", index + "a", nullptr);
    1348           25 :         }
    1349            5 :         out.add_value_if_not_empty("mapping", "last-index", std::string());
    1350              : 
    1351              : //{
    1352              : //std::ofstream p("t1.txt");
    1353              : //std::string s(buffer.str());
    1354              : //p << s;
    1355              : //}
    1356              : 
    1357              :         // make sure it did not get smashed
    1358            1 :         data = buffer.str();
    1359            1 :         CATCH_REQUIRE(data[0] == 'B');
    1360            1 :         CATCH_REQUIRE(data[1] == 'R');
    1361            1 :         CATCH_REQUIRE(data[2] == 'L');
    1362            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1363              : 
    1364              :         struct processor
    1365              :         {
    1366           25 :             static bool process_hunk(
    1367              :                           snapdev::deserializer<std::stringstream> & in
    1368              :                         , snapdev::field_t const & field
    1369              :                         , value_t & values)
    1370              :             {
    1371           25 :                 CATCH_REQUIRE(field.f_name == "mapping");
    1372           25 :                 CATCH_REQUIRE_FALSE(field.f_sub_name.empty());
    1373           25 :                 CATCH_REQUIRE(field.f_index == -1);
    1374           25 :                 CATCH_REQUIRE(values.find(field.f_sub_name) != values.end());
    1375              : 
    1376           25 :                 CATCH_REQUIRE(field.f_size == values[field.f_sub_name].length());
    1377              : 
    1378           25 :                 std::string value;
    1379           25 :                 in.read_data(value);
    1380           25 :                 CATCH_REQUIRE(value == values[field.f_sub_name]);
    1381              : 
    1382           25 :                 return true;
    1383           25 :             }
    1384              :         };
    1385              : 
    1386              :         // Note: you don't usually end up re-using the serializer buffer
    1387              :         //       but here it's practical; warning: this works because in
    1388              :         //       a C++ stream the seek position is distinct between the
    1389              :         //       write() and read() functions (tellp() vs tellg())
    1390              :         //
    1391            1 :         buffer.clear();
    1392              : 
    1393            1 :         snapdev::deserializer in(buffer);
    1394              : 
    1395              :         // WARNING: we want to use CATCH_...() macros inside the callback
    1396              :         //          so make sure not to use one around unserialize_buffer().
    1397              :         //
    1398            4 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1399            2 :                       &processor::process_hunk
    1400              :                     , std::placeholders::_1
    1401              :                     , std::placeholders::_2
    1402            2 :                     , values));
    1403            1 :         bool const r(in.deserialize(func));
    1404            1 :         CATCH_REQUIRE(r);
    1405            1 :     }
    1406           14 :     CATCH_END_SECTION()
    1407              : 
    1408           14 :     CATCH_START_SECTION("brs_basic_types: push/restore map of struct")
    1409              :     {
    1410              :         struct data_t
    1411              :         {
    1412              :             std::uint32_t   f_color = 0xaa648b;
    1413              :             float           f_height = 1.3f;
    1414              :             std::uint8_t    f_status = 3;
    1415              :             double          f_width = 3.7;
    1416              : 
    1417           69 :             bool operator == (data_t const & rhs) const
    1418              :             {
    1419              : #pragma GCC diagnostic push
    1420              : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1421           69 :                 return f_color == rhs.f_color
    1422           69 :                     && f_height == rhs.f_height
    1423           69 :                     && f_status == rhs.f_status
    1424          138 :                     && f_width == rhs.f_width;
    1425              : #pragma GCC diagnostic pop
    1426              :             }
    1427              :         };
    1428              : 
    1429            1 :         std::stringstream buffer;
    1430            1 :         snapdev::serializer out(buffer);
    1431              : 
    1432            1 :         std::string data(buffer.str());
    1433            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1434              : 
    1435            1 :         CATCH_REQUIRE(data[0] == 'B');
    1436            1 :         CATCH_REQUIRE(data[1] == 'R');
    1437            1 :         CATCH_REQUIRE(data[2] == 'L');
    1438            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1439              : 
    1440              :         // order does not matter and we can have gaps, to test that, create
    1441              :         // a map of a few values and corresponding strings
    1442              :         //
    1443            1 :         std::vector<std::string> order;
    1444              :         typedef std::map<std::string, data_t> value_t;
    1445            1 :         value_t values;
    1446            1 :         std::size_t const count(rand() % 100 + 25);
    1447           70 :         for(std::size_t i(0); i < count; ++i)
    1448              :         {
    1449           69 :             std::string index;
    1450              :             for(;;)
    1451              :             {
    1452           69 :                 int const max(rand() % 25 + 1);
    1453          954 :                 for(int j(0); j < max; ++j)
    1454              :                 {
    1455          885 :                     index += ' ' + rand() % ('~' + 1 - ' '); // ASCII except controls
    1456              :                 }
    1457           69 :                 if(values.find(index) == values.end())
    1458              :                 {
    1459              :                     // index is unique, use it
    1460           69 :                     break;
    1461              :                 }
    1462            0 :             }
    1463              : 
    1464           69 :             data_t my_data;
    1465           69 :             my_data.f_color = rand(),
    1466           69 :             my_data.f_height = rand(),
    1467          138 :             my_data.f_status = rand(),
    1468           69 :             my_data.f_width = rand(),
    1469              : 
    1470           69 :             values[index] = my_data;
    1471           69 :             order.push_back(index);
    1472              : 
    1473          207 :             out.add_value("set", index, my_data);
    1474           69 :         }
    1475              : //{
    1476              : //std::ofstream p("t1.txt");
    1477              : //std::string s(buffer.str());
    1478              : //p << s;
    1479              : //}
    1480              : 
    1481              :         // make sure it did not get smashed
    1482            1 :         data = buffer.str();
    1483            1 :         CATCH_REQUIRE(data[0] == 'B');
    1484            1 :         CATCH_REQUIRE(data[1] == 'R');
    1485            1 :         CATCH_REQUIRE(data[2] == 'L');
    1486            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1487              : 
    1488              :         struct processor
    1489              :         {
    1490           69 :             static bool process_hunk(
    1491              :                           snapdev::deserializer<std::stringstream> & in
    1492              :                         , snapdev::field_t const & field
    1493              :                         , value_t & values)
    1494              :             {
    1495           69 :                 CATCH_REQUIRE(field.f_name == "set");
    1496           69 :                 CATCH_REQUIRE_FALSE(field.f_sub_name.empty());
    1497           69 :                 CATCH_REQUIRE(field.f_index == -1);
    1498           69 :                 CATCH_REQUIRE(values.find(field.f_sub_name) != values.end());
    1499              : 
    1500           69 :                 CATCH_REQUIRE(field.f_size == sizeof(data_t));
    1501              : 
    1502           69 :                 data_t value;
    1503           69 :                 in.read_data(value);
    1504           69 :                 data_t expected(values[field.f_sub_name]);
    1505           69 :                 CATCH_REQUIRE(value == expected);
    1506              : 
    1507          138 :                 return true;
    1508              :             }
    1509              :         };
    1510              : 
    1511              :         // Note: you don't usually end up re-using the serializer buffer
    1512              :         //       but here it's practical; warning: this works because in
    1513              :         //       a C++ stream the seek position is distinct between the
    1514              :         //       write() and read() functions (tellp() vs tellg())
    1515              :         //
    1516            1 :         buffer.clear();
    1517              : 
    1518            1 :         snapdev::deserializer in(buffer);
    1519              : 
    1520              :         // WARNING: we want to use CATCH_...() macros inside the callback
    1521              :         //          so make sure not to use one around unserialize_buffer().
    1522              :         //
    1523            4 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1524            2 :                       &processor::process_hunk
    1525              :                     , std::placeholders::_1
    1526              :                     , std::placeholders::_2
    1527            2 :                     , values));
    1528            1 :         bool const r(in.deserialize(func));
    1529            1 :         CATCH_REQUIRE(r);
    1530            1 :     }
    1531           14 :     CATCH_END_SECTION()
    1532              : 
    1533           14 :     CATCH_START_SECTION("brs_basic_types: push/restore recursive")
    1534              :     {
    1535              :         class t1
    1536              :         {
    1537              :         public:
    1538            3 :             void serialize(snapdev::serializer<std::stringstream> & out) const
    1539              :             {
    1540            9 :                 out.add_value("name", f_name);
    1541            3 :             }
    1542              : 
    1543            3 :             bool process_hunk(
    1544              :                   snapdev::deserializer<std::stringstream> & in
    1545              :                 , snapdev::field_t const & field)
    1546              :             {
    1547            3 :                 CATCH_REQUIRE(field.f_sub_name.empty());
    1548            3 :                 CATCH_REQUIRE(field.f_index == -1);
    1549              : 
    1550            3 :                 if(field.f_name == "name")
    1551              :                 {
    1552            3 :                     std::string value;
    1553            3 :                     in.read_data(value);
    1554            3 :                     CATCH_REQUIRE(f_name == value);
    1555            3 :                 }
    1556              :                 else
    1557              :                 {
    1558            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
    1559              :                 }
    1560              : 
    1561            3 :                 return true;
    1562              :             }
    1563              : 
    1564            3 :             void set_name(std::string const & name)
    1565              :             {
    1566            3 :                 f_name = name;
    1567            3 :             }
    1568              : 
    1569              :         private:
    1570              :             std::string     f_name = "--undefined--";
    1571              :         };
    1572              : 
    1573              :         class t2
    1574              :         {
    1575              :         public:
    1576            1 :             void serialize(snapdev::serializer<std::stringstream> & out) const
    1577              :             {
    1578           22 :                 for(std::size_t idx(0); idx < std::size(f_sizes); ++idx)
    1579              :                 {
    1580           30 :                     out.add_value("size", idx, f_sizes[idx]);
    1581              :                 }
    1582            1 :             }
    1583              : 
    1584           10 :             bool process_hunk(
    1585              :                   snapdev::deserializer<std::stringstream> & in
    1586              :                 , snapdev::field_t const & field)
    1587              :             {
    1588           10 :                 if(field.f_name == "size")
    1589              :                 {
    1590           20 :                     CATCH_REQUIRE(static_cast<std::size_t>(field.f_index) < std::size(f_sizes));
    1591              : 
    1592           10 :                     std::int32_t value;
    1593           10 :                     in.read_data(value);
    1594           10 :                     CATCH_REQUIRE(value == f_sizes[field.f_index]);
    1595              :                 }
    1596              :                 else
    1597              :                 {
    1598            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
    1599              :                 }
    1600              : 
    1601           10 :                 return true;
    1602              :             }
    1603              : 
    1604              :         private:
    1605              :             std::int32_t    f_sizes[10] = {
    1606              :                     rand(), rand(), rand(), rand(), rand(),
    1607              :                     rand(), rand(), rand(), rand(), rand(),
    1608              :                 };
    1609              :         };
    1610              : 
    1611              :         // c includes an array of t1's and one t2
    1612              :         class c
    1613              :         {
    1614              :         public:
    1615            1 :             c()
    1616            1 :             {
    1617            1 :                 int const max(rand() % 5 + 3);
    1618            4 :                 for(int idx(0); idx < max; ++idx)
    1619              :                 {
    1620            3 :                     std::string name;
    1621            3 :                     int len(rand() % 25 + 10);
    1622           61 :                     for(int j(0); j < len; ++j)
    1623              :                     {
    1624           58 :                         name += 'a' + rand() % 26;
    1625              :                     }
    1626            3 :                     t1 t;
    1627            3 :                     t.set_name(name);
    1628            3 :                     f_t1.push_back(t);
    1629            3 :                 }
    1630              : 
    1631            1 :                 int const array_max(rand() % 90 + 10);
    1632           64 :                 for(int idx(0); idx < array_max; ++idx)
    1633              :                 {
    1634           63 :                     float nominator(static_cast<float>(rand()));
    1635           63 :                     float denominator(0.0f);
    1636              : #pragma GCC diagnostic push
    1637              : #pragma GCC diagnostic ignored "-Wfloat-equal"
    1638          126 :                     while(denominator == 0.0f)
    1639              :                     {
    1640           63 :                         denominator = static_cast<float>(rand());
    1641              :                     }
    1642              : #pragma GCC diagnostic pop
    1643           63 :                     f_t3.push_back(nominator / denominator);
    1644              :                 }
    1645            1 :             }
    1646              : 
    1647            1 :             void serialize(snapdev::serializer<std::stringstream> & out) const
    1648              :             {
    1649            3 :                 out.add_value("count", f_count);
    1650            3 :                 out.add_value("age", f_age);
    1651              : 
    1652              :                 {
    1653            1 :                     int const max(static_cast<int>(f_t1.size()));
    1654            4 :                     for(int idx(0); idx < max; ++idx)
    1655              :                     {
    1656            9 :                         snapdev::recursive r(out, "t1_array");
    1657            3 :                         f_t1[idx].serialize(out);
    1658            3 :                     }
    1659              :                 }
    1660              : 
    1661            3 :                 out.add_value("float-array", f_t3);
    1662              : 
    1663              :                 {
    1664            3 :                     snapdev::recursive r(out, "t2");
    1665            1 :                     f_t2.serialize(out);
    1666            1 :                 }
    1667              : 
    1668            5 :                 out.add_value("flower-color", std::string("bluish"));
    1669            3 :                 out.add_value("flower-height", std::string());  // unknown
    1670            3 :                 out.add_value_if_not_empty("flower-name", std::string());  // unknown -- not added
    1671              : 
    1672            3 :                 out.add_value("dog-color", "red");
    1673            3 :                 out.add_value("dog-height", "");  // unknown
    1674            5 :                 out.add_value_if_not_empty("dog-name", "");  // unknown -- not added
    1675            3 :                 out.add_value("dog-nothing", nullptr);  // unknown -- not added
    1676            5 :                 out.add_value_if_not_empty("dog-eye-color", "green");
    1677            1 :             }
    1678              : 
    1679           12 :             bool process_hunk(
    1680              :                   snapdev::deserializer<std::stringstream> & in
    1681              :                 , snapdev::field_t const & field)
    1682              :             {
    1683           12 :                 if(field.f_name == "count")
    1684              :                 {
    1685            1 :                     int value;
    1686            1 :                     in.read_data(value);
    1687            1 :                     CATCH_REQUIRE(f_count == value);
    1688              :                 }
    1689           11 :                 else if(field.f_name == "age")
    1690              :                 {
    1691            1 :                     int value;
    1692            1 :                     in.read_data(value);
    1693            1 :                     CATCH_REQUIRE(f_age == value);
    1694              :                 }
    1695           10 :                 else if(field.f_name == "t1_array")
    1696              :                 {
    1697              :                     // this is a set of sub-classes so we do not have an index
    1698              :                     //
    1699            3 :                     CATCH_REQUIRE(field.f_index == -1);
    1700            3 :                     CATCH_REQUIRE(field.f_sub_name == std::string());
    1701              : 
    1702              :                     // in a normal implementation, here we would create a new
    1703              :                     // item and append it if the load succeeds; the test will
    1704              :                     // instead verify that the f_t1_index is still valid and
    1705              :                     // test load that next f_t1 object
    1706              :                     //
    1707            3 :                     CATCH_REQUIRE(f_t1_index < f_t1.size());
    1708              : 
    1709            9 :                     snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1710            6 :                                   &t1::process_hunk
    1711            6 :                                 , &f_t1[f_t1_index]
    1712              :                                 , std::placeholders::_1
    1713            6 :                                 , std::placeholders::_2));
    1714            3 :                     bool const r(in.deserialize(func));
    1715            3 :                     CATCH_REQUIRE(r);
    1716              : 
    1717            3 :                     ++f_t1_index;
    1718            3 :                 }
    1719            7 :                 else if(field.f_name == "float-array")
    1720              :                 {
    1721            1 :                     CATCH_REQUIRE(field.f_index == -1);
    1722            1 :                     CATCH_REQUIRE(field.f_sub_name == std::string());
    1723              : 
    1724            1 :                     std::vector<float> v;
    1725            1 :                     in.read_data(v);
    1726            1 :                     CATCH_REQUIRE(v.size() == f_t3.size());
    1727            1 :                     CATCH_REQUIRE(v == f_t3);
    1728            1 :                 }
    1729            6 :                 else if(field.f_name == "t2")
    1730              :                 {
    1731            3 :                     snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1732            2 :                                   &t2::process_hunk
    1733            2 :                                 , &f_t2
    1734              :                                 , std::placeholders::_1
    1735            2 :                                 , std::placeholders::_2));
    1736            1 :                     bool const r(in.deserialize(func));
    1737            1 :                     CATCH_REQUIRE(r);
    1738            1 :                 }
    1739            5 :                 else if(field.f_name == "flower-color")
    1740              :                 {
    1741            1 :                     std::string value;
    1742            1 :                     in.read_data(value);
    1743            3 :                     CATCH_REQUIRE(value.length() == std::string("bluish").length());
    1744            3 :                     CATCH_REQUIRE(value == std::string("bluish"));
    1745            1 :                 }
    1746            4 :                 else if(field.f_name == "flower-height")
    1747              :                 {
    1748            1 :                     std::string value;
    1749            1 :                     in.read_data(value);
    1750            1 :                     CATCH_REQUIRE(value == std::string());
    1751            1 :                 }
    1752            3 :                 else if(field.f_name == "flower-name")
    1753              :                 {
    1754              :                     // this field should not be added since the value is
    1755              :                     // the empty string and we on purpose avoid adding
    1756              :                     // it (i.e. "add value (only) if not empty")
    1757              :                     //
    1758            0 :                     CATCH_REQUIRE(field.f_name == "not-expected");
    1759              :                 }
    1760            3 :                 else if(field.f_name == "dog-color")
    1761              :                 {
    1762            1 :                     std::string value;
    1763            1 :                     in.read_data(value);
    1764            3 :                     CATCH_REQUIRE(value.length() == std::string("red").length());
    1765            3 :                     CATCH_REQUIRE(value == std::string("red"));
    1766            1 :                 }
    1767            2 :                 else if(field.f_name == "dog-height")
    1768              :                 {
    1769            1 :                     std::string value;
    1770            1 :                     in.read_data(value);
    1771            1 :                     CATCH_REQUIRE(value == std::string());
    1772            1 :                 }
    1773            1 :                 else if(field.f_name == "dog-name"
    1774            1 :                      || field.f_name == "dog-nothing")
    1775              :                 {
    1776              :                     // this field should not be added since the value is
    1777              :                     // the empty string and we on purpose avoid adding
    1778              :                     // it (i.e. "add value (only) if not empty")
    1779              :                     //
    1780            0 :                     CATCH_REQUIRE(field.f_name == "not-expected");
    1781              :                 }
    1782            1 :                 else if(field.f_name == "dog-eye-color")
    1783              :                 {
    1784            1 :                     std::string value;
    1785            1 :                     in.read_data(value);
    1786            3 :                     CATCH_REQUIRE(value == std::string("green"));
    1787            1 :                 }
    1788              :                 else
    1789              :                 {
    1790            0 :                     CATCH_REQUIRE(field.f_name == "?unknown?");
    1791              :                 }
    1792              : 
    1793           12 :                 return true;
    1794              :             }
    1795              : 
    1796              :         private:
    1797              :             int                 f_count = rand();
    1798              :             int                 f_age = rand() % 100;
    1799              :             std::vector<t1>     f_t1 = std::vector<t1>();
    1800              :             t2                  f_t2 = t2();
    1801              :             std::vector<float>  f_t3 = {};
    1802              :             std::size_t         f_t1_index = 0;
    1803              :         };
    1804              : 
    1805            1 :         c o;
    1806              : 
    1807            1 :         std::stringstream buffer;
    1808            1 :         snapdev::serializer out(buffer);
    1809              : 
    1810            1 :         std::string data(buffer.str());
    1811            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1812              : 
    1813            1 :         CATCH_REQUIRE(data[0] == 'B');
    1814            1 :         CATCH_REQUIRE(data[1] == 'R');
    1815            1 :         CATCH_REQUIRE(data[2] == 'L');
    1816            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1817              : 
    1818            1 :         o.serialize(out);
    1819              : 
    1820              :         // Note: you don't usually end up re-using the serializer buffer
    1821              :         //       but here it's practical; warning: this works because in
    1822              :         //       a C++ stream the seek position is distinct between the
    1823              :         //       write() and read() functions (tellp() vs tellg())
    1824              :         //
    1825            1 :         buffer.clear();
    1826              : 
    1827            1 :         snapdev::deserializer in(buffer);
    1828              : 
    1829              :         // WARNING: we want to use CATCH_...() macros inside the callback
    1830              :         //          so make sure not to use one around unserialize_buffer().
    1831              :         //
    1832            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    1833            2 :                       &c::process_hunk
    1834            2 :                     , &o
    1835              :                     , std::placeholders::_1
    1836            2 :                     , std::placeholders::_2));
    1837            1 :         bool const r(in.deserialize(func));
    1838            1 :         CATCH_REQUIRE(r);
    1839            1 :     }
    1840           14 :     CATCH_END_SECTION()
    1841           14 : }
    1842              : 
    1843              : 
    1844           14 : CATCH_TEST_CASE("brs_invalid", "[serialization][error]")
    1845              : {
    1846           14 :     CATCH_START_SECTION("brs_invalid: name missing")
    1847              :     {
    1848            1 :         std::stringstream buffer;
    1849            1 :         snapdev::serializer out(buffer);
    1850              : 
    1851            1 :         std::string data(buffer.str());
    1852            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1853              : 
    1854            1 :         CATCH_REQUIRE(data[0] == 'B');
    1855            1 :         CATCH_REQUIRE(data[1] == 'R');
    1856            1 :         CATCH_REQUIRE(data[2] == 'L');
    1857            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1858              : 
    1859            1 :         char value = 33;
    1860              : 
    1861              :         // FIELD
    1862            5 :         CATCH_REQUIRE_THROWS_MATCHES(
    1863              :                   out.add_value(std::string(), &value, sizeof(value))
    1864              :                 , snapdev::brs_cannot_be_empty
    1865              :                 , Catch::Matchers::ExceptionMessage(
    1866              :                           "brs_error: name cannot be an empty string."));
    1867              : 
    1868              :         // FIELD: timespec
    1869            1 :         struct timespec ts;
    1870            5 :         CATCH_REQUIRE_THROWS_MATCHES(
    1871              :                   out.add_value(std::string(), ts)
    1872              :                 , snapdev::brs_cannot_be_empty
    1873              :                 , Catch::Matchers::ExceptionMessage(
    1874              :                           "brs_error: name cannot be an empty string."));
    1875              : 
    1876              :         // ARRAY
    1877            5 :         CATCH_REQUIRE_THROWS_MATCHES(
    1878              :                   out.add_value(std::string(), 5, &value, sizeof(value))
    1879              :                 , snapdev::brs_cannot_be_empty
    1880              :                 , Catch::Matchers::ExceptionMessage(
    1881              :                           "brs_error: name cannot be an empty string."));
    1882              : 
    1883              :         // MAP (main name)
    1884            7 :         CATCH_REQUIRE_THROWS_MATCHES(
    1885              :                   out.add_value(std::string(), std::string(), &value, sizeof(value))
    1886              :                 , snapdev::brs_cannot_be_empty
    1887              :                 , Catch::Matchers::ExceptionMessage(
    1888              :                           "brs_error: name cannot be an empty string."));
    1889              : 
    1890              :         // MAP (sub-name)
    1891            9 :         CATCH_REQUIRE_THROWS_MATCHES(
    1892              :                   out.add_value("good-name", std::string(), &value, sizeof(value))
    1893              :                 , snapdev::brs_cannot_be_empty
    1894              :                 , Catch::Matchers::ExceptionMessage(
    1895              :                           "brs_error: sub-name cannot be an empty string."));
    1896              : 
    1897              :         // SUB-FIELD
    1898            5 :         CATCH_REQUIRE_THROWS_MATCHES(
    1899              :                   out.start_subfield(std::string())
    1900              :                 , snapdev::brs_cannot_be_empty
    1901              :                 , Catch::Matchers::ExceptionMessage(
    1902              :                           "brs_error: name cannot be an empty string."));
    1903            1 :     }
    1904           14 :     CATCH_END_SECTION()
    1905              : 
    1906           14 :     CATCH_START_SECTION("brs_invalid: name too long")
    1907              :     {
    1908            1 :         std::stringstream buffer;
    1909            1 :         snapdev::serializer out(buffer);
    1910              : 
    1911            1 :         std::string data(buffer.str());
    1912            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1913              : 
    1914            1 :         CATCH_REQUIRE(data[0] == 'B');
    1915            1 :         CATCH_REQUIRE(data[1] == 'R');
    1916            1 :         CATCH_REQUIRE(data[2] == 'L');
    1917            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1918              : 
    1919            1 :         std::string name;
    1920            1 :         std::size_t count(1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_name));
    1921            1 :         std::size_t const max(count + rand() % 200);
    1922          286 :         for(std::size_t idx(0); idx < max; ++idx)
    1923              :         {
    1924          285 :             name += rand() % 26 + 'a';
    1925              :         }
    1926              : 
    1927            1 :         char value = 33;
    1928              : 
    1929              :         // FIELD
    1930            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1931              :                   out.add_value(name, &value, sizeof(value))
    1932              :                 , snapdev::brs_out_of_range
    1933              :                 , Catch::Matchers::ExceptionMessage(
    1934              :                           "brs_out_of_range: name or hunk too large."));
    1935              : 
    1936              :         // FIELD: timespec
    1937            1 :         struct timespec ts;
    1938            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1939              :                   out.add_value(name, ts)
    1940              :                 , snapdev::brs_out_of_range
    1941              :                 , Catch::Matchers::ExceptionMessage(
    1942              :                           "brs_out_of_range: name too large."));
    1943              : 
    1944              :         // ARRAY
    1945            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1946              :                   out.add_value(name, 17, &value, sizeof(value))
    1947              :                 , snapdev::brs_out_of_range
    1948              :                 , Catch::Matchers::ExceptionMessage(
    1949              :                           "brs_out_of_range: name, index, or hunk too large."));
    1950              : 
    1951              :         // MAP (main name)
    1952            7 :         CATCH_REQUIRE_THROWS_MATCHES(
    1953              :                   out.add_value(name, "name-too-long", &value, sizeof(value))
    1954              :                 , snapdev::brs_out_of_range
    1955              :                 , Catch::Matchers::ExceptionMessage(
    1956              :                           "brs_out_of_range: name, sub-name, or hunk too large."));
    1957              : 
    1958              :         // MAP (sub-name)
    1959              :         //
    1960              :         // sub-names make use of an std::uint8_t for their size
    1961              :         //
    1962            1 :         std::string sub_name;
    1963            1 :         std::size_t const sub_max(std::numeric_limits<std::uint8_t>::max() + 1 + rand() % 200);
    1964          364 :         for(std::size_t idx(0); idx < sub_max; ++idx)
    1965              :         {
    1966          363 :             sub_name += rand() % 26 + 'a';
    1967              :         }
    1968              : 
    1969            7 :         CATCH_REQUIRE_THROWS_MATCHES(
    1970              :                   out.add_value("sub-name-too-long", sub_name, &value, sizeof(value))
    1971              :                 , snapdev::brs_out_of_range
    1972              :                 , Catch::Matchers::ExceptionMessage(
    1973              :                           "brs_out_of_range: name, sub-name, or hunk too large."));
    1974              : 
    1975              :         // SUB-FIELD
    1976            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1977              :                   out.start_subfield(name)
    1978              :                 , snapdev::brs_out_of_range
    1979              :                 , Catch::Matchers::ExceptionMessage(
    1980              :                           "brs_out_of_range: name too large."));
    1981            1 :     }
    1982           14 :     CATCH_END_SECTION()
    1983              : 
    1984           14 :     CATCH_START_SECTION("brs_invalid: hunk too long")
    1985              :     {
    1986            1 :         std::stringstream buffer;
    1987            1 :         snapdev::serializer out(buffer);
    1988              : 
    1989            1 :         std::string data(buffer.str());
    1990            1 :         CATCH_REQUIRE(data.length() == sizeof(snapdev::magic_t));
    1991              : 
    1992            1 :         CATCH_REQUIRE(data[0] == 'B');
    1993            1 :         CATCH_REQUIRE(data[1] == 'R');
    1994            1 :         CATCH_REQUIRE(data[2] == 'L');
    1995            1 :         CATCH_REQUIRE(data[3] == snapdev::BRS_VERSION);
    1996              : 
    1997            1 :         char value = 33;
    1998           11 :         for(std::size_t extra(0); extra < 10; ++extra)
    1999              :         {
    2000          320 :             CATCH_REQUIRE_THROWS_MATCHES(
    2001              :                       out.add_value("large-hunk", &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
    2002              :                     , snapdev::brs_out_of_range
    2003              :                     , Catch::Matchers::ExceptionMessage(
    2004              :                               "brs_out_of_range: name or hunk too large."));
    2005              : 
    2006          320 :             CATCH_REQUIRE_THROWS_MATCHES(
    2007              :                       out.add_value("large-hunk", 33, &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
    2008              :                     , snapdev::brs_out_of_range
    2009              :                     , Catch::Matchers::ExceptionMessage(
    2010              :                               "brs_out_of_range: name, index, or hunk too large."));
    2011              : 
    2012          360 :             CATCH_REQUIRE_THROWS_MATCHES(
    2013              :                       out.add_value("large-hunk", "sub-name", &value, (1 << SIZEOF_BITFIELD(snapdev::hunk_sizes_t, f_hunk)) + extra)
    2014              :                     , snapdev::brs_out_of_range
    2015              :                     , Catch::Matchers::ExceptionMessage(
    2016              :                               "brs_out_of_range: name, sub-name, or hunk too large."));
    2017              :         }
    2018            1 :     }
    2019           14 :     CATCH_END_SECTION()
    2020              : 
    2021           14 :     CATCH_START_SECTION("brs_invalid: empty input")
    2022              :     {
    2023            1 :         std::stringstream buffer;
    2024              : 
    2025            4 :         CATCH_REQUIRE_THROWS_MATCHES(
    2026              :                   snapdev::deserializer<std::stringstream>(buffer)
    2027              :                 , snapdev::brs_magic_missing
    2028              :                 , Catch::Matchers::ExceptionMessage(
    2029              :                           "brs_error: magic missing at the start of the buffer."));
    2030            1 :     }
    2031           14 :     CATCH_END_SECTION()
    2032              : 
    2033           14 :     CATCH_START_SECTION("brs_invalid: magic unrecognized")
    2034              :     {
    2035            1 :         std::stringstream buffer;
    2036              : 
    2037            1 :         buffer << 'G';  // pretend it's a GIF
    2038            1 :         buffer << 'I';
    2039            1 :         buffer << 'F';
    2040            1 :         buffer << '8';
    2041            1 :         buffer << '9';
    2042            1 :         buffer << 'a';
    2043              : 
    2044            4 :         CATCH_REQUIRE_THROWS_MATCHES(
    2045              :                   snapdev::deserializer<std::stringstream>(buffer)
    2046              :                 , snapdev::brs_magic_unsupported
    2047              :                 , Catch::Matchers::ExceptionMessage(
    2048              :                           "brs_error: magic unsupported."));
    2049            1 :     }
    2050           14 :     CATCH_END_SECTION()
    2051              : 
    2052           14 :     CATCH_START_SECTION("brs_invalid: unknown hunk type")
    2053              :     {
    2054            1 :         std::stringstream buffer;
    2055              : 
    2056              :         // magic
    2057            1 :         buffer << 'B';
    2058            1 :         buffer << 'R';
    2059            1 :         buffer << 'L';
    2060            1 :         buffer << snapdev::BRS_VERSION;
    2061              : 
    2062              :         // size hunk
    2063              :         // type 3 is not recognized at the moment
    2064            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((0x03 << 0) | (3 << 2) | (11 << 9)));
    2065            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2066              : 
    2067              :         struct processor
    2068              :         {
    2069            0 :             static bool process_hunk(
    2070              :                           snapdev::deserializer<std::stringstream> & in
    2071              :                         , snapdev::field_t const & field)
    2072              :             {
    2073            0 :                 snapdev::NOT_USED(in, field);
    2074            0 :                 throw std::logic_error("process_hunk() was called!");
    2075              :             }
    2076              :         };
    2077              : 
    2078            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2079            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2080            2 :                       &processor::process_hunk
    2081              :                     , std::placeholders::_1
    2082            2 :                     , std::placeholders::_2));
    2083              : 
    2084            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    2085              :                   in.deserialize(func)
    2086              :                 , snapdev::brs_unknown_type
    2087              :                 , Catch::Matchers::ExceptionMessage(
    2088              :                           "brs_error: read a field with an unknown type."));
    2089            1 :     }
    2090           14 :     CATCH_END_SECTION()
    2091              : 
    2092           14 :     CATCH_START_SECTION("brs_invalid: field missing name")
    2093              :     {
    2094            1 :         std::stringstream buffer;
    2095              : 
    2096              :         // magic
    2097            1 :         buffer << 'B';
    2098            1 :         buffer << 'R';
    2099            1 :         buffer << 'L';
    2100            1 :         buffer << snapdev::BRS_VERSION;
    2101              : 
    2102              :         // size hunk
    2103              :         // type 3 is not recognized at the moment
    2104            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_FIELD << 0) | (3 << 2) | (11 << 9)));
    2105            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2106              : 
    2107              :         struct processor
    2108              :         {
    2109            0 :             static bool process_hunk(
    2110              :                           snapdev::deserializer<std::stringstream> & in
    2111              :                         , snapdev::field_t const & field)
    2112              :             {
    2113            0 :                 snapdev::NOT_USED(in, field);
    2114            0 :                 throw std::logic_error("process_hunk() was called!");
    2115              :             }
    2116              :         };
    2117              : 
    2118            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2119            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2120            2 :                       &processor::process_hunk
    2121              :                     , std::placeholders::_1
    2122            2 :                     , std::placeholders::_2));
    2123              : 
    2124            1 :         bool r(in.deserialize(func));
    2125            1 :         CATCH_REQUIRE_FALSE(r);
    2126            1 :     }
    2127           14 :     CATCH_END_SECTION()
    2128              : 
    2129           14 :     CATCH_START_SECTION("brs_invalid: field data mismatch reading")
    2130              :     {
    2131            1 :         std::stringstream buffer;
    2132              : 
    2133              :         // magic
    2134            1 :         buffer << 'B';
    2135            1 :         buffer << 'R';
    2136            1 :         buffer << 'L';
    2137            1 :         buffer << snapdev::BRS_VERSION;
    2138              : 
    2139              :         // size hunk
    2140              :         // type 3 is not recognized at the moment
    2141            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_FIELD << 0) | (3 << 2) | (1 << 9)));
    2142            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2143            1 :         char const * name("ABC");
    2144            1 :         buffer.write(name, 3);
    2145            1 :         std::uint8_t value(5);
    2146            1 :         buffer.write(reinterpret_cast<char const *>(&value), 1);
    2147              : 
    2148              :         struct processor
    2149              :         {
    2150            1 :             static bool process_hunk(
    2151              :                           snapdev::deserializer<std::stringstream> & in
    2152              :                         , snapdev::field_t const & field)
    2153              :             {
    2154            1 :                 CATCH_REQUIRE(field.f_name == "ABC");
    2155            1 :                 CATCH_REQUIRE(field.f_sub_name == std::string());
    2156            1 :                 CATCH_REQUIRE(field.f_index == -1);
    2157            1 :                 CATCH_REQUIRE(field.f_size == 1);
    2158              : 
    2159              :                 // reading the data other than the byte creates errors
    2160              :                 // (incorrect size--so you may be calling the read_data()
    2161              :                 // function with the wrong type)
    2162              :                 //
    2163              : 
    2164            1 :                 std::int16_t s;
    2165            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2166              :                           in.read_data(s)
    2167              :                         , snapdev::brs_logic_error
    2168              :                         , Catch::Matchers::ExceptionMessage(
    2169              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 2."));
    2170              : 
    2171            1 :                 std::uint16_t us;
    2172            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2173              :                           in.read_data(us)
    2174              :                         , snapdev::brs_logic_error
    2175              :                         , Catch::Matchers::ExceptionMessage(
    2176              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 2."));
    2177              : 
    2178            1 :                 std::int32_t i;
    2179            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2180              :                           in.read_data(i)
    2181              :                         , snapdev::brs_logic_error
    2182              :                         , Catch::Matchers::ExceptionMessage(
    2183              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 4."));
    2184              : 
    2185            1 :                 std::uint32_t ui;
    2186            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2187              :                           in.read_data(ui)
    2188              :                         , snapdev::brs_logic_error
    2189              :                         , Catch::Matchers::ExceptionMessage(
    2190              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 4."));
    2191              : 
    2192            1 :                 std::int64_t l;
    2193            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2194              :                           in.read_data(l)
    2195              :                         , snapdev::brs_logic_error
    2196              :                         , Catch::Matchers::ExceptionMessage(
    2197              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 8."));
    2198              : 
    2199            1 :                 std::uint64_t ul;
    2200            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2201              :                           in.read_data(ul)
    2202              :                         , snapdev::brs_logic_error
    2203              :                         , Catch::Matchers::ExceptionMessage(
    2204              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 8."));
    2205              : 
    2206            1 :                 float f;
    2207            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2208              :                           in.read_data(f)
    2209              :                         , snapdev::brs_logic_error
    2210              :                         , Catch::Matchers::ExceptionMessage(
    2211              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 4."));
    2212              : 
    2213            1 :                 double d;
    2214            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2215              :                           in.read_data(d)
    2216              :                         , snapdev::brs_logic_error
    2217              :                         , Catch::Matchers::ExceptionMessage(
    2218              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 8."));
    2219              : 
    2220            1 :                 long double ld;
    2221            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2222              :                           in.read_data(ld)
    2223              :                         , snapdev::brs_logic_error
    2224              :                         , Catch::Matchers::ExceptionMessage(
    2225              :                                   "brs_logic_error: hunk size is 1, but you are trying to read 16."));
    2226              : 
    2227            1 :                 std::vector<std::int64_t> vl;
    2228            3 :                 CATCH_REQUIRE_THROWS_MATCHES(
    2229              :                           in.read_data(vl)
    2230              :                         , snapdev::brs_logic_error
    2231              :                         , Catch::Matchers::ExceptionMessage(
    2232              :                                   "brs_logic_error: hunk size (1) is not a multiple of the vector item size: 8."));
    2233              : 
    2234            1 :                 std::uint8_t b(0);
    2235            1 :                 CATCH_REQUIRE(in.read_data(b));
    2236            1 :                 CATCH_REQUIRE(b == 5);
    2237              : 
    2238            1 :                 return true;
    2239            1 :             }
    2240              :         };
    2241              : 
    2242            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2243            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2244            2 :                       &processor::process_hunk
    2245              :                     , std::placeholders::_1
    2246            2 :                     , std::placeholders::_2));
    2247              : 
    2248            1 :         bool r(in.deserialize(func));
    2249            1 :         CATCH_REQUIRE(r);
    2250            1 :     }
    2251           14 :     CATCH_END_SECTION()
    2252              : 
    2253           14 :     CATCH_START_SECTION("brs_invalid: missing array index")
    2254              :     {
    2255            1 :         std::stringstream buffer;
    2256              : 
    2257              :         // magic
    2258            1 :         buffer << 'B';
    2259            1 :         buffer << 'R';
    2260            1 :         buffer << 'L';
    2261            1 :         buffer << snapdev::BRS_VERSION;
    2262              : 
    2263              :         // size hunk
    2264              :         // type 3 is not recognized at the moment
    2265            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_ARRAY << 0) | (3 << 2) | (11 << 9)));
    2266            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2267              : 
    2268              :         struct processor
    2269              :         {
    2270            0 :             static bool process_hunk(
    2271              :                           snapdev::deserializer<std::stringstream> & in
    2272              :                         , snapdev::field_t const & field)
    2273              :             {
    2274            0 :                 snapdev::NOT_USED(in, field);
    2275            0 :                 throw std::logic_error("process_hunk() was called!");
    2276              :             }
    2277              :         };
    2278              : 
    2279            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2280            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2281            2 :                       &processor::process_hunk
    2282              :                     , std::placeholders::_1
    2283            2 :                     , std::placeholders::_2));
    2284              : 
    2285            1 :         bool r(in.deserialize(func));
    2286            1 :         CATCH_REQUIRE_FALSE(r);
    2287            1 :     }
    2288           14 :     CATCH_END_SECTION()
    2289              : 
    2290           14 :     CATCH_START_SECTION("brs_invalid: missing array field name")
    2291              :     {
    2292            1 :         std::stringstream buffer;
    2293              : 
    2294              :         // magic
    2295            1 :         buffer << 'B';
    2296            1 :         buffer << 'R';
    2297            1 :         buffer << 'L';
    2298            1 :         buffer << snapdev::BRS_VERSION;
    2299              : 
    2300              :         // size hunk
    2301              :         // type 3 is not recognized at the moment
    2302            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_ARRAY << 0) | (3 << 2) | (11 << 9)));
    2303            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2304            1 :         std::uint16_t size(10);
    2305            1 :         buffer.write(reinterpret_cast<char const *>(&size), 2);
    2306              : 
    2307              :         struct processor
    2308              :         {
    2309            0 :             static bool process_hunk(
    2310              :                           snapdev::deserializer<std::stringstream> & in
    2311              :                         , snapdev::field_t const & field)
    2312              :             {
    2313            0 :                 snapdev::NOT_USED(in, field);
    2314            0 :                 throw std::logic_error("process_hunk() was called!");
    2315              :             }
    2316              :         };
    2317              : 
    2318            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2319            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2320            2 :                       &processor::process_hunk
    2321              :                     , std::placeholders::_1
    2322            2 :                     , std::placeholders::_2));
    2323              : 
    2324            1 :         bool r(in.deserialize(func));
    2325            1 :         CATCH_REQUIRE_FALSE(r);
    2326            1 :     }
    2327           14 :     CATCH_END_SECTION()
    2328              : 
    2329           14 :     CATCH_START_SECTION("brs_invalid: missing map sub-name length")
    2330              :     {
    2331            1 :         std::stringstream buffer;
    2332              : 
    2333              :         // magic
    2334            1 :         buffer << 'B';
    2335            1 :         buffer << 'R';
    2336            1 :         buffer << 'L';
    2337            1 :         buffer << snapdev::BRS_VERSION;
    2338              : 
    2339              :         // size hunk
    2340              :         // type 3 is not recognized at the moment
    2341            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
    2342            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2343              : 
    2344              :         struct processor
    2345              :         {
    2346            0 :             static bool process_hunk(
    2347              :                           snapdev::deserializer<std::stringstream> & in
    2348              :                         , snapdev::field_t const & field)
    2349              :             {
    2350            0 :                 snapdev::NOT_USED(in, field);
    2351            0 :                 throw std::logic_error("process_hunk() was called!");
    2352              :             }
    2353              :         };
    2354              : 
    2355            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2356            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2357            2 :                       &processor::process_hunk
    2358              :                     , std::placeholders::_1
    2359            2 :                     , std::placeholders::_2));
    2360              : 
    2361            1 :         bool r(in.deserialize(func));
    2362            1 :         CATCH_REQUIRE_FALSE(r);
    2363            1 :     }
    2364           14 :     CATCH_END_SECTION()
    2365              : 
    2366           14 :     CATCH_START_SECTION("brs_invalid: map sub-name length is zero")
    2367              :     {
    2368            1 :         std::stringstream buffer;
    2369              : 
    2370              :         // magic
    2371            1 :         buffer << 'B';
    2372            1 :         buffer << 'R';
    2373            1 :         buffer << 'L';
    2374            1 :         buffer << snapdev::BRS_VERSION;
    2375              : 
    2376              :         // size hunk
    2377              :         // type 3 is not recognized at the moment
    2378            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
    2379            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2380            1 :         std::uint8_t len(0);
    2381            1 :         buffer.write(reinterpret_cast<char const *>(&len), 1);
    2382              : 
    2383              :         struct processor
    2384              :         {
    2385            0 :             static bool process_hunk(
    2386              :                           snapdev::deserializer<std::stringstream> & in
    2387              :                         , snapdev::field_t const & field)
    2388              :             {
    2389            0 :                 snapdev::NOT_USED(in, field);
    2390            0 :                 throw std::logic_error("process_hunk() was called!");
    2391              :             }
    2392              :         };
    2393              : 
    2394            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2395            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2396            2 :                       &processor::process_hunk
    2397              :                     , std::placeholders::_1
    2398            2 :                     , std::placeholders::_2));
    2399              : 
    2400            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    2401              :                   in.deserialize(func)
    2402              :                 , snapdev::brs_map_name_cannot_be_empty
    2403              :                 , Catch::Matchers::ExceptionMessage(
    2404              :                           "brs_error: the length of a map's field name cannot be zero."));
    2405            1 :     }
    2406           14 :     CATCH_END_SECTION()
    2407              : 
    2408           14 :     CATCH_START_SECTION("brs_invalid: missing map sub-name")
    2409              :     {
    2410            1 :         std::stringstream buffer;
    2411              : 
    2412              :         // magic
    2413            1 :         buffer << 'B';
    2414            1 :         buffer << 'R';
    2415            1 :         buffer << 'L';
    2416            1 :         buffer << snapdev::BRS_VERSION;
    2417              : 
    2418              :         // size hunk
    2419              :         // type 3 is not recognized at the moment
    2420            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
    2421            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2422            1 :         std::uint8_t len(10);
    2423            1 :         buffer.write(reinterpret_cast<char const *>(&len), 1);
    2424              : 
    2425              :         struct processor
    2426              :         {
    2427            0 :             static bool process_hunk(
    2428              :                           snapdev::deserializer<std::stringstream> & in
    2429              :                         , snapdev::field_t const & field)
    2430              :             {
    2431            0 :                 snapdev::NOT_USED(in, field);
    2432            0 :                 throw std::logic_error("process_hunk() was called!");
    2433              :             }
    2434              :         };
    2435              : 
    2436            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2437            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2438            2 :                       &processor::process_hunk
    2439              :                     , std::placeholders::_1
    2440            2 :                     , std::placeholders::_2));
    2441              : 
    2442            1 :         bool r(in.deserialize(func));
    2443            1 :         CATCH_REQUIRE_FALSE(r);
    2444            1 :     }
    2445           14 :     CATCH_END_SECTION()
    2446              : 
    2447           14 :     CATCH_START_SECTION("brs_invalid: missing map name")
    2448              :     {
    2449            1 :         std::stringstream buffer;
    2450              : 
    2451              :         // magic
    2452            1 :         buffer << 'B';
    2453            1 :         buffer << 'R';
    2454            1 :         buffer << 'L';
    2455            1 :         buffer << snapdev::BRS_VERSION;
    2456              : 
    2457              :         // size hunk
    2458              :         // type 3 is not recognized at the moment
    2459            1 :         std::uint32_t hunk(static_cast<std::uint32_t>((snapdev::TYPE_MAP << 0) | (3 << 2) | (11 << 9)));
    2460            1 :         buffer.write(reinterpret_cast<char const *>(&hunk), 4);
    2461            1 :         std::uint8_t len(3);
    2462            1 :         buffer.write(reinterpret_cast<char const *>(&len), 1);
    2463            1 :         char const * name = "SUB";
    2464            1 :         buffer.write(name, 3);
    2465              : 
    2466              :         struct processor
    2467              :         {
    2468            0 :             static bool process_hunk(
    2469              :                           snapdev::deserializer<std::stringstream> & in
    2470              :                         , snapdev::field_t const & field)
    2471              :             {
    2472            0 :                 snapdev::NOT_USED(in, field);
    2473            0 :                 throw std::logic_error("process_hunk() was called!");
    2474              :             }
    2475              :         };
    2476              : 
    2477            1 :         snapdev::deserializer<std::stringstream> in(buffer);
    2478            3 :         snapdev::deserializer<std::stringstream>::process_hunk_t func(std::bind(
    2479            2 :                       &processor::process_hunk
    2480              :                     , std::placeholders::_1
    2481            2 :                     , std::placeholders::_2));
    2482              : 
    2483            1 :         bool r(in.deserialize(func));
    2484            1 :         CATCH_REQUIRE_FALSE(r);
    2485            1 :     }
    2486           14 :     CATCH_END_SECTION()
    2487           14 : }
    2488              : 
    2489              : 
    2490              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions