tests: add a new test.
[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 that service pinned in start state is not stopped when its dependency stops.
186 void test_pin1()
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 due to pin:
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::STOPPING);
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     assert(s1->get_state() == service_state_t::STOPPED);
224 }
225
226 // Test that STOPPING dependency of pinned service returns to STARTED when the pinned service has
227 // a start issued.
228 void test_pin2()
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, REG}});
235     s2->set_auto_restart(true);
236     sset.add_service(s1);
237     sset.add_service(s2);
238     sset.add_service(s3);
239
240     // Pin s3:
241     s3->pin_start();
242
243     // Start all three services:
244     sset.start_service(s3);
245
246     assert(s3->get_state() == service_state_t::STARTED);
247     assert(s2->get_state() == service_state_t::STARTED);
248     assert(s1->get_state() == service_state_t::STARTED);
249
250     // Issue stop to s3:
251     s3->stop(true);
252     sset.process_queues();
253
254     // s3 should remain started due to pin, but s1 and s2 are released and go STOPPING:
255     assert(s3->get_state() == service_state_t::STARTED);
256     assert(s2->get_state() == service_state_t::STOPPING);
257     assert(s1->get_state() == service_state_t::STOPPING);
258
259     // If we now issue start, STOPPING should revert to STARTED:
260     s3->start(true);
261     sset.process_queues();
262
263     assert(s3->get_state() == service_state_t::STARTED);
264     assert(s2->get_state() == service_state_t::STARTED);
265     assert(s1->get_state() == service_state_t::STARTED);
266 }
267
268 // Test that a STOPPING dependency of a pinned service stops when pin is released, even if pinned
269 // service is activated:
270 void test_pin3()
271 {
272     service_set sset;
273
274     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
275     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
276     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
277     s2->set_auto_restart(true);
278     sset.add_service(s1);
279     sset.add_service(s2);
280     sset.add_service(s3);
281
282     // Pin s3:
283     s3->pin_start();
284
285     // Start all three services:
286     sset.start_service(s3);
287
288     assert(s3->get_state() == service_state_t::STARTED);
289     assert(s2->get_state() == service_state_t::STARTED);
290     assert(s1->get_state() == service_state_t::STARTED);
291
292     // Issue force stop to s2:
293     s2->stop(true);
294     s2->forced_stop();
295     sset.process_queues();
296
297     // s3 should remain started due to pin, but s1 and s2 are released and go STOPPING:
298     assert(s3->get_state() == service_state_t::STARTED);
299     assert(s2->get_state() == service_state_t::STOPPING);
300     assert(s1->get_state() == service_state_t::STOPPING);
301
302     // If we now issue start, s2 still needs to stop (due to force stop):
303     s3->start(true);
304     sset.process_queues();
305
306     assert(s3->get_state() == service_state_t::STARTED);
307     assert(s2->get_state() == service_state_t::STOPPING);
308     assert(s1->get_state() == service_state_t::STARTED);
309
310     // When we unpin, s2 should STOP; s3 must stop as a result; s1 is released and so also stops:
311     s3->unpin();
312
313     assert(s3->get_state() == service_state_t::STOPPED);
314     assert(s2->get_state() == service_state_t::STOPPED);
315     assert(s1->get_state() == service_state_t::STOPPED);
316 }
317
318 // Test that service pinned started is released when stop issued and stops when unpinned
319 void test_pin4()
320 {
321     service_set sset;
322
323     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
324     sset.add_service(s1);
325
326     // Pin s3:
327     s1->pin_start();
328
329     // Start the service:
330     sset.start_service(s1);
331
332     assert(s1->get_state() == service_state_t::STARTED);
333
334     // Issue stop:
335     s1->stop(true);
336     sset.process_queues();
337
338     // s3 should remain started:
339     assert(s1->get_state() == service_state_t::STARTED);
340
341     // If we now unpin, s1 should stop:
342     s1->unpin();
343     sset.process_queues();
344     assert(s1->get_state() == service_state_t::STOPPED);
345 }
346
347 // Test 7: stopping a soft dependency doesn't cause the dependent to stop.
348 void test7()
349 {
350     service_set sset;
351
352     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
353     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
354     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, WAITS}});
355     sset.add_service(s1);
356     sset.add_service(s2);
357     sset.add_service(s3);
358
359     assert(sset.find_service("test-service-1") == s1);
360     assert(sset.find_service("test-service-2") == s2);
361     assert(sset.find_service("test-service-3") == s3);
362
363     // Start all three services:
364     sset.start_service(s3);
365
366     // Now stop s1, which should also force s2 but not s3 to stop:
367     sset.stop_service(s1);
368
369     assert(s3->get_state() == service_state_t::STARTED);
370     assert(s2->get_state() == service_state_t::STOPPED);
371     assert(s1->get_state() == service_state_t::STOPPED);
372 }
373
374 // Test 8: stopping a milestone dependency doesn't cause the dependent to stop
375 void test8()
376 {
377     service_set sset;
378
379     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
380     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
381     sset.add_service(s1);
382     sset.add_service(s2);
383
384     assert(sset.find_service("test-service-1") == s1);
385     assert(sset.find_service("test-service-2") == s2);
386
387     // Start the services:
388     sset.start_service(s2);
389
390     assert(s2->get_state() == service_state_t::STARTED);
391     assert(s1->get_state() == service_state_t::STARTED);
392
393     // Now stop s1, which should not stop s2:
394     sset.stop_service(s1);
395
396     assert(s2->get_state() == service_state_t::STARTED);
397     assert(s1->get_state() == service_state_t::STOPPED);
398 }
399
400 // Test 9: a failing milestone dependency causes the dependent to fail
401 void test9()
402 {
403     service_set sset;
404
405     test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
406     test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
407     sset.add_service(s1);
408     sset.add_service(s2);
409
410     assert(sset.find_service("test-service-1") == s1);
411     assert(sset.find_service("test-service-2") == s2);
412
413     // Start the services, but fail s1:
414     sset.start_service(s2);
415
416     assert(s1->get_state() == service_state_t::STARTING);
417     s1->failed_to_start();
418     sset.process_queues();
419
420     assert(s2->get_state() == service_state_t::STOPPED);
421 }
422
423 // Test 10: if start cancelled, remove from console queue
424 void test10()
425 {
426     service_set sset;
427
428     // Create s1 and s2. s2 depends on s1, and starts on the console.
429     test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
430     test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
431     onstart_flags_t s2_flags;
432     s2_flags.starts_on_console = true;
433     s2->set_flags(s2_flags);
434     sset.add_service(s1);
435     sset.add_service(s2);
436
437     // Create s3, which starts and runs on console:
438     test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {});
439     onstart_flags_t s3_flags;
440     s3_flags.starts_on_console = true;
441     s3_flags.runs_on_console = true;
442     sset.add_service(s3);
443
444     assert(sset.find_service("test-service-1") == s1);
445     assert(sset.find_service("test-service-2") == s2);
446     assert(sset.find_service("test-service-3") == s3);
447
448     // Start the s3 service, so it gets console:
449     sset.start_service(s3);
450     sset.process_queues();
451     s3->started();
452     sset.process_queues();
453
454     assert(! sset.is_queued_for_console(s3)); // should not be queued, because already has acquired
455     assert(sset.is_console_queue_empty());
456
457     // Start s2, which starts s1 as a dependency:
458
459     sset.start_service(s2);
460     sset.process_queues();
461
462     assert(s1->get_state() == service_state_t::STARTING);
463     assert(s2->get_state() == service_state_t::STARTING);
464
465     s1->started();
466     sset.process_queues();
467
468     // s2 should now be waiting for console:
469     assert(s1->get_state() == service_state_t::STARTED);
470     assert(s2->get_state() == service_state_t::STARTING);
471     assert(sset.is_queued_for_console(s2));
472
473     // stop s1, should stop s2, s2 should unqueue:
474     s1->stop();
475     sset.process_queues();
476
477     assert(s1->get_state() == service_state_t::STOPPED);
478     assert(s2->get_state() == service_state_t::STOPPED);
479     assert(! sset.is_queued_for_console(s2));
480 }
481
482 // Test 11: if a milestone dependency doesn't start, dependent doesn't start.
483 void test11()
484 {
485     service_set sset;
486
487     test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
488     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
489
490     sset.add_service(s1);
491     sset.add_service(s2);
492
493     assert(sset.find_service("test-service-1") == s1);
494     assert(sset.find_service("test-service-2") == s2);
495
496     // Request start of the s2 service:
497     sset.start_service(s2);
498     sset.process_queues();
499
500     assert(s1->get_state() == service_state_t::STARTING);
501     assert(s2->get_state() == service_state_t::STARTING);
502
503     s1->stop();
504     sset.process_queues();
505     s1->bring_down();
506     sset.process_queues();
507
508     assert(s1->get_state() == service_state_t::STOPPED);
509     assert(s2->get_state() == service_state_t::STOPPED);
510 }
511
512 // Test that active service count reaches 0 when stopping a service with different types of dependency
513 void test12()
514 {
515     service_set sset;
516
517     service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {});
518     service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {});
519     service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
520
521     service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL,
522             {{s2, WAITS}, {s3, REG}, {s4, MS}});
523
524     sset.add_service(s4);
525     sset.add_service(s3);
526     sset.add_service(s2);
527     sset.add_service(s1);
528
529     assert(sset.find_service("test-service-1") == s1);
530     assert(sset.find_service("test-service-2") == s2);
531     assert(sset.find_service("test-service-3") == s3);
532     assert(sset.find_service("test-service-4") == s4);
533
534     // Request start of the s2 service, should also start s1:
535     sset.start_service(s1);
536     sset.process_queues();
537
538     assert(s1->get_state() == service_state_t::STARTED);
539     assert(s2->get_state() == service_state_t::STARTED);
540     assert(s3->get_state() == service_state_t::STARTED);
541     assert(s4->get_state() == service_state_t::STARTED);
542
543     s1->stop();
544     sset.process_queues();
545
546     assert(s1->get_state() == service_state_t::STOPPED);
547     assert(s2->get_state() == service_state_t::STOPPED);
548     assert(s3->get_state() == service_state_t::STOPPED);
549     assert(s4->get_state() == service_state_t::STOPPED);
550
551     assert(sset.count_active_services() == 0);
552 }
553
554 #define RUN_TEST(name) \
555     std::cout << #name "... "; \
556     name(); \
557     std::cout << "PASSED" << std::endl;
558
559 int main(int argc, char **argv)
560 {
561     RUN_TEST(test1);
562     RUN_TEST(test2);
563     RUN_TEST(test3);
564     RUN_TEST(test4);
565     RUN_TEST(test5);
566     RUN_TEST(test_pin1);
567     RUN_TEST(test_pin2);
568     RUN_TEST(test_pin3);
569     RUN_TEST(test_pin4);
570     RUN_TEST(test7);
571     RUN_TEST(test8);
572     RUN_TEST(test9);
573     RUN_TEST(test10);
574     RUN_TEST(test11);
575     RUN_TEST(test12);
576 }