blob: e1be5636cf3a9ceba6dc0e6a614bf06e5640491d [file] [log] [blame]
Brian Silverman441591b2020-01-31 17:44:32 -08001#include "aos/events/epoll.h"
2
3#include <fcntl.h>
4#include <unistd.h>
5
Brian Silverman441591b2020-01-31 17:44:32 -08006#include "glog/logging.h"
Austin Schuhf74daa62021-07-21 15:29:17 -07007#include "gtest/gtest.h"
Brian Silverman441591b2020-01-31 17:44:32 -08008
Stephan Pleinesf63bde82024-01-13 15:59:33 -08009namespace aos::internal::testing {
Brian Silverman441591b2020-01-31 17:44:32 -080010
11// A simple wrapper around both ends of a pipe along with some helpers to easily
12// read/write data through it.
13class Pipe {
14 public:
15 Pipe() { PCHECK(pipe2(fds_, O_NONBLOCK) == 0); }
16 ~Pipe() {
James Kuszmaul4eb27f02021-04-25 14:08:10 -070017 if (fds_[0] >= 0) {
18 PCHECK(close(fds_[0]) == 0);
19 }
20 if (fds_[1] >= 0) {
21 PCHECK(close(fds_[1]) == 0);
22 }
Brian Silverman441591b2020-01-31 17:44:32 -080023 }
24
25 int read_fd() { return fds_[0]; }
26 int write_fd() { return fds_[1]; }
James Kuszmaul4eb27f02021-04-25 14:08:10 -070027 void close_read_fd() {
28 PCHECK(close(fds_[0]) == 0);
29 fds_[0] = -1;
30 }
Brian Silverman441591b2020-01-31 17:44:32 -080031
32 void Write(const std::string &data) {
33 CHECK_EQ(write(write_fd(), data.data(), data.size()),
34 static_cast<ssize_t>(data.size()));
35 }
36
37 std::string Read(size_t size) {
38 std::string result;
39 result.resize(size);
40 CHECK_EQ(read(read_fd(), result.data(), size), static_cast<ssize_t>(size));
41 return result;
42 }
43
44 private:
45 int fds_[2];
46};
47
48class EPollTest : public ::testing::Test {
Austin Schuhf74daa62021-07-21 15:29:17 -070049 public:
50 void RunFor(std::chrono::nanoseconds duration) {
51 TimerFd timerfd;
52 bool did_quit = false;
53 epoll_.OnReadable(timerfd.fd(), [this, &timerfd, &did_quit]() {
54 CHECK(!did_quit);
55 epoll_.Quit();
56 did_quit = true;
57 timerfd.Read();
58 });
59 timerfd.SetTime(monotonic_clock::now() + duration,
60 monotonic_clock::duration::zero());
61 epoll_.Run();
62 CHECK(did_quit);
63 epoll_.DeleteFd(timerfd.fd());
64 }
Brian Silverman441591b2020-01-31 17:44:32 -080065
66 // Tests should avoid relying on ordering for events closer in time than this,
67 // or waiting for longer than this to ensure events happen in order.
68 static constexpr std::chrono::nanoseconds tick_duration() {
69 return std::chrono::milliseconds(50);
70 }
71
Austin Schuhf74daa62021-07-21 15:29:17 -070072 EPoll epoll_;
Brian Silverman441591b2020-01-31 17:44:32 -080073};
74
75// Test that the basics of OnReadable work.
76TEST_F(EPollTest, BasicReadable) {
77 Pipe pipe;
78 bool got_data = false;
79 epoll_.OnReadable(pipe.read_fd(), [&]() {
80 ASSERT_FALSE(got_data);
81 ASSERT_EQ("some", pipe.Read(4));
82 got_data = true;
83 });
84 RunFor(tick_duration());
85 EXPECT_FALSE(got_data);
86
87 pipe.Write("some");
88 RunFor(tick_duration());
89 EXPECT_TRUE(got_data);
90
91 epoll_.DeleteFd(pipe.read_fd());
92}
93
94// Test that the basics of OnWriteable work.
95TEST_F(EPollTest, BasicWriteable) {
96 Pipe pipe;
97 int number_writes = 0;
98 epoll_.OnWriteable(pipe.write_fd(), [&]() {
99 pipe.Write(" ");
100 ++number_writes;
101 });
102
103 // First, fill up the pipe's write buffer.
104 RunFor(tick_duration());
105 EXPECT_GT(number_writes, 0);
106
107 // Now, if we try again, we shouldn't do anything.
108 const int bytes_in_pipe = number_writes;
109 number_writes = 0;
110 RunFor(tick_duration());
111 EXPECT_EQ(number_writes, 0);
112
113 // Empty the pipe, then fill it up again.
114 for (int i = 0; i < bytes_in_pipe; ++i) {
115 ASSERT_EQ(" ", pipe.Read(1));
116 }
117 number_writes = 0;
118 RunFor(tick_duration());
119 EXPECT_EQ(number_writes, bytes_in_pipe);
120
121 epoll_.DeleteFd(pipe.write_fd());
122}
123
James Kuszmaul4eb27f02021-04-25 14:08:10 -0700124// Test that the basics of OnError work.
125TEST_F(EPollTest, BasicError) {
126 // In order to trigger an error, close the read file descriptor (per the
127 // epoll_ctl manpage, this should trigger an error).
128 Pipe pipe;
129 int number_errors = 0;
Austin Schuh60e77942022-05-16 17:48:24 -0700130 epoll_.OnError(pipe.write_fd(), [&]() { ++number_errors; });
James Kuszmaul4eb27f02021-04-25 14:08:10 -0700131
132 // Sanity check that we *don't* get any errors before anything interesting has
133 // happened.
134 RunFor(tick_duration());
135 EXPECT_EQ(number_errors, 0);
136
137 pipe.close_read_fd();
138
139 // For some reason, OnError doesn't seem to play nice with the timer setup we
140 // have in this test, so just poll for a single event.
141 epoll_.Poll(false);
142
143 EXPECT_EQ(number_errors, 1);
144
145 epoll_.DeleteFd(pipe.write_fd());
146}
147
Brian Silverman441591b2020-01-31 17:44:32 -0800148TEST(EPollDeathTest, InvalidFd) {
149 EPoll epoll;
150 Pipe pipe;
151 epoll.OnReadable(pipe.read_fd(), []() {});
152 EXPECT_DEATH(epoll.OnReadable(pipe.read_fd(), []() {}),
153 "Duplicate in functions");
154 epoll.OnWriteable(pipe.read_fd(), []() {});
155 EXPECT_DEATH(epoll.OnWriteable(pipe.read_fd(), []() {}),
156 "Duplicate out functions");
157
158 epoll.DeleteFd(pipe.read_fd());
159 EXPECT_DEATH(epoll.DeleteFd(pipe.read_fd()), "fd [0-9]+ not found");
160 EXPECT_DEATH(epoll.DeleteFd(pipe.write_fd()), "fd [0-9]+ not found");
161}
162
163// Tests that enabling/disabling a writeable FD works.
164TEST_F(EPollTest, WriteableEnableDisable) {
165 Pipe pipe;
166 int number_writes = 0;
167 epoll_.OnWriteable(pipe.write_fd(), [&]() {
168 pipe.Write(" ");
169 ++number_writes;
170 });
171
172 // First, fill up the pipe's write buffer.
173 RunFor(tick_duration());
174 EXPECT_GT(number_writes, 0);
175
176 // Empty the pipe.
177 const int bytes_in_pipe = number_writes;
178 for (int i = 0; i < bytes_in_pipe; ++i) {
179 ASSERT_EQ(" ", pipe.Read(1));
180 }
181
182 // If we disable writeable checking, then nothing should happen.
183 epoll_.DisableWriteable(pipe.write_fd());
184 number_writes = 0;
185 RunFor(tick_duration());
186 EXPECT_EQ(number_writes, 0);
187
188 // Disabling it again should be a NOP.
189 epoll_.DisableWriteable(pipe.write_fd());
190
191 // And then when we re-enable, it should fill the pipe up again.
192 epoll_.EnableWriteable(pipe.write_fd());
193 number_writes = 0;
194 RunFor(tick_duration());
195 EXPECT_EQ(number_writes, bytes_in_pipe);
196
197 epoll_.DeleteFd(pipe.write_fd());
198}
199
Austin Schuhf74daa62021-07-21 15:29:17 -0700200TEST_F(EPollTest, QuitInBeforeWait) {
201 epoll_.BeforeWait([this]() { epoll_.Quit(); });
202 epoll_.Run();
203}
204
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800205} // namespace aos::internal::testing