blob: 6b8290e749d3b0354fbf29177175bc74eca9b70b [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 III5c20da22021-06-18 16:44:55 -07005#include <stdplus/io_uring.hpp>
6
7#include <gmock/gmock.h>
8#include <gtest/gtest.h>
9
10namespace stdplus
11{
12
13using testing::_;
14
William A. Kennington III43e38182021-08-19 17:29:48 -070015TEST(Convert, ChronoToKTS)
16{
17 const auto ns = 700;
18 const auto s = 5;
19 const auto kts =
20 chronoToKTS(std::chrono::nanoseconds(ns) + std::chrono::seconds(s));
21 EXPECT_EQ(kts.tv_sec, s);
22 EXPECT_EQ(kts.tv_nsec, ns);
23}
24
William A. Kennington III5c20da22021-06-18 16:44:55 -070025class MockHandler : public IoUring::CQEHandler
26{
27 public:
28 MOCK_METHOD(void, handleCQE, (io_uring_cqe&), (noexcept, override));
29};
30
31class IoUringTest : public testing::Test
32{
33 protected:
34 static void SetUpTestCase()
35 {
36 io_uring r;
37 if (io_uring_queue_init(1, &r, 0) == -ENOSYS)
38 {
39 // Not supported, skip running this test
40 exit(77);
41 }
42 io_uring_queue_exit(&r);
43 }
44 std::array<testing::StrictMock<MockHandler>, 2> h;
45 IoUring ring;
46};
47
48TEST_F(IoUringTest, NullHandler)
49{
50 auto& sqe = ring.getSQE();
51 io_uring_prep_nop(&sqe);
52 ring.submit();
53 ring.process();
54}
55
56TEST_F(IoUringTest, HandlerCalled)
57{
58 {
59 auto& sqe = ring.getSQE();
60 io_uring_prep_nop(&sqe);
61 ring.setHandler(sqe, &h[0]);
62 }
63
64 // Nothing should happen without submission
65 ring.process();
66
67 {
68 auto& sqe = ring.getSQE();
69 io_uring_prep_nop(&sqe);
70 ring.setHandler(sqe, &h[1]);
71 }
72
73 // Handle all of the outstanding requests
74 ring.submit();
75 EXPECT_CALL(h[0], handleCQE(_));
76 EXPECT_CALL(h[1], handleCQE(_));
77 ring.process();
78 testing::Mock::VerifyAndClearExpectations(&h[0]);
79 testing::Mock::VerifyAndClearExpectations(&h[1]);
80}
81
82TEST_F(IoUringTest, HandlerReplacement)
83{
84 auto& sqe = ring.getSQE();
85 io_uring_prep_nop(&sqe);
86 ring.setHandler(sqe, &h[0]);
87
88 // Setting a new handler should cancel the old one
89 EXPECT_CALL(h[0], handleCQE(_));
90 ring.setHandler(sqe, &h[1]);
91 testing::Mock::VerifyAndClearExpectations(&h[0]);
92
93 // Setting a null handler should cancel the old one
94 EXPECT_CALL(h[1], handleCQE(_));
95 ring.setHandler(sqe, nullptr);
96 testing::Mock::VerifyAndClearExpectations(&h[1]);
97
98 // Set it back twice and make sure it isn't recognized idempotently
99 ring.setHandler(sqe, &h[1]);
100 ring.setHandler(sqe, &h[1]);
101
102 // Make sure it still works
103 ring.submit();
104 EXPECT_CALL(h[1], handleCQE(_));
105 ring.process();
106 testing::Mock::VerifyAndClearExpectations(&h[1]);
107}
108
109TEST_F(IoUringTest, EventFd)
110{
111 auto& efd = ring.getEventFd();
112
113 for (size_t i = 0; i < h.size(); ++i)
114 {
115 auto& sqe = ring.getSQE();
116 io_uring_prep_nop(&sqe);
117 ring.setHandler(sqe, &h[i]);
118 ring.submit();
119 }
120
121 // Our event fd should become ready
122 pollfd pfd;
123 pfd.fd = efd.get();
124 pfd.events = POLLIN;
125 ASSERT_EQ(1, poll(&pfd, 1, 100));
126
127 // Handle all of the outstanding requests
128 EXPECT_CALL(h[0], handleCQE(_));
129 EXPECT_CALL(h[1], handleCQE(_));
130 ring.processEvents();
131 testing::Mock::VerifyAndClearExpectations(&h[0]);
132 testing::Mock::VerifyAndClearExpectations(&h[1]);
133
134 // Our event fd should be empty
135 ASSERT_EQ(0, poll(&pfd, 1, 100));
136}
137
William A. Kennington III43e38182021-08-19 17:29:48 -0700138TEST_F(IoUringTest, Wait)
139{
140 auto& sqe = ring.getSQE();
141 auto kts = chronoToKTS(std::chrono::milliseconds(1));
142 io_uring_prep_timeout(&sqe, &kts, 0, 0);
143 ring.setHandler(sqe, &h[0]);
144 ring.submit();
145 ring.wait(std::chrono::seconds(1));
146 EXPECT_CALL(h[0], handleCQE(_));
147}
148
William A. Kennington III5c20da22021-06-18 16:44:55 -0700149TEST_F(IoUringTest, HandleCalledOnDestroy)
150{
151 auto& sqe = ring.getSQE();
152 io_uring_prep_nop(&sqe);
153 ring.setHandler(sqe, &h[0]);
154 EXPECT_CALL(h[0], handleCQE(_));
155}
156
William A. Kennington IIIabc6be72021-08-17 01:58:27 -0700157TEST_F(IoUringTest, RegisterFiles)
158{
159 // Slots are always allocated linearly and re-used if invalidated
160 std::optional<IoUring::FileHandle> h;
161 h = ring.registerFile(0);
162 EXPECT_EQ(*h, 0);
163 h = ring.registerFile(1);
164 EXPECT_EQ(*h, 1);
165 // The first handle should have dropped and can be replaced
166 h = ring.registerFile(2);
167 EXPECT_EQ(*h, 0);
168}
169
William A. Kennington III5c20da22021-06-18 16:44:55 -0700170} // namespace stdplus