blob: fcbf287ac8579ff0b9048ec17f13e80f7a2f7501 [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
Austin Schuh5f79a5a2021-10-12 17:46:50 -070020namespace aos {
21namespace starter {
22
James Kuszmaul293b2172021-11-10 16:20:48 -080023class StarterdTest : public ::testing::Test {
24 public:
Austin Schuh59398d32023-05-03 08:10:55 -070025 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080026 // Nuke the shm dir:
Austin Schuh59398d32023-05-03 08:10:55 -070027 aos::util::UnlinkRecursive(FLAGS_shm_base);
James Kuszmaul293b2172021-11-10 16:20:48 -080028 }
29
30 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070031 void SetupStarterCleanup(aos::starter::Starter *starter) {
32 starter->event_loop()
33 ->AddTimer([this, starter]() {
34 if (test_done_) {
35 starter->Cleanup();
36 }
37 })
Philipp Schradera6712522023-07-05 20:25:11 -070038 ->Schedule(starter->event_loop()->monotonic_now(),
39 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070040 }
41
James Kuszmaul293b2172021-11-10 16:20:48 -080042 gflags::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070043 // Used to track when the test completes so that we can clean up the starter
44 // in its thread.
45 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080046};
47
48struct TestParams {
49 std::string config;
50 std::string hostname;
51};
52
53class StarterdConfigParamTest
54 : public StarterdTest,
55 public ::testing::WithParamInterface<TestParams> {};
56
57TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
58 gflags::FlagSaver flag_saver;
59 FLAGS_override_hostname = GetParam().hostname;
60 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070061
62 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
63 aos::configuration::ReadConfig(config_file);
64
Tyler Chatowa79419d2020-08-12 20:12:11 -070065 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080066 &config.message(),
67 absl::StrFormat(
68 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070069 {
70 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070071 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080072 "nodes": ["pi1"],
73 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070074 },
75 {
76 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070077 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080078 "nodes": ["pi1"],
79 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070080 }
81 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -070082 ArtifactPath("aos/events/ping"), FLAGS_shm_base, config_file,
83 GetParam().hostname, ArtifactPath("aos/events/pong"), FLAGS_shm_base,
James Kuszmaul293b2172021-11-10 16:20:48 -080084 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -070085
86 const aos::Configuration *config_msg = &new_config.message();
87
88 // Set up starter with config file
89 aos::starter::Starter starter(config_msg);
90
91 // Create an event loop to watch for ping messages, verifying it actually
92 // started.
93 aos::ShmEventLoop watcher_loop(config_msg);
94 watcher_loop.SkipAosLog();
95
James Kuszmaul293b2172021-11-10 16:20:48 -080096 aos::ShmEventLoop client_loop(config_msg);
97 client_loop.SkipAosLog();
98 StarterClient client(&client_loop);
99 client.SetTimeoutHandler(
100 []() { FAIL() << ": Command should not have timed out."; });
101 bool success = false;
102 client.SetSuccessHandler([&success, &client_loop]() {
103 client_loop.Exit();
104 success = true;
105 });
106
Tyler Chatowa79419d2020-08-12 20:12:11 -0700107 watcher_loop
108 .AddTimer([&watcher_loop] {
109 watcher_loop.Exit();
110 FAIL();
111 })
Philipp Schradera6712522023-07-05 20:25:11 -0700112 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700113
James Kuszmaul293b2172021-11-10 16:20:48 -0800114 std::atomic<int> test_stage = 0;
115 // Watch on the client loop since we need to interact with the StarterClient.
116 client_loop.MakeWatcher("/test", [&test_stage, &client,
117 &client_loop](const aos::examples::Ping &) {
118 switch (test_stage) {
119 case 1: {
120 test_stage = 2;
121 break;
122 }
123 case 2: {
124 {
125 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
126 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700127 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800128 test_stage = 3;
129 break;
130 }
131 }
132 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700133
134 watcher_loop.MakeWatcher(
135 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
136 const aos::starter::ApplicationStatus *app_status =
137 FindApplicationStatus(status, "ping");
138 if (app_status == nullptr) {
139 return;
140 }
141
142 switch (test_stage) {
143 case 0: {
144 if (app_status->has_state() &&
145 app_status->state() == aos::starter::State::RUNNING) {
146 test_stage = 1;
147 }
148 break;
149 }
150
151 case 3: {
152 if (app_status->has_state() &&
153 app_status->state() == aos::starter::State::STOPPED) {
154 watcher_loop.Exit();
155 SUCCEED();
156 }
157 break;
158 }
159 }
160 });
161
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700162 SetupStarterCleanup(&starter);
163
Austin Schuh59398d32023-05-03 08:10:55 -0700164 Event starter_started;
165 std::thread starterd_thread([&starter, &starter_started] {
166 starter.event_loop()->OnRun(
167 [&starter_started]() { starter_started.Set(); });
168 starter.Run();
169 });
170 starter_started.Wait();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700171
Austin Schuh59398d32023-05-03 08:10:55 -0700172 Event client_started;
173 std::thread client_thread([&client_loop, &client_started] {
174 client_loop.OnRun([&client_started]() { client_started.Set(); });
175 client_loop.Run();
176 });
177 client_started.Wait();
178
179 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700180 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800181 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700182 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700183 starterd_thread.join();
184}
185
James Kuszmaul293b2172021-11-10 16:20:48 -0800186INSTANTIATE_TEST_SUITE_P(
187 StarterdConfigParamTest, StarterdConfigParamTest,
188 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
189 TestParams{"aos/starter/multinode_pingpong_config.json",
190 "pi1"}));
191
192TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700193 const std::string config_file =
194 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700195
196 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
197 aos::configuration::ReadConfig(config_file);
198
Tyler Chatowa79419d2020-08-12 20:12:11 -0700199 auto new_config = aos::configuration::MergeWithConfig(
200 &config.message(), absl::StrFormat(
201 R"({"applications": [
202 {
203 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700204 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800205 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700206 },
207 {
208 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700209 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800210 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700211 }
212 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700213 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
214 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700215
216 const aos::Configuration *config_msg = &new_config.message();
217
218 // Set up starter with config file
219 aos::starter::Starter starter(config_msg);
220
221 // Create an event loop to watch for ping messages, verifying it actually
222 // started.
223 aos::ShmEventLoop watcher_loop(config_msg);
224 watcher_loop.SkipAosLog();
225
226 watcher_loop
227 .AddTimer([&watcher_loop] {
228 watcher_loop.Exit();
229 FAIL();
230 })
Philipp Schradera6712522023-07-05 20:25:11 -0700231 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700232
233 int test_stage = 0;
234 uint64_t id;
235
236 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
237 &id](const aos::starter::Status &status) {
238 const aos::starter::ApplicationStatus *app_status =
239 FindApplicationStatus(status, "ping");
240 if (app_status == nullptr) {
241 return;
242 }
243
244 switch (test_stage) {
245 case 0: {
246 if (app_status->has_state() &&
247 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700248 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700249 test_stage = 1;
250 ASSERT_TRUE(app_status->has_pid());
251 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
252 ASSERT_TRUE(app_status->has_id());
253 id = app_status->id();
254 }
255 break;
256 }
257
258 case 1: {
259 if (app_status->has_state() &&
260 app_status->state() == aos::starter::State::RUNNING &&
261 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700262 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700263 watcher_loop.Exit();
264 SUCCEED();
265 }
266 break;
267 }
268 }
269 });
270
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700271 SetupStarterCleanup(&starter);
272
Austin Schuh59398d32023-05-03 08:10:55 -0700273 Event starter_started;
274 std::thread starterd_thread([&starter, &starter_started] {
275 starter.event_loop()->OnRun(
276 [&starter_started]() { starter_started.Set(); });
277 starter.Run();
278 });
279 starter_started.Wait();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700280 watcher_loop.Run();
281
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700282 test_done_ = true;
283
Tyler Chatowa79419d2020-08-12 20:12:11 -0700284 starterd_thread.join();
285}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700286
James Kuszmaul293b2172021-11-10 16:20:48 -0800287TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700288 const std::string config_file =
289 ArtifactPath("aos/events/pingpong_config.json");
290
291 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
292 aos::configuration::ReadConfig(config_file);
293
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700294 auto new_config = aos::configuration::MergeWithConfig(
295 &config.message(), absl::StrFormat(
296 R"({"applications": [
297 {
298 "name": "ping",
299 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800300 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700301 "autostart": false
302 },
303 {
304 "name": "pong",
305 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800306 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700307 }
308 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700309 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
310 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700311
312 const aos::Configuration *config_msg = &new_config.message();
313
314 // Set up starter with config file
315 aos::starter::Starter starter(config_msg);
316
317 // Create an event loop to watch for the application starting up.
318 aos::ShmEventLoop watcher_loop(config_msg);
319 watcher_loop.SkipAosLog();
320
321 watcher_loop
322 .AddTimer([&watcher_loop] {
323 watcher_loop.Exit();
324 FAIL();
325 })
Philipp Schradera6712522023-07-05 20:25:11 -0700326 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700327
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700328 int pong_running_count = 0;
329 watcher_loop.MakeWatcher("/aos", [&watcher_loop, &pong_running_count](
James Kuszmaul6295a642022-03-22 15:23:59 -0700330 const aos::starter::Status &status) {
331 const aos::starter::ApplicationStatus *ping_app_status =
332 FindApplicationStatus(status, "ping");
333 const aos::starter::ApplicationStatus *pong_app_status =
334 FindApplicationStatus(status, "pong");
335 if (ping_app_status == nullptr || pong_app_status == nullptr) {
336 return;
337 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700338
James Kuszmaul6295a642022-03-22 15:23:59 -0700339 if (ping_app_status->has_state() &&
340 ping_app_status->state() != aos::starter::State::STOPPED) {
341 watcher_loop.Exit();
342 FAIL();
343 }
344 if (pong_app_status->has_state() &&
345 pong_app_status->state() == aos::starter::State::RUNNING) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700346 ++pong_running_count;
347 // Sometimes if the timing for everything is *just* off, then the
348 // process_info will say that the process name is "starter_test" because
349 // it grabbed the name after the fork() but before the execvp(). To
350 // protect against that, wait an extra cycle. If things aren't fixed by
351 // the second cycle, then that is a problem.
352 if (pong_running_count < 2) {
353 return;
354 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700355 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700356 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
357 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700358 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700359 ASSERT_TRUE(pong_app_status->process_info()->has_cpu_usage());
360 ASSERT_LE(0.0, pong_app_status->process_info()->cpu_usage());
James Kuszmaul6295a642022-03-22 15:23:59 -0700361 watcher_loop.Exit();
362 SUCCEED();
363 }
364 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700365
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700366 SetupStarterCleanup(&starter);
367
Austin Schuh59398d32023-05-03 08:10:55 -0700368 Event starter_started;
369 std::thread starterd_thread([&starter, &starter_started] {
370 starter.event_loop()->OnRun(
371 [&starter_started]() { starter_started.Set(); });
372 starter.Run();
373 });
374 starter_started.Wait();
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700375 watcher_loop.Run();
376
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700377 test_done_ = true;
378
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700379 starterd_thread.join();
380}
381
James Kuszmaule7c7e582022-01-07 18:50:01 -0800382// Tests that starterd respects autorestart.
383TEST_F(StarterdTest, DeathNoRestartTest) {
384 const std::string config_file =
385 ArtifactPath("aos/events/pingpong_config.json");
386
387 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
388 aos::configuration::ReadConfig(config_file);
389
James Kuszmaule7c7e582022-01-07 18:50:01 -0800390 auto new_config = aos::configuration::MergeWithConfig(
391 &config.message(), absl::StrFormat(
392 R"({"applications": [
393 {
394 "name": "ping",
395 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700396 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800397 "autorestart": false
398 },
399 {
400 "name": "pong",
401 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700402 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800403 }
404 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700405 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
406 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800407
408 const aos::Configuration *config_msg = &new_config.message();
409
410 // Set up starter with config file
411 aos::starter::Starter starter(config_msg);
412
413 // Create an event loop to watch for the Status message to watch the state
414 // transitions.
415 aos::ShmEventLoop watcher_loop(config_msg);
416 watcher_loop.SkipAosLog();
417
418 watcher_loop
419 .AddTimer([&watcher_loop] {
420 watcher_loop.Exit();
421 SUCCEED();
422 })
Philipp Schradera6712522023-07-05 20:25:11 -0700423 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800424
425 int test_stage = 0;
426 uint64_t id;
427
428 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
429 &id](const aos::starter::Status &status) {
430 const aos::starter::ApplicationStatus *app_status =
431 FindApplicationStatus(status, "ping");
432 if (app_status == nullptr) {
433 return;
434 }
435
436 switch (test_stage) {
437 case 0: {
438 if (app_status->has_state() &&
439 app_status->state() == aos::starter::State::RUNNING) {
440 LOG(INFO) << "Ping is running";
441 test_stage = 1;
442 ASSERT_TRUE(app_status->has_pid());
443 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
444 ASSERT_TRUE(app_status->has_id());
445 id = app_status->id();
446 }
447 break;
448 }
449
450 case 1: {
451 if (app_status->has_state() &&
452 app_status->state() == aos::starter::State::RUNNING &&
453 app_status->has_id() && app_status->id() != id) {
454 LOG(INFO) << "Ping restarted, it shouldn't...";
455 watcher_loop.Exit();
456 FAIL();
457 }
458 break;
459 }
460 }
461 });
462
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700463 SetupStarterCleanup(&starter);
464
Austin Schuh59398d32023-05-03 08:10:55 -0700465 Event starter_started;
466 std::thread starterd_thread([&starter, &starter_started] {
467 starter.event_loop()->OnRun(
468 [&starter_started]() { starter_started.Set(); });
469 starter.Run();
470 });
471 starter_started.Wait();
James Kuszmaule7c7e582022-01-07 18:50:01 -0800472 watcher_loop.Run();
473
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700474 test_done_ = true;
475
James Kuszmaule7c7e582022-01-07 18:50:01 -0800476 starterd_thread.join();
477}
478
Austin Schuh59398d32023-05-03 08:10:55 -0700479TEST_F(StarterdTest, StarterChainTest) {
480 // This test was written in response to a bug that was found
481 // in StarterClient::Succeed. The bug caused the timeout handler
482 // to be reset after the success handler was called.
483 // the bug has been fixed, and this test will ensure it does
484 // not regress.
485 const std::string config_file =
486 ArtifactPath("aos/events/pingpong_config.json");
487 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
488 aos::configuration::ReadConfig(config_file);
489 auto new_config = aos::configuration::MergeWithConfig(
490 &config.message(), absl::StrFormat(
491 R"({"applications": [
492 {
493 "name": "ping",
494 "executable_name": "%s",
495 "args": ["--shm_base", "%s"],
496 "autorestart": false
497 },
498 {
499 "name": "pong",
500 "executable_name": "%s",
501 "args": ["--shm_base", "%s"]
502 }
503 ]})",
504 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
505 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
506
507 const aos::Configuration *config_msg = &new_config.message();
508 // Set up starter with config file
509 aos::starter::Starter starter(config_msg);
510 aos::ShmEventLoop client_loop(config_msg);
511 client_loop.SkipAosLog();
512 StarterClient client(&client_loop);
513 bool success = false;
514 auto client_node = client_loop.node();
515
516 // limit the amount of time we will wait for the test to finish.
517 client_loop
518 .AddTimer([&client_loop] {
519 client_loop.Exit();
520 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
521 "The chain of stages defined below did not complete "
522 "within the time limit.";
523 })
Philipp Schradera6712522023-07-05 20:25:11 -0700524 ->Schedule(client_loop.monotonic_now() + std::chrono::seconds(20));
Austin Schuh59398d32023-05-03 08:10:55 -0700525
526 // variables have been defined, here we define the body of the test.
527 // We want stage1 to succeed, triggering stage2.
528 // We want stage2 to timeout, triggering stage3.
529
530 auto stage3 = [&client_loop, &success]() {
531 LOG(INFO) << "Begin stage3.";
532 SUCCEED();
533 success = true;
534 client_loop.Exit();
535 LOG(INFO) << "End stage3.";
536 };
537 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
538 LOG(INFO) << "Begin stage2";
539 test_done_ = true; // trigger `starter` to exit.
540
541 // wait for the starter event loop to close, so we can
542 // intentionally trigger a timeout.
543 int attempts = 0;
544 while (starter.event_loop()->is_running()) {
545 ++attempts;
546 if (attempts > 5) {
547 LOG(INFO) << "Timeout while waiting for starter to exit";
548 return;
549 }
550 LOG(INFO) << "Waiting for starter to close.";
551 std::this_thread::sleep_for(std::chrono::seconds(1));
552 }
553 client.SetTimeoutHandler(stage3);
554 client.SetSuccessHandler([]() {
555 LOG(INFO) << "stage3 success handler called.";
556 FAIL() << ": Command should not have succeeded here.";
557 });
558 // we want this command to timeout
559 client.SendCommands({{Command::START, "ping", {client_node}}},
560 std::chrono::seconds(5));
561 LOG(INFO) << "End stage2";
562 };
563 auto stage1 = [&client, &client_node, &stage2] {
564 LOG(INFO) << "Begin stage1";
565 client.SetTimeoutHandler(
566 []() { FAIL() << ": Command should not have timed out."; });
567 client.SetSuccessHandler(stage2);
568 client.SendCommands({{Command::STOP, "ping", {client_node}}},
569 std::chrono::seconds(5));
570 LOG(INFO) << "End stage1";
571 };
572 // start the test body
Philipp Schradera6712522023-07-05 20:25:11 -0700573 client_loop.AddTimer(stage1)->Schedule(client_loop.monotonic_now() +
574 std::chrono::milliseconds(1));
Austin Schuh59398d32023-05-03 08:10:55 -0700575
576 // prepare the cleanup for starter. This will finish when we call
577 // `test_done_ = true;`.
578 SetupStarterCleanup(&starter);
579
580 // run `starter.Run()` in a thread to simulate it running on
581 // another process.
582 Event started;
583 std::thread starterd_thread([&starter, &started] {
584 starter.event_loop()->OnRun([&started]() { started.Set(); });
585 starter.Run();
586 });
587
588 started.Wait();
589 client_loop.Run();
590 EXPECT_TRUE(success);
591 ASSERT_FALSE(starter.event_loop()->is_running());
592 starterd_thread.join();
593}
594
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700595} // namespace starter
596} // namespace aos