blob: 94566ae1870d1cf1ace5463f039cd73b9c89d254 [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
6#include "aos/events/ping_generated.h"
7#include "aos/events/pong_generated.h"
Austin Schuh59398d32023-05-03 08:10:55 -07008#include "aos/ipc_lib/event.h"
James Kuszmaul293b2172021-11-10 16:20:48 -08009#include "aos/network/team_number.h"
Austin Schuh373f1762021-06-02 21:07:09 -070010#include "aos/testing/path.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070011#include "aos/testing/tmpdir.h"
Austin Schuhfc0bca42021-12-31 21:49:41 -080012#include "aos/util/file.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070013#include "gtest/gtest.h"
14#include "starter_rpc_lib.h"
15#include "starterd_lib.h"
16
Austin Schuh373f1762021-06-02 21:07:09 -070017using aos::testing::ArtifactPath;
18
Austin Schuh5f79a5a2021-10-12 17:46:50 -070019namespace aos {
20namespace starter {
21
James Kuszmaul293b2172021-11-10 16:20:48 -080022class StarterdTest : public ::testing::Test {
23 public:
Austin Schuh59398d32023-05-03 08:10:55 -070024 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080025 // Nuke the shm dir:
Austin Schuh59398d32023-05-03 08:10:55 -070026 aos::util::UnlinkRecursive(FLAGS_shm_base);
James Kuszmaul293b2172021-11-10 16:20:48 -080027 }
28
29 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070030 void SetupStarterCleanup(aos::starter::Starter *starter) {
31 starter->event_loop()
32 ->AddTimer([this, starter]() {
33 if (test_done_) {
34 starter->Cleanup();
35 }
36 })
37 ->Setup(starter->event_loop()->monotonic_now(),
Austin Schuh59398d32023-05-03 08:10:55 -070038 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070039 }
40
James Kuszmaul293b2172021-11-10 16:20:48 -080041 gflags::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070042 // Used to track when the test completes so that we can clean up the starter
43 // in its thread.
44 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080045};
46
47struct TestParams {
48 std::string config;
49 std::string hostname;
50};
51
52class StarterdConfigParamTest
53 : public StarterdTest,
54 public ::testing::WithParamInterface<TestParams> {};
55
56TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
57 gflags::FlagSaver flag_saver;
58 FLAGS_override_hostname = GetParam().hostname;
59 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070060
61 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
62 aos::configuration::ReadConfig(config_file);
63
Tyler Chatowa79419d2020-08-12 20:12:11 -070064 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080065 &config.message(),
66 absl::StrFormat(
67 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070068 {
69 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070070 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080071 "nodes": ["pi1"],
72 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070073 },
74 {
75 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070076 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080077 "nodes": ["pi1"],
78 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070079 }
80 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -070081 ArtifactPath("aos/events/ping"), FLAGS_shm_base, config_file,
82 GetParam().hostname, ArtifactPath("aos/events/pong"), FLAGS_shm_base,
James Kuszmaul293b2172021-11-10 16:20:48 -080083 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -070084
85 const aos::Configuration *config_msg = &new_config.message();
86
87 // Set up starter with config file
88 aos::starter::Starter starter(config_msg);
89
90 // Create an event loop to watch for ping messages, verifying it actually
91 // started.
92 aos::ShmEventLoop watcher_loop(config_msg);
93 watcher_loop.SkipAosLog();
94
James Kuszmaul293b2172021-11-10 16:20:48 -080095 aos::ShmEventLoop client_loop(config_msg);
96 client_loop.SkipAosLog();
97 StarterClient client(&client_loop);
98 client.SetTimeoutHandler(
99 []() { FAIL() << ": Command should not have timed out."; });
100 bool success = false;
101 client.SetSuccessHandler([&success, &client_loop]() {
102 client_loop.Exit();
103 success = true;
104 });
105
Tyler Chatowa79419d2020-08-12 20:12:11 -0700106 watcher_loop
107 .AddTimer([&watcher_loop] {
108 watcher_loop.Exit();
109 FAIL();
110 })
111 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
112
James Kuszmaul293b2172021-11-10 16:20:48 -0800113 std::atomic<int> test_stage = 0;
114 // Watch on the client loop since we need to interact with the StarterClient.
115 client_loop.MakeWatcher("/test", [&test_stage, &client,
116 &client_loop](const aos::examples::Ping &) {
117 switch (test_stage) {
118 case 1: {
119 test_stage = 2;
120 break;
121 }
122 case 2: {
123 {
124 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
125 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700126 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800127 test_stage = 3;
128 break;
129 }
130 }
131 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700132
133 watcher_loop.MakeWatcher(
134 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
135 const aos::starter::ApplicationStatus *app_status =
136 FindApplicationStatus(status, "ping");
137 if (app_status == nullptr) {
138 return;
139 }
140
141 switch (test_stage) {
142 case 0: {
143 if (app_status->has_state() &&
144 app_status->state() == aos::starter::State::RUNNING) {
145 test_stage = 1;
146 }
147 break;
148 }
149
150 case 3: {
151 if (app_status->has_state() &&
152 app_status->state() == aos::starter::State::STOPPED) {
153 watcher_loop.Exit();
154 SUCCEED();
155 }
156 break;
157 }
158 }
159 });
160
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700161 SetupStarterCleanup(&starter);
162
Austin Schuh59398d32023-05-03 08:10:55 -0700163 Event starter_started;
164 std::thread starterd_thread([&starter, &starter_started] {
165 starter.event_loop()->OnRun(
166 [&starter_started]() { starter_started.Set(); });
167 starter.Run();
168 });
169 starter_started.Wait();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700170
Austin Schuh59398d32023-05-03 08:10:55 -0700171 Event client_started;
172 std::thread client_thread([&client_loop, &client_started] {
173 client_loop.OnRun([&client_started]() { client_started.Set(); });
174 client_loop.Run();
175 });
176 client_started.Wait();
177
178 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700179 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800180 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700181 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700182 starterd_thread.join();
183}
184
James Kuszmaul293b2172021-11-10 16:20:48 -0800185INSTANTIATE_TEST_SUITE_P(
186 StarterdConfigParamTest, StarterdConfigParamTest,
187 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
188 TestParams{"aos/starter/multinode_pingpong_config.json",
189 "pi1"}));
190
191TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700192 const std::string config_file =
193 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700194
195 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
196 aos::configuration::ReadConfig(config_file);
197
Tyler Chatowa79419d2020-08-12 20:12:11 -0700198 auto new_config = aos::configuration::MergeWithConfig(
199 &config.message(), absl::StrFormat(
200 R"({"applications": [
201 {
202 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700203 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800204 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700205 },
206 {
207 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700208 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800209 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700210 }
211 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700212 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
213 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700214
215 const aos::Configuration *config_msg = &new_config.message();
216
217 // Set up starter with config file
218 aos::starter::Starter starter(config_msg);
219
220 // Create an event loop to watch for ping messages, verifying it actually
221 // started.
222 aos::ShmEventLoop watcher_loop(config_msg);
223 watcher_loop.SkipAosLog();
224
225 watcher_loop
226 .AddTimer([&watcher_loop] {
227 watcher_loop.Exit();
228 FAIL();
229 })
Austin Schuha07b3ce2021-10-10 12:33:21 -0700230 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700231
232 int test_stage = 0;
233 uint64_t id;
234
235 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
236 &id](const aos::starter::Status &status) {
237 const aos::starter::ApplicationStatus *app_status =
238 FindApplicationStatus(status, "ping");
239 if (app_status == nullptr) {
240 return;
241 }
242
243 switch (test_stage) {
244 case 0: {
245 if (app_status->has_state() &&
246 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700247 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700248 test_stage = 1;
249 ASSERT_TRUE(app_status->has_pid());
250 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
251 ASSERT_TRUE(app_status->has_id());
252 id = app_status->id();
253 }
254 break;
255 }
256
257 case 1: {
258 if (app_status->has_state() &&
259 app_status->state() == aos::starter::State::RUNNING &&
260 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700261 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700262 watcher_loop.Exit();
263 SUCCEED();
264 }
265 break;
266 }
267 }
268 });
269
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700270 SetupStarterCleanup(&starter);
271
Austin Schuh59398d32023-05-03 08:10:55 -0700272 Event starter_started;
273 std::thread starterd_thread([&starter, &starter_started] {
274 starter.event_loop()->OnRun(
275 [&starter_started]() { starter_started.Set(); });
276 starter.Run();
277 });
278 starter_started.Wait();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700279 watcher_loop.Run();
280
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700281 test_done_ = true;
282
Tyler Chatowa79419d2020-08-12 20:12:11 -0700283 starterd_thread.join();
284}
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 })
325 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
326
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.
351 if (pong_running_count < 2) {
352 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 Kuszmaul6295a642022-03-22 15:23:59 -0700360 watcher_loop.Exit();
361 SUCCEED();
362 }
363 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700364
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700365 SetupStarterCleanup(&starter);
366
Austin Schuh59398d32023-05-03 08:10:55 -0700367 Event starter_started;
368 std::thread starterd_thread([&starter, &starter_started] {
369 starter.event_loop()->OnRun(
370 [&starter_started]() { starter_started.Set(); });
371 starter.Run();
372 });
373 starter_started.Wait();
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700374 watcher_loop.Run();
375
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700376 test_done_ = true;
377
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700378 starterd_thread.join();
379}
380
James Kuszmaule7c7e582022-01-07 18:50:01 -0800381// Tests that starterd respects autorestart.
382TEST_F(StarterdTest, DeathNoRestartTest) {
383 const std::string config_file =
384 ArtifactPath("aos/events/pingpong_config.json");
385
386 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
387 aos::configuration::ReadConfig(config_file);
388
James Kuszmaule7c7e582022-01-07 18:50:01 -0800389 auto new_config = aos::configuration::MergeWithConfig(
390 &config.message(), absl::StrFormat(
391 R"({"applications": [
392 {
393 "name": "ping",
394 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700395 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800396 "autorestart": false
397 },
398 {
399 "name": "pong",
400 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700401 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800402 }
403 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700404 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
405 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800406
407 const aos::Configuration *config_msg = &new_config.message();
408
409 // Set up starter with config file
410 aos::starter::Starter starter(config_msg);
411
412 // Create an event loop to watch for the Status message to watch the state
413 // transitions.
414 aos::ShmEventLoop watcher_loop(config_msg);
415 watcher_loop.SkipAosLog();
416
417 watcher_loop
418 .AddTimer([&watcher_loop] {
419 watcher_loop.Exit();
420 SUCCEED();
421 })
422 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
423
424 int test_stage = 0;
425 uint64_t id;
426
427 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
428 &id](const aos::starter::Status &status) {
429 const aos::starter::ApplicationStatus *app_status =
430 FindApplicationStatus(status, "ping");
431 if (app_status == nullptr) {
432 return;
433 }
434
435 switch (test_stage) {
436 case 0: {
437 if (app_status->has_state() &&
438 app_status->state() == aos::starter::State::RUNNING) {
439 LOG(INFO) << "Ping is running";
440 test_stage = 1;
441 ASSERT_TRUE(app_status->has_pid());
442 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
443 ASSERT_TRUE(app_status->has_id());
444 id = app_status->id();
445 }
446 break;
447 }
448
449 case 1: {
450 if (app_status->has_state() &&
451 app_status->state() == aos::starter::State::RUNNING &&
452 app_status->has_id() && app_status->id() != id) {
453 LOG(INFO) << "Ping restarted, it shouldn't...";
454 watcher_loop.Exit();
455 FAIL();
456 }
457 break;
458 }
459 }
460 });
461
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700462 SetupStarterCleanup(&starter);
463
Austin Schuh59398d32023-05-03 08:10:55 -0700464 Event starter_started;
465 std::thread starterd_thread([&starter, &starter_started] {
466 starter.event_loop()->OnRun(
467 [&starter_started]() { starter_started.Set(); });
468 starter.Run();
469 });
470 starter_started.Wait();
James Kuszmaule7c7e582022-01-07 18:50:01 -0800471 watcher_loop.Run();
472
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700473 test_done_ = true;
474
James Kuszmaule7c7e582022-01-07 18:50:01 -0800475 starterd_thread.join();
476}
477
Austin Schuh59398d32023-05-03 08:10:55 -0700478TEST_F(StarterdTest, StarterChainTest) {
479 // This test was written in response to a bug that was found
480 // in StarterClient::Succeed. The bug caused the timeout handler
481 // to be reset after the success handler was called.
482 // the bug has been fixed, and this test will ensure it does
483 // not regress.
484 const std::string config_file =
485 ArtifactPath("aos/events/pingpong_config.json");
486 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
487 aos::configuration::ReadConfig(config_file);
488 auto new_config = aos::configuration::MergeWithConfig(
489 &config.message(), absl::StrFormat(
490 R"({"applications": [
491 {
492 "name": "ping",
493 "executable_name": "%s",
494 "args": ["--shm_base", "%s"],
495 "autorestart": false
496 },
497 {
498 "name": "pong",
499 "executable_name": "%s",
500 "args": ["--shm_base", "%s"]
501 }
502 ]})",
503 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
504 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
505
506 const aos::Configuration *config_msg = &new_config.message();
507 // Set up starter with config file
508 aos::starter::Starter starter(config_msg);
509 aos::ShmEventLoop client_loop(config_msg);
510 client_loop.SkipAosLog();
511 StarterClient client(&client_loop);
512 bool success = false;
513 auto client_node = client_loop.node();
514
515 // limit the amount of time we will wait for the test to finish.
516 client_loop
517 .AddTimer([&client_loop] {
518 client_loop.Exit();
519 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
520 "The chain of stages defined below did not complete "
521 "within the time limit.";
522 })
523 ->Setup(client_loop.monotonic_now() + std::chrono::seconds(20));
524
525 // variables have been defined, here we define the body of the test.
526 // We want stage1 to succeed, triggering stage2.
527 // We want stage2 to timeout, triggering stage3.
528
529 auto stage3 = [&client_loop, &success]() {
530 LOG(INFO) << "Begin stage3.";
531 SUCCEED();
532 success = true;
533 client_loop.Exit();
534 LOG(INFO) << "End stage3.";
535 };
536 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
537 LOG(INFO) << "Begin stage2";
538 test_done_ = true; // trigger `starter` to exit.
539
540 // wait for the starter event loop to close, so we can
541 // intentionally trigger a timeout.
542 int attempts = 0;
543 while (starter.event_loop()->is_running()) {
544 ++attempts;
545 if (attempts > 5) {
546 LOG(INFO) << "Timeout while waiting for starter to exit";
547 return;
548 }
549 LOG(INFO) << "Waiting for starter to close.";
550 std::this_thread::sleep_for(std::chrono::seconds(1));
551 }
552 client.SetTimeoutHandler(stage3);
553 client.SetSuccessHandler([]() {
554 LOG(INFO) << "stage3 success handler called.";
555 FAIL() << ": Command should not have succeeded here.";
556 });
557 // we want this command to timeout
558 client.SendCommands({{Command::START, "ping", {client_node}}},
559 std::chrono::seconds(5));
560 LOG(INFO) << "End stage2";
561 };
562 auto stage1 = [&client, &client_node, &stage2] {
563 LOG(INFO) << "Begin stage1";
564 client.SetTimeoutHandler(
565 []() { FAIL() << ": Command should not have timed out."; });
566 client.SetSuccessHandler(stage2);
567 client.SendCommands({{Command::STOP, "ping", {client_node}}},
568 std::chrono::seconds(5));
569 LOG(INFO) << "End stage1";
570 };
571 // start the test body
572 client_loop.AddTimer(stage1)->Setup(client_loop.monotonic_now() +
573 std::chrono::milliseconds(1));
574
575 // prepare the cleanup for starter. This will finish when we call
576 // `test_done_ = true;`.
577 SetupStarterCleanup(&starter);
578
579 // run `starter.Run()` in a thread to simulate it running on
580 // another process.
581 Event started;
582 std::thread starterd_thread([&starter, &started] {
583 starter.event_loop()->OnRun([&started]() { started.Set(); });
584 starter.Run();
585 });
586
587 started.Wait();
588 client_loop.Run();
589 EXPECT_TRUE(success);
590 ASSERT_FALSE(starter.event_loop()->is_running());
591 starterd_thread.join();
592}
593
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700594} // namespace starter
595} // namespace aos