Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved 2 : // 3 : // https://snapwebsites.org/project/eventdispatcher 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 2 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 along 17 : // with this program; if not, write to the Free Software Foundation, Inc., 18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 : 20 : // test standalone header 21 : // 22 : #include <eventdispatcher/timer.h> 23 : 24 : 25 : // self 26 : // 27 : #include "catch_main.h" 28 : 29 : 30 : // eventdispatcher 31 : // 32 : #include <eventdispatcher/communicator.h> 33 : #include <eventdispatcher/dispatcher.h> 34 : 35 : 36 : // C 37 : // 38 : #include <unistd.h> 39 : 40 : 41 : // last include 42 : // 43 : #include <snapdev/poison.h> 44 : 45 : 46 : 47 : 48 : namespace 49 : { 50 : 51 : 52 : 53 : // use a timer to test various general connection function 54 : // 55 : class timer_test 56 : : public ed::timer 57 : { 58 : public: 59 : typedef std::shared_ptr<timer_test> pointer_t; 60 : 61 : timer_test(); 62 : 63 : virtual void process_timeout() override; 64 : virtual void connection_added() override; 65 : virtual void connection_removed() override; 66 : 67 : void set_expect_timeout(bool expect_timeout); 68 : void set_expect_add(bool expect_add); 69 : bool get_expect_add() const; 70 : void set_expect_remove(bool expect_remove); 71 : bool get_expect_remove() const; 72 : 73 : private: 74 : bool f_expect_timeout = false; 75 : bool f_expect_add = false; 76 : bool f_expect_remove = false; 77 : }; 78 : 79 : 80 : 81 11 : timer_test::timer_test() 82 11 : : timer(1'000'000) // 1s 83 : { 84 11 : set_name("timer"); 85 11 : } 86 : 87 : 88 1 : void timer_test::process_timeout() 89 : { 90 1 : if(!f_expect_timeout) 91 : { 92 0 : throw std::runtime_error("unexpectedly got process_timeout() called."); 93 : } 94 1 : f_expect_timeout = false; 95 : 96 1 : remove_from_communicator(); 97 1 : } 98 : 99 : 100 5 : void timer_test::connection_added() 101 : { 102 5 : if(!f_expect_add) 103 : { 104 0 : throw std::runtime_error("unexpectedly got added to communicator."); 105 : } 106 5 : f_expect_add = false; 107 : 108 5 : connection::connection_added(); 109 5 : } 110 : 111 : 112 5 : void timer_test::connection_removed() 113 : { 114 5 : if(!f_expect_remove) 115 : { 116 0 : throw std::runtime_error("unexpectedly got removed to communicator."); 117 : } 118 5 : f_expect_remove = false; 119 : 120 5 : connection::connection_removed(); 121 5 : } 122 : 123 : 124 2 : void timer_test::set_expect_timeout(bool expect_timeout) 125 : { 126 2 : f_expect_timeout = expect_timeout; 127 2 : } 128 : 129 : 130 5 : void timer_test::set_expect_add(bool expect_add) 131 : { 132 5 : f_expect_add = expect_add; 133 5 : } 134 : 135 : 136 5 : bool timer_test::get_expect_add() const 137 : { 138 5 : return f_expect_add; 139 : } 140 : 141 : 142 5 : void timer_test::set_expect_remove(bool expect_remove) 143 : { 144 5 : f_expect_remove = expect_remove; 145 5 : } 146 : 147 : 148 5 : bool timer_test::get_expect_remove() const 149 : { 150 5 : return f_expect_remove; 151 : } 152 : 153 : 154 : 155 : } // no name namespace 156 : 157 : 158 : 159 6 : CATCH_TEST_CASE("timer", "[timer]") 160 : { 161 6 : CATCH_START_SECTION("Timer connection") 162 : { 163 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 164 : 165 : // pretend we add a timer, nullptr is ignored 166 : // 167 1 : CATCH_REQUIRE_FALSE(communicator->add_connection(timer_test::pointer_t())); 168 1 : CATCH_REQUIRE(communicator->get_connections().empty()); 169 : 170 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 171 : 172 1 : CATCH_REQUIRE(t->get_name() == "timer"); 173 : 174 1 : t->set_name("my-timer"); 175 1 : CATCH_REQUIRE(t->get_name() == "my-timer"); 176 : 177 1 : CATCH_REQUIRE_FALSE(t->is_listener()); 178 1 : CATCH_REQUIRE_FALSE(t->is_signal()); 179 1 : CATCH_REQUIRE_FALSE(t->is_reader()); 180 1 : CATCH_REQUIRE_FALSE(t->is_writer()); 181 1 : CATCH_REQUIRE(t->get_socket() == -1); 182 1 : CATCH_REQUIRE(t->valid_socket()); 183 : 184 1 : CATCH_REQUIRE(t->is_enabled()); 185 1 : t->set_enable(false); 186 1 : CATCH_REQUIRE_FALSE(t->is_enabled()); 187 1 : t->set_enable(true); 188 1 : CATCH_REQUIRE(t->is_enabled()); 189 : 190 1 : CATCH_REQUIRE(t->get_priority() == ed::EVENT_DEFAULT_PRIORITY); 191 1 : t->set_priority(33); 192 1 : CATCH_REQUIRE(t->get_priority() == 33); 193 : 194 : // make sure the sorting works as expected 195 : { 196 1 : timer_test::pointer_t t2(std::make_shared<timer_test>()); 197 1 : CATCH_REQUIRE(ed::connection::compare(t, t2)); 198 1 : CATCH_REQUIRE_FALSE(ed::connection::compare(t2, t)); 199 : 200 1 : t->set_priority(145); 201 1 : CATCH_REQUIRE(t->get_priority() == 145); 202 : 203 1 : CATCH_REQUIRE_FALSE(ed::connection::compare(t, t2)); 204 1 : CATCH_REQUIRE(ed::connection::compare(t2, t)); 205 1 : } 206 : 207 1 : CATCH_REQUIRE(t->get_event_limit() == 5); // TODO: make 5 a constant 208 1 : t->set_event_limit(10); 209 1 : CATCH_REQUIRE(t->get_event_limit() == 10); 210 : 211 1 : CATCH_REQUIRE(t->get_processing_time_limit() == 500'000); // TODO: make 500_000 a constant 212 1 : t->set_processing_time_limit(1'200'999); 213 1 : CATCH_REQUIRE(t->get_processing_time_limit() == 1'200'999); 214 : 215 1 : CATCH_REQUIRE(t->get_timeout_delay() == 1'000'000); 216 1 : t->set_timeout_delay(5'000'000); 217 1 : CATCH_REQUIRE(t->get_timeout_delay() == 5'000'000); 218 1 : snapdev::timespec_ex const duration(11, 345'678'183); 219 1 : t->set_timeout_delay(duration); 220 1 : CATCH_REQUIRE(t->get_timeout_delay() == 11'345'678); 221 : 222 1 : snapdev::timespec_ex const date(snapdev::now() + snapdev::timespec_ex(30, 500'000'000)); 223 1 : t->set_timeout_date(date); 224 1 : CATCH_REQUIRE(t->get_timeout_date() == date.to_usec()); 225 : 226 : // make sure it works even though it does nothing 227 : // 228 1 : t->non_blocking(); 229 1 : t->keep_alive(); 230 : 231 1 : CATCH_REQUIRE_FALSE(t->is_done()); 232 1 : t->mark_done(); 233 1 : CATCH_REQUIRE(t->is_done()); 234 1 : t->mark_not_done(); 235 1 : CATCH_REQUIRE_FALSE(t->is_done()); 236 1 : } 237 6 : CATCH_END_SECTION() 238 : 239 6 : CATCH_START_SECTION("Timer add/remove connection") 240 : { 241 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 242 : 243 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 244 : 245 1 : t->set_expect_add(true); 246 1 : CATCH_REQUIRE(communicator->add_connection(t)); 247 1 : CATCH_REQUIRE_FALSE(t->get_expect_add()); 248 1 : CATCH_REQUIRE_FALSE(communicator->add_connection(t)); // duplicate is ignored 249 1 : ed::connection::vector_t const & connections(communicator->get_connections()); 250 1 : CATCH_REQUIRE(connections.size() == 1); 251 1 : CATCH_REQUIRE(connections[0] == t); 252 : 253 1 : t->set_expect_remove(true); 254 1 : communicator->remove_connection(t); 255 1 : CATCH_REQUIRE_FALSE(t->get_expect_remove()); 256 1 : } 257 6 : CATCH_END_SECTION() 258 : 259 6 : CATCH_START_SECTION("Timer add connection, remove on process_error()") 260 : { 261 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 262 : 263 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 264 : 265 1 : t->set_expect_add(true); 266 1 : CATCH_REQUIRE(communicator->add_connection(t)); 267 1 : CATCH_REQUIRE_FALSE(t->get_expect_add()); 268 : 269 1 : t->set_expect_remove(true); 270 1 : t->process_error(); 271 1 : CATCH_REQUIRE_FALSE(t->get_expect_remove()); 272 1 : } 273 6 : CATCH_END_SECTION() 274 : 275 6 : CATCH_START_SECTION("Timer add connection, expect process_timeout()") 276 : { 277 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 278 : 279 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 280 : 281 1 : t->set_expect_add(true); 282 1 : CATCH_REQUIRE(communicator->add_connection(t)); 283 1 : CATCH_REQUIRE_FALSE(t->get_expect_add()); 284 : 285 1 : snapdev::timespec_ex const start(snapdev::now()); 286 1 : t->set_expect_timeout(true); 287 1 : t->set_expect_remove(true); 288 1 : communicator->run(); 289 1 : t->set_expect_timeout(false); 290 1 : CATCH_REQUIRE_FALSE(t->get_expect_remove()); 291 1 : snapdev::timespec_ex const end(snapdev::now()); 292 1 : snapdev::timespec_ex const duration(end - start); 293 1 : CATCH_REQUIRE(duration.tv_sec >= 1); 294 1 : } 295 6 : CATCH_END_SECTION() 296 : 297 6 : CATCH_START_SECTION("Timer add connection, remove on process_hup()") 298 : { 299 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 300 : 301 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 302 : 303 1 : t->set_expect_add(true); 304 1 : CATCH_REQUIRE(communicator->add_connection(t)); 305 1 : CATCH_REQUIRE_FALSE(t->get_expect_add()); 306 : 307 1 : t->set_expect_remove(true); 308 1 : t->process_hup(); 309 1 : CATCH_REQUIRE_FALSE(t->get_expect_remove()); 310 1 : } 311 6 : CATCH_END_SECTION() 312 : 313 6 : CATCH_START_SECTION("Timer add connection, remove on process_invalid()") 314 : { 315 1 : ed::communicator::pointer_t communicator(ed::communicator::instance()); 316 : 317 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 318 : 319 1 : t->set_expect_add(true); 320 1 : CATCH_REQUIRE(communicator->add_connection(t)); 321 1 : CATCH_REQUIRE_FALSE(t->get_expect_add()); 322 : 323 1 : t->set_expect_remove(true); 324 1 : t->process_invalid(); 325 1 : CATCH_REQUIRE_FALSE(t->get_expect_remove()); 326 1 : } 327 6 : CATCH_END_SECTION() 328 6 : } 329 : 330 : 331 4 : CATCH_TEST_CASE("timer_errors", "[timer][error]") 332 : { 333 4 : CATCH_START_SECTION("timer: invalid priority (too small)") 334 : { 335 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 336 101 : for(ed::priority_t p(ed::EVENT_MIN_PRIORITY - 100); p < ed::EVENT_MIN_PRIORITY; ++p) 337 : { 338 100 : CATCH_REQUIRE_THROWS_MATCHES( 339 : t->set_priority(p) 340 : , ed::parameter_error 341 : , Catch::Matchers::ExceptionMessage( 342 : "parameter_error: connection::set_priority(): priority out of range, this instance of connection accepts priorities between " 343 : + std::to_string(ed::EVENT_MIN_PRIORITY) 344 : + " and " 345 : + std::to_string(ed::EVENT_MAX_PRIORITY) 346 : + ".")); 347 : } 348 1 : } 349 4 : CATCH_END_SECTION() 350 : 351 4 : CATCH_START_SECTION("timer: invalid priority (too large)") 352 : { 353 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 354 100 : for(ed::priority_t p(ed::EVENT_MAX_PRIORITY + 1); p < ed::EVENT_MAX_PRIORITY + 100; ++p) 355 : { 356 99 : CATCH_REQUIRE_THROWS_MATCHES( 357 : t->set_priority(p) 358 : , ed::parameter_error 359 : , Catch::Matchers::ExceptionMessage( 360 : "parameter_error: connection::set_priority(): priority out of range, this instance of connection accepts priorities between " 361 : + std::to_string(ed::EVENT_MIN_PRIORITY) 362 : + " and " 363 : + std::to_string(ed::EVENT_MAX_PRIORITY) 364 : + ".")); 365 : } 366 1 : } 367 4 : CATCH_END_SECTION() 368 : 369 4 : CATCH_START_SECTION("timer: invalid timeout delay (too small)") 370 : { 371 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 372 111 : for(std::int64_t us(-100); us < 10; ++us) 373 : { 374 110 : if(us == -1) 375 : { 376 : // -1 is similar to turning off the timer 377 : // 378 1 : continue; 379 : } 380 : 381 109 : CATCH_REQUIRE_THROWS_MATCHES( 382 : t->set_timeout_delay(us) 383 : , ed::parameter_error 384 : , Catch::Matchers::ExceptionMessage( 385 : "parameter_error: connection::set_timeout_delay(): timeout_us parameter cannot be less than 10 unless it is exactly -1, " 386 : + std::to_string(us) 387 : + " is not valid.")); 388 : } 389 1 : } 390 4 : CATCH_END_SECTION() 391 : 392 4 : CATCH_START_SECTION("timer: invalid timeout date (too small)") 393 : { 394 1 : timer_test::pointer_t t(std::make_shared<timer_test>()); 395 100 : for(std::int64_t date(-100); date < -1; ++date) 396 : { 397 99 : CATCH_REQUIRE_THROWS_MATCHES( 398 : t->set_timeout_date(date) 399 : , ed::parameter_error 400 : , Catch::Matchers::ExceptionMessage( 401 : "parameter_error: connection::set_timeout_date(): date_us parameter cannot be less than -1, " 402 : + std::to_string(date) 403 : + " is not valid.")); 404 : } 405 1 : } 406 4 : CATCH_END_SECTION() 407 4 : } 408 : 409 : 410 : 411 : // vim: ts=4 sw=4 et