blob: e68cf05aba2474507ea411faf7880c7e0c37faa0 [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
Austin Schuh83cbb1e2023-06-23 12:59:02 -070023class ThreadedStarterRunner {
24 public:
25 ThreadedStarterRunner(Starter *starter)
26 : my_thread_([this, starter]() {
27 starter->event_loop()->OnRun([this]() { event_.Set(); });
28 starter->Run();
29 }) {
30 event_.Wait();
31 }
32
33 ~ThreadedStarterRunner() { my_thread_.join(); }
34
35 private:
36 aos::Event event_;
37 std::thread my_thread_;
38};
39
James Kuszmaul293b2172021-11-10 16:20:48 -080040class StarterdTest : public ::testing::Test {
41 public:
Austin Schuh59398d32023-05-03 08:10:55 -070042 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080043 // Nuke the shm dir:
Austin Schuh59398d32023-05-03 08:10:55 -070044 aos::util::UnlinkRecursive(FLAGS_shm_base);
James Kuszmaul293b2172021-11-10 16:20:48 -080045 }
46
47 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070048 void SetupStarterCleanup(aos::starter::Starter *starter) {
49 starter->event_loop()
50 ->AddTimer([this, starter]() {
51 if (test_done_) {
52 starter->Cleanup();
53 }
54 })
Philipp Schradera6712522023-07-05 20:25:11 -070055 ->Schedule(starter->event_loop()->monotonic_now(),
56 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070057 }
58
James Kuszmaul293b2172021-11-10 16:20:48 -080059 gflags::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070060 // Used to track when the test completes so that we can clean up the starter
61 // in its thread.
62 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080063};
64
65struct TestParams {
66 std::string config;
67 std::string hostname;
68};
69
70class StarterdConfigParamTest
71 : public StarterdTest,
72 public ::testing::WithParamInterface<TestParams> {};
73
74TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
75 gflags::FlagSaver flag_saver;
76 FLAGS_override_hostname = GetParam().hostname;
77 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070078
79 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
80 aos::configuration::ReadConfig(config_file);
81
Tyler Chatowa79419d2020-08-12 20:12:11 -070082 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080083 &config.message(),
84 absl::StrFormat(
85 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070086 {
87 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070088 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080089 "nodes": ["pi1"],
90 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070091 },
92 {
93 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070094 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080095 "nodes": ["pi1"],
96 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070097 }
98 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -070099 ArtifactPath("aos/events/ping"), FLAGS_shm_base, config_file,
100 GetParam().hostname, ArtifactPath("aos/events/pong"), FLAGS_shm_base,
James Kuszmaul293b2172021-11-10 16:20:48 -0800101 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700102
103 const aos::Configuration *config_msg = &new_config.message();
104
105 // Set up starter with config file
106 aos::starter::Starter starter(config_msg);
107
108 // Create an event loop to watch for ping messages, verifying it actually
109 // started.
110 aos::ShmEventLoop watcher_loop(config_msg);
111 watcher_loop.SkipAosLog();
112
James Kuszmaul293b2172021-11-10 16:20:48 -0800113 aos::ShmEventLoop client_loop(config_msg);
114 client_loop.SkipAosLog();
115 StarterClient client(&client_loop);
116 client.SetTimeoutHandler(
117 []() { FAIL() << ": Command should not have timed out."; });
118 bool success = false;
119 client.SetSuccessHandler([&success, &client_loop]() {
120 client_loop.Exit();
121 success = true;
122 });
123
Tyler Chatowa79419d2020-08-12 20:12:11 -0700124 watcher_loop
125 .AddTimer([&watcher_loop] {
126 watcher_loop.Exit();
127 FAIL();
128 })
Philipp Schradera6712522023-07-05 20:25:11 -0700129 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700130
James Kuszmaul293b2172021-11-10 16:20:48 -0800131 std::atomic<int> test_stage = 0;
132 // Watch on the client loop since we need to interact with the StarterClient.
133 client_loop.MakeWatcher("/test", [&test_stage, &client,
134 &client_loop](const aos::examples::Ping &) {
135 switch (test_stage) {
136 case 1: {
137 test_stage = 2;
138 break;
139 }
140 case 2: {
141 {
142 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
143 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700144 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800145 test_stage = 3;
146 break;
147 }
148 }
149 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700150
151 watcher_loop.MakeWatcher(
152 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
153 const aos::starter::ApplicationStatus *app_status =
154 FindApplicationStatus(status, "ping");
155 if (app_status == nullptr) {
156 return;
157 }
158
159 switch (test_stage) {
160 case 0: {
161 if (app_status->has_state() &&
162 app_status->state() == aos::starter::State::RUNNING) {
163 test_stage = 1;
164 }
165 break;
166 }
167
168 case 3: {
169 if (app_status->has_state() &&
170 app_status->state() == aos::starter::State::STOPPED) {
171 watcher_loop.Exit();
172 SUCCEED();
173 }
174 break;
175 }
176 }
177 });
178
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700179 SetupStarterCleanup(&starter);
180
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700181 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700182
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700183 aos::Event event;
184 client_loop.OnRun([&event]() { event.Set(); });
185 std::thread client_thread([&client_loop] { client_loop.Run(); });
186 event.Wait();
Austin Schuh59398d32023-05-03 08:10:55 -0700187
188 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700189 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800190 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700191 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700192}
193
James Kuszmaul293b2172021-11-10 16:20:48 -0800194INSTANTIATE_TEST_SUITE_P(
195 StarterdConfigParamTest, StarterdConfigParamTest,
196 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
197 TestParams{"aos/starter/multinode_pingpong_config.json",
198 "pi1"}));
199
200TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700201 const std::string config_file =
202 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700203
204 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
205 aos::configuration::ReadConfig(config_file);
206
Tyler Chatowa79419d2020-08-12 20:12:11 -0700207 auto new_config = aos::configuration::MergeWithConfig(
208 &config.message(), absl::StrFormat(
209 R"({"applications": [
210 {
211 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700212 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800213 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700214 },
215 {
216 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700217 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800218 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700219 }
220 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700221 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
222 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700223
224 const aos::Configuration *config_msg = &new_config.message();
225
226 // Set up starter with config file
227 aos::starter::Starter starter(config_msg);
228
229 // Create an event loop to watch for ping messages, verifying it actually
230 // started.
231 aos::ShmEventLoop watcher_loop(config_msg);
232 watcher_loop.SkipAosLog();
233
234 watcher_loop
235 .AddTimer([&watcher_loop] {
236 watcher_loop.Exit();
237 FAIL();
238 })
Philipp Schradera6712522023-07-05 20:25:11 -0700239 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700240
241 int test_stage = 0;
242 uint64_t id;
243
244 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
245 &id](const aos::starter::Status &status) {
246 const aos::starter::ApplicationStatus *app_status =
247 FindApplicationStatus(status, "ping");
248 if (app_status == nullptr) {
249 return;
250 }
251
252 switch (test_stage) {
253 case 0: {
254 if (app_status->has_state() &&
255 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700256 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700257 test_stage = 1;
258 ASSERT_TRUE(app_status->has_pid());
259 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
260 ASSERT_TRUE(app_status->has_id());
261 id = app_status->id();
262 }
263 break;
264 }
265
266 case 1: {
267 if (app_status->has_state() &&
268 app_status->state() == aos::starter::State::RUNNING &&
269 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700270 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700271 watcher_loop.Exit();
272 SUCCEED();
273 }
274 break;
275 }
276 }
277 });
278
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700279 SetupStarterCleanup(&starter);
280
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700281 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700282 watcher_loop.Run();
283
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700284 test_done_ = true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700285}
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.
James Kuszmaul37a56af2023-07-29 15:15:16 -0700352 if (pong_running_count < 3) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700353 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 Kuszmaul8544c492023-07-31 15:00:38 -0700361 ASSERT_TRUE(pong_app_status->has_has_active_timing_report());
362 ASSERT_TRUE(pong_app_status->has_active_timing_report());
James Kuszmaul6295a642022-03-22 15:23:59 -0700363 watcher_loop.Exit();
364 SUCCEED();
365 }
366 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700367
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700368 SetupStarterCleanup(&starter);
369
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700370 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700371 watcher_loop.Run();
372
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700373 test_done_ = true;
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700374}
375
James Kuszmaule7c7e582022-01-07 18:50:01 -0800376// Tests that starterd respects autorestart.
377TEST_F(StarterdTest, DeathNoRestartTest) {
378 const std::string config_file =
379 ArtifactPath("aos/events/pingpong_config.json");
380
381 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
382 aos::configuration::ReadConfig(config_file);
383
James Kuszmaule7c7e582022-01-07 18:50:01 -0800384 auto new_config = aos::configuration::MergeWithConfig(
385 &config.message(), absl::StrFormat(
386 R"({"applications": [
387 {
388 "name": "ping",
389 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700390 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800391 "autorestart": false
392 },
393 {
394 "name": "pong",
395 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700396 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800397 }
398 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700399 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
400 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800401
402 const aos::Configuration *config_msg = &new_config.message();
403
404 // Set up starter with config file
405 aos::starter::Starter starter(config_msg);
406
407 // Create an event loop to watch for the Status message to watch the state
408 // transitions.
409 aos::ShmEventLoop watcher_loop(config_msg);
410 watcher_loop.SkipAosLog();
411
412 watcher_loop
413 .AddTimer([&watcher_loop] {
414 watcher_loop.Exit();
415 SUCCEED();
416 })
Philipp Schradera6712522023-07-05 20:25:11 -0700417 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800418
419 int test_stage = 0;
420 uint64_t id;
421
422 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
423 &id](const aos::starter::Status &status) {
424 const aos::starter::ApplicationStatus *app_status =
425 FindApplicationStatus(status, "ping");
426 if (app_status == nullptr) {
427 return;
428 }
429
430 switch (test_stage) {
431 case 0: {
432 if (app_status->has_state() &&
433 app_status->state() == aos::starter::State::RUNNING) {
434 LOG(INFO) << "Ping is running";
435 test_stage = 1;
436 ASSERT_TRUE(app_status->has_pid());
437 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
438 ASSERT_TRUE(app_status->has_id());
439 id = app_status->id();
440 }
441 break;
442 }
443
444 case 1: {
445 if (app_status->has_state() &&
446 app_status->state() == aos::starter::State::RUNNING &&
447 app_status->has_id() && app_status->id() != id) {
448 LOG(INFO) << "Ping restarted, it shouldn't...";
449 watcher_loop.Exit();
450 FAIL();
451 }
452 break;
453 }
454 }
455 });
456
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700457 SetupStarterCleanup(&starter);
458
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700459 ThreadedStarterRunner starterd_thread(&starter);
James Kuszmaule7c7e582022-01-07 18:50:01 -0800460 watcher_loop.Run();
461
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700462 test_done_ = true;
James Kuszmaule7c7e582022-01-07 18:50:01 -0800463}
464
Austin Schuh59398d32023-05-03 08:10:55 -0700465TEST_F(StarterdTest, StarterChainTest) {
466 // This test was written in response to a bug that was found
467 // in StarterClient::Succeed. The bug caused the timeout handler
468 // to be reset after the success handler was called.
469 // the bug has been fixed, and this test will ensure it does
470 // not regress.
471 const std::string config_file =
472 ArtifactPath("aos/events/pingpong_config.json");
473 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
474 aos::configuration::ReadConfig(config_file);
475 auto new_config = aos::configuration::MergeWithConfig(
476 &config.message(), absl::StrFormat(
477 R"({"applications": [
478 {
479 "name": "ping",
480 "executable_name": "%s",
481 "args": ["--shm_base", "%s"],
482 "autorestart": false
483 },
484 {
485 "name": "pong",
486 "executable_name": "%s",
487 "args": ["--shm_base", "%s"]
488 }
489 ]})",
490 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
491 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
492
493 const aos::Configuration *config_msg = &new_config.message();
494 // Set up starter with config file
495 aos::starter::Starter starter(config_msg);
496 aos::ShmEventLoop client_loop(config_msg);
497 client_loop.SkipAosLog();
498 StarterClient client(&client_loop);
499 bool success = false;
500 auto client_node = client_loop.node();
501
502 // limit the amount of time we will wait for the test to finish.
503 client_loop
504 .AddTimer([&client_loop] {
505 client_loop.Exit();
506 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
507 "The chain of stages defined below did not complete "
508 "within the time limit.";
509 })
Philipp Schradera6712522023-07-05 20:25:11 -0700510 ->Schedule(client_loop.monotonic_now() + std::chrono::seconds(20));
Austin Schuh59398d32023-05-03 08:10:55 -0700511
512 // variables have been defined, here we define the body of the test.
513 // We want stage1 to succeed, triggering stage2.
514 // We want stage2 to timeout, triggering stage3.
515
516 auto stage3 = [&client_loop, &success]() {
517 LOG(INFO) << "Begin stage3.";
518 SUCCEED();
519 success = true;
520 client_loop.Exit();
521 LOG(INFO) << "End stage3.";
522 };
523 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
524 LOG(INFO) << "Begin stage2";
525 test_done_ = true; // trigger `starter` to exit.
526
527 // wait for the starter event loop to close, so we can
528 // intentionally trigger a timeout.
529 int attempts = 0;
530 while (starter.event_loop()->is_running()) {
531 ++attempts;
532 if (attempts > 5) {
533 LOG(INFO) << "Timeout while waiting for starter to exit";
534 return;
535 }
536 LOG(INFO) << "Waiting for starter to close.";
537 std::this_thread::sleep_for(std::chrono::seconds(1));
538 }
539 client.SetTimeoutHandler(stage3);
540 client.SetSuccessHandler([]() {
541 LOG(INFO) << "stage3 success handler called.";
542 FAIL() << ": Command should not have succeeded here.";
543 });
544 // we want this command to timeout
545 client.SendCommands({{Command::START, "ping", {client_node}}},
546 std::chrono::seconds(5));
547 LOG(INFO) << "End stage2";
548 };
549 auto stage1 = [&client, &client_node, &stage2] {
550 LOG(INFO) << "Begin stage1";
551 client.SetTimeoutHandler(
552 []() { FAIL() << ": Command should not have timed out."; });
553 client.SetSuccessHandler(stage2);
554 client.SendCommands({{Command::STOP, "ping", {client_node}}},
555 std::chrono::seconds(5));
556 LOG(INFO) << "End stage1";
557 };
558 // start the test body
Philipp Schradera6712522023-07-05 20:25:11 -0700559 client_loop.AddTimer(stage1)->Schedule(client_loop.monotonic_now() +
560 std::chrono::milliseconds(1));
Austin Schuh59398d32023-05-03 08:10:55 -0700561
562 // prepare the cleanup for starter. This will finish when we call
563 // `test_done_ = true;`.
564 SetupStarterCleanup(&starter);
565
566 // run `starter.Run()` in a thread to simulate it running on
567 // another process.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700568 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh59398d32023-05-03 08:10:55 -0700569
Austin Schuh59398d32023-05-03 08:10:55 -0700570 client_loop.Run();
571 EXPECT_TRUE(success);
572 ASSERT_FALSE(starter.event_loop()->is_running());
Austin Schuh59398d32023-05-03 08:10:55 -0700573}
574
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700575} // namespace starter
576} // namespace aos