blob: 0a074e05b9b7257a19ee642517fde664ec75722b [file] [log] [blame]
Stephan Pleinesf581a072024-05-23 20:59:27 -07001#include <stdint.h>
2
3#include <atomic>
Austin Schuh59398d32023-05-03 08:10:55 -07004#include <chrono>
Tyler Chatowbf0609c2021-07-31 16:13:27 -07005#include <csignal>
Stephan Pleinesf581a072024-05-23 20:59:27 -07006#include <functional>
7#include <ostream>
8#include <string>
Tyler Chatowa79419d2020-08-12 20:12:11 -07009#include <thread>
10
Stephan Pleinesf581a072024-05-23 20:59:27 -070011#include "absl/strings/str_format.h"
12#include "flatbuffers/string.h"
13#include "gflags/gflags.h"
14#include "glog/logging.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070015#include "gtest/gtest.h"
16
Stephan Pleinesf581a072024-05-23 20:59:27 -070017#include "aos/configuration.h"
18#include "aos/events/event_loop.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070019#include "aos/events/ping_generated.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070020#include "aos/events/shm_event_loop.h"
21#include "aos/flatbuffers.h"
Austin Schuh59398d32023-05-03 08:10:55 -070022#include "aos/ipc_lib/event.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070023#include "aos/ipc_lib/shm_base.h"
24#include "aos/json_to_flatbuffer.h"
James Kuszmaul293b2172021-11-10 16:20:48 -080025#include "aos/network/team_number.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070026#include "aos/starter/starter_generated.h"
27#include "aos/starter/starter_rpc_generated.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070028#include "aos/starter/starter_rpc_lib.h"
29#include "aos/starter/starterd_lib.h"
Austin Schuh373f1762021-06-02 21:07:09 -070030#include "aos/testing/path.h"
Austin Schuhfc0bca42021-12-31 21:49:41 -080031#include "aos/util/file.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070032#include "aos/util/process_info_generated.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070033
Austin Schuh373f1762021-06-02 21:07:09 -070034using aos::testing::ArtifactPath;
35
Stephan Pleinesf63bde82024-01-13 15:59:33 -080036namespace aos::starter {
Austin Schuh5f79a5a2021-10-12 17:46:50 -070037
Austin Schuh83cbb1e2023-06-23 12:59:02 -070038class ThreadedStarterRunner {
39 public:
40 ThreadedStarterRunner(Starter *starter)
41 : my_thread_([this, starter]() {
42 starter->event_loop()->OnRun([this]() { event_.Set(); });
43 starter->Run();
44 }) {
45 event_.Wait();
46 }
47
48 ~ThreadedStarterRunner() { my_thread_.join(); }
49
50 private:
51 aos::Event event_;
52 std::thread my_thread_;
53};
54
James Kuszmaul293b2172021-11-10 16:20:48 -080055class StarterdTest : public ::testing::Test {
56 public:
Austin Schuh59398d32023-05-03 08:10:55 -070057 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080058 // Nuke the shm dir:
Austin Schuh59398d32023-05-03 08:10:55 -070059 aos::util::UnlinkRecursive(FLAGS_shm_base);
James Kuszmaul293b2172021-11-10 16:20:48 -080060 }
61
62 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070063 void SetupStarterCleanup(aos::starter::Starter *starter) {
64 starter->event_loop()
65 ->AddTimer([this, starter]() {
66 if (test_done_) {
67 starter->Cleanup();
68 }
69 })
Philipp Schradera6712522023-07-05 20:25:11 -070070 ->Schedule(starter->event_loop()->monotonic_now(),
71 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070072 }
73
James Kuszmaul293b2172021-11-10 16:20:48 -080074 gflags::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070075 // Used to track when the test completes so that we can clean up the starter
76 // in its thread.
77 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080078};
79
80struct TestParams {
81 std::string config;
82 std::string hostname;
83};
84
85class StarterdConfigParamTest
86 : public StarterdTest,
87 public ::testing::WithParamInterface<TestParams> {};
88
89TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
90 gflags::FlagSaver flag_saver;
91 FLAGS_override_hostname = GetParam().hostname;
92 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070093
94 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
95 aos::configuration::ReadConfig(config_file);
96
Tyler Chatowa79419d2020-08-12 20:12:11 -070097 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080098 &config.message(),
99 absl::StrFormat(
100 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -0700101 {
102 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700103 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800104 "nodes": ["pi1"],
105 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700106 },
107 {
108 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700109 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800110 "nodes": ["pi1"],
111 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700112 }
113 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700114 ArtifactPath("aos/events/ping"), FLAGS_shm_base, config_file,
115 GetParam().hostname, ArtifactPath("aos/events/pong"), FLAGS_shm_base,
James Kuszmaul293b2172021-11-10 16:20:48 -0800116 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700117
118 const aos::Configuration *config_msg = &new_config.message();
119
120 // Set up starter with config file
121 aos::starter::Starter starter(config_msg);
122
123 // Create an event loop to watch for ping messages, verifying it actually
124 // started.
125 aos::ShmEventLoop watcher_loop(config_msg);
126 watcher_loop.SkipAosLog();
127
James Kuszmaul293b2172021-11-10 16:20:48 -0800128 aos::ShmEventLoop client_loop(config_msg);
129 client_loop.SkipAosLog();
130 StarterClient client(&client_loop);
131 client.SetTimeoutHandler(
132 []() { FAIL() << ": Command should not have timed out."; });
133 bool success = false;
134 client.SetSuccessHandler([&success, &client_loop]() {
135 client_loop.Exit();
136 success = true;
137 });
138
Tyler Chatowa79419d2020-08-12 20:12:11 -0700139 watcher_loop
140 .AddTimer([&watcher_loop] {
141 watcher_loop.Exit();
142 FAIL();
143 })
Philipp Schradera6712522023-07-05 20:25:11 -0700144 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700145
James Kuszmaul293b2172021-11-10 16:20:48 -0800146 std::atomic<int> test_stage = 0;
147 // Watch on the client loop since we need to interact with the StarterClient.
148 client_loop.MakeWatcher("/test", [&test_stage, &client,
149 &client_loop](const aos::examples::Ping &) {
150 switch (test_stage) {
151 case 1: {
152 test_stage = 2;
153 break;
154 }
155 case 2: {
156 {
157 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
158 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700159 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800160 test_stage = 3;
161 break;
162 }
163 }
164 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700165
166 watcher_loop.MakeWatcher(
167 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
168 const aos::starter::ApplicationStatus *app_status =
169 FindApplicationStatus(status, "ping");
170 if (app_status == nullptr) {
171 return;
172 }
173
174 switch (test_stage) {
175 case 0: {
176 if (app_status->has_state() &&
177 app_status->state() == aos::starter::State::RUNNING) {
178 test_stage = 1;
179 }
180 break;
181 }
182
183 case 3: {
184 if (app_status->has_state() &&
185 app_status->state() == aos::starter::State::STOPPED) {
186 watcher_loop.Exit();
187 SUCCEED();
188 }
189 break;
190 }
191 }
192 });
193
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700194 SetupStarterCleanup(&starter);
195
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700196 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700197
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700198 aos::Event event;
199 client_loop.OnRun([&event]() { event.Set(); });
200 std::thread client_thread([&client_loop] { client_loop.Run(); });
201 event.Wait();
Austin Schuh59398d32023-05-03 08:10:55 -0700202
203 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700204 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800205 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700206 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700207}
208
James Kuszmaul293b2172021-11-10 16:20:48 -0800209INSTANTIATE_TEST_SUITE_P(
210 StarterdConfigParamTest, StarterdConfigParamTest,
211 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
212 TestParams{"aos/starter/multinode_pingpong_config.json",
213 "pi1"}));
214
215TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700216 const std::string config_file =
217 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700218
219 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
220 aos::configuration::ReadConfig(config_file);
221
Tyler Chatowa79419d2020-08-12 20:12:11 -0700222 auto new_config = aos::configuration::MergeWithConfig(
223 &config.message(), absl::StrFormat(
224 R"({"applications": [
225 {
226 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700227 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800228 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700229 },
230 {
231 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700232 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800233 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700234 }
235 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700236 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
237 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700238
239 const aos::Configuration *config_msg = &new_config.message();
240
241 // Set up starter with config file
242 aos::starter::Starter starter(config_msg);
243
244 // Create an event loop to watch for ping messages, verifying it actually
245 // started.
246 aos::ShmEventLoop watcher_loop(config_msg);
247 watcher_loop.SkipAosLog();
248
249 watcher_loop
250 .AddTimer([&watcher_loop] {
251 watcher_loop.Exit();
252 FAIL();
253 })
Philipp Schradera6712522023-07-05 20:25:11 -0700254 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700255
256 int test_stage = 0;
257 uint64_t id;
258
259 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
260 &id](const aos::starter::Status &status) {
261 const aos::starter::ApplicationStatus *app_status =
262 FindApplicationStatus(status, "ping");
263 if (app_status == nullptr) {
264 return;
265 }
266
267 switch (test_stage) {
268 case 0: {
269 if (app_status->has_state() &&
270 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700271 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700272 test_stage = 1;
273 ASSERT_TRUE(app_status->has_pid());
274 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
275 ASSERT_TRUE(app_status->has_id());
276 id = app_status->id();
277 }
278 break;
279 }
280
281 case 1: {
282 if (app_status->has_state() &&
283 app_status->state() == aos::starter::State::RUNNING &&
284 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700285 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700286 watcher_loop.Exit();
287 SUCCEED();
288 }
289 break;
290 }
291 }
292 });
293
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700294 SetupStarterCleanup(&starter);
295
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700296 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700297 watcher_loop.Run();
298
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700299 test_done_ = true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700300}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700301
James Kuszmaul293b2172021-11-10 16:20:48 -0800302TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700303 const std::string config_file =
304 ArtifactPath("aos/events/pingpong_config.json");
305
306 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
307 aos::configuration::ReadConfig(config_file);
308
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700309 auto new_config = aos::configuration::MergeWithConfig(
310 &config.message(), absl::StrFormat(
311 R"({"applications": [
312 {
313 "name": "ping",
314 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800315 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700316 "autostart": false
317 },
318 {
319 "name": "pong",
320 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800321 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700322 }
323 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700324 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
325 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700326
327 const aos::Configuration *config_msg = &new_config.message();
328
329 // Set up starter with config file
330 aos::starter::Starter starter(config_msg);
331
332 // Create an event loop to watch for the application starting up.
333 aos::ShmEventLoop watcher_loop(config_msg);
334 watcher_loop.SkipAosLog();
335
336 watcher_loop
337 .AddTimer([&watcher_loop] {
338 watcher_loop.Exit();
339 FAIL();
340 })
Philipp Schradera6712522023-07-05 20:25:11 -0700341 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700342
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700343 int pong_running_count = 0;
344 watcher_loop.MakeWatcher("/aos", [&watcher_loop, &pong_running_count](
James Kuszmaul6295a642022-03-22 15:23:59 -0700345 const aos::starter::Status &status) {
346 const aos::starter::ApplicationStatus *ping_app_status =
347 FindApplicationStatus(status, "ping");
348 const aos::starter::ApplicationStatus *pong_app_status =
349 FindApplicationStatus(status, "pong");
350 if (ping_app_status == nullptr || pong_app_status == nullptr) {
351 return;
352 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700353
James Kuszmaul6295a642022-03-22 15:23:59 -0700354 if (ping_app_status->has_state() &&
355 ping_app_status->state() != aos::starter::State::STOPPED) {
356 watcher_loop.Exit();
357 FAIL();
358 }
359 if (pong_app_status->has_state() &&
360 pong_app_status->state() == aos::starter::State::RUNNING) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700361 ++pong_running_count;
362 // Sometimes if the timing for everything is *just* off, then the
363 // process_info will say that the process name is "starter_test" because
364 // it grabbed the name after the fork() but before the execvp(). To
365 // protect against that, wait an extra cycle. If things aren't fixed by
366 // the second cycle, then that is a problem.
James Kuszmaul37a56af2023-07-29 15:15:16 -0700367 if (pong_running_count < 3) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700368 return;
369 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700370 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700371 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
372 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700373 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700374 ASSERT_TRUE(pong_app_status->process_info()->has_cpu_usage());
375 ASSERT_LE(0.0, pong_app_status->process_info()->cpu_usage());
James Kuszmaul8544c492023-07-31 15:00:38 -0700376 ASSERT_TRUE(pong_app_status->has_has_active_timing_report());
377 ASSERT_TRUE(pong_app_status->has_active_timing_report());
James Kuszmaul6295a642022-03-22 15:23:59 -0700378 watcher_loop.Exit();
379 SUCCEED();
380 }
381 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700382
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700383 SetupStarterCleanup(&starter);
384
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700385 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700386 watcher_loop.Run();
387
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700388 test_done_ = true;
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700389}
390
James Kuszmaule7c7e582022-01-07 18:50:01 -0800391// Tests that starterd respects autorestart.
392TEST_F(StarterdTest, DeathNoRestartTest) {
393 const std::string config_file =
394 ArtifactPath("aos/events/pingpong_config.json");
395
396 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
397 aos::configuration::ReadConfig(config_file);
398
James Kuszmaule7c7e582022-01-07 18:50:01 -0800399 auto new_config = aos::configuration::MergeWithConfig(
400 &config.message(), absl::StrFormat(
401 R"({"applications": [
402 {
403 "name": "ping",
404 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700405 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800406 "autorestart": false
407 },
408 {
409 "name": "pong",
410 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700411 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800412 }
413 ]})",
Austin Schuh59398d32023-05-03 08:10:55 -0700414 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
415 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800416
417 const aos::Configuration *config_msg = &new_config.message();
418
419 // Set up starter with config file
420 aos::starter::Starter starter(config_msg);
421
422 // Create an event loop to watch for the Status message to watch the state
423 // transitions.
424 aos::ShmEventLoop watcher_loop(config_msg);
425 watcher_loop.SkipAosLog();
426
427 watcher_loop
428 .AddTimer([&watcher_loop] {
429 watcher_loop.Exit();
430 SUCCEED();
431 })
Philipp Schradera6712522023-07-05 20:25:11 -0700432 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800433
434 int test_stage = 0;
435 uint64_t id;
436
437 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
438 &id](const aos::starter::Status &status) {
439 const aos::starter::ApplicationStatus *app_status =
440 FindApplicationStatus(status, "ping");
441 if (app_status == nullptr) {
442 return;
443 }
444
445 switch (test_stage) {
446 case 0: {
447 if (app_status->has_state() &&
448 app_status->state() == aos::starter::State::RUNNING) {
449 LOG(INFO) << "Ping is running";
450 test_stage = 1;
451 ASSERT_TRUE(app_status->has_pid());
452 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
453 ASSERT_TRUE(app_status->has_id());
454 id = app_status->id();
455 }
456 break;
457 }
458
459 case 1: {
460 if (app_status->has_state() &&
461 app_status->state() == aos::starter::State::RUNNING &&
462 app_status->has_id() && app_status->id() != id) {
463 LOG(INFO) << "Ping restarted, it shouldn't...";
464 watcher_loop.Exit();
465 FAIL();
466 }
467 break;
468 }
469 }
470 });
471
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700472 SetupStarterCleanup(&starter);
473
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700474 ThreadedStarterRunner starterd_thread(&starter);
James Kuszmaule7c7e582022-01-07 18:50:01 -0800475 watcher_loop.Run();
476
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700477 test_done_ = true;
James Kuszmaule7c7e582022-01-07 18:50:01 -0800478}
479
Austin Schuh59398d32023-05-03 08:10:55 -0700480TEST_F(StarterdTest, StarterChainTest) {
481 // This test was written in response to a bug that was found
482 // in StarterClient::Succeed. The bug caused the timeout handler
483 // to be reset after the success handler was called.
484 // the bug has been fixed, and this test will ensure it does
485 // not regress.
486 const std::string config_file =
487 ArtifactPath("aos/events/pingpong_config.json");
488 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
489 aos::configuration::ReadConfig(config_file);
490 auto new_config = aos::configuration::MergeWithConfig(
491 &config.message(), absl::StrFormat(
492 R"({"applications": [
493 {
494 "name": "ping",
495 "executable_name": "%s",
496 "args": ["--shm_base", "%s"],
497 "autorestart": false
498 },
499 {
500 "name": "pong",
501 "executable_name": "%s",
502 "args": ["--shm_base", "%s"]
503 }
504 ]})",
505 ArtifactPath("aos/events/ping"), FLAGS_shm_base,
506 ArtifactPath("aos/events/pong"), FLAGS_shm_base));
507
508 const aos::Configuration *config_msg = &new_config.message();
509 // Set up starter with config file
510 aos::starter::Starter starter(config_msg);
511 aos::ShmEventLoop client_loop(config_msg);
512 client_loop.SkipAosLog();
513 StarterClient client(&client_loop);
514 bool success = false;
515 auto client_node = client_loop.node();
516
517 // limit the amount of time we will wait for the test to finish.
518 client_loop
519 .AddTimer([&client_loop] {
520 client_loop.Exit();
521 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
522 "The chain of stages defined below did not complete "
523 "within the time limit.";
524 })
Philipp Schradera6712522023-07-05 20:25:11 -0700525 ->Schedule(client_loop.monotonic_now() + std::chrono::seconds(20));
Austin Schuh59398d32023-05-03 08:10:55 -0700526
527 // variables have been defined, here we define the body of the test.
528 // We want stage1 to succeed, triggering stage2.
529 // We want stage2 to timeout, triggering stage3.
530
531 auto stage3 = [&client_loop, &success]() {
532 LOG(INFO) << "Begin stage3.";
533 SUCCEED();
534 success = true;
535 client_loop.Exit();
536 LOG(INFO) << "End stage3.";
537 };
538 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
539 LOG(INFO) << "Begin stage2";
540 test_done_ = true; // trigger `starter` to exit.
541
542 // wait for the starter event loop to close, so we can
543 // intentionally trigger a timeout.
544 int attempts = 0;
545 while (starter.event_loop()->is_running()) {
546 ++attempts;
547 if (attempts > 5) {
548 LOG(INFO) << "Timeout while waiting for starter to exit";
549 return;
550 }
551 LOG(INFO) << "Waiting for starter to close.";
552 std::this_thread::sleep_for(std::chrono::seconds(1));
553 }
Austin Schuh0dcdf1d2024-03-27 12:55:41 -0700554 client.SetTimeoutHandler(std::ref(stage3));
Austin Schuh59398d32023-05-03 08:10:55 -0700555 client.SetSuccessHandler([]() {
556 LOG(INFO) << "stage3 success handler called.";
557 FAIL() << ": Command should not have succeeded here.";
558 });
559 // we want this command to timeout
560 client.SendCommands({{Command::START, "ping", {client_node}}},
561 std::chrono::seconds(5));
562 LOG(INFO) << "End stage2";
563 };
564 auto stage1 = [&client, &client_node, &stage2] {
565 LOG(INFO) << "Begin stage1";
566 client.SetTimeoutHandler(
567 []() { FAIL() << ": Command should not have timed out."; });
Austin Schuh0dcdf1d2024-03-27 12:55:41 -0700568 client.SetSuccessHandler(std::ref(stage2));
Austin Schuh59398d32023-05-03 08:10:55 -0700569 client.SendCommands({{Command::STOP, "ping", {client_node}}},
570 std::chrono::seconds(5));
571 LOG(INFO) << "End stage1";
572 };
573 // start the test body
Philipp Schradera6712522023-07-05 20:25:11 -0700574 client_loop.AddTimer(stage1)->Schedule(client_loop.monotonic_now() +
575 std::chrono::milliseconds(1));
Austin Schuh59398d32023-05-03 08:10:55 -0700576
577 // prepare the cleanup for starter. This will finish when we call
578 // `test_done_ = true;`.
579 SetupStarterCleanup(&starter);
580
581 // run `starter.Run()` in a thread to simulate it running on
582 // another process.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700583 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh59398d32023-05-03 08:10:55 -0700584
Austin Schuh59398d32023-05-03 08:10:55 -0700585 client_loop.Run();
586 EXPECT_TRUE(success);
587 ASSERT_FALSE(starter.event_loop()->is_running());
Austin Schuh59398d32023-05-03 08:10:55 -0700588}
589
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800590} // namespace aos::starter