Refactor: move "start-interruptible" flag into onstart_flags
[oweals/dinit.git] / src / tests / proctests.cc
1 #include <cassert>
2 #include <iostream>
3 #include <list>
4 #include <utility>
5 #include <string>
6
7 #include "service.h"
8 #include "proc-service.h"
9
10 // Tests of process-service related functionality.
11 //
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.
14
15 extern eventloop_t event_loop;
16
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;
20
21 // Friend interface to access base_process_service private/protected members.
22 class base_process_service_test
23 {
24     public:
25     static void exec_succeeded(base_process_service *bsp)
26     {
27         bsp->waiting_for_execstat = false;
28         bsp->exec_succeeded();
29     }
30
31     static void handle_exit(base_process_service *bsp, int exit_status)
32     {
33         bsp->pid = -1;
34         bsp->handle_exit_status(bp_sys::exit_status(true, false, exit_status));
35     }
36
37     static void handle_signal_exit(base_process_service *bsp, int signo)
38     {
39         bsp->pid = -1;
40         bsp->handle_exit_status(bp_sys::exit_status(false, true, signo));
41     }
42 };
43
44 namespace bp_sys {
45     // last signal sent:
46     extern int last_sig_sent;
47     extern pid_t last_forked_pid;
48 }
49
50 static void init_service_defaults(base_process_service &ps)
51 {
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 }
56
57 // Regular service start
58 void test_proc_service_start()
59 {
60     using namespace std;
61
62     service_set sset;
63
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;
68
69     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
70     init_service_defaults(p);
71     sset.add_service(&p);
72
73     p.start(true);
74     sset.process_queues();
75
76     assert(p.get_state() == service_state_t::STARTING);
77
78     base_process_service_test::exec_succeeded(&p);
79     sset.process_queues();
80
81     assert(p.get_state() == service_state_t::STARTED);
82     assert(event_loop.active_timers.size() == 0);
83
84     sset.remove_service(&p);
85 }
86
87 // Unexpected termination
88 void test_proc_unexpected_term()
89 {
90     using namespace std;
91
92     service_set sset;
93
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;
98
99     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
100     init_service_defaults(p);
101     sset.add_service(&p);
102
103     p.start(true);
104     sset.process_queues();
105
106     base_process_service_test::exec_succeeded(&p);
107     sset.process_queues();
108
109     assert(p.get_state() == service_state_t::STARTED);
110
111     base_process_service_test::handle_exit(&p, 0);
112     sset.process_queues();
113
114     assert(p.get_state() == service_state_t::STOPPED);
115     assert(event_loop.active_timers.size() == 0);
116
117     sset.remove_service(&p);
118 }
119
120 // Termination via stop request
121 void test_term_via_stop()
122 {
123     using namespace std;
124
125     service_set sset;
126
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;
131
132     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
133     init_service_defaults(p);
134     sset.add_service(&p);
135
136     p.start(true);
137     sset.process_queues();
138
139     base_process_service_test::exec_succeeded(&p);
140     sset.process_queues();
141
142     assert(p.get_state() == service_state_t::STARTED);
143     assert(event_loop.active_timers.size() == 0);
144
145     p.stop(true);
146     sset.process_queues();
147
148     assert(p.get_state() == service_state_t::STOPPING);
149     assert(event_loop.active_timers.size() == 1);
150
151     base_process_service_test::handle_exit(&p, 0);
152     sset.process_queues();
153
154     assert(p.get_state() == service_state_t::STOPPED);
155     assert(event_loop.active_timers.size() == 0);
156
157     sset.remove_service(&p);
158 }
159
160 // Time-out during start
161 void test_proc_start_timeout()
162 {
163     using namespace std;
164
165     service_set sset;
166
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;
171
172     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
173     init_service_defaults(p);
174     sset.add_service(&p);
175
176     p.start(true);
177     sset.process_queues();
178
179     assert(p.get_state() == service_state_t::STARTING);
180
181     p.timer_expired();
182     sset.process_queues();
183
184     assert(p.get_state() == service_state_t::STOPPING);
185
186     base_process_service_test::handle_exit(&p, 0);
187     sset.process_queues();
188
189     assert(p.get_state() == service_state_t::STOPPED);
190     assert(event_loop.active_timers.size() == 0);
191
192     sset.remove_service(&p);
193 }
194
195 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
196 void test_proc_start_timeout2()
197 {
198     using namespace std;
199
200     service_set sset;
201
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;
206
207     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
208     init_service_defaults(p);
209     sset.add_service(&p);
210
211     service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
212
213     ts.start(true);
214     sset.process_queues();
215
216     assert(p.get_state() == service_state_t::STARTING);
217     assert(ts.get_state() == service_state_t::STARTING);
218
219     p.timer_expired();
220     sset.process_queues();
221
222     assert(p.get_state() == service_state_t::STOPPING);
223
224     base_process_service_test::handle_exit(&p, 0);
225     sset.process_queues();
226
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);
230
231     sset.remove_service(&p);
232 }
233
234 // Test stop timeout
235 void test_proc_stop_timeout()
236 {
237     using namespace std;
238
239     service_set sset;
240
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;
245
246     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
247     init_service_defaults(p);
248     sset.add_service(&p);
249
250     p.start(true);
251     sset.process_queues();
252
253     assert(p.get_state() == service_state_t::STARTING);
254
255     base_process_service_test::exec_succeeded(&p);
256     sset.process_queues();
257
258     assert(p.get_state() == service_state_t::STARTED);
259
260     p.stop(true);
261     sset.process_queues();
262
263     assert(p.get_state() == service_state_t::STOPPING);
264     assert(bp_sys::last_sig_sent == SIGTERM);
265
266     p.timer_expired();
267     sset.process_queues();
268
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);
272
273     base_process_service_test::handle_exit(&p, 0);
274     sset.process_queues();
275
276     assert(p.get_state() == service_state_t::STOPPED);
277
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);
282 }
283
284 // Smooth recovery
285 void test_proc_smooth_recovery1()
286 {
287     using namespace std;
288
289     service_set sset;
290
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;
295
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);
300
301     p.start(true);
302     sset.process_queues();
303
304     base_process_service_test::exec_succeeded(&p);
305     sset.process_queues();
306
307     pid_t first_instance = bp_sys::last_forked_pid;
308
309     assert(p.get_state() == service_state_t::STARTED);
310
311     base_process_service_test::handle_exit(&p, 0);
312     sset.process_queues();
313
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);
317
318     p.timer_expired();
319     sset.process_queues();
320
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();
325
326     sset.remove_service(&p);
327 }
328
329 // Smooth recovery without restart delay
330 void test_proc_smooth_recovery2()
331 {
332     using namespace std;
333
334     service_set sset;
335
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;
340
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);
346
347     p.start(true);
348     sset.process_queues();
349
350     base_process_service_test::exec_succeeded(&p);
351     sset.process_queues();
352
353     pid_t first_instance = bp_sys::last_forked_pid;
354
355     assert(p.get_state() == service_state_t::STARTED);
356     assert(event_loop.active_timers.size() == 0);
357
358     base_process_service_test::handle_exit(&p, 0);
359     sset.process_queues();
360
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);
365
366     sset.remove_service(&p);
367 }
368
369 // Test stop timeout
370 void test_scripted_stop_timeout()
371 {
372     using namespace std;
373
374     service_set sset;
375
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;
381
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);
386
387     p.start(true);
388     sset.process_queues();
389
390     assert(p.get_state() == service_state_t::STARTING);
391
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();
396
397     assert(p.get_state() == service_state_t::STARTED);
398
399     p.stop(true);
400     sset.process_queues();
401
402     assert(p.get_state() == service_state_t::STOPPING);
403
404     base_process_service_test::exec_succeeded(&p);
405     sset.process_queues();
406
407     // should still be stopping:
408     assert(p.get_state() == service_state_t::STOPPING);
409
410     p.timer_expired();
411     sset.process_queues();
412
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);
416
417     base_process_service_test::handle_exit(&p, SIGKILL);
418     sset.process_queues();
419
420     assert(p.get_state() == service_state_t::STOPPED);
421
422     event_loop.active_timers.clear();
423     sset.remove_service(&p);
424 }
425
426 void test_scripted_start_fail()
427 {
428     using namespace std;
429
430     service_set sset;
431
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;
437
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);
442
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);
447
448     s3->start(true);
449     sset.process_queues();
450
451     assert(p.get_state() == service_state_t::STARTING);
452
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();
457
458     // failed to start:
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);
462
463     event_loop.active_timers.clear();
464     sset.remove_service(&p);
465
466     assert(sset.count_active_services() == 0);
467 }
468
469 void test_scripted_stop_fail()
470 {
471     using namespace std;
472
473     service_set sset;
474
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;
480
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);
485
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);
492
493     s4->start(true);
494     sset.process_queues();
495
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();
500
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);
505
506     pid_t last_forked = bp_sys::last_forked_pid;
507
508     s4->stop(true);
509     sset.process_queues();
510
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();
515
516     // The stop command should be executed once:
517     assert((bp_sys::last_forked_pid - last_forked) == 1);
518
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);
523
524     event_loop.active_timers.clear();
525     sset.remove_service(&p);
526 }
527
528 void test_scripted_start_skip()
529 {
530     using namespace std;
531
532     service_set sset;
533
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;
538
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;
543     p.set_flags(sflags);
544     sset.add_service(&p);
545
546     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
547     sset.add_service(s2);
548
549     s2->start(true);
550     sset.process_queues();
551     assert(p.get_state() == service_state_t::STARTING);
552
553     base_process_service_test::exec_succeeded(&p);
554     sset.process_queues();
555     assert(p.get_state() == service_state_t::STARTING);
556
557     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
558     sset.process_queues();
559
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);
565
566     s2->stop(true);
567     sset.process_queues();
568
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);
572
573     event_loop.active_timers.clear();
574     sset.remove_service(&p);
575 }
576
577 // Test interrupting start of a service marked skippable
578 void test_scripted_start_skip2()
579 {
580     using namespace std;
581
582     service_set sset;
583
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;
588
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;
594     p.set_flags(sflags);
595     sset.add_service(&p);
596
597     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
598     sset.add_service(s2);
599
600     s2->start(true);
601     sset.process_queues();
602     assert(p.get_state() == service_state_t::STARTING);
603
604     base_process_service_test::exec_succeeded(&p);
605     sset.process_queues();
606     assert(p.get_state() == service_state_t::STARTING);
607
608     s2->stop(true);  // abort startup; p should be cancelled
609     sset.process_queues();
610
611     assert(p.get_state() == service_state_t::STOPPING);
612
613     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
614     sset.process_queues();
615
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);
619
620     event_loop.active_timers.clear();
621     sset.remove_service(&p);
622 }
623
624 #define RUN_TEST(name, spacing) \
625     std::cout << #name "..." spacing; \
626     name(); \
627     std::cout << "PASSED" << std::endl;
628
629 int main(int argc, char **argv)
630 {
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, " ");
644 }