blob: 120fe3870b3cca98f98c4bd8e955ba84937d66c8 [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
290 watcher_loop.MakeWatcher(
291 "/aos", [&watcher_loop](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 }
299
300 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 watcher_loop.Exit();
308 SUCCEED();
309 }
310 });
311
312 std::thread starterd_thread([&starter] { starter.Run(); });
313 watcher_loop.Run();
314
315 starter.Cleanup();
316 starterd_thread.join();
317}
318
James Kuszmaule7c7e582022-01-07 18:50:01 -0800319// Tests that starterd respects autorestart.
320TEST_F(StarterdTest, DeathNoRestartTest) {
321 const std::string config_file =
322 ArtifactPath("aos/events/pingpong_config.json");
323
324 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
325 aos::configuration::ReadConfig(config_file);
326
327 const std::string test_dir = aos::testing::TestTmpDir();
328
329 auto new_config = aos::configuration::MergeWithConfig(
330 &config.message(), absl::StrFormat(
331 R"({"applications": [
332 {
333 "name": "ping",
334 "executable_name": "%s",
335 "args": ["--shm_base", "%s/aos"],
336 "autorestart": false
337 },
338 {
339 "name": "pong",
340 "executable_name": "%s",
341 "args": ["--shm_base", "%s/aos"]
342 }
343 ]})",
344 ArtifactPath("aos/events/ping"), test_dir,
345 ArtifactPath("aos/events/pong"), test_dir));
346
347 const aos::Configuration *config_msg = &new_config.message();
348
349 // Set up starter with config file
350 aos::starter::Starter starter(config_msg);
351
352 // Create an event loop to watch for the Status message to watch the state
353 // transitions.
354 aos::ShmEventLoop watcher_loop(config_msg);
355 watcher_loop.SkipAosLog();
356
357 watcher_loop
358 .AddTimer([&watcher_loop] {
359 watcher_loop.Exit();
360 SUCCEED();
361 })
362 ->Setup(watcher_loop.monotonic_now() + std::chrono::seconds(11));
363
364 int test_stage = 0;
365 uint64_t id;
366
367 watcher_loop.MakeWatcher("/aos", [&test_stage, &watcher_loop,
368 &id](const aos::starter::Status &status) {
369 const aos::starter::ApplicationStatus *app_status =
370 FindApplicationStatus(status, "ping");
371 if (app_status == nullptr) {
372 return;
373 }
374
375 switch (test_stage) {
376 case 0: {
377 if (app_status->has_state() &&
378 app_status->state() == aos::starter::State::RUNNING) {
379 LOG(INFO) << "Ping is running";
380 test_stage = 1;
381 ASSERT_TRUE(app_status->has_pid());
382 ASSERT_TRUE(kill(app_status->pid(), SIGINT) != -1);
383 ASSERT_TRUE(app_status->has_id());
384 id = app_status->id();
385 }
386 break;
387 }
388
389 case 1: {
390 if (app_status->has_state() &&
391 app_status->state() == aos::starter::State::RUNNING &&
392 app_status->has_id() && app_status->id() != id) {
393 LOG(INFO) << "Ping restarted, it shouldn't...";
394 watcher_loop.Exit();
395 FAIL();
396 }
397 break;
398 }
399 }
400 });
401
402 std::thread starterd_thread([&starter] { starter.Run(); });
403 watcher_loop.Run();
404
405 starter.Cleanup();
406 starterd_thread.join();
407}
408
Austin Schuh5f79a5a2021-10-12 17:46:50 -0700409} // namespace starter
410} // namespace aos