blob: 06f961acad41abbf1448abff3a05443686a0781a [file] [log] [blame]
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001#include <csignal>
Tyler Chatowa79419d2020-08-12 20:12:11 -07002#include <future>
3#include <thread>
4
5#include "aos/events/ping_generated.h"
6#include "aos/events/pong_generated.h"
James Kuszmaul293b2172021-11-10 16:20:48 -08007#include "aos/network/team_number.h"
Austin Schuh373f1762021-06-02 21:07:09 -07008#include "aos/testing/path.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -07009#include "aos/testing/tmpdir.h"
Austin Schuhfc0bca42021-12-31 21:49:41 -080010#include "aos/util/file.h"
Tyler Chatowa79419d2020-08-12 20:12:11 -070011#include "gtest/gtest.h"
12#include "starter_rpc_lib.h"
13#include "starterd_lib.h"
14
Austin Schuh373f1762021-06-02 21:07:09 -070015using aos::testing::ArtifactPath;
16
Austin Schuh5f79a5a2021-10-12 17:46:50 -070017namespace aos {
18namespace starter {
19
James Kuszmaul293b2172021-11-10 16:20:48 -080020class StarterdTest : public ::testing::Test {
21 public:
22 StarterdTest() : shm_dir_(aos::testing::TestTmpDir() + "/aos") {
23 FLAGS_shm_base = shm_dir_;
24
25 // Nuke the shm dir:
Austin Schuhfc0bca42021-12-31 21:49:41 -080026 aos::util::UnlinkRecursive(shm_dir_);
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(),
38 std::chrono::seconds(1));
39 }
40
James Kuszmaul293b2172021-11-10 16:20:48 -080041 gflags::FlagSaver flag_saver_;
42 std::string shm_dir_;
James Kuszmaul6b35e3a2022-04-06 15:00:39 -070043 // Used to track when the test completes so that we can clean up the starter
44 // in its thread.
45 std::atomic<bool> test_done_{false};
James Kuszmaul293b2172021-11-10 16:20:48 -080046};
47
48struct TestParams {
49 std::string config;
50 std::string hostname;
51};
52
53class StarterdConfigParamTest
54 : public StarterdTest,
55 public ::testing::WithParamInterface<TestParams> {};
56
57TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
58 gflags::FlagSaver flag_saver;
59 FLAGS_override_hostname = GetParam().hostname;
60 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070061
62 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
63 aos::configuration::ReadConfig(config_file);
64
Tyler Chatowa79419d2020-08-12 20:12:11 -070065 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080066 &config.message(),
67 absl::StrFormat(
68 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070069 {
70 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070071 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080072 "nodes": ["pi1"],
73 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070074 },
75 {
76 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070077 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080078 "nodes": ["pi1"],
79 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070080 }
81 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -080082 ArtifactPath("aos/events/ping"), shm_dir_, config_file,
83 GetParam().hostname, ArtifactPath("aos/events/pong"), shm_dir_,
84 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -070085
86 const aos::Configuration *config_msg = &new_config.message();
87
88 // Set up starter with config file
89 aos::starter::Starter starter(config_msg);
90
91 // Create an event loop to watch for ping messages, verifying it actually
92 // started.
93 aos::ShmEventLoop watcher_loop(config_msg);
94 watcher_loop.SkipAosLog();
95
James Kuszmaul293b2172021-11-10 16:20:48 -080096 aos::ShmEventLoop client_loop(config_msg);
97 client_loop.SkipAosLog();
98 StarterClient client(&client_loop);
99 client.SetTimeoutHandler(
100 []() { FAIL() << ": Command should not have timed out."; });
101 bool success = false;
102 client.SetSuccessHandler([&success, &client_loop]() {
103 client_loop.Exit();
104 success = true;
105 });
106
Tyler Chatowa79419d2020-08-12 20:12:11 -0700107 watcher_loop
108 .AddTimer([&watcher_loop] {
109 watcher_loop.Exit();
110 FAIL();
111 })
112 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
113
James Kuszmaul293b2172021-11-10 16:20:48 -0800114 std::atomic<int> test_stage = 0;
115 // Watch on the client loop since we need to interact with the StarterClient.
116 client_loop.MakeWatcher("/test", [&test_stage, &client,
117 &client_loop](const aos::examples::Ping &) {
118 switch (test_stage) {
119 case 1: {
120 test_stage = 2;
121 break;
122 }
123 case 2: {
124 {
125 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
126 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700127 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800128 test_stage = 3;
129 break;
130 }
131 }
132 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700133
134 watcher_loop.MakeWatcher(
135 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
136 const aos::starter::ApplicationStatus *app_status =
137 FindApplicationStatus(status, "ping");
138 if (app_status == nullptr) {
139 return;
140 }
141
142 switch (test_stage) {
143 case 0: {
144 if (app_status->has_state() &&
145 app_status->state() == aos::starter::State::RUNNING) {
146 test_stage = 1;
147 }
148 break;
149 }
150
151 case 3: {
152 if (app_status->has_state() &&
153 app_status->state() == aos::starter::State::STOPPED) {
154 watcher_loop.Exit();
155 SUCCEED();
156 }
157 break;
158 }
159 }
160 });
161
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700162 SetupStarterCleanup(&starter);
163
Tyler Chatowa79419d2020-08-12 20:12:11 -0700164 std::thread starterd_thread([&starter] { starter.Run(); });
James Kuszmaul293b2172021-11-10 16:20:48 -0800165 std::thread client_thread([&client_loop] { client_loop.Run(); });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700166 watcher_loop.Run();
167
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700168 test_done_ = true;
James Kuszmaul293b2172021-11-10 16:20:48 -0800169 client_thread.join();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700170 starterd_thread.join();
171}
172
James Kuszmaul293b2172021-11-10 16:20:48 -0800173INSTANTIATE_TEST_SUITE_P(
174 StarterdConfigParamTest, StarterdConfigParamTest,
175 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
176 TestParams{"aos/starter/multinode_pingpong_config.json",
177 "pi1"}));
178
179TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700180 const std::string config_file =
181 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700182
183 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
184 aos::configuration::ReadConfig(config_file);
185
Tyler Chatowa79419d2020-08-12 20:12:11 -0700186 auto new_config = aos::configuration::MergeWithConfig(
187 &config.message(), absl::StrFormat(
188 R"({"applications": [
189 {
190 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700191 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800192 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700193 },
194 {
195 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700196 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800197 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700198 }
199 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -0800200 ArtifactPath("aos/events/ping"), shm_dir_,
201 ArtifactPath("aos/events/pong"), shm_dir_));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700202
203 const aos::Configuration *config_msg = &new_config.message();
204
205 // Set up starter with config file
206 aos::starter::Starter starter(config_msg);
207
208 // Create an event loop to watch for ping messages, verifying it actually
209 // started.
210 aos::ShmEventLoop watcher_loop(config_msg);
211 watcher_loop.SkipAosLog();
212
213 watcher_loop
214 .AddTimer([&watcher_loop] {
215 watcher_loop.Exit();
216 FAIL();
217 })
Austin Schuha07b3ce2021-10-10 12:33:21 -0700218 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700219
220 int test_stage = 0;
221 uint64_t id;
222
223 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
224 &id](const aos::starter::Status &status) {
225 const aos::starter::ApplicationStatus *app_status =
226 FindApplicationStatus(status, "ping");
227 if (app_status == nullptr) {
228 return;
229 }
230
231 switch (test_stage) {
232 case 0: {
233 if (app_status->has_state() &&
234 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700235 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700236 test_stage = 1;
237 ASSERT_TRUE(app_status->has_pid());
238 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
239 ASSERT_TRUE(app_status->has_id());
240 id = app_status->id();
241 }
242 break;
243 }
244
245 case 1: {
246 if (app_status->has_state() &&
247 app_status->state() == aos::starter::State::RUNNING &&
248 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700249 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700250 watcher_loop.Exit();
251 SUCCEED();
252 }
253 break;
254 }
255 }
256 });
257
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700258 SetupStarterCleanup(&starter);
259
Tyler Chatowa79419d2020-08-12 20:12:11 -0700260 std::thread starterd_thread([&starter] { starter.Run(); });
261 watcher_loop.Run();
262
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700263 test_done_ = true;
264
Tyler Chatowa79419d2020-08-12 20:12:11 -0700265 starterd_thread.join();
266}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700267
James Kuszmaul293b2172021-11-10 16:20:48 -0800268TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700269 const std::string config_file =
270 ArtifactPath("aos/events/pingpong_config.json");
271
272 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
273 aos::configuration::ReadConfig(config_file);
274
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700275 auto new_config = aos::configuration::MergeWithConfig(
276 &config.message(), absl::StrFormat(
277 R"({"applications": [
278 {
279 "name": "ping",
280 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800281 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700282 "autostart": false
283 },
284 {
285 "name": "pong",
286 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800287 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700288 }
289 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -0800290 ArtifactPath("aos/events/ping"), shm_dir_,
291 ArtifactPath("aos/events/pong"), shm_dir_));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700292
293 const aos::Configuration *config_msg = &new_config.message();
294
295 // Set up starter with config file
296 aos::starter::Starter starter(config_msg);
297
298 // Create an event loop to watch for the application starting up.
299 aos::ShmEventLoop watcher_loop(config_msg);
300 watcher_loop.SkipAosLog();
301
302 watcher_loop
303 .AddTimer([&watcher_loop] {
304 watcher_loop.Exit();
305 FAIL();
306 })
307 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
308
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700309 int pong_running_count = 0;
310 watcher_loop.MakeWatcher("/aos", [&watcher_loop, &pong_running_count](
James Kuszmaul6295a642022-03-22 15:23:59 -0700311 const aos::starter::Status &status) {
312 const aos::starter::ApplicationStatus *ping_app_status =
313 FindApplicationStatus(status, "ping");
314 const aos::starter::ApplicationStatus *pong_app_status =
315 FindApplicationStatus(status, "pong");
316 if (ping_app_status == nullptr || pong_app_status == nullptr) {
317 return;
318 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700319
James Kuszmaul6295a642022-03-22 15:23:59 -0700320 if (ping_app_status->has_state() &&
321 ping_app_status->state() != aos::starter::State::STOPPED) {
322 watcher_loop.Exit();
323 FAIL();
324 }
325 if (pong_app_status->has_state() &&
326 pong_app_status->state() == aos::starter::State::RUNNING) {
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700327 ++pong_running_count;
328 // Sometimes if the timing for everything is *just* off, then the
329 // process_info will say that the process name is "starter_test" because
330 // it grabbed the name after the fork() but before the execvp(). To
331 // protect against that, wait an extra cycle. If things aren't fixed by
332 // the second cycle, then that is a problem.
333 if (pong_running_count < 2) {
334 return;
335 }
James Kuszmaul6295a642022-03-22 15:23:59 -0700336 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700337 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
338 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700339 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700340 ASSERT_TRUE(pong_app_status->process_info()->has_cpu_usage());
341 ASSERT_LE(0.0, pong_app_status->process_info()->cpu_usage());
James Kuszmaul6295a642022-03-22 15:23:59 -0700342 watcher_loop.Exit();
343 SUCCEED();
344 }
345 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700346
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700347 SetupStarterCleanup(&starter);
348
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700349 std::thread starterd_thread([&starter] { starter.Run(); });
350 watcher_loop.Run();
351
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700352 test_done_ = true;
353
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700354 starterd_thread.join();
355}
356
James Kuszmaule7c7e582022-01-07 18:50:01 -0800357// Tests that starterd respects autorestart.
358TEST_F(StarterdTest, DeathNoRestartTest) {
359 const std::string config_file =
360 ArtifactPath("aos/events/pingpong_config.json");
361
362 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
363 aos::configuration::ReadConfig(config_file);
364
365 const std::string test_dir = aos::testing::TestTmpDir();
366
367 auto new_config = aos::configuration::MergeWithConfig(
368 &config.message(), absl::StrFormat(
369 R"({"applications": [
370 {
371 "name": "ping",
372 "executable_name": "%s",
373 "args": ["--shm_base", "%s/aos"],
374 "autorestart": false
375 },
376 {
377 "name": "pong",
378 "executable_name": "%s",
379 "args": ["--shm_base", "%s/aos"]
380 }
381 ]})",
382 ArtifactPath("aos/events/ping"), test_dir,
383 ArtifactPath("aos/events/pong"), test_dir));
384
385 const aos::Configuration *config_msg = &new_config.message();
386
387 // Set up starter with config file
388 aos::starter::Starter starter(config_msg);
389
390 // Create an event loop to watch for the Status message to watch the state
391 // transitions.
392 aos::ShmEventLoop watcher_loop(config_msg);
393 watcher_loop.SkipAosLog();
394
395 watcher_loop
396 .AddTimer([&watcher_loop] {
397 watcher_loop.Exit();
398 SUCCEED();
399 })
400 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
401
402 int test_stage = 0;
403 uint64_t id;
404
405 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
406 &id](const aos::starter::Status &status) {
407 const aos::starter::ApplicationStatus *app_status =
408 FindApplicationStatus(status, "ping");
409 if (app_status == nullptr) {
410 return;
411 }
412
413 switch (test_stage) {
414 case 0: {
415 if (app_status->has_state() &&
416 app_status->state() == aos::starter::State::RUNNING) {
417 LOG(INFO) << "Ping is running";
418 test_stage = 1;
419 ASSERT_TRUE(app_status->has_pid());
420 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
421 ASSERT_TRUE(app_status->has_id());
422 id = app_status->id();
423 }
424 break;
425 }
426
427 case 1: {
428 if (app_status->has_state() &&
429 app_status->state() == aos::starter::State::RUNNING &&
430 app_status->has_id() && app_status->id() != id) {
431 LOG(INFO) << "Ping restarted, it shouldn't...";
432 watcher_loop.Exit();
433 FAIL();
434 }
435 break;
436 }
437 }
438 });
439
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700440
441 SetupStarterCleanup(&starter);
442
James Kuszmaule7c7e582022-01-07 18:50:01 -0800443 std::thread starterd_thread([&starter] { starter.Run(); });
444 watcher_loop.Run();
445
James Kuszmaul6b35e3a2022-04-06 15:00:39 -0700446 test_done_ = true;
447
James Kuszmaule7c7e582022-01-07 18:50:01 -0800448 starterd_thread.join();
449}
450
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700451} // namespace starter
452} // namespace aos