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

          Line data    Source code
       1             : // Copyright (c) 2016-2024  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           1 :         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          16 :                 n = SNAP_CATCH2_NAMESPACE::random_string(1, 15);
     136             :             }
     137          16 :             while(std::strchr(n.c_str(), '|') != nullptr);
     138          15 :             addr::addr a(get_random_address());
     139          30 :             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          15 :             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           7 :         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           7 :         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           2 :         std::string n("start");
     227           1 :         n += '\0';
     228           1 :         n += "end";
     229           3 :         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           1 :         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           7 :         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           7 :         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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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           1 :             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 1.14

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