blob: 5be1b91a6ddfe1868d1684661fea69e92fd4a873 [file] [log] [blame]
#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