blob: 755ecf88d93f77a558ffff7644afe984dd42bbae [file] [log] [blame]
William A. Kennington III5c20da22021-06-18 16:44:55 -07001#include <poll.h>
2
William A. Kennington III43e38182021-08-19 17:29:48 -07003#include <array>
4#include <chrono>
William A. Kennington IIIb95a1282021-08-19 17:46:58 -07005#include <optional>
William A. Kennington III5c20da22021-06-18 16:44:55 -07006#include <stdplus/io_uring.hpp>
William A. Kennington IIIb95a1282021-08-19 17:46:58 -07007#include <string_view>
William A. Kennington III5c20da22021-06-18 16:44:55 -07008
9#include <gmock/gmock.h>
10#include <gtest/gtest.h>
11
12namespace stdplus
13{
14
15using testing::_;
16
William A. Kennington III43e38182021-08-19 17:29:48 -070017TEST(Convert, ChronoToKTS)
18{
19 const auto ns = 700;
20 const auto s = 5;
21 const auto kts =
22 chronoToKTS(std::chrono::nanoseconds(ns) + std::chrono::seconds(s));
23 EXPECT_EQ(kts.tv_sec, s);
24 EXPECT_EQ(kts.tv_nsec, ns);
25}
26
William A. Kennington III5c20da22021-06-18 16:44:55 -070027class MockHandler : public IoUring::CQEHandler
28{
29 public:
30 MOCK_METHOD(void, handleCQE, (io_uring_cqe&), (noexcept, override));
31};
32
33class IoUringTest : public testing::Test
34{
35 protected:
36 static void SetUpTestCase()
37 {
38 io_uring r;
39 if (io_uring_queue_init(1, &r, 0) == -ENOSYS)
40 {
41 // Not supported, skip running this test
42 exit(77);
43 }
44 io_uring_queue_exit(&r);
45 }
46 std::array<testing::StrictMock<MockHandler>, 2> h;
47 IoUring ring;
William A. Kennington IIIb95a1282021-08-19 17:46:58 -070048
49 void testFdWrite(int fd, int flags,
50 std::optional<int> expected_res = std::nullopt)
51 {
52 auto& sqe = ring.getSQE();
53 std::string_view data = "Test\n";
54 io_uring_prep_write(&sqe, fd, data.data(), data.size(), 0);
55 sqe.flags |= flags;
56 ring.setHandler(sqe, &h[0]);
57 ring.submit();
58 ring.wait();
59 EXPECT_CALL(h[0], handleCQE(_)).WillOnce([&](io_uring_cqe& cqe) {
60 EXPECT_EQ(cqe.res, expected_res.value_or(data.size()));
61 });
62 ring.process();
63 testing::Mock::VerifyAndClearExpectations(&h[0]);
64 }
William A. Kennington III5c20da22021-06-18 16:44:55 -070065};
66
67TEST_F(IoUringTest, NullHandler)
68{
69 auto& sqe = ring.getSQE();
70 io_uring_prep_nop(&sqe);
71 ring.submit();
72 ring.process();
73}
74
75TEST_F(IoUringTest, HandlerCalled)
76{
77 {
78 auto& sqe = ring.getSQE();
79 io_uring_prep_nop(&sqe);
80 ring.setHandler(sqe, &h[0]);
81 }
82
83 // Nothing should happen without submission
84 ring.process();
85
86 {
87 auto& sqe = ring.getSQE();
88 io_uring_prep_nop(&sqe);
89 ring.setHandler(sqe, &h[1]);
90 }
91
92 // Handle all of the outstanding requests
93 ring.submit();
94 EXPECT_CALL(h[0], handleCQE(_));
95 EXPECT_CALL(h[1], handleCQE(_));
96 ring.process();
97 testing::Mock::VerifyAndClearExpectations(&h[0]);
98 testing::Mock::VerifyAndClearExpectations(&h[1]);
99}
100
101TEST_F(IoUringTest, HandlerReplacement)
102{
103 auto& sqe = ring.getSQE();
104 io_uring_prep_nop(&sqe);
105 ring.setHandler(sqe, &h[0]);
106
107 // Setting a new handler should cancel the old one
108 EXPECT_CALL(h[0], handleCQE(_));
109 ring.setHandler(sqe, &h[1]);
110 testing::Mock::VerifyAndClearExpectations(&h[0]);
111
112 // Setting a null handler should cancel the old one
113 EXPECT_CALL(h[1], handleCQE(_));
114 ring.setHandler(sqe, nullptr);
115 testing::Mock::VerifyAndClearExpectations(&h[1]);
116
117 // Set it back twice and make sure it isn't recognized idempotently
118 ring.setHandler(sqe, &h[1]);
119 ring.setHandler(sqe, &h[1]);
120
121 // Make sure it still works
122 ring.submit();
123 EXPECT_CALL(h[1], handleCQE(_));
124 ring.process();
125 testing::Mock::VerifyAndClearExpectations(&h[1]);
126}
127
128TEST_F(IoUringTest, EventFd)
129{
130 auto& efd = ring.getEventFd();
131
132 for (size_t i = 0; i < h.size(); ++i)
133 {
134 auto& sqe = ring.getSQE();
135 io_uring_prep_nop(&sqe);
136 ring.setHandler(sqe, &h[i]);
137 ring.submit();
138 }
139
140 // Our event fd should become ready
141 pollfd pfd;
142 pfd.fd = efd.get();
143 pfd.events = POLLIN;
144 ASSERT_EQ(1, poll(&pfd, 1, 100));
145
146 // Handle all of the outstanding requests
147 EXPECT_CALL(h[0], handleCQE(_));
148 EXPECT_CALL(h[1], handleCQE(_));
149 ring.processEvents();
150 testing::Mock::VerifyAndClearExpectations(&h[0]);
151 testing::Mock::VerifyAndClearExpectations(&h[1]);
152
153 // Our event fd should be empty
154 ASSERT_EQ(0, poll(&pfd, 1, 100));
155}
156
William A. Kennington III43e38182021-08-19 17:29:48 -0700157TEST_F(IoUringTest, Wait)
158{
159 auto& sqe = ring.getSQE();
160 auto kts = chronoToKTS(std::chrono::milliseconds(1));
161 io_uring_prep_timeout(&sqe, &kts, 0, 0);
162 ring.setHandler(sqe, &h[0]);
163 ring.submit();
164 ring.wait(std::chrono::seconds(1));
165 EXPECT_CALL(h[0], handleCQE(_));
166}
167
William A. Kennington III5c20da22021-06-18 16:44:55 -0700168TEST_F(IoUringTest, HandleCalledOnDestroy)
169{
170 auto& sqe = ring.getSQE();
171 io_uring_prep_nop(&sqe);
172 ring.setHandler(sqe, &h[0]);
173 EXPECT_CALL(h[0], handleCQE(_));
174}
175
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700176TEST_F(IoUringTest, RegisterFiles)
177{
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700178 std::optional<IoUring::FileHandle> fh;
179
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700180 // Slots are always allocated linearly and re-used if invalidated
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700181 fh = ring.registerFile(STDERR_FILENO);
William A. Kennington III1afd7732021-08-20 00:49:10 -0700182 EXPECT_GT(ring.getFiles().size(), 1);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700183 EXPECT_EQ(*fh, 0);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700184 fh = ring.registerFile(STDOUT_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700185 EXPECT_EQ(*fh, 1);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700186 EXPECT_GT(ring.getFiles().size(), 2);
187 EXPECT_EQ(ring.getFiles()[0], -1);
188 EXPECT_EQ(ring.getFiles()[1], STDOUT_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700189
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700190 // The first handle should have dropped and can be replaced
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700191 fh = ring.registerFile(STDERR_FILENO);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700192 EXPECT_EQ(*fh, 0);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700193 EXPECT_GT(ring.getFiles().size(), 1);
194 EXPECT_EQ(ring.getFiles()[0], STDERR_FILENO);
195 EXPECT_EQ(ring.getFiles()[1], -1);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700196
197 // We should be able to write to stderr via the fixed file and regular fd
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700198 testFdWrite(STDERR_FILENO, 0);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700199 testFdWrite(*fh, IOSQE_FIXED_FILE);
200
201 // Without registration we should only be able to write to the regular fd
202 fh.reset();
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700203 testFdWrite(STDERR_FILENO, 0);
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700204 testFdWrite(*fh, IOSQE_FIXED_FILE, -EBADF);
205
206 std::vector<IoUring::FileHandle> fhs;
William A. Kennington III1afd7732021-08-20 00:49:10 -0700207 EXPECT_LT(ring.getFiles().size(), 9);
208 ring.reserveFiles(9);
209 EXPECT_EQ(ring.getFiles().size(), 9);
210 for (size_t i = 0; i < 9; ++i)
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700211 {
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700212 fhs.emplace_back(ring.registerFile(STDERR_FILENO));
William A. Kennington IIIb95a1282021-08-19 17:46:58 -0700213 testFdWrite(fhs.back(), IOSQE_FIXED_FILE);
214 }
William A. Kennington III1afd7732021-08-20 00:49:10 -0700215 EXPECT_EQ(ring.getFiles().size(), 9);
William A. Kennington IIId1b35222022-07-14 14:18:22 -0700216 fhs.emplace_back(ring.registerFile(STDERR_FILENO));
William A. Kennington III1afd7732021-08-20 00:49:10 -0700217 testFdWrite(fhs.back(), IOSQE_FIXED_FILE);
218 EXPECT_GE(ring.getFiles().size(), 10);
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700219}
220
William A. Kennington III5c20da22021-06-18 16:44:55 -0700221} // namespace stdplus