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 service pinned started is released when stop issued and stops when unpinned
273 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
274 sset.add_service(s1);
279 // Start the service:
280 sset.start_service(s1);
282 assert(s1->get_state() == service_state_t::STARTED);
286 sset.process_queues();
288 // s3 should remain started:
289 assert(s1->get_state() == service_state_t::STARTED);
291 // If we now unpin, s1 should stop:
293 sset.process_queues();
294 assert(s1->get_state() == service_state_t::STOPPED);
297 // Test 7: stopping a soft dependency doesn't cause the dependent to stop.
302 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
303 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
304 service_record *s3 = new service_record(&sset, "test-service-3", service_type_t::INTERNAL, {{s2, WAITS}});
305 sset.add_service(s1);
306 sset.add_service(s2);
307 sset.add_service(s3);
309 assert(sset.find_service("test-service-1") == s1);
310 assert(sset.find_service("test-service-2") == s2);
311 assert(sset.find_service("test-service-3") == s3);
313 // Start all three services:
314 sset.start_service(s3);
316 // Now stop s1, which should also force s2 but not s3 to stop:
317 sset.stop_service(s1);
319 assert(s3->get_state() == service_state_t::STARTED);
320 assert(s2->get_state() == service_state_t::STOPPED);
321 assert(s1->get_state() == service_state_t::STOPPED);
324 // Test 8: stopping a milestone dependency doesn't cause the dependent to stop
329 service_record *s1 = new service_record(&sset, "test-service-1", service_type_t::INTERNAL, {});
330 service_record *s2 = new service_record(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
331 sset.add_service(s1);
332 sset.add_service(s2);
334 assert(sset.find_service("test-service-1") == s1);
335 assert(sset.find_service("test-service-2") == s2);
337 // Start the services:
338 sset.start_service(s2);
340 assert(s2->get_state() == service_state_t::STARTED);
341 assert(s1->get_state() == service_state_t::STARTED);
343 // Now stop s1, which should not stop s2:
344 sset.stop_service(s1);
346 assert(s2->get_state() == service_state_t::STARTED);
347 assert(s1->get_state() == service_state_t::STOPPED);
350 // Test 9: a failing milestone dependency causes the dependent to fail
355 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
356 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, MS}});
357 sset.add_service(s1);
358 sset.add_service(s2);
360 assert(sset.find_service("test-service-1") == s1);
361 assert(sset.find_service("test-service-2") == s2);
363 // Start the services, but fail s1:
364 sset.start_service(s2);
366 assert(s1->get_state() == service_state_t::STARTING);
367 s1->failed_to_start();
368 sset.process_queues();
370 assert(s2->get_state() == service_state_t::STOPPED);
373 // Test 10: if start cancelled, remove from console queue
378 // Create s1 and s2. s2 depends on s1, and starts on the console.
379 test_service *s1 = new test_service(&sset, "test-service-1", service_type_t::INTERNAL, {});
380 test_service *s2 = new test_service(&sset, "test-service-2", service_type_t::INTERNAL, {{s1, REG}});
381 onstart_flags_t s2_flags;
382 s2_flags.starts_on_console = true;
383 s2->set_flags(s2_flags);
384 sset.add_service(s1);
385 sset.add_service(s2);
387 // Create s3, which starts and runs on console:
388 test_service *s3 = new test_service(&sset, "test-service-3", service_type_t::INTERNAL, {});
389 onstart_flags_t s3_flags;
390 s3_flags.starts_on_console = true;
391 s3_flags.runs_on_console = true;
392 sset.add_service(s3);
394 assert(sset.find_service("test-service-1") == s1);
395 assert(sset.find_service("test-service-2") == s2);
396 assert(sset.find_service("test-service-3") == s3);
398 // Start the s3 service, so it gets console:
399 sset.start_service(s3);
400 sset.process_queues();
402 sset.process_queues();
404 assert(! sset.is_queued_for_console(s3)); // should not be queued, because already has acquired
405 assert(sset.is_console_queue_empty());
407 // Start s2, which starts s1 as a dependency:
409 sset.start_service(s2);
410 sset.process_queues();
412 assert(s1->get_state() == service_state_t::STARTING);
413 assert(s2->get_state() == service_state_t::STARTING);
416 sset.process_queues();
418 // s2 should now be waiting for console:
419 assert(s1->get_state() == service_state_t::STARTED);
420 assert(s2->get_state() == service_state_t::STARTING);
421 assert(sset.is_queued_for_console(s2));
423 // stop s1, should stop s2, s2 should unqueue:
425 sset.process_queues();
427 assert(s1->get_state() == service_state_t::STOPPED);
428 assert(s2->get_state() == service_state_t::STOPPED);
429 assert(! sset.is_queued_for_console(s2));
432 #define RUN_TEST(name) \
433 std::cout << #name "... "; \
435 std::cout << "PASSED" << std::endl;
437 int main(int argc, char **argv)