LCOV - code coverage report
Current view: top level - tests - catch_daemon_computer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.3 % 142 141
Test Date: 2025-08-17 08:58:50 Functions: 100.0 % 3 3
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2016-2025  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/cluck
       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              : 
      25              : // daemon
      26              : //
      27              : #include    <daemon/computer.h>
      28              : 
      29              : 
      30              : // cluck
      31              : //
      32              : //#include    <cluck/cluck.h>
      33              : #include    <cluck/exception.h>
      34              : //#include    <cluck/names.h>
      35              : //#include    <cluck/version.h>
      36              : 
      37              : 
      38              : // eventdispatcher
      39              : //
      40              : //#include    <eventdispatcher/communicator.h>
      41              : //#include    <eventdispatcher/dispatcher.h>
      42              : //#include    <eventdispatcher/names.h>
      43              : //#include    <eventdispatcher/tcp_client_permanent_message_connection.h>
      44              : //
      45              : //#include    <eventdispatcher/reporter/executor.h>
      46              : //#include    <eventdispatcher/reporter/lexer.h>
      47              : //#include    <eventdispatcher/reporter/parser.h>
      48              : //#include    <eventdispatcher/reporter/state.h>
      49              : 
      50              : 
      51              : // cppthread
      52              : //
      53              : #include    <cppthread/thread.h>
      54              : 
      55              : 
      56              : // snapdev
      57              : //
      58              : #include    <snapdev/enum_class_math.h>
      59              : #include    <snapdev/escape_special_regex_characters.h>
      60              : 
      61              : 
      62              : // C++
      63              : //
      64              : #include    <regex>
      65              : 
      66              : 
      67              : // last include
      68              : //
      69              : #include    <snapdev/poison.h>
      70              : 
      71              : 
      72              : 
      73              : namespace
      74              : {
      75              : 
      76              : 
      77              : 
      78           15 : addr::addr get_random_address()
      79              : {
      80           15 :     addr::addr a;
      81           15 :     sockaddr_in ip = {
      82              :         .sin_family = AF_INET,
      83           15 :         .sin_port = htons(20002),
      84              :         .sin_addr = {
      85              :             .s_addr = 0,
      86              :         },
      87              :         .sin_zero = {},
      88           15 :     };
      89           15 :     SNAP_CATCH2_NAMESPACE::random(ip.sin_addr.s_addr);
      90           15 :     a.set_ipv4(ip);
      91           30 :     return a;
      92            0 : }
      93              : 
      94              : 
      95              : 
      96              : } // no name namespace
      97              : 
      98              : 
      99              : 
     100            2 : CATCH_TEST_CASE("daemon_computer", "[cluckd][computer][daemon]")
     101              : {
     102            4 :     CATCH_START_SECTION("daemon_computer: verify defaults")
     103              :     {
     104            1 :         cluck_daemon::computer c;
     105              : 
     106            1 :         CATCH_REQUIRE_FALSE(c.is_self());
     107            1 :         CATCH_REQUIRE(c.get_connected());
     108            1 :         CATCH_REQUIRE(c.get_priority() == cluck_daemon::computer::PRIORITY_UNDEFINED);
     109            1 :         CATCH_REQUIRE(c.get_start_time() == snapdev::timespec_ex());
     110            1 :         CATCH_REQUIRE(c.get_name() == std::string());
     111            1 :         CATCH_REQUIRE(c.get_ip_address() == addr::addr());
     112              : 
     113              :         // without a priority, this one fails
     114              :         //
     115            3 :         CATCH_REQUIRE_THROWS_MATCHES(
     116              :               c.get_id()
     117              :             , cluck::invalid_parameter
     118              :             , Catch::Matchers::ExceptionMessage("cluck_exception: computer::get_id() can't be called when the priority is not defined."));
     119            1 :     }
     120            3 :     CATCH_END_SECTION()
     121              : 
     122            4 :     CATCH_START_SECTION("daemon_computer: verify self defaults")
     123              :     {
     124              :         // the LEADER priority cannot get saved inside the computer object
     125              :         //
     126           16 :         for(cluck_daemon::computer::priority_t p(cluck_daemon::computer::PRIORITY_USER_MIN);
     127           16 :             p <= cluck_daemon::computer::PRIORITY_MAX;
     128              :             ++p)
     129              :         {
     130           15 :             CATCH_REQUIRE(p != cluck_daemon::computer::PRIORITY_LEADER);
     131              : 
     132           15 :             std::string n;
     133              :             do
     134              :             {
     135           15 :                 n = SNAP_CATCH2_NAMESPACE::random_string(1, 15);
     136              :             }
     137           15 :             while(std::strchr(n.c_str(), '|') != nullptr);
     138           15 :             addr::addr a(get_random_address());
     139           15 :             cluck_daemon::computer c(n, p, a);
     140              : 
     141           15 :             CATCH_REQUIRE(c.is_self());
     142           15 :             CATCH_REQUIRE(c.get_connected());
     143           15 :             CATCH_REQUIRE(c.get_priority() == p);
     144           15 :             CATCH_REQUIRE(c.get_start_time() == snapdev::timespec_ex());
     145           15 :             CATCH_REQUIRE(c.get_name() == n);
     146           15 :             CATCH_REQUIRE(c.get_ip_address() == a);
     147              : 
     148              :             // with a priority, this one works
     149              :             //
     150           15 :             std::string const id(c.get_id());
     151           15 :             CATCH_REQUIRE(c.get_id() == id);        // ID does not change after first call
     152              : 
     153              :             // the ID is a string defined as:
     154              :             //    <priority> | <random ID> | <IP> | <pid> | <name>
     155              :             //
     156              :             // we do not have access to the <random ID> part so we use a
     157              :             // regex to make sure that the ID is defined as expected
     158              :             //
     159           15 :             std::string expr;
     160           15 :             if(p < 10)
     161              :             {
     162            9 :                 expr += '0';
     163              :             }
     164           15 :             expr += std::to_string(p);
     165           15 :             expr += "\\|[0-9]+\\|";
     166           15 :             expr += snapdev::escape_special_regex_characters(a.to_ipv4or6_string(addr::STRING_IP_ADDRESS | addr::STRING_IP_BRACKET_ADDRESS));
     167           15 :             expr += "\\|";
     168           15 :             expr += std::to_string(getpid());
     169           15 :             expr += "\\|";
     170           15 :             expr += snapdev::escape_special_regex_characters(n);
     171           15 :             std::regex const compiled_regex(expr);
     172           15 :             CATCH_REQUIRE(std::regex_match(id, compiled_regex));
     173              : 
     174           15 :             cluck_daemon::computer copy;
     175           15 :             CATCH_REQUIRE_FALSE(copy.is_self());
     176           15 :             CATCH_REQUIRE(copy.get_connected());
     177           15 :             CATCH_REQUIRE(copy.get_priority() == cluck_daemon::computer::PRIORITY_UNDEFINED);
     178           15 :             CATCH_REQUIRE(copy.get_start_time() == snapdev::timespec_ex());
     179           15 :             CATCH_REQUIRE(copy.get_name() == std::string());
     180           15 :             CATCH_REQUIRE(copy.get_ip_address() == addr::addr());
     181              : 
     182           15 :             CATCH_REQUIRE(copy.set_id(id));
     183           15 :             CATCH_REQUIRE(copy.get_id() == id);
     184              : 
     185           15 :             CATCH_REQUIRE_FALSE(copy.is_self());
     186           15 :             CATCH_REQUIRE(copy.get_connected());
     187           15 :             CATCH_REQUIRE(copy.get_priority() == p);
     188           15 :             CATCH_REQUIRE(copy.get_start_time() == snapdev::timespec_ex());
     189           15 :             CATCH_REQUIRE(copy.get_name() == n);
     190           15 :             CATCH_REQUIRE(copy.get_ip_address() == a);
     191              : 
     192              :             // trying to set the ID again fails
     193              :             //
     194           45 :             CATCH_REQUIRE_THROWS_MATCHES(
     195              :                   copy.set_id(id)
     196              :                 , cluck::logic_error
     197              :                 , Catch::Matchers::ExceptionMessage("logic_error: computer::set_id() cannot be called more than once."));
     198           15 :         }
     199              :     }
     200            3 :     CATCH_END_SECTION()
     201            2 : }
     202              : 
     203              : 
     204            9 : CATCH_TEST_CASE("daemon_computer_errors", "[cluckd][computer][daemon][error]")
     205              : {
     206           11 :     CATCH_START_SECTION("daemon_computer_errors: empty name")
     207              :     {
     208           10 :         CATCH_REQUIRE_THROWS_MATCHES(
     209              :               cluck_daemon::computer("", 5, addr::addr())
     210              :             , cluck::invalid_parameter
     211              :             , Catch::Matchers::ExceptionMessage("cluck_exception: the computer name cannot be an empty string."));
     212              :     }
     213           10 :     CATCH_END_SECTION()
     214              : 
     215           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid character in name")
     216              :     {
     217              :         // the | character is an issue in the serializer
     218              :         //
     219           10 :         CATCH_REQUIRE_THROWS_MATCHES(
     220              :               cluck_daemon::computer("|pipe-not-allowed|", 5, addr::addr())
     221              :             , cluck::invalid_parameter
     222              :             , Catch::Matchers::ExceptionMessage("cluck_exception: a computer name cannot include the '|' or null characters."));
     223              : 
     224              :         // the '\0' is not allowed in the name
     225              :         //
     226            3 :         std::string n("start");
     227            1 :         n += '\0';
     228            1 :         n += "end";
     229            6 :         CATCH_REQUIRE_THROWS_MATCHES(
     230              :               cluck_daemon::computer(n, 5, addr::addr())
     231              :             , cluck::invalid_parameter
     232              :             , Catch::Matchers::ExceptionMessage("cluck_exception: a computer name cannot include the '|' or null characters."));
     233            1 :     }
     234           10 :     CATCH_END_SECTION()
     235              : 
     236           11 :     CATCH_START_SECTION("daemon_computer_errors: serialized ID must be 5 parts")
     237              :     {
     238            1 :         cluck_daemon::computer c;
     239            3 :         CATCH_REQUIRE_FALSE(c.set_id("need|5|parts"));
     240            1 :     }
     241           10 :     CATCH_END_SECTION()
     242              : 
     243           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid priority with constructor")
     244              :     {
     245              :         // too small
     246            8 :         CATCH_REQUIRE_THROWS_MATCHES(
     247              :               cluck_daemon::computer("test", cluck_daemon::computer::PRIORITY_USER_MIN - 1, addr::addr())
     248              :             , cluck::invalid_parameter
     249              :             , Catch::Matchers::ExceptionMessage(
     250              :                   "cluck_exception: priority is limited to a number between "
     251              :                 + std::to_string(static_cast<int>(cluck_daemon::computer::PRIORITY_USER_MIN))
     252              :                 + " and "
     253              :                 + std::to_string(static_cast<int>(cluck_daemon::computer::PRIORITY_MAX))
     254              :                 + " inclusive."));
     255              : 
     256              :         // too large
     257            8 :         CATCH_REQUIRE_THROWS_MATCHES(
     258              :               cluck_daemon::computer("test", cluck_daemon::computer::PRIORITY_MAX + 1, addr::addr())
     259              :             , cluck::invalid_parameter
     260              :             , Catch::Matchers::ExceptionMessage(
     261              :                   "cluck_exception: priority is limited to a number between "
     262              :                 + std::to_string(static_cast<int>(cluck_daemon::computer::PRIORITY_USER_MIN))
     263              :                 + " and "
     264              :                 + std::to_string(static_cast<int>(cluck_daemon::computer::PRIORITY_MAX))
     265              :                 + " inclusive."));
     266              :     }
     267           10 :     CATCH_END_SECTION()
     268              : 
     269           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid priority in id string")
     270              :     {
     271              :         // not a number
     272              :         {
     273            1 :             cluck_daemon::computer c;
     274            3 :             CATCH_REQUIRE_FALSE(c.set_id("prio|123|127.0.0.1|5501|name"));
     275            1 :         }
     276              : 
     277              :         // too small
     278              :         {
     279            1 :             cluck_daemon::computer c;
     280            1 :             std::string too_small(std::to_string(cluck_daemon::computer::PRIORITY_USER_MIN - 1));
     281            1 :             CATCH_REQUIRE_FALSE(c.set_id(too_small + "|123|127.0.0.1|5501|name"));
     282            1 :         }
     283              : 
     284              :         // too large
     285              :         {
     286            1 :             cluck_daemon::computer c;
     287            1 :             std::string too_large(std::to_string(cluck_daemon::computer::PRIORITY_MAX + 1));
     288            1 :             CATCH_REQUIRE_FALSE(c.set_id(too_large + "|123|127.0.0.1|5501|name"));
     289            1 :         }
     290              :     }
     291           10 :     CATCH_END_SECTION()
     292              : 
     293           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid random number in id string")
     294              :     {
     295              :         // not a number
     296              :         {
     297            1 :             cluck_daemon::computer c;
     298            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|random|127.0.0.1|5501|name"));
     299            1 :         }
     300              :     }
     301           10 :     CATCH_END_SECTION()
     302              : 
     303           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid IP address in id string")
     304              :     {
     305              :         // empty
     306              :         {
     307            1 :             cluck_daemon::computer c;
     308            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001||5501|name"));
     309            1 :         }
     310              : 
     311              :         // not an IP
     312              :         {
     313            1 :             cluck_daemon::computer c;
     314            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|not an IP|5501|name"));
     315            1 :         }
     316              : 
     317              :         // "default"
     318              :         {
     319            1 :             cluck_daemon::computer c;
     320            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|0.0.0.0|5501|name"));
     321              : 
     322              :             // this is a trick that works at the moment; we set a valid
     323              :             // priority, so next we can have an invalid address that
     324              :             // remains... because the set_id() still makes updates to
     325              :             // the object even if it generates an error
     326              :             //
     327            3 :             CATCH_REQUIRE_THROWS_MATCHES(
     328              :                   c.get_id()
     329              :                 , cluck::invalid_parameter
     330              :                 , Catch::Matchers::ExceptionMessage("cluck_exception: computer::get_id() can't be called when the address is the default address."));
     331            1 :         }
     332              :     }
     333           10 :     CATCH_END_SECTION()
     334              : 
     335           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid PID")
     336              :     {
     337              :         // empty
     338              :         {
     339            1 :             cluck_daemon::computer c;
     340            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|127.0.0.1||name"));
     341            1 :         }
     342              : 
     343              :         // zero
     344              :         {
     345            1 :             cluck_daemon::computer c;
     346            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|127.0.0.1|0|name"));
     347              : 
     348              :             // the following is a trick that currently works because the
     349              :             // set_id() function saves the data until the one part that
     350              :             // is invalid
     351              :             //
     352            3 :             CATCH_REQUIRE_THROWS_MATCHES(
     353              :                   c.get_id()
     354              :                 , cluck::invalid_parameter
     355              :                 , Catch::Matchers::ExceptionMessage("cluck_exception: computer::get_id() can't be called when the pid is not defined."));
     356            1 :         }
     357              : 
     358              :         // negative
     359              :         {
     360            1 :             cluck_daemon::computer c;
     361            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|127.0.0.1|-5501|name"));
     362            1 :         }
     363              : 
     364              :         // too large
     365              :         {
     366            1 :             cluck_daemon::computer c;
     367            1 :             std::string const count(std::to_string(cppthread::get_pid_max() + 1));
     368            1 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|127.0.0.1|" + count + "|name"));
     369            1 :         }
     370              :     }
     371           10 :     CATCH_END_SECTION()
     372              : 
     373           11 :     CATCH_START_SECTION("daemon_computer_errors: invalid name")
     374              :     {
     375              :         // empty
     376              :         {
     377            1 :             cluck_daemon::computer c;
     378            3 :             CATCH_REQUIRE_FALSE(c.set_id("10|9001|127.0.0.1|2512|"));
     379            1 :         }
     380              :     }
     381           10 :     CATCH_END_SECTION()
     382            9 : }
     383              : 
     384              : 
     385              : 
     386              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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