blob: d26f414a67c906fb932310779dec8638529308db [file] [log] [blame]
Austin Schuh59398d32023-05-03 08:10:55 -07001#include <chrono>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07002#include <csignal>
Tyler Chatowa79419d2020-08-12 20:12:11 -07003#include <future>
4#include <thread>
5
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "gtest/gtest.h"
7
Tyler Chatowa79419d2020-08-12 20:12:11 -07008#include "aos/events/ping_generated.h"
9#include "aos/events/pong_generated.h"
Austin Schuh59398d32023-05-03 08:10:55 -070010#include "aos/ipc_lib/event.h"
James Kuszmaul293b2172021-11-10 16:20:48 -080011#include "aos/network/team_number.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070012#include "aos/starter/starter_rpc_lib.h"
13#include "aos/starter/starterd_lib.h"
Austin Schuh373f1762021-06-02 21:07:09 -070014#include "aos/testing/path.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070015#include "aos/testing/tmpdir.h"
Austin Schuhfc0bca42021-12-31 21:49:41 -080016#include "aos/util/file.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070017
Austin Schuh373f1762021-06-02 21:07:09 -070018using aos::testing::ArtifactPath;
19
Stephan Pleinesf63bde82024-01-13 15:59:33 -080020namespace aos::starter {
Austin Schuh5f79a5a2021-10-12 17:46:50 -070021
Austin Schuh83cbb1e2023-06-23 12:59:02 -070022class ThreadedStarterRunner {
23 public:
24 ThreadedStarterRunner(Starter *starter)
25 : my_thread_([this, starter]() {
26 starter->event_loop()->OnRun([this]() { event_.Set(); });
27 starter->Run();
28 }) {
29 event_.Wait();
30 }
31
32 ~ThreadedStarterRunner() { my_thread_.join(); }
33
34 private:
35 aos::Event event_;
36 std::thread my_thread_;
37};
38
James Kuszmaul293b2172021-11-10 16:20:48 -080039class StarterdTest : public ::testing::Test {
40 public:
Austin Schuh59398d32023-05-03 08:10:55 -070041 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080042 // Nuke the shm dir:
Austin Schuh59398d32023-05-03 08:10:55 -070043 aos::util::UnlinkRecursive(FLAGS_shm_base);
James Kuszmaul293b2172021-11-10 16:20:48 -080044 }
45
46 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070047 void SetupStarterCleanup(aos::starter::Starter *starter) {
48 starter->event_loop()
49 ->AddTimer([this, starter]() {
50 if (test_done_) {
51 starter->Cleanup();
52 }
53 })
Philipp Schradera6712522023-07-05 20:25:11 -070054 ->Schedule(starter->event_loop()->monotonic_now(),
55 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070056 }
57
James Kuszmaul293b2172021-11-10 16:20:48 -080058 gflags::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070059 // Used to track when the test completes so that we can clean up the starter
60 // in its thread.
61 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080062};
63
64struct TestParams {
65 std::string config;
66 std::string hostname;
67};
68
69class StarterdConfigParamTest
70 : public StarterdTest,
71 public ::testing::WithParamInterface<TestParams> {};
72
73TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
74 gflags::FlagSaver flag_saver;
75 FLAGS_override_hostname = GetParam().hostname;
76 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070077
78 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
79 aos::configuration::ReadConfig(config_file);
80
Tyler Chatowa79419d2020-08-12 20:12:11 -070081 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080082 &config.message(),
83 absl::StrFormat(
84 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070085 {
86 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070087 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080088 "nodes": ["pi1"],
89 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070090 },
91 {
92 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070093 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080094 "nodes": ["pi1"],
95 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070096 }
97 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -070098 ArtifactPath("aos/events/ping"), FLAGS_shm_base, config_file,
99 GetParam().hostname, ArtifactPath("aos/events/pong"), FLAGS_shm_base,
James Kuszmaul293b2172021-11-10 16:20:48 -0800100 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700101
102 const aos::Configuration *config_msg = &new_config.message();
103
104 // Set up starter with config file
105 aos::starter::Starter starter(config_msg);
106
107 // Create an event loop to watch for ping messages, verifying it actually
108 // started.
109 aos::ShmEventLoop watcher_loop(config_msg);
110 watcher_loop.SkipAosLog();
111
James Kuszmaul293b2172021-11-10 16:20:48 -0800112 aos::ShmEventLoop client_loop(config_msg);
113 client_loop.SkipAosLog();
114 StarterClient client(&client_loop);
115 client.SetTimeoutHandler(
116 []() { FAIL() << ": Command should not have timed out."; });
117 bool success = false;
118 client.SetSuccessHandler([&success, &client_loop]() {
119 client_loop.Exit();
120 success = true;
121 });
122
Tyler Chatowa79419d2020-08-12 20:12:11 -0700123 watcher_loop
124 .AddTimer([&watcher_loop] {
125 watcher_loop.Exit();
126 FAIL();
127 })
Philipp Schradera6712522023-07-05 20:25:11 -0700128 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700129
James Kuszmaul293b2172021-11-10 16:20:48 -0800130 std::atomic<int> test_stage = 0;
131 // Watch on the client loop since we need to interact with the StarterClient.
132 client_loop.MakeWatcher("/test", [&test_stage, &client,
133 &client_loop](const aos::examples::Ping &) {
134 switch (test_stage) {
135 case 1: {
136 test_stage = 2;
137 break;
138 }
139 case 2: {
140 {
141 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
142 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700143 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800144 test_stage = 3;
145 break;
146 }
147 }
148 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700149
150 watcher_loop.MakeWatcher(
151 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
152 const aos::starter::ApplicationStatus *app_status =
153 FindApplicationStatus(status, "ping");
154 if (app_status == nullptr) {
155 return;
156 }
157
158 switch (test_stage) {
159 case 0: {
160 if (app_status->has_state() &&
161 app_status->state() == aos::starter::State::RUNNING) {
162 test_stage = 1;
163 }
164 break;
165 }
166
167 case 3: {
168 if (app_status->has_state() &&
169 app_status->state() == aos::starter::State::STOPPED) {
170 watcher_loop.Exit();
171 SUCCEED();
172 }
173 break;
174 }
175 }
176 });
177
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700178 SetupStarterCleanup(&starter);
179
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700180 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700181
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700182 aos::Event event;
183 client_loop.OnRun([&event]() { event.Set(); });
184 std::thread client_thread([&client_loop] { client_loop.Run(); });
185 event.Wait();
Austin Schuh59398d32023-05-03 08:10:55 -0700186
187 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700188 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800189 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700190 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700191}
192
James Kuszmaul293b2172021-11-10 16:20:48 -0800193INSTANTIATE_TEST_SUITE_P(
194 StarterdConfigParamTest, StarterdConfigParamTest,
195 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
196 TestParams{"aos/starter/multinode_pingpong_config.json",
197 "pi1"}));
198
199TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700200 const std::string config_file =
201 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700202
203 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
204 aos::configuration::ReadConfig(config_file);
205
Tyler Chatowa79419d2020-08-12 20:12:11 -0700206 auto new_config = aos::configuration::MergeWithConfig(
207 &config.message(), absl::StrFormat(
208 R"({"applications": [
209 {
210 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700211 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800212 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700213 },
214 {
215 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700216 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800217 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700218 }
219 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700220 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
221 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700222
223 const aos::Configuration *config_msg = &new_config.message();
224
225 // Set up starter with config file
226 aos::starter::Starter starter(config_msg);
227
228 // Create an event loop to watch for ping messages, verifying it actually
229 // started.
230 aos::ShmEventLoop watcher_loop(config_msg);
231 watcher_loop.SkipAosLog();
232
233 watcher_loop
234 .AddTimer([&watcher_loop] {
235 watcher_loop.Exit();
236 FAIL();
237 })
Philipp Schradera6712522023-07-05 20:25:11 -0700238 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700239
240 int test_stage = 0;
241 uint64_t id;
242
243 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
244 &id](const aos::starter::Status &status) {
245 const aos::starter::ApplicationStatus *app_status =
246 FindApplicationStatus(status, "ping");
247 if (app_status == nullptr) {
248 return;
249 }
250
251 switch (test_stage) {
252 case 0: {
253 if (app_status->has_state() &&
254 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700255 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700256 test_stage = 1;
257 ASSERT_TRUE(app_status->has_pid());
258 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
259 ASSERT_TRUE(app_status->has_id());
260 id = app_status->id();
261 }
262 break;
263 }
264
265 case 1: {
266 if (app_status->has_state() &&
267 app_status->state() == aos::starter::State::RUNNING &&
268 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700269 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700270 watcher_loop.Exit();
271 SUCCEED();
272 }
273 break;
274 }
275 }
276 });
277
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700278 SetupStarterCleanup(&starter);
279
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700280 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700281 watcher_loop.Run();
282
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700283 test_done_ = true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700284}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700285
James Kuszmaul293b2172021-11-10 16:20:48 -0800286TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700287 const std::string config_file =
288 ArtifactPath("aos/events/pingpong_config.json");
289
290 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
291 aos::configuration::ReadConfig(config_file);
292
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700293 auto new_config = aos::configuration::MergeWithConfig(
294 &config.message(), absl::StrFormat(
295 R"({"applications": [
296 {
297 "name": "ping",
298 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800299 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700300 "autostart": false
301 },
302 {
303 "name": "pong",
304 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800305 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700306 }
307 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700308 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
309 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700310
311 const aos::Configuration *config_msg = &new_config.message();
312
313 // Set up starter with config file
314 aos::starter::Starter starter(config_msg);
315
316 // Create an event loop to watch for the application starting up.
317 aos::ShmEventLoop watcher_loop(config_msg);
318 watcher_loop.SkipAosLog();
319
320 watcher_loop
321 .AddTimer([&watcher_loop] {
322 watcher_loop.Exit();
323 FAIL();
324 })
Philipp Schradera6712522023-07-05 20:25:11 -0700325 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700326
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700327 int pong_running_count = 0;
328 watcher_loop.MakeWatcher("/aos", [&watcher_loop, &pong_running_count](
James Kuszmaul6295a642022-03-22 15:23:59 -0700329 const aos::starter::Status &status) {
330 const aos::starter::ApplicationStatus *ping_app_status =
331 FindApplicationStatus(status, "ping");
332 const aos::starter::ApplicationStatus *pong_app_status =
333 FindApplicationStatus(status, "pong");
334 if (ping_app_status == nullptr || pong_app_status == nullptr) {
335 return;
336 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700337
James Kuszmaul6295a642022-03-22 15:23:59 -0700338 if (ping_app_status->has_state() &&
339 ping_app_status->state() != aos::starter::State::STOPPED) {
340 watcher_loop.Exit();
341 FAIL();
342 }
343 if (pong_app_status->has_state() &&
344 pong_app_status->state() == aos::starter::State::RUNNING) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700345 ++pong_running_count;
346 // Sometimes if the timing for everything is *just* off, then the
347 // process_info will say that the process name is "starter_test" because
348 // it grabbed the name after the fork() but before the execvp(). To
349 // protect against that, wait an extra cycle. If things aren't fixed by
350 // the second cycle, then that is a problem.
James Kuszmaul37a56af2023-07-29 15:15:16 -0700351 if (pong_running_count < 3) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700352 return;
353 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700354 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700355 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
356 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700357 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700358 ASSERT_TRUE(pong_app_status->process_info()->has_cpu_usage());
359 ASSERT_LE(0.0, pong_app_status->process_info()->cpu_usage());
James Kuszmaul8544c492023-07-31 15:00:38 -0700360 ASSERT_TRUE(pong_app_status->has_has_active_timing_report());
361 ASSERT_TRUE(pong_app_status->has_active_timing_report());
James Kuszmaul6295a642022-03-22 15:23:59 -0700362 watcher_loop.Exit();
363 SUCCEED();
364 }
365 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700366
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700367 SetupStarterCleanup(&starter);
368
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700369 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700370 watcher_loop.Run();
371
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700372 test_done_ = true;
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700373}
374
James Kuszmaule7c7e582022-01-07 18:50:01 -0800375// Tests that starterd respects autorestart.
376TEST_F(StarterdTest, DeathNoRestartTest) {
377 const std::string config_file =
378 ArtifactPath("aos/events/pingpong_config.json");
379
380 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
381 aos::configuration::ReadConfig(config_file);
382
James Kuszmaule7c7e582022-01-07 18:50:01 -0800383 auto new_config = aos::configuration::MergeWithConfig(
384 &config.message(), absl::StrFormat(
385 R"({"applications": [
386 {
387 "name": "ping",
388 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700389 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800390 "autorestart": false
391 },
392 {
393 "name": "pong",
394 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700395 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800396 }
397 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700398 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
399 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800400
401 const aos::Configuration *config_msg = &new_config.message();
402
403 // Set up starter with config file
404 aos::starter::Starter starter(config_msg);
405
406 // Create an event loop to watch for the Status message to watch the state
407 // transitions.
408 aos::ShmEventLoop watcher_loop(config_msg);
409 watcher_loop.SkipAosLog();
410
411 watcher_loop
412 .AddTimer([&watcher_loop] {
413 watcher_loop.Exit();
414 SUCCEED();
415 })
Philipp Schradera6712522023-07-05 20:25:11 -0700416 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800417
418 int test_stage = 0;
419 uint64_t id;
420
421 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
422 &id](const aos::starter::Status &status) {
423 const aos::starter::ApplicationStatus *app_status =
424 FindApplicationStatus(status, "ping");
425 if (app_status == nullptr) {
426 return;
427 }
428
429 switch (test_stage) {
430 case 0: {
431 if (app_status->has_state() &&
432 app_status->state() == aos::starter::State::RUNNING) {
433 LOG(INFO) << "Ping is running";
434 test_stage = 1;
435 ASSERT_TRUE(app_status->has_pid());
436 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
437 ASSERT_TRUE(app_status->has_id());
438 id = app_status->id();
439 }
440 break;
441 }
442
443 case 1: {
444 if (app_status->has_state() &&
445 app_status->state() == aos::starter::State::RUNNING &&
446 app_status->has_id() && app_status->id() != id) {
447 LOG(INFO) << "Ping restarted, it shouldn't...";
448 watcher_loop.Exit();
449 FAIL();
450 }
451 break;
452 }
453 }
454 });
455
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700456 SetupStarterCleanup(&starter);
457
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700458 ThreadedStarterRunner starterd_thread(&starter);
James Kuszmaule7c7e582022-01-07 18:50:01 -0800459 watcher_loop.Run();
460
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700461 test_done_ = true;
James Kuszmaule7c7e582022-01-07 18:50:01 -0800462}
463
Austin Schuh59398d32023-05-03 08:10:55 -0700464TEST_F(StarterdTest, StarterChainTest) {
465 // This test was written in response to a bug that was found
466 // in StarterClient::Succeed. The bug caused the timeout handler
467 // to be reset after the success handler was called.
468 // the bug has been fixed, and this test will ensure it does
469 // not regress.
470 const std::string config_file =
471 ArtifactPath("aos/events/pingpong_config.json");
472 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
473 aos::configuration::ReadConfig(config_file);
474 auto new_config = aos::configuration::MergeWithConfig(
475 &config.message(), absl::StrFormat(
476 R"({"applications": [
477 {
478 "name": "ping",
479 "executable_name": "%s",
480 "args": ["--shm_base", "%s"],
481 "autorestart": false
482 },
483 {
484 "name": "pong",
485 "executable_name": "%s",
486 "args": ["--shm_base", "%s"]
487 }
488 ]})",
489 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
490 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
491
492 const aos::Configuration *config_msg = &new_config.message();
493 // Set up starter with config file
494 aos::starter::Starter starter(config_msg);
495 aos::ShmEventLoop client_loop(config_msg);
496 client_loop.SkipAosLog();
497 StarterClient client(&client_loop);
498 bool success = false;
499 auto client_node = client_loop.node();
500
501 // limit the amount of time we will wait for the test to finish.
502 client_loop
503 .AddTimer([&client_loop] {
504 client_loop.Exit();
505 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
506 "The chain of stages defined below did not complete "
507 "within the time limit.";
508 })
Philipp Schradera6712522023-07-05 20:25:11 -0700509 ->Schedule(client_loop.monotonic_now() + std::chrono::seconds(20));
Austin Schuh59398d32023-05-03 08:10:55 -0700510
511 // variables have been defined, here we define the body of the test.
512 // We want stage1 to succeed, triggering stage2.
513 // We want stage2 to timeout, triggering stage3.
514
515 auto stage3 = [&client_loop, &success]() {
516 LOG(INFO) << "Begin stage3.";
517 SUCCEED();
518 success = true;
519 client_loop.Exit();
520 LOG(INFO) << "End stage3.";
521 };
522 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
523 LOG(INFO) << "Begin stage2";
524 test_done_ = true; // trigger `starter` to exit.
525
526 // wait for the starter event loop to close, so we can
527 // intentionally trigger a timeout.
528 int attempts = 0;
529 while (starter.event_loop()->is_running()) {
530 ++attempts;
531 if (attempts > 5) {
532 LOG(INFO) << "Timeout while waiting for starter to exit";
533 return;
534 }
535 LOG(INFO) << "Waiting for starter to close.";
536 std::this_thread::sleep_for(std::chrono::seconds(1));
537 }
538 client.SetTimeoutHandler(stage3);
539 client.SetSuccessHandler([]() {
540 LOG(INFO) << "stage3 success handler called.";
541 FAIL() << ": Command should not have succeeded here.";
542 });
543 // we want this command to timeout
544 client.SendCommands({{Command::START, "ping", {client_node}}},
545 std::chrono::seconds(5));
546 LOG(INFO) << "End stage2";
547 };
548 auto stage1 = [&client, &client_node, &stage2] {
549 LOG(INFO) << "Begin stage1";
550 client.SetTimeoutHandler(
551 []() { FAIL() << ": Command should not have timed out."; });
552 client.SetSuccessHandler(stage2);
553 client.SendCommands({{Command::STOP, "ping", {client_node}}},
554 std::chrono::seconds(5));
555 LOG(INFO) << "End stage1";
556 };
557 // start the test body
Philipp Schradera6712522023-07-05 20:25:11 -0700558 client_loop.AddTimer(stage1)->Schedule(client_loop.monotonic_now() +
559 std::chrono::milliseconds(1));
Austin Schuh59398d32023-05-03 08:10:55 -0700560
561 // prepare the cleanup for starter. This will finish when we call
562 // `test_done_ = true;`.
563 SetupStarterCleanup(&starter);
564
565 // run `starter.Run()` in a thread to simulate it running on
566 // another process.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700567 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh59398d32023-05-03 08:10:55 -0700568
Austin Schuh59398d32023-05-03 08:10:55 -0700569 client_loop.Run();
570 EXPECT_TRUE(success);
571 ASSERT_FALSE(starter.event_loop()->is_running());
Austin Schuh59398d32023-05-03 08:10:55 -0700572}
573
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800574} // namespace aos::starter