8 #include "proc-service.h"
10 // Tests of process-service related functionality.
12 // These tests work mostly by completely mocking out the base_process_service class. The mock
13 // implementations can be found in test-baseproc.cc.
15 extern eventloop_t event_loop;
17 constexpr static auto REG = dependency_type::REGULAR;
19 // Friend interface to access base_process_service private/protected members.
20 class base_process_service_test
23 static void exec_succeeded(base_process_service *bsp)
25 bsp->waiting_for_execstat = false;
26 bsp->exec_succeeded();
29 static void exec_failed(base_process_service *bsp, int errcode)
31 bsp->waiting_for_execstat = false;
32 bsp->exec_failed(errcode);
35 static void handle_exit(base_process_service *bsp, int exit_status)
38 bsp->handle_exit_status(bp_sys::exit_status(true, false, exit_status));
41 static void handle_signal_exit(base_process_service *bsp, int signo)
44 bsp->handle_exit_status(bp_sys::exit_status(false, true, signo));
50 extern int last_sig_sent;
51 extern pid_t last_forked_pid;
54 static void init_service_defaults(base_process_service &ps)
56 ps.set_restart_interval(time_val(10,0), 3);
57 ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
58 ps.set_stop_timeout(time_val(10,0));
61 // Regular service start
62 void test_proc_service_start()
68 string command = "test-command";
69 list<pair<unsigned,unsigned>> command_offsets;
70 command_offsets.emplace_back(0, command.length());
71 std::list<prelim_dep> depends;
73 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
74 init_service_defaults(p);
78 sset.process_queues();
80 assert(p.get_state() == service_state_t::STARTING);
82 base_process_service_test::exec_succeeded(&p);
83 sset.process_queues();
85 assert(p.get_state() == service_state_t::STARTED);
86 assert(event_loop.active_timers.size() == 0);
88 sset.remove_service(&p);
91 // Unexpected termination
92 void test_proc_unexpected_term()
98 string command = "test-command";
99 list<pair<unsigned,unsigned>> command_offsets;
100 command_offsets.emplace_back(0, command.length());
101 std::list<prelim_dep> depends;
103 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
104 init_service_defaults(p);
105 sset.add_service(&p);
108 sset.process_queues();
110 base_process_service_test::exec_succeeded(&p);
111 sset.process_queues();
113 assert(p.get_state() == service_state_t::STARTED);
115 base_process_service_test::handle_exit(&p, 0);
116 sset.process_queues();
118 assert(p.get_state() == service_state_t::STOPPED);
119 assert(p.get_stop_reason() == stopped_reason_t::TERMINATED);
120 assert(event_loop.active_timers.size() == 0);
122 sset.remove_service(&p);
125 // Termination via stop request
126 void test_term_via_stop()
132 string command = "test-command";
133 list<pair<unsigned,unsigned>> command_offsets;
134 command_offsets.emplace_back(0, command.length());
135 std::list<prelim_dep> depends;
137 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
138 init_service_defaults(p);
139 sset.add_service(&p);
142 sset.process_queues();
144 base_process_service_test::exec_succeeded(&p);
145 sset.process_queues();
147 assert(p.get_state() == service_state_t::STARTED);
148 assert(event_loop.active_timers.size() == 0);
151 sset.process_queues();
153 assert(p.get_state() == service_state_t::STOPPING);
154 assert(event_loop.active_timers.size() == 1);
156 base_process_service_test::handle_exit(&p, 0);
157 sset.process_queues();
159 assert(p.get_state() == service_state_t::STOPPED);
160 assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
161 assert(event_loop.active_timers.size() == 0);
163 sset.remove_service(&p);
166 // Time-out during start
167 void test_proc_start_timeout()
173 string command = "test-command";
174 list<pair<unsigned,unsigned>> command_offsets;
175 command_offsets.emplace_back(0, command.length());
176 std::list<prelim_dep> depends;
178 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
179 init_service_defaults(p);
180 sset.add_service(&p);
183 sset.process_queues();
185 assert(p.get_state() == service_state_t::STARTING);
188 sset.process_queues();
190 assert(p.get_state() == service_state_t::STOPPING);
192 base_process_service_test::handle_exit(&p, 0);
193 sset.process_queues();
195 assert(p.get_state() == service_state_t::STOPPED);
196 assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
197 assert(event_loop.active_timers.size() == 0);
199 sset.remove_service(&p);
202 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
203 void test_proc_start_timeout2()
209 string command = "test-command";
210 list<pair<unsigned,unsigned>> command_offsets;
211 command_offsets.emplace_back(0, command.length());
212 std::list<prelim_dep> depends;
214 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
215 init_service_defaults(p);
216 sset.add_service(&p);
218 service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
221 sset.process_queues();
223 assert(p.get_state() == service_state_t::STARTING);
224 assert(ts.get_state() == service_state_t::STARTING);
227 sset.process_queues();
229 assert(p.get_state() == service_state_t::STOPPING);
231 base_process_service_test::handle_exit(&p, 0);
232 sset.process_queues();
234 assert(p.get_state() == service_state_t::STOPPED);
235 assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
236 assert(ts.get_state() == service_state_t::STARTED);
237 assert(event_loop.active_timers.size() == 0);
239 sset.remove_service(&p);
242 // Test exec() failure for process service start.
243 void test_proc_start_execfail()
249 string command = "test-command";
250 list<pair<unsigned,unsigned>> command_offsets;
251 command_offsets.emplace_back(0, command.length());
252 std::list<prelim_dep> depends;
254 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
255 init_service_defaults(p);
256 sset.add_service(&p);
259 sset.process_queues();
261 assert(p.get_state() == service_state_t::STARTING);
263 base_process_service_test::exec_failed(&p, ENOENT);
264 sset.process_queues();
266 assert(p.get_state() == service_state_t::STOPPED);
267 assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
268 assert(event_loop.active_timers.size() == 0);
270 sset.remove_service(&p);
275 void test_proc_stop_timeout()
281 string command = "test-command";
282 list<pair<unsigned,unsigned>> command_offsets;
283 command_offsets.emplace_back(0, command.length());
284 std::list<prelim_dep> depends;
286 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
287 init_service_defaults(p);
288 sset.add_service(&p);
291 sset.process_queues();
293 assert(p.get_state() == service_state_t::STARTING);
295 base_process_service_test::exec_succeeded(&p);
296 sset.process_queues();
298 assert(p.get_state() == service_state_t::STARTED);
301 sset.process_queues();
303 assert(p.get_state() == service_state_t::STOPPING);
304 assert(bp_sys::last_sig_sent == SIGTERM);
307 sset.process_queues();
309 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
310 assert(p.get_state() == service_state_t::STOPPING);
311 assert(bp_sys::last_sig_sent == SIGKILL);
313 base_process_service_test::handle_exit(&p, 0);
314 sset.process_queues();
316 assert(p.get_state() == service_state_t::STOPPED);
317 assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
319 // Note that timer is still active as we faked its expiry above
320 //assert(event_loop.active_timers.size() == 0);
321 event_loop.active_timers.clear();
322 sset.remove_service(&p);
326 void test_proc_smooth_recovery1()
332 string command = "test-command";
333 list<pair<unsigned,unsigned>> command_offsets;
334 command_offsets.emplace_back(0, command.length());
335 std::list<prelim_dep> depends;
337 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
338 init_service_defaults(p);
339 p.set_smooth_recovery(true);
340 sset.add_service(&p);
343 sset.process_queues();
345 base_process_service_test::exec_succeeded(&p);
346 sset.process_queues();
348 pid_t first_instance = bp_sys::last_forked_pid;
350 assert(p.get_state() == service_state_t::STARTED);
352 base_process_service_test::handle_exit(&p, 0);
353 sset.process_queues();
355 // since time hasn't been changed, we expect that the process has not yet been re-launched:
356 assert(first_instance == bp_sys::last_forked_pid);
357 assert(p.get_state() == service_state_t::STARTED);
360 sset.process_queues();
362 // Now a new process should've been launched:
363 assert(first_instance + 1 == bp_sys::last_forked_pid);
364 assert(p.get_state() == service_state_t::STARTED);
365 event_loop.active_timers.clear();
367 sset.remove_service(&p);
370 // Smooth recovery without restart delay
371 void test_proc_smooth_recovery2()
377 string command = "test-command";
378 list<pair<unsigned,unsigned>> command_offsets;
379 command_offsets.emplace_back(0, command.length());
380 std::list<prelim_dep> depends;
382 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
383 init_service_defaults(p);
384 p.set_smooth_recovery(true);
385 p.set_restart_delay(time_val(0, 0));
386 sset.add_service(&p);
389 sset.process_queues();
391 base_process_service_test::exec_succeeded(&p);
392 sset.process_queues();
394 pid_t first_instance = bp_sys::last_forked_pid;
396 assert(p.get_state() == service_state_t::STARTED);
397 assert(event_loop.active_timers.size() == 0);
399 base_process_service_test::handle_exit(&p, 0);
400 sset.process_queues();
402 // no restart delay, process should restart immediately:
403 assert(first_instance + 1 == bp_sys::last_forked_pid);
404 assert(p.get_state() == service_state_t::STARTED);
405 assert(event_loop.active_timers.size() == 0);
407 sset.remove_service(&p);
411 void test_scripted_stop_timeout()
417 string command = "test-command";
418 string stopcommand = "stop-command";
419 list<pair<unsigned,unsigned>> command_offsets;
420 command_offsets.emplace_back(0, command.length());
421 std::list<prelim_dep> depends;
423 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
424 init_service_defaults(p);
425 p.set_stop_command(stopcommand, command_offsets);
426 sset.add_service(&p);
429 sset.process_queues();
431 assert(p.get_state() == service_state_t::STARTING);
433 base_process_service_test::exec_succeeded(&p);
434 sset.process_queues();
435 base_process_service_test::handle_exit(&p, 0);
436 sset.process_queues();
438 assert(p.get_state() == service_state_t::STARTED);
441 sset.process_queues();
443 assert(p.get_state() == service_state_t::STOPPING);
445 base_process_service_test::exec_succeeded(&p);
446 sset.process_queues();
448 // should still be stopping:
449 assert(p.get_state() == service_state_t::STOPPING);
452 sset.process_queues();
454 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
455 assert(p.get_state() == service_state_t::STOPPING);
456 assert(bp_sys::last_sig_sent == SIGKILL);
458 base_process_service_test::handle_exit(&p, SIGKILL);
459 sset.process_queues();
461 assert(p.get_state() == service_state_t::STOPPED);
462 assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
464 event_loop.active_timers.clear();
465 sset.remove_service(&p);
468 void test_scripted_start_fail()
474 string command = "test-command";
475 string stopcommand = "stop-command";
476 list<pair<unsigned,unsigned>> command_offsets;
477 command_offsets.emplace_back(0, command.length());
478 std::list<prelim_dep> depends;
480 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
481 init_service_defaults(p);
482 p.set_stop_command(stopcommand, command_offsets);
483 sset.add_service(&p);
485 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
486 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
487 sset.add_service(s2);
488 sset.add_service(s3);
491 sset.process_queues();
493 assert(p.get_state() == service_state_t::STARTING);
495 base_process_service_test::exec_succeeded(&p);
496 sset.process_queues();
497 base_process_service_test::handle_exit(&p, 0x1); // exit fail
498 sset.process_queues();
501 assert(p.get_state() == service_state_t::STOPPED);
502 assert(s2->get_state() == service_state_t::STOPPED);
503 assert(s3->get_state() == service_state_t::STOPPED);
504 assert(p.get_stop_reason() == stopped_reason_t::FAILED);
505 assert(s2->get_stop_reason() == stopped_reason_t::DEPFAILED);
506 assert(s3->get_stop_reason() == stopped_reason_t::DEPFAILED);
508 event_loop.active_timers.clear();
509 sset.remove_service(&p);
511 assert(sset.count_active_services() == 0);
514 void test_scripted_stop_fail()
520 string command = "test-command";
521 string stopcommand = "stop-command";
522 list<pair<unsigned,unsigned>> command_offsets;
523 command_offsets.emplace_back(0, command.length());
524 std::list<prelim_dep> depends;
526 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
527 init_service_defaults(p);
528 p.set_stop_command(stopcommand, command_offsets);
529 sset.add_service(&p);
531 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
532 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}, {&p, REG}});
533 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{&p, REG}, {s3, REG}});
534 sset.add_service(s2);
535 sset.add_service(s3);
536 sset.add_service(s4);
539 sset.process_queues();
541 base_process_service_test::exec_succeeded(&p);
542 sset.process_queues();
543 base_process_service_test::handle_exit(&p, 0x0); // success
544 sset.process_queues();
546 assert(p.get_state() == service_state_t::STARTED);
547 assert(s2->get_state() == service_state_t::STARTED);
548 assert(s3->get_state() == service_state_t::STARTED);
549 assert(s4->get_state() == service_state_t::STARTED);
551 pid_t last_forked = bp_sys::last_forked_pid;
554 sset.process_queues();
556 base_process_service_test::exec_succeeded(&p);
557 sset.process_queues();
558 base_process_service_test::handle_exit(&p, 0x1); // failure
559 sset.process_queues();
561 // The stop command should be executed once:
562 assert((bp_sys::last_forked_pid - last_forked) == 1);
564 assert(p.get_state() == service_state_t::STOPPED);
565 assert(s2->get_state() == service_state_t::STOPPED);
566 assert(s3->get_state() == service_state_t::STOPPED);
567 assert(s4->get_state() == service_state_t::STOPPED);
569 event_loop.active_timers.clear();
570 sset.remove_service(&p);
573 void test_scripted_start_skip()
579 string command = "test-command";
580 list<pair<unsigned,unsigned>> command_offsets;
581 command_offsets.emplace_back(0, command.length());
582 std::list<prelim_dep> depends;
584 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
585 init_service_defaults(p);
586 service_flags_t sflags;
587 sflags.skippable = true;
589 sset.add_service(&p);
591 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
592 sset.add_service(s2);
595 sset.process_queues();
596 assert(p.get_state() == service_state_t::STARTING);
598 base_process_service_test::exec_succeeded(&p);
599 sset.process_queues();
600 assert(p.get_state() == service_state_t::STARTING);
602 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
603 sset.process_queues();
605 assert(p.get_state() == service_state_t::STARTED);
606 assert(s2->get_state() == service_state_t::STARTED);
607 assert(p.was_start_skipped());
608 assert(! s2->was_start_skipped());
609 assert(sset.count_active_services() == 2);
612 sset.process_queues();
614 assert(p.get_state() == service_state_t::STOPPED);
615 assert(s2->get_state() == service_state_t::STOPPED);
616 assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
617 assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
618 assert(sset.count_active_services() == 0);
620 event_loop.active_timers.clear();
621 sset.remove_service(&p);
624 // Test interrupting start of a service marked skippable
625 void test_scripted_start_skip2()
631 string command = "test-command";
632 list<pair<unsigned,unsigned>> command_offsets;
633 command_offsets.emplace_back(0, command.length());
634 std::list<prelim_dep> depends;
636 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
637 init_service_defaults(p);
638 service_flags_t sflags;
639 sflags.skippable = true;
640 sflags.start_interruptible = true;
642 sset.add_service(&p);
644 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
645 sset.add_service(s2);
648 sset.process_queues();
649 assert(p.get_state() == service_state_t::STARTING);
651 base_process_service_test::exec_succeeded(&p);
652 sset.process_queues();
653 assert(p.get_state() == service_state_t::STARTING);
655 s2->stop(true); // abort startup; p should be cancelled
656 sset.process_queues();
658 assert(p.get_state() == service_state_t::STOPPING);
660 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
661 sset.process_queues();
663 assert(p.get_state() == service_state_t::STOPPED);
664 assert(s2->get_state() == service_state_t::STOPPED);
665 assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
666 assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
667 assert(sset.count_active_services() == 0);
669 event_loop.active_timers.clear();
670 sset.remove_service(&p);
674 #define RUN_TEST(name, spacing) \
675 std::cout << #name "..." spacing; \
677 std::cout << "PASSED" << std::endl;
679 int main(int argc, char **argv)
681 RUN_TEST(test_proc_service_start, " ");
682 RUN_TEST(test_proc_unexpected_term, " ");
683 RUN_TEST(test_term_via_stop, " ");
684 RUN_TEST(test_proc_start_timeout, " ");
685 RUN_TEST(test_proc_start_timeout2, " ");
686 RUN_TEST(test_proc_start_execfail, " ");
687 RUN_TEST(test_proc_stop_timeout, " ");
688 RUN_TEST(test_proc_smooth_recovery1, "");
689 RUN_TEST(test_proc_smooth_recovery2, "");
690 RUN_TEST(test_scripted_stop_timeout, "");
691 RUN_TEST(test_scripted_start_fail, " ");
692 RUN_TEST(test_scripted_stop_fail, " ");
693 RUN_TEST(test_scripted_start_skip, " ");
694 RUN_TEST(test_scripted_start_skip2, " ");