blob: 65d43aa35ec5af95e02e9c2558967fba7aafd6dc [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
Austin Schuh99f7c6a2024-06-25 22:07:44 -070011#include "absl/flags/flag.h"
12#include "absl/flags/reflection.h"
13#include "absl/log/check.h"
14#include "absl/log/log.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070015#include "absl/strings/str_format.h"
16#include "flatbuffers/string.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070017#include "gtest/gtest.h"
18
Stephan Pleinesf581a072024-05-23 20:59:27 -070019#include "aos/configuration.h"
20#include "aos/events/event_loop.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070021#include "aos/events/ping_generated.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070022#include "aos/events/shm_event_loop.h"
23#include "aos/flatbuffers.h"
Austin Schuh59398d32023-05-03 08:10:55 -070024#include "aos/ipc_lib/event.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070025#include "aos/ipc_lib/shm_base.h"
26#include "aos/json_to_flatbuffer.h"
James Kuszmaul293b2172021-11-10 16:20:48 -080027#include "aos/network/team_number.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070028#include "aos/starter/starter_generated.h"
29#include "aos/starter/starter_rpc_generated.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070030#include "aos/starter/starter_rpc_lib.h"
31#include "aos/starter/starterd_lib.h"
Austin Schuh373f1762021-06-02 21:07:09 -070032#include "aos/testing/path.h"
Austin Schuhfc0bca42021-12-31 21:49:41 -080033#include "aos/util/file.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070034#include "aos/util/process_info_generated.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070035
Austin Schuh373f1762021-06-02 21:07:09 -070036using aos::testing::ArtifactPath;
37
Stephan Pleinesf63bde82024-01-13 15:59:33 -080038namespace aos::starter {
Austin Schuh5f79a5a2021-10-12 17:46:50 -070039
Austin Schuh83cbb1e2023-06-23 12:59:02 -070040class ThreadedStarterRunner {
41 public:
42 ThreadedStarterRunner(Starter *starter)
43 : my_thread_([this, starter]() {
44 starter->event_loop()->OnRun([this]() { event_.Set(); });
45 starter->Run();
46 }) {
47 event_.Wait();
48 }
49
50 ~ThreadedStarterRunner() { my_thread_.join(); }
51
52 private:
53 aos::Event event_;
54 std::thread my_thread_;
55};
56
James Kuszmaul293b2172021-11-10 16:20:48 -080057class StarterdTest : public ::testing::Test {
58 public:
Austin Schuh59398d32023-05-03 08:10:55 -070059 StarterdTest() {
James Kuszmaul293b2172021-11-10 16:20:48 -080060 // Nuke the shm dir:
Austin Schuh99f7c6a2024-06-25 22:07:44 -070061 aos::util::UnlinkRecursive(absl::GetFlag(FLAGS_shm_base));
James Kuszmaul293b2172021-11-10 16:20:48 -080062 }
63
64 protected:
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070065 void SetupStarterCleanup(aos::starter::Starter *starter) {
66 starter->event_loop()
67 ->AddTimer([this, starter]() {
68 if (test_done_) {
69 starter->Cleanup();
70 }
71 })
Philipp Schradera6712522023-07-05 20:25:11 -070072 ->Schedule(starter->event_loop()->monotonic_now(),
73 std::chrono::milliseconds(100));
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070074 }
75
Austin Schuh99f7c6a2024-06-25 22:07:44 -070076 absl::FlagSaver flag_saver_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070077 // Used to track when the test completes so that we can clean up the starter
78 // in its thread.
79 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080080};
81
82struct TestParams {
83 std::string config;
84 std::string hostname;
85};
86
87class StarterdConfigParamTest
88 : public StarterdTest,
89 public ::testing::WithParamInterface<TestParams> {};
90
91TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
Austin Schuh99f7c6a2024-06-25 22:07:44 -070092 absl::FlagSaver flag_saver;
93 absl::SetFlag(&FLAGS_override_hostname, GetParam().hostname);
James Kuszmaul293b2172021-11-10 16:20:48 -080094 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070095
96 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
97 aos::configuration::ReadConfig(config_file);
98
Tyler Chatowa79419d2020-08-12 20:12:11 -070099 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -0800100 &config.message(),
101 absl::StrFormat(
102 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -0700103 {
104 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700105 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800106 "nodes": ["pi1"],
107 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700108 },
109 {
110 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700111 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800112 "nodes": ["pi1"],
113 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700114 }
115 ]})",
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700116 ArtifactPath("aos/events/ping"), absl::GetFlag(FLAGS_shm_base),
117 config_file, GetParam().hostname, ArtifactPath("aos/events/pong"),
118 absl::GetFlag(FLAGS_shm_base), config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700119
120 const aos::Configuration *config_msg = &new_config.message();
121
122 // Set up starter with config file
123 aos::starter::Starter starter(config_msg);
124
125 // Create an event loop to watch for ping messages, verifying it actually
126 // started.
127 aos::ShmEventLoop watcher_loop(config_msg);
128 watcher_loop.SkipAosLog();
129
James Kuszmaul293b2172021-11-10 16:20:48 -0800130 aos::ShmEventLoop client_loop(config_msg);
131 client_loop.SkipAosLog();
132 StarterClient client(&client_loop);
133 client.SetTimeoutHandler(
134 []() { FAIL() << ": Command should not have timed out."; });
135 bool success = false;
136 client.SetSuccessHandler([&success, &client_loop]() {
137 client_loop.Exit();
138 success = true;
139 });
140
Tyler Chatowa79419d2020-08-12 20:12:11 -0700141 watcher_loop
142 .AddTimer([&watcher_loop] {
143 watcher_loop.Exit();
144 FAIL();
145 })
Philipp Schradera6712522023-07-05 20:25:11 -0700146 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700147
James Kuszmaul293b2172021-11-10 16:20:48 -0800148 std::atomic<int> test_stage = 0;
149 // Watch on the client loop since we need to interact with the StarterClient.
150 client_loop.MakeWatcher("/test", [&test_stage, &client,
151 &client_loop](const aos::examples::Ping &) {
152 switch (test_stage) {
153 case 1: {
154 test_stage = 2;
155 break;
156 }
157 case 2: {
158 {
159 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
160 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700161 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800162 test_stage = 3;
163 break;
164 }
165 }
166 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700167
168 watcher_loop.MakeWatcher(
169 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
170 const aos::starter::ApplicationStatus *app_status =
171 FindApplicationStatus(status, "ping");
172 if (app_status == nullptr) {
173 return;
174 }
175
176 switch (test_stage) {
177 case 0: {
178 if (app_status->has_state() &&
179 app_status->state() == aos::starter::State::RUNNING) {
180 test_stage = 1;
181 }
182 break;
183 }
184
185 case 3: {
186 if (app_status->has_state() &&
187 app_status->state() == aos::starter::State::STOPPED) {
188 watcher_loop.Exit();
189 SUCCEED();
190 }
191 break;
192 }
193 }
194 });
195
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700196 SetupStarterCleanup(&starter);
197
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700198 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700199
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700200 aos::Event event;
201 client_loop.OnRun([&event]() { event.Set(); });
202 std::thread client_thread([&client_loop] { client_loop.Run(); });
203 event.Wait();
Austin Schuh59398d32023-05-03 08:10:55 -0700204
205 watcher_loop.Run();
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700206 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800207 client_thread.join();
Austin Schuhcad2af92023-05-28 13:56:55 -0700208 ASSERT_TRUE(success);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700209}
210
James Kuszmaul293b2172021-11-10 16:20:48 -0800211INSTANTIATE_TEST_SUITE_P(
212 StarterdConfigParamTest, StarterdConfigParamTest,
213 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
214 TestParams{"aos/starter/multinode_pingpong_config.json",
215 "pi1"}));
216
217TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700218 const std::string config_file =
219 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700220
221 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
222 aos::configuration::ReadConfig(config_file);
223
Tyler Chatowa79419d2020-08-12 20:12:11 -0700224 auto new_config = aos::configuration::MergeWithConfig(
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700225 &config.message(),
226 absl::StrFormat(
227 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -0700228 {
229 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700230 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800231 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700232 },
233 {
234 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700235 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800236 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700237 }
238 ]})",
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700239 ArtifactPath("aos/events/ping"), absl::GetFlag(FLAGS_shm_base),
240 ArtifactPath("aos/events/pong"), absl::GetFlag(FLAGS_shm_base)));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700241
242 const aos::Configuration *config_msg = &new_config.message();
243
244 // Set up starter with config file
245 aos::starter::Starter starter(config_msg);
246
247 // Create an event loop to watch for ping messages, verifying it actually
248 // started.
249 aos::ShmEventLoop watcher_loop(config_msg);
250 watcher_loop.SkipAosLog();
251
252 watcher_loop
253 .AddTimer([&watcher_loop] {
254 watcher_loop.Exit();
255 FAIL();
256 })
Philipp Schradera6712522023-07-05 20:25:11 -0700257 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700258
259 int test_stage = 0;
260 uint64_t id;
261
262 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
263 &id](const aos::starter::Status &status) {
264 const aos::starter::ApplicationStatus *app_status =
265 FindApplicationStatus(status, "ping");
266 if (app_status == nullptr) {
267 return;
268 }
269
270 switch (test_stage) {
271 case 0: {
272 if (app_status->has_state() &&
273 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700274 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700275 test_stage = 1;
276 ASSERT_TRUE(app_status->has_pid());
277 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
278 ASSERT_TRUE(app_status->has_id());
279 id = app_status->id();
280 }
281 break;
282 }
283
284 case 1: {
285 if (app_status->has_state() &&
286 app_status->state() == aos::starter::State::RUNNING &&
287 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700288 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700289 watcher_loop.Exit();
290 SUCCEED();
291 }
292 break;
293 }
294 }
295 });
296
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700297 SetupStarterCleanup(&starter);
298
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700299 ThreadedStarterRunner starterd_thread(&starter);
Tyler Chatowa79419d2020-08-12 20:12:11 -0700300 watcher_loop.Run();
301
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700302 test_done_ = true;
Tyler Chatowa79419d2020-08-12 20:12:11 -0700303}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700304
James Kuszmaul293b2172021-11-10 16:20:48 -0800305TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700306 const std::string config_file =
307 ArtifactPath("aos/events/pingpong_config.json");
308
309 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
310 aos::configuration::ReadConfig(config_file);
311
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700312 auto new_config = aos::configuration::MergeWithConfig(
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700313 &config.message(),
314 absl::StrFormat(
315 R"({"applications": [
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700316 {
317 "name": "ping",
318 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800319 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700320 "autostart": false
321 },
322 {
323 "name": "pong",
324 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800325 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700326 }
327 ]})",
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700328 ArtifactPath("aos/events/ping"), absl::GetFlag(FLAGS_shm_base),
329 ArtifactPath("aos/events/pong"), absl::GetFlag(FLAGS_shm_base)));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700330
331 const aos::Configuration *config_msg = &new_config.message();
332
333 // Set up starter with config file
334 aos::starter::Starter starter(config_msg);
335
336 // Create an event loop to watch for the application starting up.
337 aos::ShmEventLoop watcher_loop(config_msg);
338 watcher_loop.SkipAosLog();
339
340 watcher_loop
341 .AddTimer([&watcher_loop] {
342 watcher_loop.Exit();
343 FAIL();
344 })
Philipp Schradera6712522023-07-05 20:25:11 -0700345 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(7));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700346
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700347 int pong_running_count = 0;
348 watcher_loop.MakeWatcher("/aos", [&watcher_loop, &pong_running_count](
James Kuszmaul6295a642022-03-22 15:23:59 -0700349 const aos::starter::Status &status) {
350 const aos::starter::ApplicationStatus *ping_app_status =
351 FindApplicationStatus(status, "ping");
352 const aos::starter::ApplicationStatus *pong_app_status =
353 FindApplicationStatus(status, "pong");
354 if (ping_app_status == nullptr || pong_app_status == nullptr) {
355 return;
356 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700357
James Kuszmaul6295a642022-03-22 15:23:59 -0700358 if (ping_app_status->has_state() &&
359 ping_app_status->state() != aos::starter::State::STOPPED) {
360 watcher_loop.Exit();
361 FAIL();
362 }
363 if (pong_app_status->has_state() &&
364 pong_app_status->state() == aos::starter::State::RUNNING) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700365 ++pong_running_count;
366 // Sometimes if the timing for everything is *just* off, then the
367 // process_info will say that the process name is "starter_test" because
368 // it grabbed the name after the fork() but before the execvp(). To
369 // protect against that, wait an extra cycle. If things aren't fixed by
370 // the second cycle, then that is a problem.
James Kuszmaul37a56af2023-07-29 15:15:16 -0700371 if (pong_running_count < 3) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700372 return;
373 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700374 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700375 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
376 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700377 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700378 ASSERT_TRUE(pong_app_status->process_info()->has_cpu_usage());
379 ASSERT_LE(0.0, pong_app_status->process_info()->cpu_usage());
James Kuszmaul8544c492023-07-31 15:00:38 -0700380 ASSERT_TRUE(pong_app_status->has_has_active_timing_report());
381 ASSERT_TRUE(pong_app_status->has_active_timing_report());
James Kuszmaul6295a642022-03-22 15:23:59 -0700382 watcher_loop.Exit();
383 SUCCEED();
384 }
385 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700386
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700387 SetupStarterCleanup(&starter);
388
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700389 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700390 watcher_loop.Run();
391
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700392 test_done_ = true;
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700393}
394
James Kuszmaule7c7e582022-01-07 18:50:01 -0800395// Tests that starterd respects autorestart.
396TEST_F(StarterdTest, DeathNoRestartTest) {
397 const std::string config_file =
398 ArtifactPath("aos/events/pingpong_config.json");
399
400 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
401 aos::configuration::ReadConfig(config_file);
402
James Kuszmaule7c7e582022-01-07 18:50:01 -0800403 auto new_config = aos::configuration::MergeWithConfig(
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700404 &config.message(),
405 absl::StrFormat(
406 R"({"applications": [
James Kuszmaule7c7e582022-01-07 18:50:01 -0800407 {
408 "name": "ping",
409 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700410 "args": ["--shm_base", "%s"],
James Kuszmaule7c7e582022-01-07 18:50:01 -0800411 "autorestart": false
412 },
413 {
414 "name": "pong",
415 "executable_name": "%s",
Austin Schuh59398d32023-05-03 08:10:55 -0700416 "args": ["--shm_base", "%s"]
James Kuszmaule7c7e582022-01-07 18:50:01 -0800417 }
418 ]})",
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700419 ArtifactPath("aos/events/ping"), absl::GetFlag(FLAGS_shm_base),
420 ArtifactPath("aos/events/pong"), absl::GetFlag(FLAGS_shm_base)));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800421
422 const aos::Configuration *config_msg = &new_config.message();
423
424 // Set up starter with config file
425 aos::starter::Starter starter(config_msg);
426
427 // Create an event loop to watch for the Status message to watch the state
428 // transitions.
429 aos::ShmEventLoop watcher_loop(config_msg);
430 watcher_loop.SkipAosLog();
431
432 watcher_loop
433 .AddTimer([&watcher_loop] {
434 watcher_loop.Exit();
435 SUCCEED();
436 })
Philipp Schradera6712522023-07-05 20:25:11 -0700437 ->Schedule(watcher_loop.monotonic_now() + std::chrono::seconds(11));
James Kuszmaule7c7e582022-01-07 18:50:01 -0800438
439 int test_stage = 0;
440 uint64_t id;
441
442 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
443 &id](const aos::starter::Status &status) {
444 const aos::starter::ApplicationStatus *app_status =
445 FindApplicationStatus(status, "ping");
446 if (app_status == nullptr) {
447 return;
448 }
449
450 switch (test_stage) {
451 case 0: {
452 if (app_status->has_state() &&
453 app_status->state() == aos::starter::State::RUNNING) {
454 LOG(INFO) << "Ping is running";
455 test_stage = 1;
456 ASSERT_TRUE(app_status->has_pid());
457 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
458 ASSERT_TRUE(app_status->has_id());
459 id = app_status->id();
460 }
461 break;
462 }
463
464 case 1: {
465 if (app_status->has_state() &&
466 app_status->state() == aos::starter::State::RUNNING &&
467 app_status->has_id() && app_status->id() != id) {
468 LOG(INFO) << "Ping restarted, it shouldn't...";
469 watcher_loop.Exit();
470 FAIL();
471 }
472 break;
473 }
474 }
475 });
476
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700477 SetupStarterCleanup(&starter);
478
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700479 ThreadedStarterRunner starterd_thread(&starter);
James Kuszmaule7c7e582022-01-07 18:50:01 -0800480 watcher_loop.Run();
481
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700482 test_done_ = true;
James Kuszmaule7c7e582022-01-07 18:50:01 -0800483}
484
Austin Schuh59398d32023-05-03 08:10:55 -0700485TEST_F(StarterdTest, StarterChainTest) {
486 // This test was written in response to a bug that was found
487 // in StarterClient::Succeed. The bug caused the timeout handler
488 // to be reset after the success handler was called.
489 // the bug has been fixed, and this test will ensure it does
490 // not regress.
491 const std::string config_file =
492 ArtifactPath("aos/events/pingpong_config.json");
493 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
494 aos::configuration::ReadConfig(config_file);
495 auto new_config = aos::configuration::MergeWithConfig(
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700496 &config.message(),
497 absl::StrFormat(
498 R"({"applications": [
Austin Schuh59398d32023-05-03 08:10:55 -0700499 {
500 "name": "ping",
501 "executable_name": "%s",
502 "args": ["--shm_base", "%s"],
503 "autorestart": false
504 },
505 {
506 "name": "pong",
507 "executable_name": "%s",
508 "args": ["--shm_base", "%s"]
509 }
510 ]})",
Austin Schuh99f7c6a2024-06-25 22:07:44 -0700511 ArtifactPath("aos/events/ping"), absl::GetFlag(FLAGS_shm_base),
512 ArtifactPath("aos/events/pong"), absl::GetFlag(FLAGS_shm_base)));
Austin Schuh59398d32023-05-03 08:10:55 -0700513
514 const aos::Configuration *config_msg = &new_config.message();
515 // Set up starter with config file
516 aos::starter::Starter starter(config_msg);
517 aos::ShmEventLoop client_loop(config_msg);
518 client_loop.SkipAosLog();
519 StarterClient client(&client_loop);
520 bool success = false;
521 auto client_node = client_loop.node();
522
523 // limit the amount of time we will wait for the test to finish.
524 client_loop
525 .AddTimer([&client_loop] {
526 client_loop.Exit();
527 FAIL() << "ERROR: The test has failed, the watcher has timed out. "
528 "The chain of stages defined below did not complete "
529 "within the time limit.";
530 })
Philipp Schradera6712522023-07-05 20:25:11 -0700531 ->Schedule(client_loop.monotonic_now() + std::chrono::seconds(20));
Austin Schuh59398d32023-05-03 08:10:55 -0700532
533 // variables have been defined, here we define the body of the test.
534 // We want stage1 to succeed, triggering stage2.
535 // We want stage2 to timeout, triggering stage3.
536
537 auto stage3 = [&client_loop, &success]() {
538 LOG(INFO) << "Begin stage3.";
539 SUCCEED();
540 success = true;
541 client_loop.Exit();
542 LOG(INFO) << "End stage3.";
543 };
544 auto stage2 = [this, &starter, &client, &client_node, &stage3] {
545 LOG(INFO) << "Begin stage2";
546 test_done_ = true; // trigger `starter` to exit.
547
548 // wait for the starter event loop to close, so we can
549 // intentionally trigger a timeout.
550 int attempts = 0;
551 while (starter.event_loop()->is_running()) {
552 ++attempts;
553 if (attempts > 5) {
554 LOG(INFO) << "Timeout while waiting for starter to exit";
555 return;
556 }
557 LOG(INFO) << "Waiting for starter to close.";
558 std::this_thread::sleep_for(std::chrono::seconds(1));
559 }
Austin Schuh0dcdf1d2024-03-27 12:55:41 -0700560 client.SetTimeoutHandler(std::ref(stage3));
Austin Schuh59398d32023-05-03 08:10:55 -0700561 client.SetSuccessHandler([]() {
562 LOG(INFO) << "stage3 success handler called.";
563 FAIL() << ": Command should not have succeeded here.";
564 });
565 // we want this command to timeout
566 client.SendCommands({{Command::START, "ping", {client_node}}},
567 std::chrono::seconds(5));
568 LOG(INFO) << "End stage2";
569 };
570 auto stage1 = [&client, &client_node, &stage2] {
571 LOG(INFO) << "Begin stage1";
572 client.SetTimeoutHandler(
573 []() { FAIL() << ": Command should not have timed out."; });
Austin Schuh0dcdf1d2024-03-27 12:55:41 -0700574 client.SetSuccessHandler(std::ref(stage2));
Austin Schuh59398d32023-05-03 08:10:55 -0700575 client.SendCommands({{Command::STOP, "ping", {client_node}}},
576 std::chrono::seconds(5));
577 LOG(INFO) << "End stage1";
578 };
579 // start the test body
Philipp Schradera6712522023-07-05 20:25:11 -0700580 client_loop.AddTimer(stage1)->Schedule(client_loop.monotonic_now() +
581 std::chrono::milliseconds(1));
Austin Schuh59398d32023-05-03 08:10:55 -0700582
583 // prepare the cleanup for starter. This will finish when we call
584 // `test_done_ = true;`.
585 SetupStarterCleanup(&starter);
586
587 // run `starter.Run()` in a thread to simulate it running on
588 // another process.
Austin Schuh83cbb1e2023-06-23 12:59:02 -0700589 ThreadedStarterRunner starterd_thread(&starter);
Austin Schuh59398d32023-05-03 08:10:55 -0700590
Austin Schuh59398d32023-05-03 08:10:55 -0700591 client_loop.Run();
592 EXPECT_TRUE(success);
593 ASSERT_FALSE(starter.event_loop()->is_running());
Austin Schuh59398d32023-05-03 08:10:55 -0700594}
595
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800596} // namespace aos::starter