LCOV - code coverage report
Current view: top level - tests - catch_network.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 94.3 % 176 166
Test Date: 2025-06-19 11:28:46 Functions: 88.9 % 9 8
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2025  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/prinbee
       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              : // self
      20              : //
      21              : #include    "catch_main.h"
      22              : 
      23              : 
      24              : // prinbee
      25              : //
      26              : #include    <prinbee/network/binary_client.h>
      27              : #include    <prinbee/network/binary_message.h>
      28              : #include    <prinbee/network/crc16.h>
      29              : 
      30              : 
      31              : 
      32              : // advgetopt
      33              : //
      34              : //#include    <advgetopt/options.h>
      35              : 
      36              : 
      37              : // snapdev
      38              : //
      39              : //#include    <snapdev/enum_class_math.h>
      40              : #include    <snapdev/raii_generic_deleter.h>
      41              : 
      42              : 
      43              : // eventdispatcher
      44              : //
      45              : #include    <eventdispatcher/communicator.h>
      46              : //#include    <eventdispatcher/dispatcher.h>
      47              : //#include    <eventdispatcher/names.h>
      48              : //#include    <eventdispatcher/tcp_client_permanent_message_connection.h>
      49              : 
      50              : #include    <eventdispatcher/reporter/executor.h>
      51              : //#include    <eventdispatcher/reporter/lexer.h>
      52              : #include    <eventdispatcher/reporter/parser.h>
      53              : //#include    <eventdispatcher/reporter/state.h>
      54              : //#include    <eventdispatcher/reporter/variable_string.h>
      55              : 
      56              : 
      57              : // C++
      58              : //
      59              : //#include    <iomanip>
      60              : 
      61              : 
      62              : // last include
      63              : //
      64              : #include    <snapdev/poison.h>
      65              : 
      66              : 
      67              : 
      68              : namespace
      69              : {
      70              : 
      71              : 
      72              : 
      73            1 : addr::addr get_address()
      74              : {
      75            1 :     addr::addr a;
      76            1 :     sockaddr_in ip = {
      77              :         .sin_family = AF_INET,
      78            1 :         .sin_port = htons(20002),
      79              :         .sin_addr = {
      80            1 :             .s_addr = htonl(0x7f000001),
      81              :         },
      82              :         .sin_zero = {},
      83            1 :     };
      84            1 :     a.set_ipv4(ip);
      85            2 :     return a;
      86            0 : }
      87              : 
      88              : 
      89              : class binary_client_test
      90              :     : public prinbee::binary_client
      91              : {
      92              : public:
      93            1 :     binary_client_test(addr::addr const & a)
      94            1 :         : binary_client(a)
      95              :     {
      96            1 :     }
      97              : 
      98            0 :     virtual void process_message(prinbee::binary_message & msg) override
      99              :     {
     100            0 :         throw std::runtime_error("boom -- " + std::to_string(msg.get_data_size()));
     101              :     }
     102              : 
     103            1 :     virtual void process_connected() override
     104              :     {
     105            2 : SNAP_LOG_ERROR << "--------- process connected!" << SNAP_LOG_SEND;
     106            1 :         prinbee::binary_message msg;
     107            1 :         msg.set_name(prinbee::g_message_ping);
     108            1 :         send_message(msg);
     109              : 
     110              :         // important, we need to call this one to disable the timer otherwise
     111              :         // we'll try to reconnect over and over again
     112              :         //
     113            1 :         binary_client::process_connected();
     114            2 :     }
     115              : 
     116              : private:
     117              : };
     118              : 
     119              : 
     120              : 
     121              : }
     122              : // no name namespace
     123              : 
     124              : 
     125            2 : CATCH_TEST_CASE("network_crc16", "[crc16][valid][invalid]")
     126              : {
     127            4 :     CATCH_START_SECTION("network_crc16: verify empty buffer")
     128              :     {
     129            1 :         std::vector<std::uint8_t> data;
     130            1 :         std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
     131            1 :         CATCH_REQUIRE(crc16 == 0);
     132            1 :         data.push_back(0);
     133            1 :         data.push_back(0);
     134            1 :         CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
     135            1 :     }
     136            3 :     CATCH_END_SECTION()
     137              : 
     138            4 :     CATCH_START_SECTION("network_crc16: verify negation")
     139              :     {
     140            1 :         std::size_t const size(rand() % 64536 + 1024);
     141            3 :         std::vector<std::uint8_t> data(size);
     142        17503 :         for(std::size_t i(0); i < size; ++i)
     143              :         {
     144        17502 :             data[i] = rand();
     145              :         }
     146            1 :         std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
     147              : 
     148              :         // test against all values
     149              :         //
     150            1 :         data.push_back(0);
     151            1 :         data.push_back(0);
     152        65537 :         for(std::uint32_t check(0); check < 0x10000; ++check)
     153              :         {
     154        65536 :             data[data.size() - 2] = check;
     155        65536 :             data[data.size() - 1] = check >> 8;
     156        65536 :             if(check == crc16)
     157              :             {
     158              :                 // only one that works
     159              :                 //
     160            1 :                 CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
     161              :             }
     162              :             else
     163              :             {
     164        65535 :                 CATCH_REQUIRE_FALSE(prinbee::crc16_compute(data.data(), data.size()) == 0);
     165              :             }
     166              :         }
     167            1 :     }
     168            3 :     CATCH_END_SECTION()
     169              : 
     170              :     // this doesn't work--the CRC16 has to be at the end
     171              :     //CATCH_START_SECTION("network_crc16: verify negation inserting CRC16 at the start")
     172              :     //{
     173              :     //    std::size_t const size(rand() % 64536 + 1024);
     174              :     //    std::vector<std::uint8_t> data(size);
     175              :     //    for(std::size_t i(0); i < size; ++i)
     176              :     //    {
     177              :     //        data[i] = rand();
     178              :     //    }
     179              :     //    std::uint16_t const crc16(prinbee::crc16_compute(data.data(), data.size()));
     180              :     //    data.insert(data.begin(), crc16);
     181              :     //    data.insert(data.begin(), crc16 >> 8);
     182              :     //    CATCH_REQUIRE(prinbee::crc16_compute(data.data(), data.size()) == 0);
     183              :     //}
     184              :     //CATCH_END_SECTION()
     185            2 : }
     186              : 
     187              : 
     188            5 : CATCH_TEST_CASE("network_message", "[network][message][valid]")
     189              : {
     190            7 :     CATCH_START_SECTION("network_message: verify name")
     191              :     {
     192            1 :         prinbee::message_name_t one(prinbee::create_message_name("1"));
     193            1 :         char const * p1(reinterpret_cast<char const *>(&one));
     194            1 :         CATCH_REQUIRE(p1[0] == '1');
     195            1 :         CATCH_REQUIRE(p1[1] == '\0');
     196            1 :         CATCH_REQUIRE(p1[2] == '\0');
     197            1 :         CATCH_REQUIRE(p1[3] == '\0');
     198              : 
     199            1 :         prinbee::message_name_t two(prinbee::create_message_name("!?"));
     200            1 :         char const * p2(reinterpret_cast<char const *>(&two));
     201            1 :         CATCH_REQUIRE(p2[0] == '!');
     202            1 :         CATCH_REQUIRE(p2[1] == '?');
     203            1 :         CATCH_REQUIRE(p2[2] == '\0');
     204            1 :         CATCH_REQUIRE(p2[3] == '\0');
     205              : 
     206            1 :         prinbee::message_name_t abc(prinbee::create_message_name("ABC"));
     207            1 :         char const * p3(reinterpret_cast<char const *>(&abc));
     208            1 :         CATCH_REQUIRE(p3[0] == 'A');
     209            1 :         CATCH_REQUIRE(p3[1] == 'B');
     210            1 :         CATCH_REQUIRE(p3[2] == 'C');
     211            1 :         CATCH_REQUIRE(p3[3] == '\0');
     212              : 
     213            1 :         prinbee::message_name_t name(prinbee::create_message_name("NAME"));
     214            1 :         char const * p4(reinterpret_cast<char const *>(&name));
     215            1 :         CATCH_REQUIRE(p4[0] == 'N');
     216            1 :         CATCH_REQUIRE(p4[1] == 'A');
     217            1 :         CATCH_REQUIRE(p4[2] == 'M');
     218            1 :         CATCH_REQUIRE(p4[3] == 'E');
     219              :     }
     220            6 :     CATCH_END_SECTION()
     221              : 
     222            7 :     CATCH_START_SECTION("network_message: check defaults")
     223              :     {
     224            1 :         prinbee::binary_message msg;
     225              : 
     226            1 :         CATCH_REQUIRE(msg.get_name() == prinbee::g_message_unknown);
     227              : 
     228            1 :         CATCH_REQUIRE_FALSE(msg.has_pointer());
     229              : 
     230            1 :         std::size_t size(0);
     231            1 :         CATCH_REQUIRE_FALSE(msg.get_data_pointer(size));
     232            1 :         CATCH_REQUIRE(size == 0);
     233              : 
     234            1 :         CATCH_REQUIRE(msg.get_data().empty());
     235            1 :     }
     236            6 :     CATCH_END_SECTION()
     237              : 
     238            7 :     CATCH_START_SECTION("network_message: check name")
     239              :     {
     240            1 :         prinbee::binary_message msg;
     241              : 
     242            1 :         CATCH_REQUIRE(msg.get_name() == prinbee::g_message_unknown);
     243              : 
     244          101 :         for(int i(0); i < 100; ++i)
     245              :         {
     246          100 :             std::stringstream ss;
     247          100 :             ss << "i" << i;
     248          100 :             msg.set_name(prinbee::create_message_name(ss.str().c_str()));
     249              : 
     250          100 :             char name[4] = {};
     251          100 :             int p(0);
     252          100 :             name[p++] = 'i';
     253          100 :             if(i >= 10)
     254              :             {
     255           90 :                 name[p++] = i / 10 + '0';
     256              :             }
     257          100 :             name[p++] = i % 10 + '0';
     258          100 :             prinbee::message_name_t expected = *reinterpret_cast<std::uint32_t *>(name);
     259          100 :             CATCH_REQUIRE(msg.get_name() == expected);
     260          100 :         }
     261              : 
     262            1 :         std::size_t size(0);
     263            1 :         CATCH_REQUIRE_FALSE(msg.get_data_pointer(size));
     264            1 :         CATCH_REQUIRE(size == 0);
     265              : 
     266            1 :         CATCH_REQUIRE(msg.get_data().empty());
     267            1 :     }
     268            6 :     CATCH_END_SECTION()
     269              : 
     270            7 :     CATCH_START_SECTION("network_message: check pointer")
     271              :     {
     272            1 :         prinbee::binary_message msg;
     273              : 
     274            1 :         CATCH_REQUIRE_FALSE(msg.has_pointer());
     275              : 
     276            1 :         std::size_t const size(rand() % 1000 + 10);
     277            1 :         void * ptr(malloc(size));
     278            1 :         CATCH_REQUIRE(ptr != nullptr);
     279              : 
     280              :         // we're responsible for that pointer...
     281              :         // (this is ugly, but that way we avoid one copy per message, some
     282              :         // of which are really large)
     283              :         //
     284            1 :         snapdev::raii_buffer_t auto_free(reinterpret_cast<char *>(ptr));
     285              : 
     286            1 :         msg.set_data_by_pointer(ptr, size);
     287              : 
     288            1 :         CATCH_REQUIRE(msg.has_pointer());
     289              : 
     290            1 :         std::size_t sz(0);
     291            1 :         CATCH_REQUIRE(msg.get_data_pointer(sz) == ptr);
     292            1 :         CATCH_REQUIRE(sz == size);
     293              : 
     294              :         // if we have a pointer, there is no data buffer
     295              :         //
     296            1 :         CATCH_REQUIRE(msg.get_data().size() == 0);
     297            1 :     }
     298            6 :     CATCH_END_SECTION()
     299              : 
     300            7 :     CATCH_START_SECTION("network_message: check data")
     301              :     {
     302            1 :         prinbee::binary_message msg;
     303              : 
     304            1 :         CATCH_REQUIRE_FALSE(msg.has_pointer());
     305              : 
     306            1 :         std::size_t const size(rand() % 1000 + 10);
     307            1 :         void * ptr(malloc(size));
     308            1 :         CATCH_REQUIRE(ptr != nullptr);
     309              : 
     310              :         // we're responsible for that pointer...
     311              :         // (this is ugly, but that way we avoid one copy per message, some
     312              :         // of which are really large)
     313              :         //
     314            1 :         snapdev::raii_buffer_t auto_free(reinterpret_cast<char *>(ptr));
     315              : 
     316            1 :         msg.set_data(ptr, size);
     317              : 
     318            1 :         std::vector<std::uint8_t> data(msg.get_data());
     319            1 :         CATCH_REQUIRE(data.size() == size);
     320            1 :         CATCH_REQUIRE(memcmp(data.data(), ptr, size) == 0);
     321              : 
     322              :         // if we a buffer, there is no pointer
     323              :         //
     324            1 :         CATCH_REQUIRE_FALSE(msg.has_pointer());
     325            1 :     }
     326            6 :     CATCH_END_SECTION()
     327            5 : }
     328              : 
     329              : 
     330            3 : CATCH_TEST_CASE("network_message_invalid", "[network][message][invalid]")
     331              : {
     332            5 :     CATCH_START_SECTION("network_message_invalid: the nullptr string is not a valid name")
     333              :     {
     334            3 :         CATCH_REQUIRE_THROWS_MATCHES(
     335              :                   prinbee::create_message_name(nullptr)
     336              :                 , prinbee::invalid_parameter
     337              :                 , Catch::Matchers::ExceptionMessage(
     338              :                           "prinbee_exception: name cannot be null."));
     339              :     }
     340            4 :     CATCH_END_SECTION()
     341              : 
     342            5 :     CATCH_START_SECTION("network_message: the empty string is not a valid name")
     343              :     {
     344            3 :         CATCH_REQUIRE_THROWS_MATCHES(
     345              :                   prinbee::create_message_name("")
     346              :                 , prinbee::invalid_parameter
     347              :                 , Catch::Matchers::ExceptionMessage(
     348              :                           "prinbee_exception: name cannot be empty."));
     349              :     }
     350            4 :     CATCH_END_SECTION()
     351              : 
     352            5 :     CATCH_START_SECTION("network_message: too many characters")
     353              :     {
     354            3 :         CATCH_REQUIRE_THROWS_MATCHES(
     355              :                   prinbee::create_message_name("ELEPHANT")
     356              :                 , prinbee::invalid_parameter
     357              :                 , Catch::Matchers::ExceptionMessage(
     358              :                           "prinbee_exception: name cannot be more than 4 characters."));
     359              :     }
     360            4 :     CATCH_END_SECTION()
     361            3 : }
     362              : 
     363              : 
     364            1 : CATCH_TEST_CASE("network_binary_client", "[network][message][valid]")
     365              : {
     366            3 :     CATCH_START_SECTION("network_binary_client: verify readiness")
     367              :     {
     368            1 :         std::string const source_dir(SNAP_CATCH2_NAMESPACE::g_source_dir());
     369            1 :         std::string const filename(source_dir + "/tests/rprtr/binary_client.rprtr");
     370            1 :         SNAP_CATCH2_NAMESPACE::reporter::lexer::pointer_t l(SNAP_CATCH2_NAMESPACE::reporter::create_lexer(filename));
     371            1 :         CATCH_REQUIRE(l != nullptr);
     372            1 :         SNAP_CATCH2_NAMESPACE::reporter::state::pointer_t s(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::state>());
     373            1 :         SNAP_CATCH2_NAMESPACE::reporter::parser::pointer_t p(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::parser>(l, s));
     374            1 :         p->parse_program();
     375            1 :         SNAP_CATCH2_NAMESPACE::reporter::executor::pointer_t e(std::make_shared<SNAP_CATCH2_NAMESPACE::reporter::executor>(s));
     376            1 :         e->start();
     377            1 :         binary_client_test::pointer_t client(std::make_shared<binary_client_test>(get_address()));
     378            1 :         ed::communicator::instance()->add_connection(client);
     379            1 :         e->set_thread_done_callback([client]()
     380              :             {
     381            1 :                 ed::communicator::instance()->remove_connection(client);
     382            1 :             });
     383              :         try
     384              :         {
     385            1 :             CATCH_REQUIRE(e->run());
     386              :         }
     387            0 :         catch(std::exception const & ex)
     388              :         {
     389            0 :             SNAP_LOG_FATAL
     390              :                 << "an exception occurred while running cluckd (1 cluckd): "
     391              :                 << ex
     392              :                 << SNAP_LOG_SEND;
     393            0 :             libexcept::exception_base_t const * b(dynamic_cast<libexcept::exception_base_t const *>(&ex));
     394            0 :             if(b != nullptr) for(auto const & line : b->get_stack_trace())
     395              :             {
     396            0 :                 SNAP_LOG_FATAL
     397              :                     << "    "
     398              :                     << line
     399              :                     << SNAP_LOG_SEND;
     400              :             }
     401            0 :             throw;
     402            0 :         }
     403            1 :         CATCH_REQUIRE(s->get_exit_code() == 0);
     404            1 :     }
     405            2 :     CATCH_END_SECTION()
     406            1 : }
     407              : 
     408              : 
     409              : 
     410              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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