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;
18 constexpr static auto WAITS = dependency_type::WAITS_FOR;
19 constexpr static auto MS = dependency_type::MILESTONE;
21 // Friend interface to access base_process_service private/protected members.
22 class base_process_service_test
25 static void exec_succeeded(base_process_service *bsp)
27 bsp->waiting_for_execstat = false;
28 bsp->exec_succeeded();
31 static void handle_exit(base_process_service *bsp, int exit_status)
34 bsp->handle_exit_status(bp_sys::exit_status(true, false, exit_status));
37 static void handle_signal_exit(base_process_service *bsp, int signo)
40 bsp->handle_exit_status(bp_sys::exit_status(false, true, signo));
46 extern int last_sig_sent;
47 extern pid_t last_forked_pid;
50 static void init_service_defaults(base_process_service &ps)
52 ps.set_restart_interval(time_val(10,0), 3);
53 ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
54 ps.set_stop_timeout(time_val(10,0));
57 // Regular service start
58 void test_proc_service_start()
64 string command = "test-command";
65 list<pair<unsigned,unsigned>> command_offsets;
66 command_offsets.emplace_back(0, command.length());
67 std::list<prelim_dep> depends;
69 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
70 init_service_defaults(p);
74 sset.process_queues();
76 assert(p.get_state() == service_state_t::STARTING);
78 base_process_service_test::exec_succeeded(&p);
79 sset.process_queues();
81 assert(p.get_state() == service_state_t::STARTED);
82 assert(event_loop.active_timers.size() == 0);
84 sset.remove_service(&p);
87 // Unexpected termination
88 void test_proc_unexpected_term()
94 string command = "test-command";
95 list<pair<unsigned,unsigned>> command_offsets;
96 command_offsets.emplace_back(0, command.length());
97 std::list<prelim_dep> depends;
99 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
100 init_service_defaults(p);
101 sset.add_service(&p);
104 sset.process_queues();
106 base_process_service_test::exec_succeeded(&p);
107 sset.process_queues();
109 assert(p.get_state() == service_state_t::STARTED);
111 base_process_service_test::handle_exit(&p, 0);
112 sset.process_queues();
114 assert(p.get_state() == service_state_t::STOPPED);
115 assert(event_loop.active_timers.size() == 0);
117 sset.remove_service(&p);
120 // Termination via stop request
121 void test_term_via_stop()
127 string command = "test-command";
128 list<pair<unsigned,unsigned>> command_offsets;
129 command_offsets.emplace_back(0, command.length());
130 std::list<prelim_dep> depends;
132 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
133 init_service_defaults(p);
134 sset.add_service(&p);
137 sset.process_queues();
139 base_process_service_test::exec_succeeded(&p);
140 sset.process_queues();
142 assert(p.get_state() == service_state_t::STARTED);
143 assert(event_loop.active_timers.size() == 0);
146 sset.process_queues();
148 assert(p.get_state() == service_state_t::STOPPING);
149 assert(event_loop.active_timers.size() == 1);
151 base_process_service_test::handle_exit(&p, 0);
152 sset.process_queues();
154 assert(p.get_state() == service_state_t::STOPPED);
155 assert(event_loop.active_timers.size() == 0);
157 sset.remove_service(&p);
160 // Time-out during start
161 void test_proc_start_timeout()
167 string command = "test-command";
168 list<pair<unsigned,unsigned>> command_offsets;
169 command_offsets.emplace_back(0, command.length());
170 std::list<prelim_dep> depends;
172 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
173 init_service_defaults(p);
174 sset.add_service(&p);
177 sset.process_queues();
179 assert(p.get_state() == service_state_t::STARTING);
182 sset.process_queues();
184 assert(p.get_state() == service_state_t::STOPPING);
186 base_process_service_test::handle_exit(&p, 0);
187 sset.process_queues();
189 assert(p.get_state() == service_state_t::STOPPED);
190 assert(event_loop.active_timers.size() == 0);
192 sset.remove_service(&p);
195 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
196 void test_proc_start_timeout2()
202 string command = "test-command";
203 list<pair<unsigned,unsigned>> command_offsets;
204 command_offsets.emplace_back(0, command.length());
205 std::list<prelim_dep> depends;
207 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
208 init_service_defaults(p);
209 sset.add_service(&p);
211 service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
214 sset.process_queues();
216 assert(p.get_state() == service_state_t::STARTING);
217 assert(ts.get_state() == service_state_t::STARTING);
220 sset.process_queues();
222 assert(p.get_state() == service_state_t::STOPPING);
224 base_process_service_test::handle_exit(&p, 0);
225 sset.process_queues();
227 assert(p.get_state() == service_state_t::STOPPED);
228 assert(ts.get_state() == service_state_t::STARTED);
229 assert(event_loop.active_timers.size() == 0);
231 sset.remove_service(&p);
235 void test_proc_stop_timeout()
241 string command = "test-command";
242 list<pair<unsigned,unsigned>> command_offsets;
243 command_offsets.emplace_back(0, command.length());
244 std::list<prelim_dep> depends;
246 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
247 init_service_defaults(p);
248 sset.add_service(&p);
251 sset.process_queues();
253 assert(p.get_state() == service_state_t::STARTING);
255 base_process_service_test::exec_succeeded(&p);
256 sset.process_queues();
258 assert(p.get_state() == service_state_t::STARTED);
261 sset.process_queues();
263 assert(p.get_state() == service_state_t::STOPPING);
264 assert(bp_sys::last_sig_sent == SIGTERM);
267 sset.process_queues();
269 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
270 assert(p.get_state() == service_state_t::STOPPING);
271 assert(bp_sys::last_sig_sent == SIGKILL);
273 base_process_service_test::handle_exit(&p, 0);
274 sset.process_queues();
276 assert(p.get_state() == service_state_t::STOPPED);
278 // Note that timer is still active as we faked its expiry above
279 //assert(event_loop.active_timers.size() == 0);
280 event_loop.active_timers.clear();
281 sset.remove_service(&p);
285 void test_proc_smooth_recovery1()
291 string command = "test-command";
292 list<pair<unsigned,unsigned>> command_offsets;
293 command_offsets.emplace_back(0, command.length());
294 std::list<prelim_dep> depends;
296 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
297 init_service_defaults(p);
298 p.set_smooth_recovery(true);
299 sset.add_service(&p);
302 sset.process_queues();
304 base_process_service_test::exec_succeeded(&p);
305 sset.process_queues();
307 pid_t first_instance = bp_sys::last_forked_pid;
309 assert(p.get_state() == service_state_t::STARTED);
311 base_process_service_test::handle_exit(&p, 0);
312 sset.process_queues();
314 // since time hasn't been changed, we expect that the process has not yet been re-launched:
315 assert(first_instance == bp_sys::last_forked_pid);
316 assert(p.get_state() == service_state_t::STARTED);
319 sset.process_queues();
321 // Now a new process should've been launched:
322 assert(first_instance + 1 == bp_sys::last_forked_pid);
323 assert(p.get_state() == service_state_t::STARTED);
324 event_loop.active_timers.clear();
326 sset.remove_service(&p);
329 // Smooth recovery without restart delay
330 void test_proc_smooth_recovery2()
336 string command = "test-command";
337 list<pair<unsigned,unsigned>> command_offsets;
338 command_offsets.emplace_back(0, command.length());
339 std::list<prelim_dep> depends;
341 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
342 init_service_defaults(p);
343 p.set_smooth_recovery(true);
344 p.set_restart_delay(time_val(0, 0));
345 sset.add_service(&p);
348 sset.process_queues();
350 base_process_service_test::exec_succeeded(&p);
351 sset.process_queues();
353 pid_t first_instance = bp_sys::last_forked_pid;
355 assert(p.get_state() == service_state_t::STARTED);
356 assert(event_loop.active_timers.size() == 0);
358 base_process_service_test::handle_exit(&p, 0);
359 sset.process_queues();
361 // no restart delay, process should restart immediately:
362 assert(first_instance + 1 == bp_sys::last_forked_pid);
363 assert(p.get_state() == service_state_t::STARTED);
364 assert(event_loop.active_timers.size() == 0);
366 sset.remove_service(&p);
370 void test_scripted_stop_timeout()
376 string command = "test-command";
377 string stopcommand = "stop-command";
378 list<pair<unsigned,unsigned>> command_offsets;
379 command_offsets.emplace_back(0, command.length());
380 std::list<prelim_dep> depends;
382 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
383 init_service_defaults(p);
384 p.set_stop_command(stopcommand, command_offsets);
385 sset.add_service(&p);
388 sset.process_queues();
390 assert(p.get_state() == service_state_t::STARTING);
392 base_process_service_test::exec_succeeded(&p);
393 sset.process_queues();
394 base_process_service_test::handle_exit(&p, 0);
395 sset.process_queues();
397 assert(p.get_state() == service_state_t::STARTED);
400 sset.process_queues();
402 assert(p.get_state() == service_state_t::STOPPING);
404 base_process_service_test::exec_succeeded(&p);
405 sset.process_queues();
407 // should still be stopping:
408 assert(p.get_state() == service_state_t::STOPPING);
411 sset.process_queues();
413 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
414 assert(p.get_state() == service_state_t::STOPPING);
415 assert(bp_sys::last_sig_sent == SIGKILL);
417 base_process_service_test::handle_exit(&p, SIGKILL);
418 sset.process_queues();
420 assert(p.get_state() == service_state_t::STOPPED);
422 event_loop.active_timers.clear();
423 sset.remove_service(&p);
426 void test_scripted_start_fail()
432 string command = "test-command";
433 string stopcommand = "stop-command";
434 list<pair<unsigned,unsigned>> command_offsets;
435 command_offsets.emplace_back(0, command.length());
436 std::list<prelim_dep> depends;
438 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
439 init_service_defaults(p);
440 p.set_stop_command(stopcommand, command_offsets);
441 sset.add_service(&p);
443 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
444 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
445 sset.add_service(s2);
446 sset.add_service(s3);
449 sset.process_queues();
451 assert(p.get_state() == service_state_t::STARTING);
453 base_process_service_test::exec_succeeded(&p);
454 sset.process_queues();
455 base_process_service_test::handle_exit(&p, 0x1); // exit fail
456 sset.process_queues();
459 assert(p.get_state() == service_state_t::STOPPED);
460 assert(s2->get_state() == service_state_t::STOPPED);
461 assert(s3->get_state() == service_state_t::STOPPED);
463 event_loop.active_timers.clear();
464 sset.remove_service(&p);
466 assert(sset.count_active_services() == 0);
469 void test_scripted_stop_fail()
475 string command = "test-command";
476 string stopcommand = "stop-command";
477 list<pair<unsigned,unsigned>> command_offsets;
478 command_offsets.emplace_back(0, command.length());
479 std::list<prelim_dep> depends;
481 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
482 init_service_defaults(p);
483 p.set_stop_command(stopcommand, command_offsets);
484 sset.add_service(&p);
486 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
487 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}, {&p, REG}});
488 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{&p, REG}, {s3, REG}});
489 sset.add_service(s2);
490 sset.add_service(s3);
491 sset.add_service(s4);
494 sset.process_queues();
496 base_process_service_test::exec_succeeded(&p);
497 sset.process_queues();
498 base_process_service_test::handle_exit(&p, 0x0); // success
499 sset.process_queues();
501 assert(p.get_state() == service_state_t::STARTED);
502 assert(s2->get_state() == service_state_t::STARTED);
503 assert(s3->get_state() == service_state_t::STARTED);
504 assert(s4->get_state() == service_state_t::STARTED);
506 pid_t last_forked = bp_sys::last_forked_pid;
509 sset.process_queues();
511 base_process_service_test::exec_succeeded(&p);
512 sset.process_queues();
513 base_process_service_test::handle_exit(&p, 0x1); // failure
514 sset.process_queues();
516 // The stop command should be executed once:
517 assert((bp_sys::last_forked_pid - last_forked) == 1);
519 assert(p.get_state() == service_state_t::STOPPED);
520 assert(s2->get_state() == service_state_t::STOPPED);
521 assert(s3->get_state() == service_state_t::STOPPED);
522 assert(s4->get_state() == service_state_t::STOPPED);
524 event_loop.active_timers.clear();
525 sset.remove_service(&p);
528 void test_scripted_start_skip()
534 string command = "test-command";
535 list<pair<unsigned,unsigned>> command_offsets;
536 command_offsets.emplace_back(0, command.length());
537 std::list<prelim_dep> depends;
539 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
540 init_service_defaults(p);
541 onstart_flags_t sflags;
542 sflags.skippable = true;
544 sset.add_service(&p);
546 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
547 sset.add_service(s2);
550 sset.process_queues();
551 assert(p.get_state() == service_state_t::STARTING);
553 base_process_service_test::exec_succeeded(&p);
554 sset.process_queues();
555 assert(p.get_state() == service_state_t::STARTING);
557 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
558 sset.process_queues();
560 assert(p.get_state() == service_state_t::STARTED);
561 assert(s2->get_state() == service_state_t::STARTED);
562 assert(p.was_start_skipped());
563 assert(! s2->was_start_skipped());
564 assert(sset.count_active_services() == 2);
567 sset.process_queues();
569 assert(p.get_state() == service_state_t::STOPPED);
570 assert(s2->get_state() == service_state_t::STOPPED);
571 assert(sset.count_active_services() == 0);
573 event_loop.active_timers.clear();
574 sset.remove_service(&p);
577 // Test interrupting start of a service marked skippable
578 void test_scripted_start_skip2()
584 string command = "test-command";
585 list<pair<unsigned,unsigned>> command_offsets;
586 command_offsets.emplace_back(0, command.length());
587 std::list<prelim_dep> depends;
589 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
590 init_service_defaults(p);
591 onstart_flags_t sflags;
592 sflags.skippable = true;
593 sflags.start_interruptible = true;
595 sset.add_service(&p);
597 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
598 sset.add_service(s2);
601 sset.process_queues();
602 assert(p.get_state() == service_state_t::STARTING);
604 base_process_service_test::exec_succeeded(&p);
605 sset.process_queues();
606 assert(p.get_state() == service_state_t::STARTING);
608 s2->stop(true); // abort startup; p should be cancelled
609 sset.process_queues();
611 assert(p.get_state() == service_state_t::STOPPING);
613 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
614 sset.process_queues();
616 assert(p.get_state() == service_state_t::STOPPED);
617 assert(s2->get_state() == service_state_t::STOPPED);
618 assert(sset.count_active_services() == 0);
620 event_loop.active_timers.clear();
621 sset.remove_service(&p);
624 #define RUN_TEST(name, spacing) \
625 std::cout << #name "..." spacing; \
627 std::cout << "PASSED" << std::endl;
629 int main(int argc, char **argv)
631 RUN_TEST(test_proc_service_start, " ");
632 RUN_TEST(test_proc_unexpected_term, " ");
633 RUN_TEST(test_term_via_stop, " ");
634 RUN_TEST(test_proc_start_timeout, " ");
635 RUN_TEST(test_proc_start_timeout2, " ");
636 RUN_TEST(test_proc_stop_timeout, " ");
637 RUN_TEST(test_proc_smooth_recovery1, "");
638 RUN_TEST(test_proc_smooth_recovery2, "");
639 RUN_TEST(test_scripted_stop_timeout, "");
640 RUN_TEST(test_scripted_start_fail, " ");
641 RUN_TEST(test_scripted_stop_fail, " ");
642 RUN_TEST(test_scripted_start_skip, " ");
643 RUN_TEST(test_scripted_start_skip2, " ");