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));
55 ps.set_start_interruptible(false);
58 // Regular service start
59 void test_proc_service_start()
65 string command = "test-command";
66 list<pair<unsigned,unsigned>> command_offsets;
67 command_offsets.emplace_back(0, command.length());
68 std::list<prelim_dep> depends;
70 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
71 init_service_defaults(p);
75 sset.process_queues();
77 assert(p.get_state() == service_state_t::STARTING);
79 base_process_service_test::exec_succeeded(&p);
80 sset.process_queues();
82 assert(p.get_state() == service_state_t::STARTED);
83 assert(event_loop.active_timers.size() == 0);
85 sset.remove_service(&p);
88 // Unexpected termination
89 void test_proc_unexpected_term()
95 string command = "test-command";
96 list<pair<unsigned,unsigned>> command_offsets;
97 command_offsets.emplace_back(0, command.length());
98 std::list<prelim_dep> depends;
100 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
101 init_service_defaults(p);
102 sset.add_service(&p);
105 sset.process_queues();
107 base_process_service_test::exec_succeeded(&p);
108 sset.process_queues();
110 assert(p.get_state() == service_state_t::STARTED);
112 base_process_service_test::handle_exit(&p, 0);
113 sset.process_queues();
115 assert(p.get_state() == service_state_t::STOPPED);
116 assert(event_loop.active_timers.size() == 0);
118 sset.remove_service(&p);
121 // Termination via stop request
122 void test_term_via_stop()
128 string command = "test-command";
129 list<pair<unsigned,unsigned>> command_offsets;
130 command_offsets.emplace_back(0, command.length());
131 std::list<prelim_dep> depends;
133 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
134 init_service_defaults(p);
135 sset.add_service(&p);
138 sset.process_queues();
140 base_process_service_test::exec_succeeded(&p);
141 sset.process_queues();
143 assert(p.get_state() == service_state_t::STARTED);
144 assert(event_loop.active_timers.size() == 0);
147 sset.process_queues();
149 assert(p.get_state() == service_state_t::STOPPING);
150 assert(event_loop.active_timers.size() == 1);
152 base_process_service_test::handle_exit(&p, 0);
153 sset.process_queues();
155 assert(p.get_state() == service_state_t::STOPPED);
156 assert(event_loop.active_timers.size() == 0);
158 sset.remove_service(&p);
161 // Time-out during start
162 void test_proc_start_timeout()
168 string command = "test-command";
169 list<pair<unsigned,unsigned>> command_offsets;
170 command_offsets.emplace_back(0, command.length());
171 std::list<prelim_dep> depends;
173 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
174 init_service_defaults(p);
175 sset.add_service(&p);
178 sset.process_queues();
180 assert(p.get_state() == service_state_t::STARTING);
183 sset.process_queues();
185 assert(p.get_state() == service_state_t::STOPPING);
187 base_process_service_test::handle_exit(&p, 0);
188 sset.process_queues();
190 assert(p.get_state() == service_state_t::STOPPED);
191 assert(event_loop.active_timers.size() == 0);
193 sset.remove_service(&p);
196 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
197 void test_proc_start_timeout2()
203 string command = "test-command";
204 list<pair<unsigned,unsigned>> command_offsets;
205 command_offsets.emplace_back(0, command.length());
206 std::list<prelim_dep> depends;
208 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
209 init_service_defaults(p);
210 sset.add_service(&p);
212 service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
215 sset.process_queues();
217 assert(p.get_state() == service_state_t::STARTING);
218 assert(ts.get_state() == service_state_t::STARTING);
221 sset.process_queues();
223 assert(p.get_state() == service_state_t::STOPPING);
225 base_process_service_test::handle_exit(&p, 0);
226 sset.process_queues();
228 assert(p.get_state() == service_state_t::STOPPED);
229 assert(ts.get_state() == service_state_t::STARTED);
230 assert(event_loop.active_timers.size() == 0);
232 sset.remove_service(&p);
236 void test_proc_stop_timeout()
242 string command = "test-command";
243 list<pair<unsigned,unsigned>> command_offsets;
244 command_offsets.emplace_back(0, command.length());
245 std::list<prelim_dep> depends;
247 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
248 init_service_defaults(p);
249 sset.add_service(&p);
252 sset.process_queues();
254 assert(p.get_state() == service_state_t::STARTING);
256 base_process_service_test::exec_succeeded(&p);
257 sset.process_queues();
259 assert(p.get_state() == service_state_t::STARTED);
262 sset.process_queues();
264 assert(p.get_state() == service_state_t::STOPPING);
265 assert(bp_sys::last_sig_sent == SIGTERM);
268 sset.process_queues();
270 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
271 assert(p.get_state() == service_state_t::STOPPING);
272 assert(bp_sys::last_sig_sent == SIGKILL);
274 base_process_service_test::handle_exit(&p, 0);
275 sset.process_queues();
277 assert(p.get_state() == service_state_t::STOPPED);
279 // Note that timer is still active as we faked its expiry above
280 //assert(event_loop.active_timers.size() == 0);
281 event_loop.active_timers.clear();
282 sset.remove_service(&p);
286 void test_proc_smooth_recovery1()
292 string command = "test-command";
293 list<pair<unsigned,unsigned>> command_offsets;
294 command_offsets.emplace_back(0, command.length());
295 std::list<prelim_dep> depends;
297 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
298 init_service_defaults(p);
299 p.set_smooth_recovery(true);
300 sset.add_service(&p);
303 sset.process_queues();
305 base_process_service_test::exec_succeeded(&p);
306 sset.process_queues();
308 pid_t first_instance = bp_sys::last_forked_pid;
310 assert(p.get_state() == service_state_t::STARTED);
312 base_process_service_test::handle_exit(&p, 0);
313 sset.process_queues();
315 // since time hasn't been changed, we expect that the process has not yet been re-launched:
316 assert(first_instance == bp_sys::last_forked_pid);
317 assert(p.get_state() == service_state_t::STARTED);
320 sset.process_queues();
322 // Now a new process should've been launched:
323 assert(first_instance + 1 == bp_sys::last_forked_pid);
324 assert(p.get_state() == service_state_t::STARTED);
325 event_loop.active_timers.clear();
327 sset.remove_service(&p);
330 // Smooth recovery without restart delay
331 void test_proc_smooth_recovery2()
337 string command = "test-command";
338 list<pair<unsigned,unsigned>> command_offsets;
339 command_offsets.emplace_back(0, command.length());
340 std::list<prelim_dep> depends;
342 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
343 init_service_defaults(p);
344 p.set_smooth_recovery(true);
345 p.set_restart_delay(time_val(0, 0));
346 sset.add_service(&p);
349 sset.process_queues();
351 base_process_service_test::exec_succeeded(&p);
352 sset.process_queues();
354 pid_t first_instance = bp_sys::last_forked_pid;
356 assert(p.get_state() == service_state_t::STARTED);
357 assert(event_loop.active_timers.size() == 0);
359 base_process_service_test::handle_exit(&p, 0);
360 sset.process_queues();
362 // no restart delay, process should restart immediately:
363 assert(first_instance + 1 == bp_sys::last_forked_pid);
364 assert(p.get_state() == service_state_t::STARTED);
365 assert(event_loop.active_timers.size() == 0);
367 sset.remove_service(&p);
371 void test_scripted_stop_timeout()
377 string command = "test-command";
378 string stopcommand = "stop-command";
379 list<pair<unsigned,unsigned>> command_offsets;
380 command_offsets.emplace_back(0, command.length());
381 std::list<prelim_dep> depends;
383 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
384 init_service_defaults(p);
385 p.set_stop_command(stopcommand, command_offsets);
386 sset.add_service(&p);
389 sset.process_queues();
391 assert(p.get_state() == service_state_t::STARTING);
393 base_process_service_test::exec_succeeded(&p);
394 sset.process_queues();
395 base_process_service_test::handle_exit(&p, 0);
396 sset.process_queues();
398 assert(p.get_state() == service_state_t::STARTED);
401 sset.process_queues();
403 assert(p.get_state() == service_state_t::STOPPING);
405 base_process_service_test::exec_succeeded(&p);
406 sset.process_queues();
408 // should still be stopping:
409 assert(p.get_state() == service_state_t::STOPPING);
412 sset.process_queues();
414 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
415 assert(p.get_state() == service_state_t::STOPPING);
416 assert(bp_sys::last_sig_sent == SIGKILL);
418 base_process_service_test::handle_exit(&p, SIGKILL);
419 sset.process_queues();
421 assert(p.get_state() == service_state_t::STOPPED);
423 event_loop.active_timers.clear();
424 sset.remove_service(&p);
427 void test_scripted_start_fail()
433 string command = "test-command";
434 string stopcommand = "stop-command";
435 list<pair<unsigned,unsigned>> command_offsets;
436 command_offsets.emplace_back(0, command.length());
437 std::list<prelim_dep> depends;
439 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
440 init_service_defaults(p);
441 p.set_stop_command(stopcommand, command_offsets);
442 sset.add_service(&p);
444 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
445 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
446 sset.add_service(s2);
447 sset.add_service(s3);
450 sset.process_queues();
452 assert(p.get_state() == service_state_t::STARTING);
454 base_process_service_test::exec_succeeded(&p);
455 sset.process_queues();
456 base_process_service_test::handle_exit(&p, 0x1); // exit fail
457 sset.process_queues();
460 assert(p.get_state() == service_state_t::STOPPED);
461 assert(s2->get_state() == service_state_t::STOPPED);
462 assert(s3->get_state() == service_state_t::STOPPED);
464 event_loop.active_timers.clear();
465 sset.remove_service(&p);
467 assert(sset.count_active_services() == 0);
470 void test_scripted_stop_fail()
476 string command = "test-command";
477 string stopcommand = "stop-command";
478 list<pair<unsigned,unsigned>> command_offsets;
479 command_offsets.emplace_back(0, command.length());
480 std::list<prelim_dep> depends;
482 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
483 init_service_defaults(p);
484 p.set_stop_command(stopcommand, command_offsets);
485 sset.add_service(&p);
487 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
488 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}, {&p, REG}});
489 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{&p, REG}, {s3, REG}});
490 sset.add_service(s2);
491 sset.add_service(s3);
492 sset.add_service(s4);
495 sset.process_queues();
497 base_process_service_test::exec_succeeded(&p);
498 sset.process_queues();
499 base_process_service_test::handle_exit(&p, 0x0); // success
500 sset.process_queues();
502 assert(p.get_state() == service_state_t::STARTED);
503 assert(s2->get_state() == service_state_t::STARTED);
504 assert(s3->get_state() == service_state_t::STARTED);
505 assert(s4->get_state() == service_state_t::STARTED);
507 pid_t last_forked = bp_sys::last_forked_pid;
510 sset.process_queues();
512 base_process_service_test::exec_succeeded(&p);
513 sset.process_queues();
514 base_process_service_test::handle_exit(&p, 0x1); // failure
515 sset.process_queues();
517 // The stop command should be executed once:
518 assert((bp_sys::last_forked_pid - last_forked) == 1);
520 assert(p.get_state() == service_state_t::STOPPED);
521 assert(s2->get_state() == service_state_t::STOPPED);
522 assert(s3->get_state() == service_state_t::STOPPED);
523 assert(s4->get_state() == service_state_t::STOPPED);
525 event_loop.active_timers.clear();
526 sset.remove_service(&p);
529 void test_scripted_start_skip()
535 string command = "test-command";
536 list<pair<unsigned,unsigned>> command_offsets;
537 command_offsets.emplace_back(0, command.length());
538 std::list<prelim_dep> depends;
540 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
541 init_service_defaults(p);
542 onstart_flags_t sflags;
543 sflags.skippable = true;
545 sset.add_service(&p);
547 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
548 sset.add_service(s2);
551 sset.process_queues();
552 assert(p.get_state() == service_state_t::STARTING);
554 base_process_service_test::exec_succeeded(&p);
555 sset.process_queues();
556 assert(p.get_state() == service_state_t::STARTING);
558 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
559 sset.process_queues();
561 assert(p.get_state() == service_state_t::STARTED);
562 assert(s2->get_state() == service_state_t::STARTED);
563 assert(p.was_start_skipped());
564 assert(! s2->was_start_skipped());
565 assert(sset.count_active_services() == 2);
568 sset.process_queues();
570 assert(p.get_state() == service_state_t::STOPPED);
571 assert(s2->get_state() == service_state_t::STOPPED);
572 assert(sset.count_active_services() == 0);
574 event_loop.active_timers.clear();
575 sset.remove_service(&p);
578 // Test interrupting start of a service marked skippable
579 void test_scripted_start_skip2()
585 string command = "test-command";
586 list<pair<unsigned,unsigned>> command_offsets;
587 command_offsets.emplace_back(0, command.length());
588 std::list<prelim_dep> depends;
590 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
591 init_service_defaults(p);
592 onstart_flags_t sflags;
593 sflags.skippable = true;
595 p.set_start_interruptible(true);
596 sset.add_service(&p);
598 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
599 sset.add_service(s2);
602 sset.process_queues();
603 assert(p.get_state() == service_state_t::STARTING);
605 base_process_service_test::exec_succeeded(&p);
606 sset.process_queues();
607 assert(p.get_state() == service_state_t::STARTING);
609 s2->stop(true); // abort startup; p should be cancelled
610 sset.process_queues();
612 assert(p.get_state() == service_state_t::STOPPING);
614 base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
615 sset.process_queues();
617 assert(p.get_state() == service_state_t::STOPPED);
618 assert(s2->get_state() == service_state_t::STOPPED);
619 assert(sset.count_active_services() == 0);
621 event_loop.active_timers.clear();
622 sset.remove_service(&p);
625 #define RUN_TEST(name, spacing) \
626 std::cout << #name "..." spacing; \
628 std::cout << "PASSED" << std::endl;
630 int main(int argc, char **argv)
632 RUN_TEST(test_proc_service_start, " ");
633 RUN_TEST(test_proc_unexpected_term, " ");
634 RUN_TEST(test_term_via_stop, " ");
635 RUN_TEST(test_proc_start_timeout, " ");
636 RUN_TEST(test_proc_start_timeout2, " ");
637 RUN_TEST(test_proc_stop_timeout, " ");
638 RUN_TEST(test_proc_smooth_recovery1, "");
639 RUN_TEST(test_proc_smooth_recovery2, "");
640 RUN_TEST(test_scripted_stop_timeout, "");
641 RUN_TEST(test_scripted_start_fail, " ");
642 RUN_TEST(test_scripted_stop_fail, " ");
643 RUN_TEST(test_scripted_start_skip, " ");
644 RUN_TEST(test_scripted_start_skip2, " ");