blob: cab22b4b6179d4b0da3dfccfa4e10fc893c5de55 [file] [log] [blame]
James Kuszmauld42edb42022-01-07 18:00:16 -08001#include "aos/starter/subprocess.h"
2
Adam Snaider70deaf22023-08-11 13:58:34 -07003#include <signal.h>
4#include <sys/types.h>
5
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "gtest/gtest.h"
7
James Kuszmauld42edb42022-01-07 18:00:16 -08008#include "aos/events/shm_event_loop.h"
9#include "aos/testing/path.h"
10#include "aos/testing/tmpdir.h"
11#include "aos/util/file.h"
James Kuszmauld42edb42022-01-07 18:00:16 -080012
13namespace aos::starter::testing {
14
15class SubprocessTest : public ::testing::Test {
16 protected:
17 SubprocessTest() : shm_dir_(aos::testing::TestTmpDir() + "/aos") {
18 FLAGS_shm_base = shm_dir_;
19
20 // Nuke the shm dir:
21 aos::util::UnlinkRecursive(shm_dir_);
22 }
23
24 gflags::FlagSaver flag_saver_;
25 std::string shm_dir_;
26};
27
28TEST_F(SubprocessTest, CaptureOutputs) {
29 const std::string config_file =
30 ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
31
32 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
33 aos::configuration::ReadConfig(config_file);
34 aos::ShmEventLoop event_loop(&config.message());
35 bool observed_stopped = false;
36 Application echo_stdout(
37 "echo", "echo", &event_loop, [&observed_stopped, &echo_stdout]() {
38 if (echo_stdout.status() == aos::starter::State::STOPPED) {
39 observed_stopped = true;
40 }
41 });
42 ASSERT_FALSE(echo_stdout.autorestart());
43 echo_stdout.set_args({"abcdef"});
44 echo_stdout.set_capture_stdout(true);
45 echo_stdout.set_capture_stderr(true);
46
47 echo_stdout.Start();
48 aos::TimerHandler *exit_timer =
49 event_loop.AddTimer([&event_loop]() { event_loop.Exit(); });
50 event_loop.OnRun([&event_loop, exit_timer]() {
Austin Schuh63851a42022-05-16 13:31:37 -070051 // Note: we are using the backup poll in this test to capture SIGCHLD. This
52 // runs at 1 hz, so make sure we let it run at least once.
Philipp Schradera6712522023-07-05 20:25:11 -070053 exit_timer->Schedule(event_loop.monotonic_now() +
54 std::chrono::milliseconds(1500));
James Kuszmauld42edb42022-01-07 18:00:16 -080055 });
56
57 event_loop.Run();
58
James Kuszmauld42edb42022-01-07 18:00:16 -080059 ASSERT_EQ("abcdef\n", echo_stdout.GetStdout());
60 ASSERT_TRUE(echo_stdout.GetStderr().empty());
61 EXPECT_TRUE(observed_stopped);
62 EXPECT_EQ(aos::starter::State::STOPPED, echo_stdout.status());
63
64 observed_stopped = false;
65
66 // Run again, the output should've been cleared.
67 echo_stdout.set_args({"ghijkl"});
68 echo_stdout.Start();
69 event_loop.Run();
70 ASSERT_EQ("ghijkl\n", echo_stdout.GetStdout());
71 EXPECT_TRUE(observed_stopped);
72}
73
74TEST_F(SubprocessTest, CaptureStderr) {
75 const std::string config_file =
76 ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
77
78 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
79 aos::configuration::ReadConfig(config_file);
80 aos::ShmEventLoop event_loop(&config.message());
81 bool observed_stopped = false;
82 Application echo_stderr(
83 "echo", "sh", &event_loop, [&observed_stopped, &echo_stderr]() {
84 if (echo_stderr.status() == aos::starter::State::STOPPED) {
85 observed_stopped = true;
86 }
87 });
88 echo_stderr.set_args({"-c", "echo abcdef >&2"});
89 echo_stderr.set_capture_stdout(true);
90 echo_stderr.set_capture_stderr(true);
91
92 echo_stderr.Start();
Austin Schuh63851a42022-05-16 13:31:37 -070093 // Note: we are using the backup poll in this test to capture SIGCHLD. This
94 // runs at 1 hz, so make sure we let it run at least once.
James Kuszmauld42edb42022-01-07 18:00:16 -080095 event_loop.AddTimer([&event_loop]() { event_loop.Exit(); })
Philipp Schradera6712522023-07-05 20:25:11 -070096 ->Schedule(event_loop.monotonic_now() + std::chrono::milliseconds(1500));
James Kuszmauld42edb42022-01-07 18:00:16 -080097
98 event_loop.Run();
99
100 ASSERT_EQ("abcdef\n", echo_stderr.GetStderr());
101 ASSERT_TRUE(echo_stderr.GetStdout().empty());
102 ASSERT_TRUE(observed_stopped);
103 ASSERT_EQ(aos::starter::State::STOPPED, echo_stderr.status());
104}
105
payton.rehl2841b1c2023-05-25 17:23:55 -0700106TEST_F(SubprocessTest, UnactiveQuietFlag) {
107 const std::string config_file =
108 ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
109
110 ::testing::internal::CaptureStderr();
111
112 // Set up application without quiet flag active
113 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
114 aos::configuration::ReadConfig(config_file);
115 aos::ShmEventLoop event_loop(&config.message());
116 bool observed_stopped = false;
117 Application error_out(
118 "false", "false", &event_loop,
119 [&observed_stopped, &error_out]() {
120 if (error_out.status() == aos::starter::State::STOPPED) {
121 observed_stopped = true;
122 }
123 },
124 Application::QuietLogging::kNo);
125 ASSERT_FALSE(error_out.autorestart());
126
127 error_out.Start();
128 aos::TimerHandler *exit_timer =
129 event_loop.AddTimer([&event_loop]() { event_loop.Exit(); });
130 event_loop.OnRun([&event_loop, exit_timer]() {
131 exit_timer->Schedule(event_loop.monotonic_now() +
132 std::chrono::milliseconds(1500));
133 });
134
135 event_loop.Run();
136
137 // Ensure presence of logs without quiet flag
138 std::string output = ::testing::internal::GetCapturedStderr();
139 std::string expectedStart = "Failed to start 'false'";
140 std::string expectedRun = "exited unexpectedly with status";
141
142 ASSERT_TRUE(output.find(expectedStart) != std::string::npos ||
143 output.find(expectedRun) != std::string::npos);
144 EXPECT_TRUE(observed_stopped);
145 EXPECT_EQ(aos::starter::State::STOPPED, error_out.status());
146}
147
148TEST_F(SubprocessTest, ActiveQuietFlag) {
149 const std::string config_file =
150 ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
151
152 ::testing::internal::CaptureStderr();
153
154 // Set up application with quiet flag active
155 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
156 aos::configuration::ReadConfig(config_file);
157 aos::ShmEventLoop event_loop(&config.message());
158 bool observed_stopped = false;
159 Application error_out(
160 "false", "false", &event_loop,
161 [&observed_stopped, &error_out]() {
162 if (error_out.status() == aos::starter::State::STOPPED) {
163 observed_stopped = true;
164 }
165 },
166 Application::QuietLogging::kYes);
167 ASSERT_FALSE(error_out.autorestart());
168
169 error_out.Start();
170 aos::TimerHandler *exit_timer =
171 event_loop.AddTimer([&event_loop]() { event_loop.Exit(); });
172 event_loop.OnRun([&event_loop, exit_timer]() {
173 exit_timer->Schedule(event_loop.monotonic_now() +
174 std::chrono::milliseconds(1500));
175 });
176
177 event_loop.Run();
178
179 // Ensure lack of logs with quiet flag
180 ASSERT_TRUE(::testing::internal::GetCapturedStderr().empty());
181 EXPECT_TRUE(observed_stopped);
182 EXPECT_EQ(aos::starter::State::STOPPED, error_out.status());
183}
184
Adam Snaider70deaf22023-08-11 13:58:34 -0700185// Tests that Nothing Badâ„¢ happens if the event loop outlives the Application.
186//
187// Note that this is a bit of a hope test, as there is no guarantee that we
188// will trigger a crash even if the resources tied to the event loop in the
189// aos::Application aren't properly released.
190TEST_F(SubprocessTest, ShortLivedApp) {
191 const std::string config_file =
192 ::aos::testing::ArtifactPath("aos/events/pingpong_config.json");
193
194 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
195 aos::configuration::ReadConfig(config_file);
196 aos::ShmEventLoop event_loop(&config.message());
197
198 auto application =
199 std::make_unique<Application>("sleep", "sleep", &event_loop, []() {});
200 application->set_args({"10"});
201 application->Start();
202 pid_t pid = application->get_pid();
203
204 int ticks = 0;
205 aos::TimerHandler *exit_timer = event_loop.AddTimer([&event_loop, &ticks,
206 &application, pid]() {
207 ticks++;
208 if (application && application->status() == aos::starter::State::RUNNING) {
209 // Kill the application, it will autorestart.
210 kill(pid, SIGTERM);
211 application.reset();
212 }
213
214 // event loop lives for longer.
215 if (ticks >= 5) {
216 // Now we exit.
217 event_loop.Exit();
218 }
219 });
220
221 event_loop.OnRun([&event_loop, exit_timer]() {
222 exit_timer->Schedule(event_loop.monotonic_now(),
223 std::chrono::milliseconds(1000));
224 });
225
226 event_loop.Run();
227}
James Kuszmauld42edb42022-01-07 18:00:16 -0800228} // namespace aos::starter::testing