Add second smooth recovery test.
[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 // Friend interface to access base_process_service private/protected members.
16 class base_process_service_test
17 {
18     public:
19     static void exec_succeeded(base_process_service *bsp)
20     {
21         bsp->waiting_for_execstat = false;
22         bsp->exec_succeeded();
23     }
24
25     static void handle_exit(base_process_service *bsp, int exit_status)
26     {
27         bsp->pid = -1;
28         bsp->handle_exit_status(exit_status);
29     }
30 };
31
32 namespace bp_sys {
33     // last signal sent:
34     extern int last_sig_sent;
35     extern pid_t last_forked_pid;
36 }
37
38 // Regular service start
39 void test_proc_service_start()
40 {
41     using namespace std;
42
43     service_set sset;
44
45     string command = "test-command";
46     list<pair<unsigned,unsigned>> command_offsets;
47     command_offsets.emplace_back(0, command.length());
48     std::list<prelim_dep> depends;
49
50     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
51     p.start(true);
52     sset.process_queues();
53
54     assert(p.get_state() == service_state_t::STARTING);
55
56     base_process_service_test::exec_succeeded(&p);
57     sset.process_queues();
58
59     assert(p.get_state() == service_state_t::STARTED);
60 }
61
62 // Unexpected termination
63 void test_proc_unexpected_term()
64 {
65     using namespace std;
66
67     service_set sset;
68
69     string command = "test-command";
70     list<pair<unsigned,unsigned>> command_offsets;
71     command_offsets.emplace_back(0, command.length());
72     std::list<prelim_dep> depends;
73
74     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
75     p.start(true);
76     sset.process_queues();
77
78     base_process_service_test::exec_succeeded(&p);
79     sset.process_queues();
80
81     assert(p.get_state() == service_state_t::STARTED);
82
83     base_process_service_test::handle_exit(&p, 0);
84     sset.process_queues();
85
86     assert(p.get_state() == service_state_t::STOPPED);
87 }
88
89 // Termination via stop request
90 void test_term_via_stop()
91 {
92     using namespace std;
93
94     service_set sset;
95
96     string command = "test-command";
97     list<pair<unsigned,unsigned>> command_offsets;
98     command_offsets.emplace_back(0, command.length());
99     std::list<prelim_dep> depends;
100
101     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
102     p.start(true);
103     sset.process_queues();
104
105     base_process_service_test::exec_succeeded(&p);
106     sset.process_queues();
107
108     assert(p.get_state() == service_state_t::STARTED);
109
110     p.stop(true);
111     sset.process_queues();
112
113     assert(p.get_state() == service_state_t::STOPPING);
114
115     base_process_service_test::handle_exit(&p, 0);
116     sset.process_queues();
117
118     assert(p.get_state() == service_state_t::STOPPED);
119 }
120
121 // Time-out during start
122 void test_proc_start_timeout()
123 {
124     using namespace std;
125
126     service_set sset;
127
128     string command = "test-command";
129     list<pair<unsigned,unsigned>> command_offsets;
130     command_offsets.emplace_back(0, command.length());
131     std::list<prelim_dep> depends;
132
133     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
134     p.start(true);
135     sset.process_queues();
136
137     assert(p.get_state() == service_state_t::STARTING);
138
139     p.timer_expired();
140     sset.process_queues();
141
142     assert(p.get_state() == service_state_t::STOPPING);
143
144     base_process_service_test::handle_exit(&p, 0);
145     sset.process_queues();
146
147     assert(p.get_state() == service_state_t::STOPPED);
148 }
149
150 // Test stop timeout
151 void test_proc_stop_timeout()
152 {
153     using namespace std;
154
155     service_set sset;
156
157     string command = "test-command";
158     list<pair<unsigned,unsigned>> command_offsets;
159     command_offsets.emplace_back(0, command.length());
160     std::list<prelim_dep> depends;
161
162     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
163     p.start(true);
164     sset.process_queues();
165
166     assert(p.get_state() == service_state_t::STARTING);
167
168     base_process_service_test::exec_succeeded(&p);
169     sset.process_queues();
170
171     assert(p.get_state() == service_state_t::STARTED);
172
173     p.stop(true);
174     sset.process_queues();
175
176     assert(p.get_state() == service_state_t::STOPPING);
177     assert(bp_sys::last_sig_sent == SIGTERM);
178
179     p.timer_expired();
180     sset.process_queues();
181
182     // kill signal (SIGKILL) should have been sent; process not dead until it's dead, however
183     assert(p.get_state() == service_state_t::STOPPING);
184     assert(bp_sys::last_sig_sent == SIGKILL);
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 }
191
192 // Smooth recovery
193 void test_proc_smooth_recovery1()
194 {
195     using namespace std;
196
197     service_set sset;
198
199     string command = "test-command";
200     list<pair<unsigned,unsigned>> command_offsets;
201     command_offsets.emplace_back(0, command.length());
202     std::list<prelim_dep> depends;
203
204     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
205     p.set_smooth_recovery(true);
206
207     p.start(true);
208     sset.process_queues();
209
210     base_process_service_test::exec_succeeded(&p);
211     sset.process_queues();
212
213     pid_t first_instance = bp_sys::last_forked_pid;
214
215     assert(p.get_state() == service_state_t::STARTED);
216
217     base_process_service_test::handle_exit(&p, 0);
218     sset.process_queues();
219
220     // since time hasn't been changed, we expect that the process has not yet been re-launched:
221     assert(first_instance == bp_sys::last_forked_pid);
222     assert(p.get_state() == service_state_t::STARTED);
223
224     p.timer_expired();
225     sset.process_queues();
226
227     // Now a new process should've been launched:
228     assert(first_instance + 1 == bp_sys::last_forked_pid);
229     assert(p.get_state() == service_state_t::STARTED);
230 }
231
232 void test_proc_smooth_recovery2()
233 {
234     using namespace std;
235
236     service_set sset;
237
238     string command = "test-command";
239     list<pair<unsigned,unsigned>> command_offsets;
240     command_offsets.emplace_back(0, command.length());
241     std::list<prelim_dep> depends;
242
243     process_service p = process_service(&sset, "testproc", std::move(command), command_offsets, depends);
244     p.set_smooth_recovery(true);
245     p.set_restart_delay(time_val(0, 0));
246
247     p.start(true);
248     sset.process_queues();
249
250     base_process_service_test::exec_succeeded(&p);
251     sset.process_queues();
252
253     pid_t first_instance = bp_sys::last_forked_pid;
254
255     assert(p.get_state() == service_state_t::STARTED);
256
257     base_process_service_test::handle_exit(&p, 0);
258     sset.process_queues();
259
260     // no restart delay, process should restart immediately:
261     assert(first_instance + 1 == bp_sys::last_forked_pid);
262     assert(p.get_state() == service_state_t::STARTED);
263 }
264
265 #define RUN_TEST(name, spacing) \
266     std::cout << #name "..." spacing; \
267     name(); \
268     std::cout << "PASSED" << std::endl;
269
270 int main(int argc, char **argv)
271 {
272     RUN_TEST(test_proc_service_start, "   ");
273     RUN_TEST(test_proc_unexpected_term, " ");
274     RUN_TEST(test_term_via_stop, "        ");
275     RUN_TEST(test_proc_start_timeout, "   ");
276     RUN_TEST(test_proc_stop_timeout, "    ");
277     RUN_TEST(test_proc_smooth_recovery1, "");
278     RUN_TEST(test_proc_smooth_recovery2, "");
279 }