| #include <poll.h> |
| |
| #include <stdplus/io_uring.hpp> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace stdplus |
| { |
| |
| using testing::_; |
| |
| class MockHandler : public IoUring::CQEHandler |
| { |
| public: |
| MOCK_METHOD(void, handleCQE, (io_uring_cqe&), (noexcept, override)); |
| }; |
| |
| class IoUringTest : public testing::Test |
| { |
| protected: |
| static void SetUpTestCase() |
| { |
| io_uring r; |
| if (io_uring_queue_init(1, &r, 0) == -ENOSYS) |
| { |
| // Not supported, skip running this test |
| exit(77); |
| } |
| io_uring_queue_exit(&r); |
| } |
| std::array<testing::StrictMock<MockHandler>, 2> h; |
| IoUring ring; |
| }; |
| |
| TEST_F(IoUringTest, NullHandler) |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.submit(); |
| ring.process(); |
| } |
| |
| TEST_F(IoUringTest, HandlerCalled) |
| { |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.setHandler(sqe, &h[0]); |
| } |
| |
| // Nothing should happen without submission |
| ring.process(); |
| |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.setHandler(sqe, &h[1]); |
| } |
| |
| // Handle all of the outstanding requests |
| ring.submit(); |
| EXPECT_CALL(h[0], handleCQE(_)); |
| EXPECT_CALL(h[1], handleCQE(_)); |
| ring.process(); |
| testing::Mock::VerifyAndClearExpectations(&h[0]); |
| testing::Mock::VerifyAndClearExpectations(&h[1]); |
| } |
| |
| TEST_F(IoUringTest, HandlerReplacement) |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.setHandler(sqe, &h[0]); |
| |
| // Setting a new handler should cancel the old one |
| EXPECT_CALL(h[0], handleCQE(_)); |
| ring.setHandler(sqe, &h[1]); |
| testing::Mock::VerifyAndClearExpectations(&h[0]); |
| |
| // Setting a null handler should cancel the old one |
| EXPECT_CALL(h[1], handleCQE(_)); |
| ring.setHandler(sqe, nullptr); |
| testing::Mock::VerifyAndClearExpectations(&h[1]); |
| |
| // Set it back twice and make sure it isn't recognized idempotently |
| ring.setHandler(sqe, &h[1]); |
| ring.setHandler(sqe, &h[1]); |
| |
| // Make sure it still works |
| ring.submit(); |
| EXPECT_CALL(h[1], handleCQE(_)); |
| ring.process(); |
| testing::Mock::VerifyAndClearExpectations(&h[1]); |
| } |
| |
| TEST_F(IoUringTest, EventFd) |
| { |
| auto& efd = ring.getEventFd(); |
| |
| for (size_t i = 0; i < h.size(); ++i) |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.setHandler(sqe, &h[i]); |
| ring.submit(); |
| } |
| |
| // Our event fd should become ready |
| pollfd pfd; |
| pfd.fd = efd.get(); |
| pfd.events = POLLIN; |
| ASSERT_EQ(1, poll(&pfd, 1, 100)); |
| |
| // Handle all of the outstanding requests |
| EXPECT_CALL(h[0], handleCQE(_)); |
| EXPECT_CALL(h[1], handleCQE(_)); |
| ring.processEvents(); |
| testing::Mock::VerifyAndClearExpectations(&h[0]); |
| testing::Mock::VerifyAndClearExpectations(&h[1]); |
| |
| // Our event fd should be empty |
| ASSERT_EQ(0, poll(&pfd, 1, 100)); |
| } |
| |
| TEST_F(IoUringTest, HandleCalledOnDestroy) |
| { |
| auto& sqe = ring.getSQE(); |
| io_uring_prep_nop(&sqe); |
| ring.setHandler(sqe, &h[0]); |
| EXPECT_CALL(h[0], handleCQE(_)); |
| } |
| |
| } // namespace stdplus |