Rename service_type (type) to service_type_t.
[oweals/dinit.git] / src / tests / tests.cc
1 #include <cassert>
2 #include <iostream>
3
4 #include "service.h"
5 #include "test_service.h"
6
7 constexpr static auto REG = dependency_type::REGULAR;
8 constexpr static auto WAITS = dependency_type::WAITS_FOR;
9 constexpr static auto MS = dependency_type::MILESTONE;
10
11 // Test 1: starting a service starts dependencies; stopping the service releases and
12 // stops dependencies.
13 void test1()
14 {
15     service_set sset;
16
17     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
18     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
19     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
20     sset.add_service(s1);
21     sset.add_service(s2);
22     sset.add_service(s3);
23
24     assert(sset.find_service("test-service-1") == s1);
25     assert(sset.find_service("test-service-2") == s2);
26     assert(sset.find_service("test-service-3") == s3);
27
28     // s3 depends on s2, which depends on s1. So starting s3 should start all three services:
29     sset.start_service(s3);
30
31     assert(s1->get_state() == service_state_t::STARTED);
32     assert(s2->get_state() == service_state_t::STARTED);
33     assert(s3->get_state() == service_state_t::STARTED);
34
35     // stopping s3 should release the other two services:
36     sset.stop_service(s3);
37
38     assert(s3->get_state() == service_state_t::STOPPED);
39     assert(s2->get_state() == service_state_t::STOPPED);
40     assert(s1->get_state() == service_state_t::STOPPED);
41 }
42
43 // Test 2: Multiple dependents will hold a dependency active if one of the dependents is
44 // stopped/released.
45 void test2()
46 {
47     service_set sset;
48
49     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
50     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
51     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
52     service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{s2, REG}});
53     sset.add_service(s1);
54     sset.add_service(s2);
55     sset.add_service(s3);
56     sset.add_service(s4);
57
58     // s3 depends on s2, which depends on s1. Similarly with s4. After starting both, all services
59     // should be started:
60     sset.start_service(s3);
61     sset.start_service(s4);
62
63     assert(s1->get_state() == service_state_t::STARTED);
64     assert(s2->get_state() == service_state_t::STARTED);
65     assert(s3->get_state() == service_state_t::STARTED);
66     assert(s4->get_state() == service_state_t::STARTED);
67
68     // after stopping s3, s4 should hold the other two services:
69     sset.stop_service(s3);
70
71     assert(s4->get_state() == service_state_t::STARTED);
72     assert(s3->get_state() == service_state_t::STOPPED);
73     assert(s2->get_state() == service_state_t::STARTED);
74     assert(s1->get_state() == service_state_t::STARTED);
75
76     // Now if we stop s4, s2 and s1 should also be released:
77     sset.stop_service(s4);
78
79     assert(s4->get_state() == service_state_t::STOPPED);
80     assert(s3->get_state() == service_state_t::STOPPED);
81     assert(s2->get_state() == service_state_t::STOPPED);
82     assert(s1->get_state() == service_state_t::STOPPED);
83 }
84
85 // Test 3: stopping a dependency causes its dependents to stop.
86 void test3()
87 {
88     service_set sset;
89
90     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
91     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
92     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
93     sset.add_service(s1);
94     sset.add_service(s2);
95     sset.add_service(s3);
96
97     assert(sset.find_service("test-service-1") == s1);
98     assert(sset.find_service("test-service-2") == s2);
99     assert(sset.find_service("test-service-3") == s3);
100
101     // Start all three services:
102     sset.start_service(s3);
103
104     // Now stop s1, which should also force s2 and s3 to stop:
105     sset.stop_service(s1);
106
107     assert(s3->get_state() == service_state_t::STOPPED);
108     assert(s2->get_state() == service_state_t::STOPPED);
109     assert(s1->get_state() == service_state_t::STOPPED);
110 }
111
112 // Test 4: an explicitly activated service with automatic restart will restart if it
113 // stops due to a dependency stopping, therefore also causing the dependency to restart.
114 void test4()
115 {
116     service_set sset;
117
118     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
119     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
120     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
121     s2->set_auto_restart(true);
122     sset.add_service(s1);
123     sset.add_service(s2);
124     sset.add_service(s3);
125
126     assert(sset.find_service("test-service-1") == s1);
127     assert(sset.find_service("test-service-2") == s2);
128     assert(sset.find_service("test-service-3") == s3);
129
130     // Start all three services:
131     sset.start_service(s3);
132
133     // Also explicitly activate s2:
134     sset.start_service(s2);
135
136     // Now stop s1, which should also force s2 and s3 to stop.
137     // s2 (and therefore s1) should restart:
138     sset.stop_service(s1);
139
140     assert(s3->get_state() == service_state_t::STOPPED);
141     assert(s2->get_state() == service_state_t::STARTED);
142     assert(s1->get_state() == service_state_t::STARTED);
143 }
144
145 // Test 5: test that services which do not start immediately correctly chain start of
146 // dependent services.
147 void test5()
148 {
149     service_set sset;
150
151     test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
152     test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
153     test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
154
155     sset.add_service(s1);
156     sset.add_service(s2);
157     sset.add_service(s3);
158
159     sset.start_service(s3);
160
161     // All three should transition to STARTING state:
162     assert(s3->get_state() == service_state_t::STARTING);
163     assert(s2->get_state() == service_state_t::STARTING);
164     assert(s1->get_state() == service_state_t::STARTING);
165
166     s1->started();
167     sset.process_queues();
168     assert(s3->get_state() == service_state_t::STARTING);
169     assert(s2->get_state() == service_state_t::STARTING);
170     assert(s1->get_state() == service_state_t::STARTED);
171
172     s2->started();
173     sset.process_queues();
174     assert(s3->get_state() == service_state_t::STARTING);
175     assert(s2->get_state() == service_state_t::STARTED);
176     assert(s1->get_state() == service_state_t::STARTED);
177
178     s3->started();
179     sset.process_queues();
180     assert(s3->get_state() == service_state_t::STARTED);
181     assert(s2->get_state() == service_state_t::STARTED);
182     assert(s1->get_state() == service_state_t::STARTED);
183 }
184
185 // Test 6: service pinned in start state is not stopped when its dependency stops.
186 void test6()
187 {
188     service_set sset;
189
190     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
191     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
192     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
193     s2->set_auto_restart(true);
194     sset.add_service(s1);
195     sset.add_service(s2);
196     sset.add_service(s3);
197
198     // Pin s3:
199     s3->pin_start();
200
201     // Start all three services:
202     sset.start_service(s3);
203
204     assert(s3->get_state() == service_state_t::STARTED);
205     assert(s2->get_state() == service_state_t::STARTED);
206     assert(s1->get_state() == service_state_t::STARTED);
207
208     // Stop s2:
209     s2->forced_stop();
210     s2->stop(true);
211     sset.process_queues();
212
213     // s3 should remain started:
214     assert(s3->get_state() == service_state_t::STARTED);
215     assert(s2->get_state() == service_state_t::STOPPING);
216     assert(s1->get_state() == service_state_t::STARTED);
217
218     // If we now unpin, s3 should stop:
219     s3->unpin();
220     sset.process_queues();
221     assert(s3->get_state() == service_state_t::STOPPED);
222     assert(s2->get_state() == service_state_t::STOPPED);
223     // s1 will stop because it is no longer required:
224     assert(s1->get_state() == service_state_t::STOPPED);
225 }
226
227 // Test 7: stopping a soft dependency doesn't cause the dependent to stop.
228 void test7()
229 {
230     service_set sset;
231
232     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
233     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
234     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, WAITS}});
235     sset.add_service(s1);
236     sset.add_service(s2);
237     sset.add_service(s3);
238
239     assert(sset.find_service("test-service-1") == s1);
240     assert(sset.find_service("test-service-2") == s2);
241     assert(sset.find_service("test-service-3") == s3);
242
243     // Start all three services:
244     sset.start_service(s3);
245
246     // Now stop s1, which should also force s2 but not s3 to stop:
247     sset.stop_service(s1);
248
249     assert(s3->get_state() == service_state_t::STARTED);
250     assert(s2->get_state() == service_state_t::STOPPED);
251     assert(s1->get_state() == service_state_t::STOPPED);
252 }
253
254 // Test 8: stopping a milestone dependency doesn't cause the dependent to stop
255 void test8()
256 {
257     service_set sset;
258
259     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
260     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
261     sset.add_service(s1);
262     sset.add_service(s2);
263
264     assert(sset.find_service("test-service-1") == s1);
265     assert(sset.find_service("test-service-2") == s2);
266
267     // Start the services:
268     sset.start_service(s2);
269
270     assert(s2->get_state() == service_state_t::STARTED);
271     assert(s1->get_state() == service_state_t::STARTED);
272
273     // Now stop s1, which should not stop s2:
274     sset.stop_service(s1);
275
276     assert(s2->get_state() == service_state_t::STARTED);
277     assert(s1->get_state() == service_state_t::STOPPED);
278 }
279
280 // Test 9: a failing milestone dependency causes the dependent to fail
281 void test9()
282 {
283     service_set sset;
284
285     test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
286     test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
287     sset.add_service(s1);
288     sset.add_service(s2);
289
290     assert(sset.find_service("test-service-1") == s1);
291     assert(sset.find_service("test-service-2") == s2);
292
293     // Start the services, but fail s1:
294     sset.start_service(s2);
295
296     assert(s1->get_state() == service_state_t::STARTING);
297     s1->failed_to_start();
298     sset.process_queues();
299
300     assert(s2->get_state() == service_state_t::STOPPED);
301 }
302
303 #define RUN_TEST(name) \
304     std::cout << #name "... "; \
305     name(); \
306     std::cout << "PASSED" << std::endl;
307
308 int main(int argc, char **argv)
309 {
310     RUN_TEST(test1);
311     RUN_TEST(test2);
312     RUN_TEST(test3);
313     RUN_TEST(test4);
314     RUN_TEST(test5);
315     RUN_TEST(test6);
316     RUN_TEST(test7);
317     RUN_TEST(test8);
318     RUN_TEST(test9);
319 }