blob: d489f8f8ab49f2c334f612da738bfd9c3bd89d8f [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:
30 gflags::FlagSaver flag_saver_;
31 std::string shm_dir_;
32};
33
34struct TestParams {
35 std::string config;
36 std::string hostname;
37};
38
39class StarterdConfigParamTest
40 : public StarterdTest,
41 public ::testing::WithParamInterface<TestParams> {};
42
43TEST_P(StarterdConfigParamTest, MultiNodeStartStopTest) {
44 gflags::FlagSaver flag_saver;
45 FLAGS_override_hostname = GetParam().hostname;
46 const std::string config_file = ArtifactPath(GetParam().config);
Tyler Chatowa79419d2020-08-12 20:12:11 -070047
48 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
49 aos::configuration::ReadConfig(config_file);
50
Tyler Chatowa79419d2020-08-12 20:12:11 -070051 auto new_config = aos::configuration::MergeWithConfig(
James Kuszmaul293b2172021-11-10 16:20:48 -080052 &config.message(),
53 absl::StrFormat(
54 R"({"applications": [
Tyler Chatowa79419d2020-08-12 20:12:11 -070055 {
56 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -070057 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080058 "nodes": ["pi1"],
59 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070060 },
61 {
62 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -070063 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -080064 "nodes": ["pi1"],
65 "args": ["--shm_base", "%s", "--config", "%s", "--override_hostname", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -070066 }
67 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -080068 ArtifactPath("aos/events/ping"), shm_dir_, config_file,
69 GetParam().hostname, ArtifactPath("aos/events/pong"), shm_dir_,
70 config_file, GetParam().hostname));
Tyler Chatowa79419d2020-08-12 20:12:11 -070071
72 const aos::Configuration *config_msg = &new_config.message();
73
74 // Set up starter with config file
75 aos::starter::Starter starter(config_msg);
76
77 // Create an event loop to watch for ping messages, verifying it actually
78 // started.
79 aos::ShmEventLoop watcher_loop(config_msg);
80 watcher_loop.SkipAosLog();
81
James Kuszmaul293b2172021-11-10 16:20:48 -080082 aos::ShmEventLoop client_loop(config_msg);
83 client_loop.SkipAosLog();
84 StarterClient client(&client_loop);
85 client.SetTimeoutHandler(
86 []() { FAIL() << ": Command should not have timed out."; });
87 bool success = false;
88 client.SetSuccessHandler([&success, &client_loop]() {
89 client_loop.Exit();
90 success = true;
91 });
92
Tyler Chatowa79419d2020-08-12 20:12:11 -070093 watcher_loop
94 .AddTimer([&watcher_loop] {
95 watcher_loop.Exit();
96 FAIL();
97 })
98 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
99
James Kuszmaul293b2172021-11-10 16:20:48 -0800100 std::atomic<int> test_stage = 0;
101 // Watch on the client loop since we need to interact with the StarterClient.
102 client_loop.MakeWatcher("/test", [&test_stage, &client,
103 &client_loop](const aos::examples::Ping &) {
104 switch (test_stage) {
105 case 1: {
106 test_stage = 2;
107 break;
108 }
109 case 2: {
110 {
111 client.SendCommands({{Command::STOP, "ping", {client_loop.node()}}},
112 std::chrono::seconds(3));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700113 }
James Kuszmaul293b2172021-11-10 16:20:48 -0800114 test_stage = 3;
115 break;
116 }
117 }
118 });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700119
120 watcher_loop.MakeWatcher(
121 "/aos", [&test_stage, &watcher_loop](const aos::starter::Status &status) {
122 const aos::starter::ApplicationStatus *app_status =
123 FindApplicationStatus(status, "ping");
124 if (app_status == nullptr) {
125 return;
126 }
127
128 switch (test_stage) {
129 case 0: {
130 if (app_status->has_state() &&
131 app_status->state() == aos::starter::State::RUNNING) {
132 test_stage = 1;
133 }
134 break;
135 }
136
137 case 3: {
138 if (app_status->has_state() &&
139 app_status->state() == aos::starter::State::STOPPED) {
140 watcher_loop.Exit();
141 SUCCEED();
142 }
143 break;
144 }
145 }
146 });
147
148 std::thread starterd_thread([&starter] { starter.Run(); });
James Kuszmaul293b2172021-11-10 16:20:48 -0800149 std::thread client_thread([&client_loop] { client_loop.Run(); });
Tyler Chatowa79419d2020-08-12 20:12:11 -0700150 watcher_loop.Run();
151
152 starter.Cleanup();
James Kuszmaul293b2172021-11-10 16:20:48 -0800153 client_thread.join();
Tyler Chatowa79419d2020-08-12 20:12:11 -0700154 starterd_thread.join();
155}
156
James Kuszmaul293b2172021-11-10 16:20:48 -0800157INSTANTIATE_TEST_SUITE_P(
158 StarterdConfigParamTest, StarterdConfigParamTest,
159 ::testing::Values(TestParams{"aos/events/pingpong_config.json", ""},
160 TestParams{"aos/starter/multinode_pingpong_config.json",
161 "pi1"}));
162
163TEST_F(StarterdTest, DeathTest) {
Austin Schuh373f1762021-06-02 21:07:09 -0700164 const std::string config_file =
165 ArtifactPath("aos/events/pingpong_config.json");
Tyler Chatowa79419d2020-08-12 20:12:11 -0700166
167 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
168 aos::configuration::ReadConfig(config_file);
169
Tyler Chatowa79419d2020-08-12 20:12:11 -0700170 auto new_config = aos::configuration::MergeWithConfig(
171 &config.message(), absl::StrFormat(
172 R"({"applications": [
173 {
174 "name": "ping",
Austin Schuh373f1762021-06-02 21:07:09 -0700175 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800176 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700177 },
178 {
179 "name": "pong",
Austin Schuh373f1762021-06-02 21:07:09 -0700180 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800181 "args": ["--shm_base", "%s"]
Tyler Chatowa79419d2020-08-12 20:12:11 -0700182 }
183 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -0800184 ArtifactPath("aos/events/ping"), shm_dir_,
185 ArtifactPath("aos/events/pong"), shm_dir_));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700186
187 const aos::Configuration *config_msg = &new_config.message();
188
189 // Set up starter with config file
190 aos::starter::Starter starter(config_msg);
191
192 // Create an event loop to watch for ping messages, verifying it actually
193 // started.
194 aos::ShmEventLoop watcher_loop(config_msg);
195 watcher_loop.SkipAosLog();
196
197 watcher_loop
198 .AddTimer([&watcher_loop] {
199 watcher_loop.Exit();
200 FAIL();
201 })
Austin Schuha07b3ce2021-10-10 12:33:21 -0700202 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
Tyler Chatowa79419d2020-08-12 20:12:11 -0700203
204 int test_stage = 0;
205 uint64_t id;
206
207 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
208 &id](const aos::starter::Status &status) {
209 const aos::starter::ApplicationStatus *app_status =
210 FindApplicationStatus(status, "ping");
211 if (app_status == nullptr) {
212 return;
213 }
214
215 switch (test_stage) {
216 case 0: {
217 if (app_status->has_state() &&
218 app_status->state() == aos::starter::State::RUNNING) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700219 LOG(INFO) << "Ping is running";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700220 test_stage = 1;
221 ASSERT_TRUE(app_status->has_pid());
222 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
223 ASSERT_TRUE(app_status->has_id());
224 id = app_status->id();
225 }
226 break;
227 }
228
229 case 1: {
230 if (app_status->has_state() &&
231 app_status->state() == aos::starter::State::RUNNING &&
232 app_status->has_id() && app_status->id() != id) {
Austin Schuha07b3ce2021-10-10 12:33:21 -0700233 LOG(INFO) << "Ping restarted";
Tyler Chatowa79419d2020-08-12 20:12:11 -0700234 watcher_loop.Exit();
235 SUCCEED();
236 }
237 break;
238 }
239 }
240 });
241
242 std::thread starterd_thread([&starter] { starter.Run(); });
243 watcher_loop.Run();
244
245 starter.Cleanup();
246 starterd_thread.join();
247}
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700248
James Kuszmaul293b2172021-11-10 16:20:48 -0800249TEST_F(StarterdTest, Autostart) {
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700250 const std::string config_file =
251 ArtifactPath("aos/events/pingpong_config.json");
252
253 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
254 aos::configuration::ReadConfig(config_file);
255
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700256 auto new_config = aos::configuration::MergeWithConfig(
257 &config.message(), absl::StrFormat(
258 R"({"applications": [
259 {
260 "name": "ping",
261 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800262 "args": ["--shm_base", "%s"],
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700263 "autostart": false
264 },
265 {
266 "name": "pong",
267 "executable_name": "%s",
James Kuszmaul293b2172021-11-10 16:20:48 -0800268 "args": ["--shm_base", "%s"]
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700269 }
270 ]})",
James Kuszmaul293b2172021-11-10 16:20:48 -0800271 ArtifactPath("aos/events/ping"), shm_dir_,
272 ArtifactPath("aos/events/pong"), shm_dir_));
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700273
274 const aos::Configuration *config_msg = &new_config.message();
275
276 // Set up starter with config file
277 aos::starter::Starter starter(config_msg);
278
279 // Create an event loop to watch for the application starting up.
280 aos::ShmEventLoop watcher_loop(config_msg);
281 watcher_loop.SkipAosLog();
282
283 watcher_loop
284 .AddTimer([&watcher_loop] {
285 watcher_loop.Exit();
286 FAIL();
287 })
288 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(7));
289
James Kuszmaul6295a642022-03-22 15:23:59 -0700290 watcher_loop.MakeWatcher("/aos", [&watcher_loop](
291 const aos::starter::Status &status) {
292 const aos::starter::ApplicationStatus *ping_app_status =
293 FindApplicationStatus(status, "ping");
294 const aos::starter::ApplicationStatus *pong_app_status =
295 FindApplicationStatus(status, "pong");
296 if (ping_app_status == nullptr || pong_app_status == nullptr) {
297 return;
298 }
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700299
James Kuszmaul6295a642022-03-22 15:23:59 -0700300 if (ping_app_status->has_state() &&
301 ping_app_status->state() != aos::starter::State::STOPPED) {
302 watcher_loop.Exit();
303 FAIL();
304 }
305 if (pong_app_status->has_state() &&
306 pong_app_status->state() == aos::starter::State::RUNNING) {
307 ASSERT_TRUE(pong_app_status->has_process_info());
Austin Schuh6665e922022-04-01 10:06:25 -0700308 ASSERT_EQ("pong", pong_app_status->process_info()->name()->string_view())
309 << aos::FlatbufferToJson(&status);
James Kuszmaul6295a642022-03-22 15:23:59 -0700310 ASSERT_EQ(pong_app_status->pid(), pong_app_status->process_info()->pid());
311 ASSERT_LT(0.0, pong_app_status->process_info()->cpu_usage());
312 watcher_loop.Exit();
313 SUCCEED();
314 }
315 });
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700316
317 std::thread starterd_thread([&starter] { starter.Run(); });
318 watcher_loop.Run();
319
320 starter.Cleanup();
321 starterd_thread.join();
322}
323
James Kuszmaule7c7e582022-01-07 18:50:01 -0800324// Tests that starterd respects autorestart.
325TEST_F(StarterdTest, DeathNoRestartTest) {
326 const std::string config_file =
327 ArtifactPath("aos/events/pingpong_config.json");
328
329 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
330 aos::configuration::ReadConfig(config_file);
331
332 const std::string test_dir = aos::testing::TestTmpDir();
333
334 auto new_config = aos::configuration::MergeWithConfig(
335 &config.message(), absl::StrFormat(
336 R"({"applications": [
337 {
338 "name": "ping",
339 "executable_name": "%s",
340 "args": ["--shm_base", "%s/aos"],
341 "autorestart": false
342 },
343 {
344 "name": "pong",
345 "executable_name": "%s",
346 "args": ["--shm_base", "%s/aos"]
347 }
348 ]})",
349 ArtifactPath("aos/events/ping"), test_dir,
350 ArtifactPath("aos/events/pong"), test_dir));
351
352 const aos::Configuration *config_msg = &new_config.message();
353
354 // Set up starter with config file
355 aos::starter::Starter starter(config_msg);
356
357 // Create an event loop to watch for the Status message to watch the state
358 // transitions.
359 aos::ShmEventLoop watcher_loop(config_msg);
360 watcher_loop.SkipAosLog();
361
362 watcher_loop
363 .AddTimer([&watcher_loop] {
364 watcher_loop.Exit();
365 SUCCEED();
366 })
367 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
368
369 int test_stage = 0;
370 uint64_t id;
371
372 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
373 &id](const aos::starter::Status &status) {
374 const aos::starter::ApplicationStatus *app_status =
375 FindApplicationStatus(status, "ping");
376 if (app_status == nullptr) {
377 return;
378 }
379
380 switch (test_stage) {
381 case 0: {
382 if (app_status->has_state() &&
383 app_status->state() == aos::starter::State::RUNNING) {
384 LOG(INFO) << "Ping is running";
385 test_stage = 1;
386 ASSERT_TRUE(app_status->has_pid());
387 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
388 ASSERT_TRUE(app_status->has_id());
389 id = app_status->id();
390 }
391 break;
392 }
393
394 case 1: {
395 if (app_status->has_state() &&
396 app_status->state() == aos::starter::State::RUNNING &&
397 app_status->has_id() && app_status->id() != id) {
398 LOG(INFO) << "Ping restarted, it shouldn't...";
399 watcher_loop.Exit();
400 FAIL();
401 }
402 break;
403 }
404 }
405 });
406
407 std::thread starterd_thread([&starter] { starter.Run(); });
408 watcher_loop.Run();
409
410 starter.Cleanup();
411 starterd_thread.join();
412}
413
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700414} // namespace starter
415} // namespace aos