blob: cbb28f96b1c8965284b32b744563e74841ae7785 [file] [log] [blame]
Alex Perrycb7da4b2019-08-28 19:35:56 -07001#include "aos/events/shm_event_loop.h"
Parker Schuhe4a70d62017-12-27 20:10:20 -08002
Austin Schuh5f1cc5c2019-12-01 18:01:11 -08003#include <string_view>
4
Alex Perrycb7da4b2019-08-28 19:35:56 -07005#include "aos/events/event_loop_param_test.h"
6#include "glog/logging.h"
Parker Schuhe4a70d62017-12-27 20:10:20 -08007#include "gtest/gtest.h"
8
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "aos/events/test_message_generated.h"
Jim Ostrowski2192ddb2020-06-24 19:07:31 -070010#include "aos/network/team_number.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -070011
Parker Schuhe4a70d62017-12-27 20:10:20 -080012namespace aos {
13namespace testing {
14namespace {
Austin Schuh3115a202019-05-27 21:02:14 -070015namespace chrono = ::std::chrono;
Parker Schuhe4a70d62017-12-27 20:10:20 -080016
17class ShmEventLoopTestFactory : public EventLoopTestFactory {
18 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -070019 ShmEventLoopTestFactory() {
20 // Put all the queue files in ${TEST_TMPDIR} if it is set, otherwise
21 // everything will be reusing /dev/shm when sharded.
22 char *test_tmpdir = getenv("TEST_TMPDIR");
23 if (test_tmpdir != nullptr) {
24 FLAGS_shm_base = std::string(test_tmpdir) + "/aos";
25 }
26
27 // Clean up anything left there before.
Brian Silverman177567e2020-08-12 19:51:33 -070028 unlink((FLAGS_shm_base + "/test/aos.TestMessage.v3").c_str());
29 unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v3").c_str());
30 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
31 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v3").c_str());
32 unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v3").c_str());
33 unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v3").c_str());
Alex Perrycb7da4b2019-08-28 19:35:56 -070034 }
35
Austin Schuh217a9782019-12-21 23:02:50 -080036 ~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
37
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080038 ::std::unique_ptr<EventLoop> Make(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080039 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080040 FLAGS_override_hostname =
41 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080042 }
43 ::std::unique_ptr<ShmEventLoop> loop(new ShmEventLoop(configuration()));
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080044 loop->set_name(name);
Austin Schuh217a9782019-12-21 23:02:50 -080045 return std::move(loop);
Parker Schuhe4a70d62017-12-27 20:10:20 -080046 }
47
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080048 ::std::unique_ptr<EventLoop> MakePrimary(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080049 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080050 FLAGS_override_hostname =
51 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080052 }
Austin Schuh44019f92019-05-19 19:58:27 -070053 ::std::unique_ptr<ShmEventLoop> loop =
Alex Perrycb7da4b2019-08-28 19:35:56 -070054 ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop(configuration()));
Austin Schuh44019f92019-05-19 19:58:27 -070055 primary_event_loop_ = loop.get();
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080056 loop->set_name(name);
57 return std::move(loop);
Austin Schuh44019f92019-05-19 19:58:27 -070058 }
59
Alex Perrycb7da4b2019-08-28 19:35:56 -070060 void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
Austin Schuh44019f92019-05-19 19:58:27 -070061
Alex Perrycb7da4b2019-08-28 19:35:56 -070062 void Exit() override { CHECK_NOTNULL(primary_event_loop_)->Exit(); }
Austin Schuh9fe68f72019-08-10 19:32:03 -070063
Austin Schuh52d325c2019-06-23 18:59:06 -070064 void SleepFor(::std::chrono::nanoseconds duration) override {
65 ::std::this_thread::sleep_for(duration);
66 }
67
Austin Schuh44019f92019-05-19 19:58:27 -070068 private:
Austin Schuh44019f92019-05-19 19:58:27 -070069 ::aos::ShmEventLoop *primary_event_loop_;
Parker Schuhe4a70d62017-12-27 20:10:20 -080070};
71
Brian Silverman77162972020-08-12 19:52:40 -070072INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyTest, AbstractEventLoopTest,
73 ::testing::Values(std::make_pair(
74 []() { return new ShmEventLoopTestFactory(); },
75 ReadMethod::COPY)));
Parker Schuhe4a70d62017-12-27 20:10:20 -080076
Brian Silverman77162972020-08-12 19:52:40 -070077INSTANTIATE_TEST_CASE_P(ShmEventLoopCopyDeathTest, AbstractEventLoopDeathTest,
78 ::testing::Values(std::make_pair(
79 []() { return new ShmEventLoopTestFactory(); },
80 ReadMethod::COPY)));
81
82INSTANTIATE_TEST_CASE_P(ShmEventLoopPinTest, AbstractEventLoopTest,
83 ::testing::Values(std::make_pair(
84 []() { return new ShmEventLoopTestFactory(); },
85 ReadMethod::PIN)));
86
87INSTANTIATE_TEST_CASE_P(ShmEventLoopPinDeathTest, AbstractEventLoopDeathTest,
88 ::testing::Values(std::make_pair(
89 []() { return new ShmEventLoopTestFactory(); },
90 ReadMethod::PIN)));
Austin Schuh6b6dfa52019-06-12 20:16:20 -070091
Parker Schuhe4a70d62017-12-27 20:10:20 -080092} // namespace
James Kuszmaulc79768b2019-02-18 15:08:44 -080093
Austin Schuh3115a202019-05-27 21:02:14 -070094bool IsRealtime() {
95 int scheduler;
Alex Perrycb7da4b2019-08-28 19:35:56 -070096 PCHECK((scheduler = sched_getscheduler(0)) != -1);
97
98 LOG(INFO) << "scheduler is " << scheduler;
Austin Schuh3115a202019-05-27 21:02:14 -070099 return scheduler == SCHED_FIFO || scheduler == SCHED_RR;
100}
James Kuszmaulc79768b2019-02-18 15:08:44 -0800101
Austin Schuh3115a202019-05-27 21:02:14 -0700102// Tests that every handler type is realtime and runs. There are threads
103// involved and it's easy to miss one.
104TEST(ShmEventLoopTest, AllHandlersAreRealtime) {
105 ShmEventLoopTestFactory factory;
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800106 auto loop = factory.MakePrimary("primary");
107 auto loop2 = factory.Make("loop2");
Austin Schuh3115a202019-05-27 21:02:14 -0700108
109 loop->SetRuntimeRealtimePriority(1);
110
111 auto sender = loop2->MakeSender<TestMessage>("/test");
112
113 bool did_onrun = false;
114 bool did_timer = false;
115 bool did_watcher = false;
116
Alex Perrycb7da4b2019-08-28 19:35:56 -0700117 auto timer = loop->AddTimer([&did_timer, &factory]() {
Austin Schuh3115a202019-05-27 21:02:14 -0700118 EXPECT_TRUE(IsRealtime());
119 did_timer = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700120 factory.Exit();
Austin Schuh3115a202019-05-27 21:02:14 -0700121 });
122
123 loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
124 EXPECT_TRUE(IsRealtime());
125 did_watcher = true;
126 });
127
128 loop->OnRun([&loop, &did_onrun, &sender, timer]() {
129 EXPECT_TRUE(IsRealtime());
130 did_onrun = true;
131 timer->Setup(loop->monotonic_now() + chrono::milliseconds(100));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700132
133 aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
134 TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
135 builder.add_value(200);
136 msg.Send(builder.Finish());
Austin Schuh3115a202019-05-27 21:02:14 -0700137 });
James Kuszmaulc79768b2019-02-18 15:08:44 -0800138
Austin Schuh3115a202019-05-27 21:02:14 -0700139 factory.Run();
James Kuszmaulc79768b2019-02-18 15:08:44 -0800140
Austin Schuh3115a202019-05-27 21:02:14 -0700141 EXPECT_TRUE(did_onrun);
142 EXPECT_TRUE(did_timer);
143 EXPECT_TRUE(did_watcher);
James Kuszmaulc79768b2019-02-18 15:08:44 -0800144}
Austin Schuh52d325c2019-06-23 18:59:06 -0700145
146// Tests that missing a deadline inside the function still results in PhasedLoop
147// running at the right offset.
148TEST(ShmEventLoopTest, DelayedPhasedLoop) {
149 ShmEventLoopTestFactory factory;
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800150 auto loop1 = factory.MakePrimary("primary");
Austin Schuh52d325c2019-06-23 18:59:06 -0700151
152 ::std::vector<::aos::monotonic_clock::time_point> times;
153
154 constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
155
156 loop1->AddPhasedLoop(
Austin Schuh9fe68f72019-08-10 19:32:03 -0700157 [&times, &loop1, &kOffset, &factory](int count) {
Austin Schuh52d325c2019-06-23 18:59:06 -0700158 const ::aos::monotonic_clock::time_point monotonic_now =
159 loop1->monotonic_now();
160
161 // Compute our offset.
162 const ::aos::monotonic_clock::duration remainder =
163 monotonic_now.time_since_epoch() -
164 chrono::duration_cast<chrono::seconds>(
165 monotonic_now.time_since_epoch());
166
167 // Make sure we we are called near where we should be even when we
168 // delay.
169 constexpr chrono::milliseconds kEpsilon(200);
170 EXPECT_LT(remainder, kOffset + kEpsilon);
171 EXPECT_GT(remainder, kOffset - kEpsilon);
172
173 // Confirm that we see the missed count when we sleep.
174 if (times.size() == 0) {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800175 CHECK_EQ(count, 1);
Austin Schuh52d325c2019-06-23 18:59:06 -0700176 } else {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800177 CHECK_EQ(count, 3);
Austin Schuh52d325c2019-06-23 18:59:06 -0700178 }
179
180 times.push_back(loop1->monotonic_now());
181 if (times.size() == 2) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700182 factory.Exit();
Austin Schuh52d325c2019-06-23 18:59:06 -0700183 }
184
185 // Now, add a large delay. This should push us up to 3 cycles.
186 ::std::this_thread::sleep_for(chrono::milliseconds(2500));
187 },
188 chrono::seconds(1), kOffset);
189
190 factory.Run();
191
192 EXPECT_EQ(times.size(), 2u);
193}
194
Brian Silverman5120afb2020-01-31 17:44:35 -0800195// Test GetWatcherSharedMemory in a few basic scenarios.
196TEST(ShmEventLoopDeathTest, GetWatcherSharedMemory) {
197 ShmEventLoopTestFactory factory;
198 auto generic_loop1 = factory.MakePrimary("primary");
199 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
200 const auto channel = configuration::GetChannel(
201 loop1->configuration(), "/test", TestMessage::GetFullyQualifiedName(),
202 loop1->name(), loop1->node());
203
204 // First verify it handles an invalid channel reasonably.
205 EXPECT_DEATH(loop1->GetWatcherSharedMemory(channel),
206 "No watcher found for channel");
207
208 // Then, actually create a watcher, and verify it returns something sane.
209 loop1->MakeWatcher("/test", [](const TestMessage &) {});
210 EXPECT_FALSE(loop1->GetWatcherSharedMemory(channel).empty());
211}
212
213TEST(ShmEventLoopTest, GetSenderSharedMemory) {
214 ShmEventLoopTestFactory factory;
215 auto generic_loop1 = factory.MakePrimary("primary");
216 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
217
Brian Silverman6d2b3592020-06-18 14:40:15 -0700218 // check that GetSenderSharedMemory returns non-null/non-empty memory span.
Brian Silverman5120afb2020-01-31 17:44:35 -0800219 auto sender = loop1->MakeSender<TestMessage>("/test");
220 EXPECT_FALSE(loop1->GetSenderSharedMemory(&sender).empty());
221}
222
Brian Silverman6d2b3592020-06-18 14:40:15 -0700223TEST(ShmEventLoopTest, GetFetcherPrivateMemory) {
224 ShmEventLoopTestFactory factory;
225 auto generic_loop1 = factory.MakePrimary("primary");
226 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
227
228 // check that GetFetcherPrivateMemory returns non-null/non-empty memory span.
229 auto fetcher = loop1->MakeFetcher<TestMessage>("/test");
230 EXPECT_FALSE(loop1->GetFetcherPrivateMemory(&fetcher).empty());
231}
232
Austin Schuh39788ff2019-12-01 18:22:57 -0800233// TODO(austin): Test that missing a deadline with a timer recovers as expected.
234
Parker Schuhe4a70d62017-12-27 20:10:20 -0800235} // namespace testing
236} // namespace aos