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