base_process_service: properly clean up in destructor.
[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 // Friend interface to access base_process_service private/protected members.
18 class base_process_service_test
19 {
20     public:
21     static void exec_succeeded(base_process_service *bsp)
22     {
23         bsp->waiting_for_execstat = false;
24         bsp->exec_succeeded();
25     }
26
27     static void handle_exit(base_process_service *bsp, int exit_status)
28     {
29         bsp->pid = -1;
30         bsp->handle_exit_status(exit_status);
31     }
32 };
33
34 namespace bp_sys {
35     // last signal sent:
36     extern int last_sig_sent;
37     extern pid_t last_forked_pid;
38 }
39
40 static void init_service_defaults(base_process_service &ps)
41 {
42     ps.set_restart_interval(time_val(10,0), 3);
43     ps.set_restart_delay(time_val(0, 200000000)); // 200 milliseconds
44     ps.set_stop_timeout(time_val(10,0));
45     ps.set_start_interruptible(false);
46 }
47
48 // Regular service start
49 void test_proc_service_start()
50 {
51     using namespace std;
52
53     service_set sset;
54
55     string command = "test-command";
56     list<pair<unsigned,unsigned>> command_offsets;
57     command_offsets.emplace_back(0, command.length());
58     std::list<prelim_dep> depends;
59
60     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
61     init_service_defaults(p);
62
63     p.start(true);
64     sset.process_queues();
65
66     assert(p.get_state() == service_state_t::STARTING);
67
68     base_process_service_test::exec_succeeded(&p);
69     sset.process_queues();
70
71     assert(p.get_state() == service_state_t::STARTED);
72     assert(event_loop.active_timers.size() == 0);
73 }
74
75 // Unexpected termination
76 void test_proc_unexpected_term()
77 {
78     using namespace std;
79
80     service_set sset;
81
82     string command = "test-command";
83     list<pair<unsigned,unsigned>> command_offsets;
84     command_offsets.emplace_back(0, command.length());
85     std::list<prelim_dep> depends;
86
87     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
88     p.start(true);
89     sset.process_queues();
90
91     base_process_service_test::exec_succeeded(&p);
92     sset.process_queues();
93
94     assert(p.get_state() == service_state_t::STARTED);
95
96     base_process_service_test::handle_exit(&p, 0);
97     sset.process_queues();
98
99     assert(p.get_state() == service_state_t::STOPPED);
100     assert(event_loop.active_timers.size() == 0);
101 }
102
103 // Termination via stop request
104 void test_term_via_stop()
105 {
106     using namespace std;
107
108     service_set sset;
109
110     string command = "test-command";
111     list<pair<unsigned,unsigned>> command_offsets;
112     command_offsets.emplace_back(0, command.length());
113     std::list<prelim_dep> depends;
114
115     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
116     init_service_defaults(p);
117
118     p.start(true);
119     sset.process_queues();
120
121     base_process_service_test::exec_succeeded(&p);
122     sset.process_queues();
123
124     assert(p.get_state() == service_state_t::STARTED);
125     assert(event_loop.active_timers.size() == 0);
126
127     p.stop(true);
128     sset.process_queues();
129
130     assert(p.get_state() == service_state_t::STOPPING);
131     assert(event_loop.active_timers.size() == 1);
132
133     base_process_service_test::handle_exit(&p, 0);
134     sset.process_queues();
135
136     assert(p.get_state() == service_state_t::STOPPED);
137     assert(event_loop.active_timers.size() == 0);
138 }
139
140 // Time-out during start
141 void test_proc_start_timeout()
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 = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
153     init_service_defaults(p);
154
155     p.start(true);
156     sset.process_queues();
157
158     assert(p.get_state() == service_state_t::STARTING);
159
160     p.timer_expired();
161     sset.process_queues();
162
163     assert(p.get_state() == service_state_t::STOPPING);
164
165     base_process_service_test::handle_exit(&p, 0);
166     sset.process_queues();
167
168     assert(p.get_state() == service_state_t::STOPPED);
169     assert(event_loop.active_timers.size() == 0);
170 }
171
172 // Test stop timeout
173 void test_proc_stop_timeout()
174 {
175     using namespace std;
176
177     service_set sset;
178
179     string command = "test-command";
180     list<pair<unsigned,unsigned>> command_offsets;
181     command_offsets.emplace_back(0, command.length());
182     std::list<prelim_dep> depends;
183
184     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
185     init_service_defaults(p);
186
187     p.start(true);
188     sset.process_queues();
189
190     assert(p.get_state() == service_state_t::STARTING);
191
192     base_process_service_test::exec_succeeded(&p);
193     sset.process_queues();
194
195     assert(p.get_state() == service_state_t::STARTED);
196
197     p.stop(true);
198     sset.process_queues();
199
200     assert(p.get_state() == service_state_t::STOPPING);
201     assert(bp_sys::last_sig_sent == SIGTERM);
202
203     p.timer_expired();
204     sset.process_queues();
205
206     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
207     assert(p.get_state() == service_state_t::STOPPING);
208     assert(bp_sys::last_sig_sent == SIGKILL);
209
210     base_process_service_test::handle_exit(&p, 0);
211     sset.process_queues();
212
213     assert(p.get_state() == service_state_t::STOPPED);
214     // Note that timer is still active as we faked its expiry above
215     //assert(event_loop.active_timers.size() == 0);
216     event_loop.active_timers.clear();
217 }
218
219 // Smooth recovery
220 void test_proc_smooth_recovery1()
221 {
222     using namespace std;
223
224     service_set sset;
225
226     string command = "test-command";
227     list<pair<unsigned,unsigned>> command_offsets;
228     command_offsets.emplace_back(0, command.length());
229     std::list<prelim_dep> depends;
230
231     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
232     init_service_defaults(p);
233     p.set_smooth_recovery(true);
234
235     p.start(true);
236     sset.process_queues();
237
238     base_process_service_test::exec_succeeded(&p);
239     sset.process_queues();
240
241     pid_t first_instance = bp_sys::last_forked_pid;
242
243     assert(p.get_state() == service_state_t::STARTED);
244
245     base_process_service_test::handle_exit(&p, 0);
246     sset.process_queues();
247
248     // since time hasn't been changed, we expect that the process has not yet been re-launched:
249     assert(first_instance == bp_sys::last_forked_pid);
250     assert(p.get_state() == service_state_t::STARTED);
251
252     p.timer_expired();
253     sset.process_queues();
254
255     // Now a new process should've been launched:
256     assert(first_instance + 1 == bp_sys::last_forked_pid);
257     assert(p.get_state() == service_state_t::STARTED);
258     event_loop.active_timers.clear();
259 }
260
261 // Smooth recovery without restart delay
262 void test_proc_smooth_recovery2()
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 = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
274     init_service_defaults(p);
275     p.set_smooth_recovery(true);
276     p.set_restart_delay(time_val(0, 0));
277
278     p.start(true);
279     sset.process_queues();
280
281     base_process_service_test::exec_succeeded(&p);
282     sset.process_queues();
283
284     pid_t first_instance = bp_sys::last_forked_pid;
285
286     assert(p.get_state() == service_state_t::STARTED);
287     assert(event_loop.active_timers.size() == 0);
288
289     base_process_service_test::handle_exit(&p, 0);
290     sset.process_queues();
291
292     // no restart delay, process should restart immediately:
293     assert(first_instance + 1 == bp_sys::last_forked_pid);
294     assert(p.get_state() == service_state_t::STARTED);
295     assert(event_loop.active_timers.size() == 0);
296 }
297
298 // Test stop timeout
299 void test_scripted_stop_timeout()
300 {
301     using namespace std;
302
303     service_set sset;
304
305     string command = "test-command";
306     string stopcommand = "stop-command";
307     list<pair<unsigned,unsigned>> command_offsets;
308     command_offsets.emplace_back(0, command.length());
309     std::list<prelim_dep> depends;
310
311     scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
312     init_service_defaults(p);
313     p.set_stop_command(stopcommand, command_offsets);
314
315     p.start(true);
316     sset.process_queues();
317
318     assert(p.get_state() == service_state_t::STARTING);
319
320     base_process_service_test::exec_succeeded(&p);
321     sset.process_queues();
322     base_process_service_test::handle_exit(&p, 0);
323     sset.process_queues();
324
325     assert(p.get_state() == service_state_t::STARTED);
326
327     p.stop(true);
328     sset.process_queues();
329
330     assert(p.get_state() == service_state_t::STOPPING);
331
332     base_process_service_test::exec_succeeded(&p);
333     sset.process_queues();
334
335     // should still be stopping:
336     assert(p.get_state() == service_state_t::STOPPING);
337
338     p.timer_expired();
339     sset.process_queues();
340
341     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
342     assert(p.get_state() == service_state_t::STOPPING);
343     assert(bp_sys::last_sig_sent == SIGKILL);
344
345     base_process_service_test::handle_exit(&p, SIGKILL);
346     sset.process_queues();
347
348     assert(p.get_state() == service_state_t::STOPPED);
349     event_loop.active_timers.clear();
350 }
351
352
353 #define RUN_TEST(name, spacing) \
354     std::cout << #name "..." spacing; \
355     name(); \
356     std::cout << "PASSED" << std::endl;
357
358 int main(int argc, char **argv)
359 {
360     RUN_TEST(test_proc_service_start, "   ");
361     RUN_TEST(test_proc_unexpected_term, " ");
362     RUN_TEST(test_term_via_stop, "        ");
363     RUN_TEST(test_proc_start_timeout, "   ");
364     RUN_TEST(test_proc_stop_timeout, "    ");
365     RUN_TEST(test_proc_smooth_recovery1, "");
366     RUN_TEST(test_proc_smooth_recovery2, "");
367     RUN_TEST(test_scripted_stop_timeout, "");
368 }