blob: 0c397044228ddee81ead73c763066743665421ff [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"
10
11DECLARE_string(shm_base);
Austin Schuh217a9782019-12-21 23:02:50 -080012DECLARE_string(override_hostname);
Alex Perrycb7da4b2019-08-28 19:35:56 -070013
Parker Schuhe4a70d62017-12-27 20:10:20 -080014namespace aos {
15namespace testing {
16namespace {
Austin Schuh3115a202019-05-27 21:02:14 -070017namespace chrono = ::std::chrono;
Parker Schuhe4a70d62017-12-27 20:10:20 -080018
19class ShmEventLoopTestFactory : public EventLoopTestFactory {
20 public:
Alex Perrycb7da4b2019-08-28 19:35:56 -070021 ShmEventLoopTestFactory() {
22 // Put all the queue files in ${TEST_TMPDIR} if it is set, otherwise
23 // everything will be reusing /dev/shm when sharded.
24 char *test_tmpdir = getenv("TEST_TMPDIR");
25 if (test_tmpdir != nullptr) {
26 FLAGS_shm_base = std::string(test_tmpdir) + "/aos";
27 }
28
29 // Clean up anything left there before.
Austin Schuh3328d132020-02-28 13:54:57 -080030 unlink((FLAGS_shm_base + "/test/aos.TestMessage.v2").c_str());
31 unlink((FLAGS_shm_base + "/test1/aos.TestMessage.v2").c_str());
32 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v2").c_str());
33 unlink((FLAGS_shm_base + "/test2/aos.TestMessage.v2").c_str());
34 unlink((FLAGS_shm_base + "/aos/aos.timing.Report.v2").c_str());
35 unlink((FLAGS_shm_base + "/aos/aos.logging.LogMessageFbs.v2").c_str());
Alex Perrycb7da4b2019-08-28 19:35:56 -070036 }
37
Austin Schuh217a9782019-12-21 23:02:50 -080038 ~ShmEventLoopTestFactory() { FLAGS_override_hostname = ""; }
39
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080040 ::std::unique_ptr<EventLoop> Make(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080041 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080042 FLAGS_override_hostname =
43 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080044 }
45 ::std::unique_ptr<ShmEventLoop> loop(new ShmEventLoop(configuration()));
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080046 loop->set_name(name);
Austin Schuh217a9782019-12-21 23:02:50 -080047 return std::move(loop);
Parker Schuhe4a70d62017-12-27 20:10:20 -080048 }
49
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080050 ::std::unique_ptr<EventLoop> MakePrimary(std::string_view name) override {
Austin Schuh217a9782019-12-21 23:02:50 -080051 if (configuration()->has_nodes()) {
Austin Schuh898f4972020-01-11 17:21:25 -080052 FLAGS_override_hostname =
53 std::string(my_node()->hostname()->string_view());
Austin Schuh217a9782019-12-21 23:02:50 -080054 }
Austin Schuh44019f92019-05-19 19:58:27 -070055 ::std::unique_ptr<ShmEventLoop> loop =
Alex Perrycb7da4b2019-08-28 19:35:56 -070056 ::std::unique_ptr<ShmEventLoop>(new ShmEventLoop(configuration()));
Austin Schuh44019f92019-05-19 19:58:27 -070057 primary_event_loop_ = loop.get();
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080058 loop->set_name(name);
59 return std::move(loop);
Austin Schuh44019f92019-05-19 19:58:27 -070060 }
61
Alex Perrycb7da4b2019-08-28 19:35:56 -070062 void Run() override { CHECK_NOTNULL(primary_event_loop_)->Run(); }
Austin Schuh44019f92019-05-19 19:58:27 -070063
Alex Perrycb7da4b2019-08-28 19:35:56 -070064 void Exit() override { CHECK_NOTNULL(primary_event_loop_)->Exit(); }
Austin Schuh9fe68f72019-08-10 19:32:03 -070065
Austin Schuh52d325c2019-06-23 18:59:06 -070066 void SleepFor(::std::chrono::nanoseconds duration) override {
67 ::std::this_thread::sleep_for(duration);
68 }
69
Austin Schuh44019f92019-05-19 19:58:27 -070070 private:
Austin Schuh44019f92019-05-19 19:58:27 -070071 ::aos::ShmEventLoop *primary_event_loop_;
Parker Schuhe4a70d62017-12-27 20:10:20 -080072};
73
74INSTANTIATE_TEST_CASE_P(ShmEventLoopTest, AbstractEventLoopTest,
75 ::testing::Values([]() {
76 return new ShmEventLoopTestFactory();
77 }));
78
Austin Schuh6b6dfa52019-06-12 20:16:20 -070079INSTANTIATE_TEST_CASE_P(ShmEventLoopDeathTest, AbstractEventLoopDeathTest,
80 ::testing::Values([]() {
81 return new ShmEventLoopTestFactory();
82 }));
83
Parker Schuhe4a70d62017-12-27 20:10:20 -080084} // namespace
James Kuszmaulc79768b2019-02-18 15:08:44 -080085
Austin Schuh3115a202019-05-27 21:02:14 -070086bool IsRealtime() {
87 int scheduler;
Alex Perrycb7da4b2019-08-28 19:35:56 -070088 PCHECK((scheduler = sched_getscheduler(0)) != -1);
89
90 LOG(INFO) << "scheduler is " << scheduler;
Austin Schuh3115a202019-05-27 21:02:14 -070091 return scheduler == SCHED_FIFO || scheduler == SCHED_RR;
92}
James Kuszmaulc79768b2019-02-18 15:08:44 -080093
Austin Schuh3115a202019-05-27 21:02:14 -070094// Tests that every handler type is realtime and runs. There are threads
95// involved and it's easy to miss one.
96TEST(ShmEventLoopTest, AllHandlersAreRealtime) {
97 ShmEventLoopTestFactory factory;
Austin Schuh5f1cc5c2019-12-01 18:01:11 -080098 auto loop = factory.MakePrimary("primary");
99 auto loop2 = factory.Make("loop2");
Austin Schuh3115a202019-05-27 21:02:14 -0700100
101 loop->SetRuntimeRealtimePriority(1);
102
103 auto sender = loop2->MakeSender<TestMessage>("/test");
104
105 bool did_onrun = false;
106 bool did_timer = false;
107 bool did_watcher = false;
108
Alex Perrycb7da4b2019-08-28 19:35:56 -0700109 auto timer = loop->AddTimer([&did_timer, &factory]() {
Austin Schuh3115a202019-05-27 21:02:14 -0700110 EXPECT_TRUE(IsRealtime());
111 did_timer = true;
Austin Schuh9fe68f72019-08-10 19:32:03 -0700112 factory.Exit();
Austin Schuh3115a202019-05-27 21:02:14 -0700113 });
114
115 loop->MakeWatcher("/test", [&did_watcher](const TestMessage &) {
116 EXPECT_TRUE(IsRealtime());
117 did_watcher = true;
118 });
119
120 loop->OnRun([&loop, &did_onrun, &sender, timer]() {
121 EXPECT_TRUE(IsRealtime());
122 did_onrun = true;
123 timer->Setup(loop->monotonic_now() + chrono::milliseconds(100));
Alex Perrycb7da4b2019-08-28 19:35:56 -0700124
125 aos::Sender<TestMessage>::Builder msg = sender.MakeBuilder();
126 TestMessage::Builder builder = msg.MakeBuilder<TestMessage>();
127 builder.add_value(200);
128 msg.Send(builder.Finish());
Austin Schuh3115a202019-05-27 21:02:14 -0700129 });
James Kuszmaulc79768b2019-02-18 15:08:44 -0800130
Austin Schuh3115a202019-05-27 21:02:14 -0700131 factory.Run();
James Kuszmaulc79768b2019-02-18 15:08:44 -0800132
Austin Schuh3115a202019-05-27 21:02:14 -0700133 EXPECT_TRUE(did_onrun);
134 EXPECT_TRUE(did_timer);
135 EXPECT_TRUE(did_watcher);
James Kuszmaulc79768b2019-02-18 15:08:44 -0800136}
Austin Schuh52d325c2019-06-23 18:59:06 -0700137
138// Tests that missing a deadline inside the function still results in PhasedLoop
139// running at the right offset.
140TEST(ShmEventLoopTest, DelayedPhasedLoop) {
141 ShmEventLoopTestFactory factory;
Austin Schuh5f1cc5c2019-12-01 18:01:11 -0800142 auto loop1 = factory.MakePrimary("primary");
Austin Schuh52d325c2019-06-23 18:59:06 -0700143
144 ::std::vector<::aos::monotonic_clock::time_point> times;
145
146 constexpr chrono::milliseconds kOffset = chrono::milliseconds(400);
147
148 loop1->AddPhasedLoop(
Austin Schuh9fe68f72019-08-10 19:32:03 -0700149 [&times, &loop1, &kOffset, &factory](int count) {
Austin Schuh52d325c2019-06-23 18:59:06 -0700150 const ::aos::monotonic_clock::time_point monotonic_now =
151 loop1->monotonic_now();
152
153 // Compute our offset.
154 const ::aos::monotonic_clock::duration remainder =
155 monotonic_now.time_since_epoch() -
156 chrono::duration_cast<chrono::seconds>(
157 monotonic_now.time_since_epoch());
158
159 // Make sure we we are called near where we should be even when we
160 // delay.
161 constexpr chrono::milliseconds kEpsilon(200);
162 EXPECT_LT(remainder, kOffset + kEpsilon);
163 EXPECT_GT(remainder, kOffset - kEpsilon);
164
165 // Confirm that we see the missed count when we sleep.
166 if (times.size() == 0) {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800167 CHECK_EQ(count, 1);
Austin Schuh52d325c2019-06-23 18:59:06 -0700168 } else {
Austin Schuhde8a8ff2019-11-30 15:25:36 -0800169 CHECK_EQ(count, 3);
Austin Schuh52d325c2019-06-23 18:59:06 -0700170 }
171
172 times.push_back(loop1->monotonic_now());
173 if (times.size() == 2) {
Austin Schuh9fe68f72019-08-10 19:32:03 -0700174 factory.Exit();
Austin Schuh52d325c2019-06-23 18:59:06 -0700175 }
176
177 // Now, add a large delay. This should push us up to 3 cycles.
178 ::std::this_thread::sleep_for(chrono::milliseconds(2500));
179 },
180 chrono::seconds(1), kOffset);
181
182 factory.Run();
183
184 EXPECT_EQ(times.size(), 2u);
185}
186
Brian Silverman5120afb2020-01-31 17:44:35 -0800187// Test GetWatcherSharedMemory in a few basic scenarios.
188TEST(ShmEventLoopDeathTest, GetWatcherSharedMemory) {
189 ShmEventLoopTestFactory factory;
190 auto generic_loop1 = factory.MakePrimary("primary");
191 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
192 const auto channel = configuration::GetChannel(
193 loop1->configuration(), "/test", TestMessage::GetFullyQualifiedName(),
194 loop1->name(), loop1->node());
195
196 // First verify it handles an invalid channel reasonably.
197 EXPECT_DEATH(loop1->GetWatcherSharedMemory(channel),
198 "No watcher found for channel");
199
200 // Then, actually create a watcher, and verify it returns something sane.
201 loop1->MakeWatcher("/test", [](const TestMessage &) {});
202 EXPECT_FALSE(loop1->GetWatcherSharedMemory(channel).empty());
203}
204
205TEST(ShmEventLoopTest, GetSenderSharedMemory) {
206 ShmEventLoopTestFactory factory;
207 auto generic_loop1 = factory.MakePrimary("primary");
208 ShmEventLoop *const loop1 = static_cast<ShmEventLoop *>(generic_loop1.get());
209
210 // check that GetSenderSharedMemory returns non-null/non-empty memory span
211 auto sender = loop1->MakeSender<TestMessage>("/test");
212 EXPECT_FALSE(loop1->GetSenderSharedMemory(&sender).empty());
213}
214
Austin Schuh39788ff2019-12-01 18:22:57 -0800215// TODO(austin): Test that missing a deadline with a timer recovers as expected.
216
Parker Schuhe4a70d62017-12-27 20:10:20 -0800217} // namespace testing
218} // namespace aos