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));
40 extern int last_sig_sent;
41 extern pid_t last_forked_pid;
44 static void init_service_defaults(base_process_service &ps)
46 ps.set_restart_interval(time_val(10,0), 3);
47 ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
48 ps.set_stop_timeout(time_val(10,0));
49 ps.set_start_interruptible(false);
52 // Regular service start
53 void test_proc_service_start()
59 string command = "test-command";
60 list<pair<unsigned,unsigned>> command_offsets;
61 command_offsets.emplace_back(0, command.length());
62 std::list<prelim_dep> depends;
64 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
65 init_service_defaults(p);
69 sset.process_queues();
71 assert(p.get_state() == service_state_t::STARTING);
73 base_process_service_test::exec_succeeded(&p);
74 sset.process_queues();
76 assert(p.get_state() == service_state_t::STARTED);
77 assert(event_loop.active_timers.size() == 0);
79 sset.remove_service(&p);
82 // Unexpected termination
83 void test_proc_unexpected_term()
89 string command = "test-command";
90 list<pair<unsigned,unsigned>> command_offsets;
91 command_offsets.emplace_back(0, command.length());
92 std::list<prelim_dep> depends;
94 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
95 init_service_defaults(p);
99 sset.process_queues();
101 base_process_service_test::exec_succeeded(&p);
102 sset.process_queues();
104 assert(p.get_state() == service_state_t::STARTED);
106 base_process_service_test::handle_exit(&p, 0);
107 sset.process_queues();
109 assert(p.get_state() == service_state_t::STOPPED);
110 assert(event_loop.active_timers.size() == 0);
112 sset.remove_service(&p);
115 // Termination via stop request
116 void test_term_via_stop()
122 string command = "test-command";
123 list<pair<unsigned,unsigned>> command_offsets;
124 command_offsets.emplace_back(0, command.length());
125 std::list<prelim_dep> depends;
127 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
128 init_service_defaults(p);
129 sset.add_service(&p);
132 sset.process_queues();
134 base_process_service_test::exec_succeeded(&p);
135 sset.process_queues();
137 assert(p.get_state() == service_state_t::STARTED);
138 assert(event_loop.active_timers.size() == 0);
141 sset.process_queues();
143 assert(p.get_state() == service_state_t::STOPPING);
144 assert(event_loop.active_timers.size() == 1);
146 base_process_service_test::handle_exit(&p, 0);
147 sset.process_queues();
149 assert(p.get_state() == service_state_t::STOPPED);
150 assert(event_loop.active_timers.size() == 0);
152 sset.remove_service(&p);
155 // Time-out during start
156 void test_proc_start_timeout()
162 string command = "test-command";
163 list<pair<unsigned,unsigned>> command_offsets;
164 command_offsets.emplace_back(0, command.length());
165 std::list<prelim_dep> depends;
167 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
168 init_service_defaults(p);
169 sset.add_service(&p);
172 sset.process_queues();
174 assert(p.get_state() == service_state_t::STARTING);
177 sset.process_queues();
179 assert(p.get_state() == service_state_t::STOPPING);
181 base_process_service_test::handle_exit(&p, 0);
182 sset.process_queues();
184 assert(p.get_state() == service_state_t::STOPPED);
185 assert(event_loop.active_timers.size() == 0);
187 sset.remove_service(&p);
190 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
191 void test_proc_start_timeout2()
197 string command = "test-command";
198 list<pair<unsigned,unsigned>> command_offsets;
199 command_offsets.emplace_back(0, command.length());
200 std::list<prelim_dep> depends;
202 process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
203 init_service_defaults(p);
204 sset.add_service(&p);
206 service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
209 sset.process_queues();
211 assert(p.get_state() == service_state_t::STARTING);
212 assert(ts.get_state() == service_state_t::STARTING);
215 sset.process_queues();
217 assert(p.get_state() == service_state_t::STOPPING);
219 base_process_service_test::handle_exit(&p, 0);
220 sset.process_queues();
222 assert(p.get_state() == service_state_t::STOPPED);
223 assert(ts.get_state() == service_state_t::STARTED);
224 assert(event_loop.active_timers.size() == 0);
226 sset.remove_service(&p);
230 void test_proc_stop_timeout()
236 string command = "test-command";
237 list<pair<unsigned,unsigned>> command_offsets;
238 command_offsets.emplace_back(0, command.length());
239 std::list<prelim_dep> depends;
241 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
242 init_service_defaults(p);
243 sset.add_service(&p);
246 sset.process_queues();
248 assert(p.get_state() == service_state_t::STARTING);
250 base_process_service_test::exec_succeeded(&p);
251 sset.process_queues();
253 assert(p.get_state() == service_state_t::STARTED);
256 sset.process_queues();
258 assert(p.get_state() == service_state_t::STOPPING);
259 assert(bp_sys::last_sig_sent == SIGTERM);
262 sset.process_queues();
264 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
265 assert(p.get_state() == service_state_t::STOPPING);
266 assert(bp_sys::last_sig_sent == SIGKILL);
268 base_process_service_test::handle_exit(&p, 0);
269 sset.process_queues();
271 assert(p.get_state() == service_state_t::STOPPED);
273 // Note that timer is still active as we faked its expiry above
274 //assert(event_loop.active_timers.size() == 0);
275 event_loop.active_timers.clear();
276 sset.remove_service(&p);
280 void test_proc_smooth_recovery1()
286 string command = "test-command";
287 list<pair<unsigned,unsigned>> command_offsets;
288 command_offsets.emplace_back(0, command.length());
289 std::list<prelim_dep> depends;
291 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
292 init_service_defaults(p);
293 p.set_smooth_recovery(true);
294 sset.add_service(&p);
297 sset.process_queues();
299 base_process_service_test::exec_succeeded(&p);
300 sset.process_queues();
302 pid_t first_instance = bp_sys::last_forked_pid;
304 assert(p.get_state() == service_state_t::STARTED);
306 base_process_service_test::handle_exit(&p, 0);
307 sset.process_queues();
309 // since time hasn't been changed, we expect that the process has not yet been re-launched:
310 assert(first_instance == bp_sys::last_forked_pid);
311 assert(p.get_state() == service_state_t::STARTED);
314 sset.process_queues();
316 // Now a new process should've been launched:
317 assert(first_instance + 1 == bp_sys::last_forked_pid);
318 assert(p.get_state() == service_state_t::STARTED);
319 event_loop.active_timers.clear();
321 sset.remove_service(&p);
324 // Smooth recovery without restart delay
325 void test_proc_smooth_recovery2()
331 string command = "test-command";
332 list<pair<unsigned,unsigned>> command_offsets;
333 command_offsets.emplace_back(0, command.length());
334 std::list<prelim_dep> depends;
336 process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
337 init_service_defaults(p);
338 p.set_smooth_recovery(true);
339 p.set_restart_delay(time_val(0, 0));
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);
351 assert(event_loop.active_timers.size() == 0);
353 base_process_service_test::handle_exit(&p, 0);
354 sset.process_queues();
356 // no restart delay, process should restart immediately:
357 assert(first_instance + 1 == bp_sys::last_forked_pid);
358 assert(p.get_state() == service_state_t::STARTED);
359 assert(event_loop.active_timers.size() == 0);
361 sset.remove_service(&p);
365 void test_scripted_stop_timeout()
371 string command = "test-command";
372 string stopcommand = "stop-command";
373 list<pair<unsigned,unsigned>> command_offsets;
374 command_offsets.emplace_back(0, command.length());
375 std::list<prelim_dep> depends;
377 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
378 init_service_defaults(p);
379 p.set_stop_command(stopcommand, command_offsets);
380 sset.add_service(&p);
383 sset.process_queues();
385 assert(p.get_state() == service_state_t::STARTING);
387 base_process_service_test::exec_succeeded(&p);
388 sset.process_queues();
389 base_process_service_test::handle_exit(&p, 0);
390 sset.process_queues();
392 assert(p.get_state() == service_state_t::STARTED);
395 sset.process_queues();
397 assert(p.get_state() == service_state_t::STOPPING);
399 base_process_service_test::exec_succeeded(&p);
400 sset.process_queues();
402 // should still be stopping:
403 assert(p.get_state() == service_state_t::STOPPING);
406 sset.process_queues();
408 // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
409 assert(p.get_state() == service_state_t::STOPPING);
410 assert(bp_sys::last_sig_sent == SIGKILL);
412 base_process_service_test::handle_exit(&p, SIGKILL);
413 sset.process_queues();
415 assert(p.get_state() == service_state_t::STOPPED);
417 event_loop.active_timers.clear();
418 sset.remove_service(&p);
421 void test_scripted_start_fail()
427 string command = "test-command";
428 string stopcommand = "stop-command";
429 list<pair<unsigned,unsigned>> command_offsets;
430 command_offsets.emplace_back(0, command.length());
431 std::list<prelim_dep> depends;
433 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
434 init_service_defaults(p);
435 p.set_stop_command(stopcommand, command_offsets);
436 sset.add_service(&p);
438 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
439 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
440 sset.add_service(s2);
441 sset.add_service(s3);
444 sset.process_queues();
446 assert(p.get_state() == service_state_t::STARTING);
448 base_process_service_test::exec_succeeded(&p);
449 sset.process_queues();
450 base_process_service_test::handle_exit(&p, 0x1); // exit fail
451 sset.process_queues();
454 assert(p.get_state() == service_state_t::STOPPED);
455 assert(s2->get_state() == service_state_t::STOPPED);
456 assert(s3->get_state() == service_state_t::STOPPED);
458 event_loop.active_timers.clear();
459 sset.remove_service(&p);
461 assert(sset.count_active_services() == 0);
464 void test_scripted_stop_fail()
470 string command = "test-command";
471 string stopcommand = "stop-command";
472 list<pair<unsigned,unsigned>> command_offsets;
473 command_offsets.emplace_back(0, command.length());
474 std::list<prelim_dep> depends;
476 scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
477 init_service_defaults(p);
478 p.set_stop_command(stopcommand, command_offsets);
479 sset.add_service(&p);
481 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
482 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}, {&p, REG}});
483 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{&p, REG}, {s3, REG}});
484 sset.add_service(s2);
485 sset.add_service(s3);
486 sset.add_service(s4);
489 sset.process_queues();
491 base_process_service_test::exec_succeeded(&p);
492 sset.process_queues();
493 base_process_service_test::handle_exit(&p, 0x0); // success
494 sset.process_queues();
496 assert(p.get_state() == service_state_t::STARTED);
497 assert(s2->get_state() == service_state_t::STARTED);
498 assert(s3->get_state() == service_state_t::STARTED);
499 assert(s4->get_state() == service_state_t::STARTED);
501 pid_t last_forked = bp_sys::last_forked_pid;
504 sset.process_queues();
506 base_process_service_test::exec_succeeded(&p);
507 sset.process_queues();
508 base_process_service_test::handle_exit(&p, 0x1); // failure
509 sset.process_queues();
511 // The stop command should be executed once:
512 assert((bp_sys::last_forked_pid - last_forked) == 1);
514 assert(p.get_state() == service_state_t::STOPPED);
515 assert(s2->get_state() == service_state_t::STOPPED);
516 assert(s3->get_state() == service_state_t::STOPPED);
517 assert(s4->get_state() == service_state_t::STOPPED);
519 event_loop.active_timers.clear();
520 sset.remove_service(&p);
523 #define RUN_TEST(name, spacing) \
524 std::cout << #name "..." spacing; \
526 std::cout << "PASSED" << std::endl;
528 int main(int argc, char **argv)
530 RUN_TEST(test_proc_service_start, " ");
531 RUN_TEST(test_proc_unexpected_term, " ");
532 RUN_TEST(test_term_via_stop, " ");
533 RUN_TEST(test_proc_start_timeout, " ");
534 RUN_TEST(test_proc_start_timeout2, " ");
535 RUN_TEST(test_proc_stop_timeout, " ");
536 RUN_TEST(test_proc_smooth_recovery1, "");
537 RUN_TEST(test_proc_smooth_recovery2, "");
538 RUN_TEST(test_scripted_stop_timeout, "");
539 RUN_TEST(test_scripted_start_fail, " ");
540 RUN_TEST(test_scripted_stop_fail, " ");