5 #include "test_service.h"
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;
11 class test_listener : public service_listener
14 bool got_started = false;
15 bool got_stopped = false;
16 bool start_cancelled = false;
17 bool stop_cancelled = false;
19 void service_event(service_record * service, service_event_t event) noexcept override
22 case service_event_t::STARTED:
25 case service_event_t::STOPPED:
28 case service_event_t::STARTCANCELLED:
29 start_cancelled = true;
31 case service_event_t::STOPCANCELLED:
32 stop_cancelled = true;
34 case service_event_t::FAILEDSTART:
40 // Test 1: starting a service starts dependencies; stopping the service releases and
41 // stops dependencies.
46 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
47 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
48 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
53 assert(sset.find_service("test-service-1") == s1);
54 assert(sset.find_service("test-service-2") == s2);
55 assert(sset.find_service("test-service-3") == s3);
57 // s3 depends on s2, which depends on s1. So starting s3 should start all three services:
58 sset.start_service(s3);
60 assert(s1->get_state() == service_state_t::STARTED);
61 assert(s2->get_state() == service_state_t::STARTED);
62 assert(s3->get_state() == service_state_t::STARTED);
64 // stopping s3 should release the other two services:
65 sset.stop_service(s3);
67 assert(s3->get_state() == service_state_t::STOPPED);
68 assert(s2->get_state() == service_state_t::STOPPED);
69 assert(s1->get_state() == service_state_t::STOPPED);
72 // Test 2: Multiple dependents will hold a dependency active if one of the dependents is
78 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
79 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
80 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
81 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {{s2, REG}});
87 // s3 depends on s2, which depends on s1. Similarly with s4. After starting both, all services
89 sset.start_service(s3);
90 sset.start_service(s4);
92 assert(s1->get_state() == service_state_t::STARTED);
93 assert(s2->get_state() == service_state_t::STARTED);
94 assert(s3->get_state() == service_state_t::STARTED);
95 assert(s4->get_state() == service_state_t::STARTED);
97 // after stopping s3, s4 should hold the other two services:
98 sset.stop_service(s3);
100 assert(s4->get_state() == service_state_t::STARTED);
101 assert(s3->get_state() == service_state_t::STOPPED);
102 assert(s2->get_state() == service_state_t::STARTED);
103 assert(s1->get_state() == service_state_t::STARTED);
105 // Now if we stop s4, s2 and s1 should also be released:
106 sset.stop_service(s4);
108 assert(s4->get_state() == service_state_t::STOPPED);
109 assert(s3->get_state() == service_state_t::STOPPED);
110 assert(s2->get_state() == service_state_t::STOPPED);
111 assert(s1->get_state() == service_state_t::STOPPED);
114 // Test 3: stopping a dependency causes its dependents to stop.
119 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
120 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
121 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
122 sset.add_service(s1);
123 sset.add_service(s2);
124 sset.add_service(s3);
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);
130 // Start all three services:
131 sset.start_service(s3);
133 // Now stop s1, which should also force s2 and s3 to stop:
134 sset.stop_service(s1);
136 assert(s3->get_state() == service_state_t::STOPPED);
137 assert(s2->get_state() == service_state_t::STOPPED);
138 assert(s1->get_state() == service_state_t::STOPPED);
141 // Test 4: an explicitly activated service with automatic restart will restart if it
142 // stops due to a dependency stopping, therefore also causing the dependency to restart.
147 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
148 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
149 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
150 s2->set_auto_restart(true);
151 sset.add_service(s1);
152 sset.add_service(s2);
153 sset.add_service(s3);
155 assert(sset.find_service("test-service-1") == s1);
156 assert(sset.find_service("test-service-2") == s2);
157 assert(sset.find_service("test-service-3") == s3);
159 // Start all three services:
160 sset.start_service(s3);
162 // Also explicitly activate s2:
163 sset.start_service(s2);
165 // Now stop s1, which should also force s2 and s3 to stop.
166 // s2 (and therefore s1) should restart:
167 sset.stop_service(s1);
169 assert(s3->get_state() == service_state_t::STOPPED);
170 assert(s2->get_state() == service_state_t::STARTED);
171 assert(s1->get_state() == service_state_t::STARTED);
174 // Test 5: test that services which do not start immediately correctly chain start of
175 // dependent services.
180 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
181 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
182 test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
184 sset.add_service(s1);
185 sset.add_service(s2);
186 sset.add_service(s3);
188 sset.start_service(s3);
190 // All three should transition to STARTING state:
191 assert(s3->get_state() == service_state_t::STARTING);
192 assert(s2->get_state() == service_state_t::STARTING);
193 assert(s1->get_state() == service_state_t::STARTING);
196 sset.process_queues();
197 assert(s3->get_state() == service_state_t::STARTING);
198 assert(s2->get_state() == service_state_t::STARTING);
199 assert(s1->get_state() == service_state_t::STARTED);
202 sset.process_queues();
203 assert(s3->get_state() == service_state_t::STARTING);
204 assert(s2->get_state() == service_state_t::STARTED);
205 assert(s1->get_state() == service_state_t::STARTED);
208 sset.process_queues();
209 assert(s3->get_state() == service_state_t::STARTED);
210 assert(s2->get_state() == service_state_t::STARTED);
211 assert(s1->get_state() == service_state_t::STARTED);
214 // Test that service pinned in start state is not stopped when its dependency stops.
219 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
220 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
221 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
222 s2->set_auto_restart(true);
223 sset.add_service(s1);
224 sset.add_service(s2);
225 sset.add_service(s3);
230 // Start all three services:
231 sset.start_service(s3);
233 assert(s3->get_state() == service_state_t::STARTED);
234 assert(s2->get_state() == service_state_t::STARTED);
235 assert(s1->get_state() == service_state_t::STARTED);
240 sset.process_queues();
242 // s3 should remain started due to pin:
243 assert(s3->get_state() == service_state_t::STARTED);
244 assert(s2->get_state() == service_state_t::STOPPING);
245 assert(s1->get_state() == service_state_t::STARTED);
247 // If we now unpin, s3 should stop:
249 sset.process_queues();
250 assert(s3->get_state() == service_state_t::STOPPED);
251 assert(s2->get_state() == service_state_t::STOPPED);
252 assert(s1->get_state() == service_state_t::STOPPED);
255 // Test that issuing a stop to a pinned-started service does not stop the service or its dependencies.
260 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
261 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
262 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
263 s2->set_auto_restart(true);
264 sset.add_service(s1);
265 sset.add_service(s2);
266 sset.add_service(s3);
271 // Start all three services:
272 sset.start_service(s3);
274 assert(s3->get_state() == service_state_t::STARTED);
275 assert(s2->get_state() == service_state_t::STARTED);
276 assert(s1->get_state() == service_state_t::STARTED);
280 sset.process_queues();
282 // s3 should remain started due to pin, s1 and s2 not released:
283 assert(s3->get_state() == service_state_t::STARTED);
284 assert(s2->get_state() == service_state_t::STARTED);
285 assert(s1->get_state() == service_state_t::STARTED);
288 // Test that a STOPPING dependency of a pinned service stops when pin is released, even if pinned
289 // service is activated:
294 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
295 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
296 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
297 s2->set_auto_restart(true);
298 sset.add_service(s1);
299 sset.add_service(s2);
300 sset.add_service(s3);
305 // Start all three services:
306 sset.start_service(s3);
308 assert(s3->get_state() == service_state_t::STARTED);
309 assert(s2->get_state() == service_state_t::STARTED);
310 assert(s1->get_state() == service_state_t::STARTED);
312 // Issue force stop to s2:
315 sset.process_queues();
317 // s3 should remain started due to pin, but s1 and s2 are released and go STOPPING:
318 assert(s3->get_state() == service_state_t::STARTED);
319 assert(s2->get_state() == service_state_t::STOPPING);
320 assert(s1->get_state() == service_state_t::STARTED);
322 // If we now issue start, s2 still needs to stop (due to force stop):
324 sset.process_queues();
326 assert(s3->get_state() == service_state_t::STARTED);
327 assert(s2->get_state() == service_state_t::STOPPING);
328 assert(s1->get_state() == service_state_t::STARTED);
330 // When we unpin, s2 should STOP; s3 must stop as a result; s1 is released and so also stops:
333 assert(s3->get_state() == service_state_t::STOPPED);
334 assert(s2->get_state() == service_state_t::STOPPED);
335 assert(s1->get_state() == service_state_t::STOPPED);
338 // Test that service pinned started is released when stop issued and stops when unpinned
343 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
344 sset.add_service(s1);
349 // Start the service:
350 sset.start_service(s1);
352 assert(s1->get_state() == service_state_t::STARTED);
354 // Issue forced stop:
357 sset.process_queues();
359 // s3 should remain started:
360 assert(s1->get_state() == service_state_t::STARTED);
362 // If we now unpin, s1 should stop:
364 sset.process_queues();
365 assert(s1->get_state() == service_state_t::STOPPED);
368 // Test 7: stopping a soft dependency doesn't cause the dependent to stop.
373 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
374 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
375 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, WAITS}});
376 sset.add_service(s1);
377 sset.add_service(s2);
378 sset.add_service(s3);
380 assert(sset.find_service("test-service-1") == s1);
381 assert(sset.find_service("test-service-2") == s2);
382 assert(sset.find_service("test-service-3") == s3);
384 // Start all three services:
385 sset.start_service(s3);
387 // Now stop s1, which should also force s2 but not s3 to stop:
388 sset.stop_service(s1);
390 assert(s3->get_state() == service_state_t::STARTED);
391 assert(s2->get_state() == service_state_t::STOPPED);
392 assert(s1->get_state() == service_state_t::STOPPED);
395 // Test 8: stopping a milestone dependency doesn't cause the dependent to stop
400 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
401 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
402 sset.add_service(s1);
403 sset.add_service(s2);
405 assert(sset.find_service("test-service-1") == s1);
406 assert(sset.find_service("test-service-2") == s2);
408 // Start the services:
409 sset.start_service(s2);
411 assert(s2->get_state() == service_state_t::STARTED);
412 assert(s1->get_state() == service_state_t::STARTED);
414 // Now stop s1, which should not stop s2:
415 sset.stop_service(s1);
417 assert(s2->get_state() == service_state_t::STARTED);
418 assert(s1->get_state() == service_state_t::STOPPED);
421 // Test 9: a failing milestone dependency causes the dependent to fail
426 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
427 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
428 sset.add_service(s1);
429 sset.add_service(s2);
431 assert(sset.find_service("test-service-1") == s1);
432 assert(sset.find_service("test-service-2") == s2);
434 // Start the services, but fail s1:
435 sset.start_service(s2);
437 assert(s1->get_state() == service_state_t::STARTING);
438 s1->failed_to_start();
439 sset.process_queues();
441 assert(s1->get_state() == service_state_t::STOPPED);
442 assert(s2->get_state() == service_state_t::STOPPED);
445 // Test 10: if start cancelled, remove from console queue
450 // Create s1 and s2. s2 depends on s1, and starts on the console.
451 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
452 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
453 service_flags_t s2_flags;
454 s2_flags.starts_on_console = true;
455 s2->set_flags(s2_flags);
456 sset.add_service(s1);
457 sset.add_service(s2);
459 // Create s3, which starts and runs on console:
460 test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {});
461 service_flags_t s3_flags;
462 s3_flags.starts_on_console = true;
463 s3_flags.runs_on_console = true;
464 sset.add_service(s3);
466 assert(sset.find_service("test-service-1") == s1);
467 assert(sset.find_service("test-service-2") == s2);
468 assert(sset.find_service("test-service-3") == s3);
470 // Start the s3 service, so it gets console:
471 sset.start_service(s3);
472 sset.process_queues();
474 sset.process_queues();
476 assert(! sset.is_queued_for_console(s3)); // should not be queued, because already has acquired
477 assert(sset.is_console_queue_empty());
479 // Start s2, which starts s1 as a dependency:
481 sset.start_service(s2);
482 sset.process_queues();
484 assert(s1->get_state() == service_state_t::STARTING);
485 assert(s2->get_state() == service_state_t::STARTING);
488 sset.process_queues();
490 // s2 should now be waiting for console:
491 assert(s1->get_state() == service_state_t::STARTED);
492 assert(s2->get_state() == service_state_t::STARTING);
493 assert(sset.is_queued_for_console(s2));
495 // stop s1, should stop s2, s2 should unqueue:
497 sset.process_queues();
499 assert(s1->get_state() == service_state_t::STOPPED);
500 assert(s2->get_state() == service_state_t::STOPPED);
501 assert(! sset.is_queued_for_console(s2));
504 // Test 11: if a milestone dependency doesn't start, dependent doesn't start.
509 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
510 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
512 sset.add_service(s1);
513 sset.add_service(s2);
515 assert(sset.find_service("test-service-1") == s1);
516 assert(sset.find_service("test-service-2") == s2);
518 // Request start of the s2 service:
519 sset.start_service(s2);
520 sset.process_queues();
522 assert(s1->get_state() == service_state_t::STARTING);
523 assert(s2->get_state() == service_state_t::STARTING);
526 sset.process_queues();
528 sset.process_queues();
530 assert(s1->get_state() == service_state_t::STOPPED);
531 assert(s2->get_state() == service_state_t::STOPPED);
534 // Test that active service count reaches 0 when stopping a service with different types of dependency
539 service_record *s4 = new service_record(&sset, "test-service-4", service_type_t::INTERNAL, {});
540 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {});
541 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {});
543 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL,
544 {{s2, WAITS}, {s3, REG}, {s4, MS}});
546 sset.add_service(s4);
547 sset.add_service(s3);
548 sset.add_service(s2);
549 sset.add_service(s1);
551 assert(sset.find_service("test-service-1") == s1);
552 assert(sset.find_service("test-service-2") == s2);
553 assert(sset.find_service("test-service-3") == s3);
554 assert(sset.find_service("test-service-4") == s4);
556 // Request start of the s2 service, should also start s1:
557 sset.start_service(s1);
558 sset.process_queues();
560 assert(s1->get_state() == service_state_t::STARTED);
561 assert(s2->get_state() == service_state_t::STARTED);
562 assert(s3->get_state() == service_state_t::STARTED);
563 assert(s4->get_state() == service_state_t::STARTED);
566 sset.process_queues();
568 assert(s1->get_state() == service_state_t::STOPPED);
569 assert(s2->get_state() == service_state_t::STOPPED);
570 assert(s3->get_state() == service_state_t::STOPPED);
571 assert(s4->get_state() == service_state_t::STOPPED);
573 assert(sset.count_active_services() == 0);
576 // Tests for "restart" functionality.
581 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
582 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, WAITS}});
583 test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, REG}});
585 sset.add_service(s1);
586 sset.add_service(s2);
587 sset.add_service(s3);
589 // Start all services via s3
590 sset.start_service(s3);
592 sset.process_queues();
594 sset.process_queues();
596 sset.process_queues();
598 assert(s3->get_state() == service_state_t::STARTED);
599 assert(s2->get_state() == service_state_t::STARTED);
600 assert(s1->get_state() == service_state_t::STARTED);
604 s1->add_listener(&tl);
608 sset.process_queues();
610 assert(s3->get_state() == service_state_t::STARTED);
611 assert(s2->get_state() == service_state_t::STARTED);
612 assert(s1->get_state() == service_state_t::STARTING);
613 assert(! tl.got_started);
616 sset.process_queues();
618 assert(s3->get_state() == service_state_t::STARTED);
619 assert(s2->get_state() == service_state_t::STARTED);
620 assert(s1->get_state() == service_state_t::STARTED);
621 assert(tl.got_started);
624 // Make sure a service only restarts once (i.e. restart flag doesn't get stuck)
629 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
630 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, WAITS}});
632 sset.add_service(s1);
633 sset.add_service(s2);
635 // Start all services via s2
636 sset.start_service(s2);
638 sset.process_queues();
640 sset.process_queues();
642 assert(s2->get_state() == service_state_t::STARTED);
643 assert(s1->get_state() == service_state_t::STARTED);
647 sset.process_queues();
649 assert(s2->get_state() == service_state_t::STARTED);
650 assert(s1->get_state() == service_state_t::STARTING);
653 sset.process_queues();
655 assert(s2->get_state() == service_state_t::STARTED);
656 assert(s1->get_state() == service_state_t::STARTED);
658 // Ok, we restarted s1. Now stop it:
661 sset.process_queues();
663 assert(s2->get_state() == service_state_t::STARTED);
664 assert(s1->get_state() == service_state_t::STOPPED); // didn't restart
667 // Test that restart can be cancelled if dependents stop
672 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
673 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, WAITS}});
675 sset.add_service(s1);
676 sset.add_service(s2);
678 // Start all services via s2
679 sset.start_service(s2);
681 sset.process_queues();
683 sset.process_queues();
685 assert(s2->get_state() == service_state_t::STARTED);
686 assert(s1->get_state() == service_state_t::STARTED);
689 s1->add_listener(&tl);
691 s1->auto_stop = false;
695 sset.process_queues();
697 assert(s1->get_state() == service_state_t::STOPPING);
700 sset.process_queues();
702 sset.process_queues();
704 assert(s2->get_state() == service_state_t::STOPPED);
705 assert(s1->get_state() == service_state_t::STOPPED);
707 assert(tl.start_cancelled);
708 assert(! tl.got_started);
712 #define RUN_TEST(name, spacing) \
713 std::cout << #name "..." spacing << std::flush; \
715 std::cout << "PASSED" << std::endl;
717 int main(int argc, char **argv)
719 RUN_TEST(test1, " ");
720 RUN_TEST(test2, " ");
721 RUN_TEST(test3, " ");
722 RUN_TEST(test4, " ");
723 RUN_TEST(test5, " ");
724 RUN_TEST(test_pin1, " ");
725 RUN_TEST(test_pin2, " ");
726 RUN_TEST(test_pin3, " ");
727 RUN_TEST(test_pin4, " ");
728 RUN_TEST(test7, " ");
729 RUN_TEST(test8, " ");
730 RUN_TEST(test9, " ");
731 RUN_TEST(test10, " ");
732 RUN_TEST(test11, " ");
733 RUN_TEST(test12, " ");
734 RUN_TEST(test13, " ");
735 RUN_TEST(test14, " ");
736 RUN_TEST(test15, " ");