Add a test, adjustments to other tests.
[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     sset.add_service(&p);
63
64     p.start(true);
65     sset.process_queues();
66
67     assert(p.get_state() == service_state_t::STARTING);
68
69     base_process_service_test::exec_succeeded(&p);
70     sset.process_queues();
71
72     assert(p.get_state() == service_state_t::STARTED);
73     assert(event_loop.active_timers.size() == 0);
74
75     sset.remove_service(&p);
76 }
77
78 // Unexpected termination
79 void test_proc_unexpected_term()
80 {
81     using namespace std;
82
83     service_set sset;
84
85     string command = "test-command";
86     list<pair<unsigned,unsigned>> command_offsets;
87     command_offsets.emplace_back(0, command.length());
88     std::list<prelim_dep> depends;
89
90     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
91     init_service_defaults(p);
92     sset.add_service(&p);
93
94     p.start(true);
95     sset.process_queues();
96
97     base_process_service_test::exec_succeeded(&p);
98     sset.process_queues();
99
100     assert(p.get_state() == service_state_t::STARTED);
101
102     base_process_service_test::handle_exit(&p, 0);
103     sset.process_queues();
104
105     assert(p.get_state() == service_state_t::STOPPED);
106     assert(event_loop.active_timers.size() == 0);
107
108     sset.remove_service(&p);
109 }
110
111 // Termination via stop request
112 void test_term_via_stop()
113 {
114     using namespace std;
115
116     service_set sset;
117
118     string command = "test-command";
119     list<pair<unsigned,unsigned>> command_offsets;
120     command_offsets.emplace_back(0, command.length());
121     std::list<prelim_dep> depends;
122
123     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
124     init_service_defaults(p);
125     sset.add_service(&p);
126
127     p.start(true);
128     sset.process_queues();
129
130     base_process_service_test::exec_succeeded(&p);
131     sset.process_queues();
132
133     assert(p.get_state() == service_state_t::STARTED);
134     assert(event_loop.active_timers.size() == 0);
135
136     p.stop(true);
137     sset.process_queues();
138
139     assert(p.get_state() == service_state_t::STOPPING);
140     assert(event_loop.active_timers.size() == 1);
141
142     base_process_service_test::handle_exit(&p, 0);
143     sset.process_queues();
144
145     assert(p.get_state() == service_state_t::STOPPED);
146     assert(event_loop.active_timers.size() == 0);
147
148     sset.remove_service(&p);
149 }
150
151 // Time-out during start
152 void test_proc_start_timeout()
153 {
154     using namespace std;
155
156     service_set sset;
157
158     string command = "test-command";
159     list<pair<unsigned,unsigned>> command_offsets;
160     command_offsets.emplace_back(0, command.length());
161     std::list<prelim_dep> depends;
162
163     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
164     init_service_defaults(p);
165     sset.add_service(&p);
166
167     p.start(true);
168     sset.process_queues();
169
170     assert(p.get_state() == service_state_t::STARTING);
171
172     p.timer_expired();
173     sset.process_queues();
174
175     assert(p.get_state() == service_state_t::STOPPING);
176
177     base_process_service_test::handle_exit(&p, 0);
178     sset.process_queues();
179
180     assert(p.get_state() == service_state_t::STOPPED);
181     assert(event_loop.active_timers.size() == 0);
182
183     sset.remove_service(&p);
184 }
185
186 // Test that a timeout doesn't stop a "waits for" dependent to fail to start
187 void test_proc_start_timeout2()
188 {
189     using namespace std;
190
191     service_set sset;
192
193     string command = "test-command";
194     list<pair<unsigned,unsigned>> command_offsets;
195     command_offsets.emplace_back(0, command.length());
196     std::list<prelim_dep> depends;
197
198     process_service p {&sset, "testproc", std::move(command), command_offsets, depends};
199     init_service_defaults(p);
200     sset.add_service(&p);
201
202     service_record ts {&sset, "test-service-1", service_type_t::INTERNAL, {{&p, dependency_type::WAITS_FOR}} };
203
204     ts.start(true);
205     sset.process_queues();
206
207     assert(p.get_state() == service_state_t::STARTING);
208     assert(ts.get_state() == service_state_t::STARTING);
209
210     p.timer_expired();
211     sset.process_queues();
212
213     assert(p.get_state() == service_state_t::STOPPING);
214
215     base_process_service_test::handle_exit(&p, 0);
216     sset.process_queues();
217
218     assert(p.get_state() == service_state_t::STOPPED);
219     assert(ts.get_state() == service_state_t::STARTED);
220     assert(event_loop.active_timers.size() == 0);
221
222     sset.remove_service(&p);
223 }
224
225 // Test stop timeout
226 void test_proc_stop_timeout()
227 {
228     using namespace std;
229
230     service_set sset;
231
232     string command = "test-command";
233     list<pair<unsigned,unsigned>> command_offsets;
234     command_offsets.emplace_back(0, command.length());
235     std::list<prelim_dep> depends;
236
237     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
238     init_service_defaults(p);
239     sset.add_service(&p);
240
241     p.start(true);
242     sset.process_queues();
243
244     assert(p.get_state() == service_state_t::STARTING);
245
246     base_process_service_test::exec_succeeded(&p);
247     sset.process_queues();
248
249     assert(p.get_state() == service_state_t::STARTED);
250
251     p.stop(true);
252     sset.process_queues();
253
254     assert(p.get_state() == service_state_t::STOPPING);
255     assert(bp_sys::last_sig_sent == SIGTERM);
256
257     p.timer_expired();
258     sset.process_queues();
259
260     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
261     assert(p.get_state() == service_state_t::STOPPING);
262     assert(bp_sys::last_sig_sent == SIGKILL);
263
264     base_process_service_test::handle_exit(&p, 0);
265     sset.process_queues();
266
267     assert(p.get_state() == service_state_t::STOPPED);
268
269     // Note that timer is still active as we faked its expiry above
270     //assert(event_loop.active_timers.size() == 0);
271     event_loop.active_timers.clear();
272     sset.remove_service(&p);
273 }
274
275 // Smooth recovery
276 void test_proc_smooth_recovery1()
277 {
278     using namespace std;
279
280     service_set sset;
281
282     string command = "test-command";
283     list<pair<unsigned,unsigned>> command_offsets;
284     command_offsets.emplace_back(0, command.length());
285     std::list<prelim_dep> depends;
286
287     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
288     init_service_defaults(p);
289     p.set_smooth_recovery(true);
290     sset.add_service(&p);
291
292     p.start(true);
293     sset.process_queues();
294
295     base_process_service_test::exec_succeeded(&p);
296     sset.process_queues();
297
298     pid_t first_instance = bp_sys::last_forked_pid;
299
300     assert(p.get_state() == service_state_t::STARTED);
301
302     base_process_service_test::handle_exit(&p, 0);
303     sset.process_queues();
304
305     // since time hasn't been changed, we expect that the process has not yet been re-launched:
306     assert(first_instance == bp_sys::last_forked_pid);
307     assert(p.get_state() == service_state_t::STARTED);
308
309     p.timer_expired();
310     sset.process_queues();
311
312     // Now a new process should've been launched:
313     assert(first_instance + 1 == bp_sys::last_forked_pid);
314     assert(p.get_state() == service_state_t::STARTED);
315     event_loop.active_timers.clear();
316
317     sset.remove_service(&p);
318 }
319
320 // Smooth recovery without restart delay
321 void test_proc_smooth_recovery2()
322 {
323     using namespace std;
324
325     service_set sset;
326
327     string command = "test-command";
328     list<pair<unsigned,unsigned>> command_offsets;
329     command_offsets.emplace_back(0, command.length());
330     std::list<prelim_dep> depends;
331
332     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
333     init_service_defaults(p);
334     p.set_smooth_recovery(true);
335     p.set_restart_delay(time_val(0, 0));
336     sset.add_service(&p);
337
338     p.start(true);
339     sset.process_queues();
340
341     base_process_service_test::exec_succeeded(&p);
342     sset.process_queues();
343
344     pid_t first_instance = bp_sys::last_forked_pid;
345
346     assert(p.get_state() == service_state_t::STARTED);
347     assert(event_loop.active_timers.size() == 0);
348
349     base_process_service_test::handle_exit(&p, 0);
350     sset.process_queues();
351
352     // no restart delay, process should restart immediately:
353     assert(first_instance + 1 == bp_sys::last_forked_pid);
354     assert(p.get_state() == service_state_t::STARTED);
355     assert(event_loop.active_timers.size() == 0);
356
357     sset.remove_service(&p);
358 }
359
360 // Test stop timeout
361 void test_scripted_stop_timeout()
362 {
363     using namespace std;
364
365     service_set sset;
366
367     string command = "test-command";
368     string stopcommand = "stop-command";
369     list<pair<unsigned,unsigned>> command_offsets;
370     command_offsets.emplace_back(0, command.length());
371     std::list<prelim_dep> depends;
372
373     scripted_service p = scripted_service(&sset, "testscripted", std::move(command), command_offsets, depends);
374     init_service_defaults(p);
375     p.set_stop_command(stopcommand, command_offsets);
376     sset.add_service(&p);
377
378     p.start(true);
379     sset.process_queues();
380
381     assert(p.get_state() == service_state_t::STARTING);
382
383     base_process_service_test::exec_succeeded(&p);
384     sset.process_queues();
385     base_process_service_test::handle_exit(&p, 0);
386     sset.process_queues();
387
388     assert(p.get_state() == service_state_t::STARTED);
389
390     p.stop(true);
391     sset.process_queues();
392
393     assert(p.get_state() == service_state_t::STOPPING);
394
395     base_process_service_test::exec_succeeded(&p);
396     sset.process_queues();
397
398     // should still be stopping:
399     assert(p.get_state() == service_state_t::STOPPING);
400
401     p.timer_expired();
402     sset.process_queues();
403
404     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
405     assert(p.get_state() == service_state_t::STOPPING);
406     assert(bp_sys::last_sig_sent == SIGKILL);
407
408     base_process_service_test::handle_exit(&p, SIGKILL);
409     sset.process_queues();
410
411     assert(p.get_state() == service_state_t::STOPPED);
412
413     event_loop.active_timers.clear();
414     sset.remove_service(&p);
415 }
416
417
418 #define RUN_TEST(name, spacing) \
419     std::cout << #name "..." spacing; \
420     name(); \
421     std::cout << "PASSED" << std::endl;
422
423 int main(int argc, char **argv)
424 {
425     RUN_TEST(test_proc_service_start, "   ");
426     RUN_TEST(test_proc_unexpected_term, " ");
427     RUN_TEST(test_term_via_stop, "        ");
428     RUN_TEST(test_proc_start_timeout, "   ");
429     RUN_TEST(test_proc_start_timeout2, "  ");
430     RUN_TEST(test_proc_stop_timeout, "    ");
431     RUN_TEST(test_proc_smooth_recovery1, "");
432     RUN_TEST(test_proc_smooth_recovery2, "");
433     RUN_TEST(test_scripted_stop_timeout, "");
434 }