263c2a87a81a24444b412c7004d410ff1aa748d7
[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
20 // Friend interface to access base_process_service private/protected members.
21 class base_process_service_test
22 {
23     public:
24     static void exec_succeeded(base_process_service *bsp)
25     {
26         bsp->waiting_for_execstat = false;
27         bsp->exec_succeeded();
28     }
29
30     static void exec_failed(base_process_service *bsp, int errcode)
31     {
32         bsp->waiting_for_execstat = false;
33         bsp->exec_failed(errcode);
34     }
35
36     static void handle_exit(base_process_service *bsp, int exit_status)
37     {
38         bsp->pid = -1;
39         bsp->handle_exit_status(bp_sys::exit_status(true, false, exit_status));
40     }
41
42     static void handle_signal_exit(base_process_service *bsp, int signo)
43     {
44         bsp->pid = -1;
45         bsp->handle_exit_status(bp_sys::exit_status(false, true, signo));
46     }
47
48     static int get_notification_fd(base_process_service *bsp)
49     {
50         return bsp->notification_fd;
51     }
52 };
53
54 namespace bp_sys {
55     // last signal sent:
56     extern int last_sig_sent;
57     extern pid_t last_forked_pid;
58 }
59
60 static void init_service_defaults(base_process_service &ps)
61 {
62     ps.set_restart_interval(time_val(10,0), 3);
63     ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
64     ps.set_stop_timeout(time_val(10,0));
65 }
66
67 // Regular service start
68 void test_proc_service_start()
69 {
70     using namespace std;
71
72     service_set sset;
73
74     string command = "test-command";
75     list<pair<unsigned,unsigned>> command_offsets;
76     command_offsets.emplace_back(0, command.length());
77     std::list<prelim_dep> depends;
78
79     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
80     init_service_defaults(p);
81     sset.add_service(&p);
82
83     p.start(true);
84     sset.process_queues();
85
86     assert(p.get_state() == service_state_t::STARTING);
87
88     base_process_service_test::exec_succeeded(&p);
89     sset.process_queues();
90
91     assert(p.get_state() == service_state_t::STARTED);
92     assert(event_loop.active_timers.size() == 0);
93
94     sset.remove_service(&p);
95 }
96
97 // Test start with readiness notification
98 void test_proc_notify_start()
99 {
100     using namespace std;
101
102     service_set sset;
103
104     string command = "test-command";
105     list<pair<unsigned,unsigned>> command_offsets;
106     command_offsets.emplace_back(0, command.length());
107     std::list<prelim_dep> depends;
108
109     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
110     init_service_defaults(p);
111     p.set_notification_fd(3);
112     sset.add_service(&p);
113
114     p.start(true);
115     sset.process_queues();
116
117     assert(p.get_state() == service_state_t::STARTING);
118
119     base_process_service_test::exec_succeeded(&p);
120     sset.process_queues();
121
122     assert(p.get_state() == service_state_t::STARTING);
123
124     int nfd = base_process_service_test::get_notification_fd(&p);
125     assert(nfd > 0);
126
127     char notifystr[] = "ok started\n";
128     std::vector<char> rnotifystr;
129     rnotifystr.insert(rnotifystr.end(), notifystr, notifystr + sizeof(notifystr));
130     bp_sys::supply_read_data(nfd, std::move(rnotifystr));
131
132     event_loop.regd_fd_watchers[nfd]->fd_event(event_loop, nfd, dasynq::IN_EVENTS);
133
134     assert(p.get_state() == service_state_t::STARTED);
135     assert(event_loop.active_timers.size() == 0);
136
137     sset.remove_service(&p);
138 }
139
140 // Unexpected termination
141 void test_proc_unexpected_term()
142 {
143     using namespace std;
144
145     service_set sset;
146
147     string command = "test-command";
148     list<pair<unsigned,unsigned>> command_offsets;
149     command_offsets.emplace_back(0, command.length());
150     std::list<prelim_dep> depends;
151
152     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
153     init_service_defaults(p);
154     sset.add_service(&p);
155
156     p.start(true);
157     sset.process_queues();
158
159     base_process_service_test::exec_succeeded(&p);
160     sset.process_queues();
161
162     assert(p.get_state() == service_state_t::STARTED);
163
164     base_process_service_test::handle_exit(&p, 0);
165     sset.process_queues();
166
167     assert(p.get_state() == service_state_t::STOPPED);
168     assert(p.get_stop_reason() == stopped_reason_t::TERMINATED);
169     assert(event_loop.active_timers.size() == 0);
170
171     sset.remove_service(&p);
172 }
173
174 // Unexpected termination
175 void test_proc_term_restart()
176 {
177     using namespace std;
178
179     service_set sset;
180
181     string command = "test-command";
182     list<pair<unsigned,unsigned>> command_offsets;
183     command_offsets.emplace_back(0, command.length());
184     std::list<prelim_dep> depends;
185
186     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
187     init_service_defaults(p);
188     p.set_auto_restart(true);
189     sset.add_service(&p);
190
191     p.start(true);
192     sset.process_queues();
193
194     base_process_service_test::exec_succeeded(&p);
195     sset.process_queues();
196
197     assert(p.get_state() == service_state_t::STARTED);
198     assert(event_loop.active_timers.size() == 0);
199
200     base_process_service_test::handle_exit(&p, 0);
201     sset.process_queues();
202
203     // Starting, restart timer should be armed:
204     assert(p.get_state() == service_state_t::STARTING);
205     assert(event_loop.active_timers.size() == 1);
206
207     event_loop.advance_time(time_val(0, 200000000));
208     assert(event_loop.active_timers.size() == 0);
209
210     sset.process_queues();
211     base_process_service_test::exec_succeeded(&p);
212     sset.process_queues();
213
214     assert(p.get_state() == service_state_t::STARTED);
215     assert(event_loop.active_timers.size() == 0);
216
217     sset.remove_service(&p);
218 }
219
220 // Termination via stop request
221 void test_term_via_stop()
222 {
223     using namespace std;
224
225     service_set sset;
226
227     string command = "test-command";
228     list<pair<unsigned,unsigned>> command_offsets;
229     command_offsets.emplace_back(0, command.length());
230     std::list<prelim_dep> depends;
231
232     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
233     init_service_defaults(p);
234     sset.add_service(&p);
235
236     p.start(true);
237     sset.process_queues();
238
239     base_process_service_test::exec_succeeded(&p);
240     sset.process_queues();
241
242     assert(p.get_state() == service_state_t::STARTED);
243     assert(event_loop.active_timers.size() == 0);
244
245     p.stop(true);
246     sset.process_queues();
247
248     assert(p.get_state() == service_state_t::STOPPING);
249     assert(event_loop.active_timers.size() == 1);
250
251     base_process_service_test::handle_exit(&p, 0);
252     sset.process_queues();
253
254     assert(p.get_state() == service_state_t::STOPPED);
255     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
256     assert(event_loop.active_timers.size() == 0);
257
258     sset.remove_service(&p);
259 }
260
261 // Termination via stop request, ensure reason is reset:
262 void test_term_via_stop2()
263 {
264     using namespace std;
265
266     service_set sset;
267
268     string command = "test-command";
269     list<pair<unsigned,unsigned>> command_offsets;
270     command_offsets.emplace_back(0, command.length());
271     std::list<prelim_dep> depends;
272
273     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
274     init_service_defaults(p);
275     sset.add_service(&p);
276
277     p.start(true);
278     sset.process_queues();
279
280     // first set it up with failure reason:
281
282     base_process_service_test::exec_failed(&p, ENOENT);
283     sset.process_queues();
284
285     assert(p.get_state() == service_state_t::STOPPED);
286     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
287
288     // now restart clean:
289
290     p.start(true);
291     sset.process_queues();
292
293     base_process_service_test::exec_succeeded(&p);
294     sset.process_queues();
295
296     assert(p.get_state() == service_state_t::STARTED);
297     assert(event_loop.active_timers.size() == 0);
298
299     p.stop(true);
300     sset.process_queues();
301     assert(p.get_state() == service_state_t::STOPPING);
302
303     base_process_service_test::handle_exit(&p, 0);
304     sset.process_queues();
305     assert(p.get_state() == service_state_t::STOPPED);
306     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
307     assert(event_loop.active_timers.size() == 0);
308
309     sset.remove_service(&p);
310 }
311
312 // Time-out during start
313 void test_proc_start_timeout()
314 {
315     using namespace std;
316
317     service_set sset;
318
319     string command = "test-command";
320     list<pair<unsigned,unsigned>> command_offsets;
321     command_offsets.emplace_back(0, command.length());
322     std::list<prelim_dep> depends;
323
324     scripted_service p {&sset, "testproc", std::move(command), command_offsets, depends};
325     init_service_defaults(p);
326     p.set_start_timeout(time_val(10,0));
327     sset.add_service(&p);
328
329     p.start(true);
330     sset.process_queues();
331
332     assert(p.get_state() == service_state_t::STARTING);
333
334     event_loop.advance_time(time_val(10,0));
335     sset.process_queues();
336
337     assert(p.get_state() == service_state_t::STOPPING);
338
339     base_process_service_test::handle_signal_exit(&p, SIGTERM);
340     sset.process_queues();
341
342     // We set no stop script, so state should now be STOPPED with no timer set
343     assert(p.get_state() == service_state_t::STOPPED);
344     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
345     assert(event_loop.active_timers.size() == 0);
346
347     sset.remove_service(&p);
348 }
349
350 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
351 void test_proc_start_timeout2()
352 {
353     using namespace std;
354
355     service_set sset;
356
357     string command = "test-command";
358     list<pair<unsigned,unsigned>> command_offsets;
359     command_offsets.emplace_back(0, command.length());
360     std::list<prelim_dep> depends;
361
362     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
363     init_service_defaults(p);
364     sset.add_service(&p);
365
366     service_record ts {&sset, "test-service-1", service_type_t::INTERNAL,
367         {{&p, dependency_type::WAITS_FOR}} };
368
369     ts.start(true);
370     sset.process_queues();
371
372     assert(p.get_state() == service_state_t::STARTING);
373     assert(ts.get_state() == service_state_t::STARTING);
374
375     p.timer_expired();
376     sset.process_queues();
377
378     assert(p.get_state() == service_state_t::STOPPING);
379
380     base_process_service_test::handle_exit(&p, 0);
381     sset.process_queues();
382
383     assert(p.get_state() == service_state_t::STOPPED);
384     assert(p.get_stop_reason() == stopped_reason_t::TIMEDOUT);
385     assert(ts.get_state() == service_state_t::STARTED);
386     assert(event_loop.active_timers.size() == 0);
387
388     sset.remove_service(&p);
389 }
390
391 // Test exec() failure for process service start.
392 void test_proc_start_execfail()
393 {
394     using namespace std;
395
396     service_set sset;
397
398     string command = "test-command";
399     list<pair<unsigned,unsigned>> command_offsets;
400     command_offsets.emplace_back(0, command.length());
401     std::list<prelim_dep> depends;
402
403     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
404     init_service_defaults(p);
405     sset.add_service(&p);
406
407     p.start(true);
408     sset.process_queues();
409
410     assert(p.get_state() == service_state_t::STARTING);
411
412     base_process_service_test::exec_failed(&p, ENOENT);
413     sset.process_queues();
414
415     assert(p.get_state() == service_state_t::STOPPED);
416     assert(p.get_stop_reason() == stopped_reason_t::EXECFAILED);
417     assert(event_loop.active_timers.size() == 0);
418
419     sset.remove_service(&p);
420 }
421
422 // Test no ready notification before process terminates
423 void test_proc_notify_fail()
424 {
425     using namespace std;
426
427     service_set sset;
428
429     string command = "test-command";
430     list<pair<unsigned,unsigned>> command_offsets;
431     command_offsets.emplace_back(0, command.length());
432     std::list<prelim_dep> depends;
433
434     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
435     init_service_defaults(p);
436     p.set_notification_fd(3);
437     sset.add_service(&p);
438
439     p.start(true);
440     sset.process_queues();
441
442     assert(p.get_state() == service_state_t::STARTING);
443
444     base_process_service_test::exec_succeeded(&p);
445     sset.process_queues();
446
447     assert(p.get_state() == service_state_t::STARTING);
448
449     int nfd = base_process_service_test::get_notification_fd(&p);
450     assert(nfd > 0);
451
452     // Signal EOF on notify fd:
453     event_loop.regd_fd_watchers[nfd]->fd_event(event_loop, nfd, dasynq::IN_EVENTS);
454
455     assert(p.get_state() == service_state_t::STOPPING);
456
457     base_process_service_test::handle_exit(&p, 0);
458     sset.process_queues();
459
460     assert(p.get_state() == service_state_t::STOPPED);
461     assert(event_loop.active_timers.size() == 0);
462
463     sset.remove_service(&p);
464 }
465
466 // Test stop timeout
467 void test_proc_stop_timeout()
468 {
469     using namespace std;
470
471     service_set sset;
472
473     string command = "test-command";
474     list<pair<unsigned,unsigned>> command_offsets;
475     command_offsets.emplace_back(0, command.length());
476     std::list<prelim_dep> depends;
477
478     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
479     init_service_defaults(p);
480     sset.add_service(&p);
481
482     p.start(true);
483     sset.process_queues();
484
485     assert(p.get_state() == service_state_t::STARTING);
486
487     base_process_service_test::exec_succeeded(&p);
488     sset.process_queues();
489
490     assert(p.get_state() == service_state_t::STARTED);
491
492     p.stop(true);
493     sset.process_queues();
494
495     assert(p.get_state() == service_state_t::STOPPING);
496     assert(bp_sys::last_sig_sent == SIGTERM);
497
498     p.timer_expired();
499     sset.process_queues();
500
501     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
502     assert(p.get_state() == service_state_t::STOPPING);
503     assert(bp_sys::last_sig_sent == SIGKILL);
504
505     base_process_service_test::handle_exit(&p, 0);
506     sset.process_queues();
507
508     assert(p.get_state() == service_state_t::STOPPED);
509     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
510
511     // Note that timer is still active as we faked its expiry above
512     //assert(event_loop.active_timers.size() == 0);
513     event_loop.active_timers.clear();
514     sset.remove_service(&p);
515 }
516
517 // Smooth recovery
518 void test_proc_smooth_recovery1()
519 {
520     using namespace std;
521
522     service_set sset;
523
524     string command = "test-command";
525     list<pair<unsigned,unsigned>> command_offsets;
526     command_offsets.emplace_back(0, command.length());
527     std::list<prelim_dep> depends;
528
529     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
530     init_service_defaults(p);
531     p.set_smooth_recovery(true);
532     sset.add_service(&p);
533
534     p.start(true);
535     sset.process_queues();
536
537     base_process_service_test::exec_succeeded(&p);
538     sset.process_queues();
539
540     pid_t first_instance = bp_sys::last_forked_pid;
541
542     assert(p.get_state() == service_state_t::STARTED);
543
544     base_process_service_test::handle_exit(&p, 0);
545     sset.process_queues();
546
547     // since time hasn't been changed, we expect that the process has not yet been re-launched:
548     assert(first_instance == bp_sys::last_forked_pid);
549     assert(p.get_state() == service_state_t::STARTED);
550
551     p.timer_expired();
552     sset.process_queues();
553
554     // Now a new process should've been launched:
555     assert(first_instance + 1 == bp_sys::last_forked_pid);
556     assert(p.get_state() == service_state_t::STARTED);
557     event_loop.active_timers.clear();
558
559     sset.remove_service(&p);
560 }
561
562 // Smooth recovery without restart delay
563 void test_proc_smooth_recovery2()
564 {
565     using namespace std;
566
567     service_set sset;
568
569     string command = "test-command";
570     list<pair<unsigned,unsigned>> command_offsets;
571     command_offsets.emplace_back(0, command.length());
572     std::list<prelim_dep> depends;
573
574     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
575     init_service_defaults(p);
576     p.set_smooth_recovery(true);
577     p.set_restart_delay(time_val(0, 0));
578     sset.add_service(&p);
579
580     p.start(true);
581     sset.process_queues();
582
583     base_process_service_test::exec_succeeded(&p);
584     sset.process_queues();
585
586     pid_t first_instance = bp_sys::last_forked_pid;
587
588     assert(p.get_state() == service_state_t::STARTED);
589     assert(event_loop.active_timers.size() == 0);
590
591     base_process_service_test::handle_exit(&p, 0);
592     sset.process_queues();
593
594     // no restart delay, process should restart immediately:
595     assert(first_instance + 1 == bp_sys::last_forked_pid);
596     assert(p.get_state() == service_state_t::STARTED);
597     assert(event_loop.active_timers.size() == 0);
598
599     sset.remove_service(&p);
600 }
601
602 // Test stop timeout
603 void test_scripted_stop_timeout()
604 {
605     using namespace std;
606
607     service_set sset;
608
609     string command = "test-command";
610     string stopcommand = "stop-command";
611     list<pair<unsigned,unsigned>> command_offsets;
612     command_offsets.emplace_back(0, command.length());
613     std::list<prelim_dep> depends;
614
615     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
616     init_service_defaults(p);
617     p.set_stop_command(stopcommand, command_offsets);
618     sset.add_service(&p);
619
620     p.start(true);
621     sset.process_queues();
622
623     assert(p.get_state() == service_state_t::STARTING);
624
625     base_process_service_test::exec_succeeded(&p);
626     sset.process_queues();
627     base_process_service_test::handle_exit(&p, 0);
628     sset.process_queues();
629
630     assert(p.get_state() == service_state_t::STARTED);
631
632     p.stop(true);
633     sset.process_queues();
634
635     assert(p.get_state() == service_state_t::STOPPING);
636
637     base_process_service_test::exec_succeeded(&p);
638     sset.process_queues();
639
640     // should still be stopping:
641     assert(p.get_state() == service_state_t::STOPPING);
642
643     p.timer_expired();
644     sset.process_queues();
645
646     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
647     assert(p.get_state() == service_state_t::STOPPING);
648     assert(bp_sys::last_sig_sent == SIGKILL);
649
650     base_process_service_test::handle_exit(&p, SIGKILL);
651     sset.process_queues();
652
653     assert(p.get_state() == service_state_t::STOPPED);
654     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
655
656     event_loop.active_timers.clear();
657     sset.remove_service(&p);
658 }
659
660 void test_scripted_start_fail()
661 {
662     using namespace std;
663
664     service_set sset;
665
666     string command = "test-command";
667     string stopcommand = "stop-command";
668     list<pair<unsigned,unsigned>> command_offsets;
669     command_offsets.emplace_back(0, command.length());
670     std::list<prelim_dep> depends;
671
672     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
673     init_service_defaults(p);
674     p.set_stop_command(stopcommand, command_offsets);
675     sset.add_service(&p);
676
677     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
678     service_record *s3 = new service_record(&sset, "test-service-3",
679             service_type_t::INTERNAL, {{&p, REG}, {s2, REG}});
680     sset.add_service(s2);
681     sset.add_service(s3);
682
683     s3->start(true);
684     sset.process_queues();
685
686     assert(p.get_state() == service_state_t::STARTING);
687
688     base_process_service_test::exec_succeeded(&p);
689     sset.process_queues();
690     base_process_service_test::handle_exit(&p, 0x1);  // exit fail
691     sset.process_queues();
692
693     // failed to start:
694     assert(p.get_state() == service_state_t::STOPPED);
695     assert(s2->get_state() == service_state_t::STOPPED);
696     assert(s3->get_state() == service_state_t::STOPPED);
697     assert(p.get_stop_reason() == stopped_reason_t::FAILED);
698     assert(s2->get_stop_reason() == stopped_reason_t::DEPFAILED);
699     assert(s3->get_stop_reason() == stopped_reason_t::DEPFAILED);
700
701     event_loop.active_timers.clear();
702     sset.remove_service(&p);
703
704     assert(sset.count_active_services() == 0);
705 }
706
707 void test_scripted_stop_fail()
708 {
709     using namespace std;
710
711     service_set sset;
712
713     string command = "test-command";
714     string stopcommand = "stop-command";
715     list<pair<unsigned,unsigned>> command_offsets;
716     command_offsets.emplace_back(0, command.length());
717     std::list<prelim_dep> depends;
718
719     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
720     init_service_defaults(p);
721     p.set_stop_command(stopcommand, command_offsets);
722     sset.add_service(&p);
723
724     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
725     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL,
726             {{s2, REG}, {&p, REG}});
727     service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL,
728             {{&p, REG}, {s3, REG}});
729     sset.add_service(s2);
730     sset.add_service(s3);
731     sset.add_service(s4);
732
733     s4->start(true);
734     sset.process_queues();
735
736     base_process_service_test::exec_succeeded(&p);
737     sset.process_queues();
738     base_process_service_test::handle_exit(&p, 0x0);  // success
739     sset.process_queues();
740
741     assert(p.get_state() == service_state_t::STARTED);
742     assert(s2->get_state() == service_state_t::STARTED);
743     assert(s3->get_state() == service_state_t::STARTED);
744     assert(s4->get_state() == service_state_t::STARTED);
745
746     pid_t last_forked = bp_sys::last_forked_pid;
747
748     s4->stop(true);
749     sset.process_queues();
750
751     base_process_service_test::exec_succeeded(&p);
752     sset.process_queues();
753     base_process_service_test::handle_exit(&p, 0x1);  // failure
754     sset.process_queues();
755
756     // The stop command should be executed once:
757     assert((bp_sys::last_forked_pid - last_forked) == 1);
758
759     assert(p.get_state() == service_state_t::STOPPED);
760     assert(s2->get_state() == service_state_t::STOPPED);
761     assert(s3->get_state() == service_state_t::STOPPED);
762     assert(s4->get_state() == service_state_t::STOPPED);
763
764     event_loop.active_timers.clear();
765     sset.remove_service(&p);
766 }
767
768 void test_scripted_start_skip()
769 {
770     using namespace std;
771
772     service_set sset;
773
774     string command = "test-command";
775     list<pair<unsigned,unsigned>> command_offsets;
776     command_offsets.emplace_back(0, command.length());
777     std::list<prelim_dep> depends;
778
779     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
780     init_service_defaults(p);
781     service_flags_t sflags;
782     sflags.skippable = true;
783     p.set_flags(sflags);
784     sset.add_service(&p);
785
786     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
787     sset.add_service(s2);
788
789     s2->start(true);
790     sset.process_queues();
791     assert(p.get_state() == service_state_t::STARTING);
792
793     base_process_service_test::exec_succeeded(&p);
794     sset.process_queues();
795     assert(p.get_state() == service_state_t::STARTING);
796
797     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
798     sset.process_queues();
799
800     assert(p.get_state() == service_state_t::STARTED);
801     assert(s2->get_state() == service_state_t::STARTED);
802     assert(p.was_start_skipped());
803     assert(! s2->was_start_skipped());
804     assert(sset.count_active_services() == 2);
805
806     s2->stop(true);
807     sset.process_queues();
808
809     assert(p.get_state() == service_state_t::STOPPED);
810     assert(s2->get_state() == service_state_t::STOPPED);
811     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
812     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
813     assert(sset.count_active_services() == 0);
814
815     event_loop.active_timers.clear();
816     sset.remove_service(&p);
817 }
818
819 // Test interrupting start of a service marked skippable
820 void test_scripted_start_skip2()
821 {
822     using namespace std;
823
824     service_set sset;
825
826     string command = "test-command";
827     list<pair<unsigned,unsigned>> command_offsets;
828     command_offsets.emplace_back(0, command.length());
829     std::list<prelim_dep> depends;
830
831     scripted_service p {&sset, "testscripted", std::move(command), command_offsets, depends};
832     init_service_defaults(p);
833     service_flags_t sflags;
834     sflags.skippable = true;
835     sflags.start_interruptible = true;
836     p.set_flags(sflags);
837     sset.add_service(&p);
838
839     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{&p, REG}});
840     sset.add_service(s2);
841
842     s2->start(true);
843     sset.process_queues();
844     assert(p.get_state() == service_state_t::STARTING);
845
846     base_process_service_test::exec_succeeded(&p);
847     sset.process_queues();
848     assert(p.get_state() == service_state_t::STARTING);
849
850     s2->stop(true);  // abort startup; p should be cancelled
851     sset.process_queues();
852
853     assert(p.get_state() == service_state_t::STOPPING);
854
855     base_process_service_test::handle_signal_exit(&p, SIGINT); // interrupted
856     sset.process_queues();
857
858     assert(p.get_state() == service_state_t::STOPPED);
859     assert(s2->get_state() == service_state_t::STOPPED);
860     assert(p.get_stop_reason() == stopped_reason_t::NORMAL);
861     assert(s2->get_stop_reason() == stopped_reason_t::NORMAL);
862     assert(sset.count_active_services() == 0);
863
864     event_loop.active_timers.clear();
865     sset.remove_service(&p);
866 }
867
868 // Test that starting a service with a waits-for dependency on another - currently stopping - service,
869 // causes that service to re-start.
870 void test_waitsfor_restart()
871 {
872     using namespace std;
873
874     service_set sset;
875
876     string command = "test-command";
877     list<pair<unsigned,unsigned>> command_offsets;
878     command_offsets.emplace_back(0, command.length());
879     std::list<prelim_dep> depends;
880
881     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
882     init_service_defaults(p);
883     sset.add_service(&p);
884
885     service_record tp {&sset, "test-service", service_type_t::INTERNAL, {{&p, WAITS}}};
886     sset.add_service(&tp);
887
888     // start p:
889
890     p.start(true);
891     sset.process_queues();
892
893     assert(p.get_state() == service_state_t::STARTING);
894
895     base_process_service_test::exec_succeeded(&p);
896     sset.process_queues();
897
898     assert(p.get_state() == service_state_t::STARTED);
899     assert(event_loop.active_timers.size() == 0);
900
901     // begin stopping p:
902
903     p.stop(true);
904     sset.process_queues();
905
906     assert(p.get_state() == service_state_t::STOPPING);
907
908     // start tp (which waits-for p):
909
910     tp.start(true);
911     sset.process_queues();
912
913     assert(tp.get_state() == service_state_t::STARTING);
914     assert(p.get_state() == service_state_t::STOPPING);
915
916     base_process_service_test::handle_signal_exit(&p, SIGTERM);
917     sset.process_queues();
918
919     assert(tp.get_state() == service_state_t::STARTING);
920     assert(p.get_state() == service_state_t::STARTING);
921
922     base_process_service_test::exec_succeeded(&p);
923     sset.process_queues();
924
925     assert(tp.get_state() == service_state_t::STARTED);
926     assert(p.get_state() == service_state_t::STARTED);
927
928     sset.remove_service(&tp);
929     sset.remove_service(&p);
930 }
931
932
933 #define RUN_TEST(name, spacing) \
934     std::cout << #name "..." spacing << std::flush; \
935     name(); \
936     std::cout << "PASSED" << std::endl;
937
938 int main(int argc, char **argv)
939 {
940     RUN_TEST(test_proc_service_start, "   ");
941     RUN_TEST(test_proc_notify_start, "    ");
942     RUN_TEST(test_proc_unexpected_term, " ");
943     RUN_TEST(test_proc_term_restart, "    ");
944     RUN_TEST(test_term_via_stop, "        ");
945     RUN_TEST(test_term_via_stop2, "       ");
946     RUN_TEST(test_proc_start_timeout, "   ");
947     RUN_TEST(test_proc_start_timeout2, "  ");
948     RUN_TEST(test_proc_start_execfail, "  ");
949     RUN_TEST(test_proc_notify_fail, "     ");
950     RUN_TEST(test_proc_stop_timeout, "    ");
951     RUN_TEST(test_proc_smooth_recovery1, "");
952     RUN_TEST(test_proc_smooth_recovery2, "");
953     RUN_TEST(test_scripted_stop_timeout, "");
954     RUN_TEST(test_scripted_start_fail, "  ");
955     RUN_TEST(test_scripted_stop_fail, "   ");
956     RUN_TEST(test_scripted_start_skip, "  ");
957     RUN_TEST(test_scripted_start_skip2, " ");
958     RUN_TEST(test_waitsfor_restart, "     ");
959 }