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