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 33 : 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 3 : 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
|