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 // Test 1: starting a service starts dependencies; stopping the service releases and
12 // stops dependencies.
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}});
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);
28 // s3 depends on s2, which depends on s1. So starting s3 should start all three services:
29 sset.start_service(s3);
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);
35 // stopping s3 should release the other two services:
36 sset.stop_service(s3);
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);
43 // Test 2: Multiple dependents will hold a dependency active if one of the dependents is
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}});
58 // s3 depends on s2, which depends on s1. Similarly with s4. After starting both, all services
60 sset.start_service(s3);
61 sset.start_service(s4);
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);
68 // after stopping s3, s4 should hold the other two services:
69 sset.stop_service(s3);
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);
76 // Now if we stop s4, s2 and s1 should also be released:
77 sset.stop_service(s4);
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);
85 // Test 3: stopping a dependency causes its dependents to stop.
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}});
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);
101 // Start all three services:
102 sset.start_service(s3);
104 // Now stop s1, which should also force s2 and s3 to stop:
105 sset.stop_service(s1);
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);
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.
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);
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 // Also explicitly activate s2:
134 sset.start_service(s2);
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);
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);
145 // Test 5: test that services which do not start immediately correctly chain start of
146 // dependent services.
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}});
155 sset.add_service(s1);
156 sset.add_service(s2);
157 sset.add_service(s3);
159 sset.start_service(s3);
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);
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);
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);
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);
185 // Test that service pinned in start state is not stopped when its dependency stops.
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);
201 // Start all three services:
202 sset.start_service(s3);
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);
211 sset.process_queues();
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);
218 // If we now unpin, s3 should stop:
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);
226 // Test that STOPPING dependency of pinned service returns to STARTED when the pinned service has
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);
243 // Start all three services:
244 sset.start_service(s3);
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);
252 sset.process_queues();
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);
259 // If we now issue start, STOPPING should revert to STARTED:
261 sset.process_queues();
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);
268 // Test that a STOPPING dependency of a pinned service stops when pin is released, even if pinned
269 // service is activated:
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);
285 // Start all three services:
286 sset.start_service(s3);
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);
292 // Issue force stop to s2:
295 sset.process_queues();
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);
302 // If we now issue start, s2 still needs to stop (due to force stop):
304 sset.process_queues();
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);
310 // When we unpin, s2 should STOP; s3 must stop as a result; s1 is released and so also stops:
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);
318 // Test that service pinned started is released when stop issued and stops when unpinned
323 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
324 sset.add_service(s1);
329 // Start the service:
330 sset.start_service(s1);
332 assert(s1->get_state() == service_state_t::STARTED);
336 sset.process_queues();
338 // s3 should remain started:
339 assert(s1->get_state() == service_state_t::STARTED);
341 // If we now unpin, s1 should stop:
343 sset.process_queues();
344 assert(s1->get_state() == service_state_t::STOPPED);
347 // Test 7: stopping a soft dependency doesn't cause the dependent to stop.
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);
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);
363 // Start all three services:
364 sset.start_service(s3);
366 // Now stop s1, which should also force s2 but not s3 to stop:
367 sset.stop_service(s1);
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);
374 // Test 8: stopping a milestone dependency doesn't cause the dependent to stop
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);
384 assert(sset.find_service("test-service-1") == s1);
385 assert(sset.find_service("test-service-2") == s2);
387 // Start the services:
388 sset.start_service(s2);
390 assert(s2->get_state() == service_state_t::STARTED);
391 assert(s1->get_state() == service_state_t::STARTED);
393 // Now stop s1, which should not stop s2:
394 sset.stop_service(s1);
396 assert(s2->get_state() == service_state_t::STARTED);
397 assert(s1->get_state() == service_state_t::STOPPED);
400 // Test 9: a failing milestone dependency causes the dependent to fail
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);
410 assert(sset.find_service("test-service-1") == s1);
411 assert(sset.find_service("test-service-2") == s2);
413 // Start the services, but fail s1:
414 sset.start_service(s2);
416 assert(s1->get_state() == service_state_t::STARTING);
417 s1->failed_to_start();
418 sset.process_queues();
420 assert(s2->get_state() == service_state_t::STOPPED);
423 // Test 10: if start cancelled, remove from console queue
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);
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);
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);
448 // Start the s3 service, so it gets console:
449 sset.start_service(s3);
450 sset.process_queues();
452 sset.process_queues();
454 assert(! sset.is_queued_for_console(s3)); // should not be queued, because already has acquired
455 assert(sset.is_console_queue_empty());
457 // Start s2, which starts s1 as a dependency:
459 sset.start_service(s2);
460 sset.process_queues();
462 assert(s1->get_state() == service_state_t::STARTING);
463 assert(s2->get_state() == service_state_t::STARTING);
466 sset.process_queues();
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));
473 // stop s1, should stop s2, s2 should unqueue:
475 sset.process_queues();
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));
482 // Test 11: if a milestone dependency doesn't start, dependent doesn't start.
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}});
490 sset.add_service(s1);
491 sset.add_service(s2);
493 assert(sset.find_service("test-service-1") == s1);
494 assert(sset.find_service("test-service-2") == s2);
496 // Request start of the s2 service:
497 sset.start_service(s2);
498 sset.process_queues();
500 assert(s1->get_state() == service_state_t::STARTING);
501 assert(s2->get_state() == service_state_t::STARTING);
504 sset.process_queues();
506 sset.process_queues();
508 assert(s1->get_state() == service_state_t::STOPPED);
509 assert(s2->get_state() == service_state_t::STOPPED);
512 // Test that active service count reaches 0 when stopping a service with different types of dependency
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, {});
521 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL,
522 {{s2, WAITS}, {s3, REG}, {s4, MS}});
524 sset.add_service(s4);
525 sset.add_service(s3);
526 sset.add_service(s2);
527 sset.add_service(s1);
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);
534 // Request start of the s2 service, should also start s1:
535 sset.start_service(s1);
536 sset.process_queues();
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);
544 sset.process_queues();
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);
551 assert(sset.count_active_services() == 0);
554 #define RUN_TEST(name) \
555 std::cout << #name "... "; \
557 std::cout << "PASSED" << std::endl;
559 int main(int argc, char **argv)