diff options
Diffstat (limited to 'src/test/librbd/test_mock_ObjectMap.cc')
-rw-r--r-- | src/test/librbd/test_mock_ObjectMap.cc | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/test/librbd/test_mock_ObjectMap.cc b/src/test/librbd/test_mock_ObjectMap.cc new file mode 100644 index 000000000..39e29172c --- /dev/null +++ b/src/test/librbd/test_mock_ObjectMap.cc @@ -0,0 +1,285 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "librbd/ObjectMap.h" +#include "librbd/object_map/RefreshRequest.h" +#include "librbd/object_map/UnlockRequest.h" +#include "librbd/object_map/UpdateRequest.h" +#include <boost/scope_exit.hpp> + +namespace librbd { + +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace + +namespace object_map { + +template <> +struct RefreshRequest<MockTestImageCtx> { + Context *on_finish = nullptr; + ceph::BitVector<2u> *object_map = nullptr; + static RefreshRequest *s_instance; + static RefreshRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*, + ceph::BitVector<2u> *object_map, + uint64_t snap_id, Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + s_instance->object_map = object_map; + return s_instance; + } + + MOCK_METHOD0(send, void()); + + RefreshRequest() { + s_instance = this; + } +}; + +template <> +struct UnlockRequest<MockTestImageCtx> { + Context *on_finish = nullptr; + static UnlockRequest *s_instance; + static UnlockRequest *create(MockTestImageCtx &image_ctx, + Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + return s_instance; + } + + MOCK_METHOD0(send, void()); + UnlockRequest() { + s_instance = this; + } +}; + +template <> +struct UpdateRequest<MockTestImageCtx> { + Context *on_finish = nullptr; + static UpdateRequest *s_instance; + static UpdateRequest *create(MockTestImageCtx &image_ctx, ceph::shared_mutex*, + ceph::BitVector<2u> *object_map, + uint64_t snap_id, + uint64_t start_object_no, uint64_t end_object_no, + uint8_t new_state, + const boost::optional<uint8_t> ¤t_state, + const ZTracer::Trace &parent_trace, + bool ignore_enoent, Context *on_finish) { + ceph_assert(s_instance != nullptr); + s_instance->on_finish = on_finish; + s_instance->construct(snap_id, start_object_no, end_object_no, new_state, + current_state, ignore_enoent); + return s_instance; + } + + MOCK_METHOD6(construct, void(uint64_t snap_id, uint64_t start_object_no, + uint64_t end_object_no, uint8_t new_state, + const boost::optional<uint8_t> ¤t_state, + bool ignore_enoent)); + MOCK_METHOD0(send, void()); + UpdateRequest() { + s_instance = this; + } +}; + +RefreshRequest<MockTestImageCtx> *RefreshRequest<MockTestImageCtx>::s_instance = nullptr; +UnlockRequest<MockTestImageCtx> *UnlockRequest<MockTestImageCtx>::s_instance = nullptr; +UpdateRequest<MockTestImageCtx> *UpdateRequest<MockTestImageCtx>::s_instance = nullptr; + +} // namespace object_map +} // namespace librbd + +#include "librbd/ObjectMap.cc" + +namespace librbd { + +using testing::_; +using testing::InSequence; +using testing::Invoke; + +class TestMockObjectMap : public TestMockFixture { +public: + typedef ObjectMap<MockTestImageCtx> MockObjectMap; + typedef object_map::RefreshRequest<MockTestImageCtx> MockRefreshRequest; + typedef object_map::UnlockRequest<MockTestImageCtx> MockUnlockRequest; + typedef object_map::UpdateRequest<MockTestImageCtx> MockUpdateRequest; + + void expect_refresh(MockTestImageCtx &mock_image_ctx, + MockRefreshRequest &mock_refresh_request, + const ceph::BitVector<2u> &object_map, int r) { + EXPECT_CALL(mock_refresh_request, send()) + .WillOnce(Invoke([&mock_image_ctx, &mock_refresh_request, &object_map, r]() { + *mock_refresh_request.object_map = object_map; + mock_image_ctx.image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r); + })); + } + + void expect_unlock(MockTestImageCtx &mock_image_ctx, + MockUnlockRequest &mock_unlock_request, int r) { + EXPECT_CALL(mock_unlock_request, send()) + .WillOnce(Invoke([&mock_image_ctx, &mock_unlock_request, r]() { + mock_image_ctx.image_ctx->op_work_queue->queue(mock_unlock_request.on_finish, r); + })); + } + + void expect_update(MockTestImageCtx &mock_image_ctx, + MockUpdateRequest &mock_update_request, + uint64_t snap_id, uint64_t start_object_no, + uint64_t end_object_no, uint8_t new_state, + const boost::optional<uint8_t> ¤t_state, + bool ignore_enoent, Context **on_finish) { + EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no, + end_object_no, new_state, + current_state, ignore_enoent)) + .Times(1); + EXPECT_CALL(mock_update_request, send()) + .WillOnce(Invoke([&mock_update_request, on_finish]() { + *on_finish = mock_update_request.on_finish; + })); + } + +}; + +TEST_F(TestMockObjectMap, NonDetainedUpdate) { + REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + ceph::BitVector<2u> object_map; + object_map.resize(4); + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0); + + MockUpdateRequest mock_update_request; + Context *finish_update_1; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 0, 1, 1, {}, false, &finish_update_1); + Context *finish_update_2; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 1, 2, 1, {}, false, &finish_update_2); + + MockUnlockRequest mock_unlock_request; + expect_unlock(mock_image_ctx, mock_unlock_request, 0); + + MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP); + BOOST_SCOPE_EXIT(&mock_object_map) { + mock_object_map->put(); + } BOOST_SCOPE_EXIT_END + + C_SaferCond open_ctx; + mock_object_map->open(&open_ctx); + ASSERT_EQ(0, open_ctx.wait()); + + C_SaferCond update_ctx1; + C_SaferCond update_ctx2; + { + std::shared_lock image_locker{mock_image_ctx.image_lock}; + mock_object_map->aio_update(CEPH_NOSNAP, 0, 1, {}, {}, false, &update_ctx1); + mock_object_map->aio_update(CEPH_NOSNAP, 1, 1, {}, {}, false, &update_ctx2); + } + + finish_update_2->complete(0); + ASSERT_EQ(0, update_ctx2.wait()); + + finish_update_1->complete(0); + ASSERT_EQ(0, update_ctx1.wait()); + + C_SaferCond close_ctx; + mock_object_map->close(&close_ctx); + ASSERT_EQ(0, close_ctx.wait()); +} + +TEST_F(TestMockObjectMap, DetainedUpdate) { + REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP); + + librbd::ImageCtx *ictx; + ASSERT_EQ(0, open_image(m_image_name, &ictx)); + + MockTestImageCtx mock_image_ctx(*ictx); + + InSequence seq; + ceph::BitVector<2u> object_map; + object_map.resize(4); + MockRefreshRequest mock_refresh_request; + expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0); + + MockUpdateRequest mock_update_request; + Context *finish_update_1; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 1, 4, 1, {}, false, &finish_update_1); + Context *finish_update_2 = nullptr; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 1, 3, 1, {}, false, &finish_update_2); + Context *finish_update_3 = nullptr; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 2, 3, 1, {}, false, &finish_update_3); + Context *finish_update_4 = nullptr; + expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP, + 0, 2, 1, {}, false, &finish_update_4); + + MockUnlockRequest mock_unlock_request; + expect_unlock(mock_image_ctx, mock_unlock_request, 0); + + MockObjectMap *mock_object_map = new MockObjectMap(mock_image_ctx, CEPH_NOSNAP); + BOOST_SCOPE_EXIT(&mock_object_map) { + mock_object_map->put(); + } BOOST_SCOPE_EXIT_END + + C_SaferCond open_ctx; + mock_object_map->open(&open_ctx); + ASSERT_EQ(0, open_ctx.wait()); + + C_SaferCond update_ctx1; + C_SaferCond update_ctx2; + C_SaferCond update_ctx3; + C_SaferCond update_ctx4; + { + std::shared_lock image_locker{mock_image_ctx.image_lock}; + mock_object_map->aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, false, + &update_ctx1); + mock_object_map->aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, false, + &update_ctx2); + mock_object_map->aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, false, + &update_ctx3); + mock_object_map->aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, false, + &update_ctx4); + } + + // updates 2, 3, and 4 are blocked on update 1 + ASSERT_EQ(nullptr, finish_update_2); + finish_update_1->complete(0); + ASSERT_EQ(0, update_ctx1.wait()); + + // updates 3 and 4 are blocked on update 2 + ASSERT_NE(nullptr, finish_update_2); + ASSERT_EQ(nullptr, finish_update_3); + ASSERT_EQ(nullptr, finish_update_4); + finish_update_2->complete(0); + ASSERT_EQ(0, update_ctx2.wait()); + + ASSERT_NE(nullptr, finish_update_3); + ASSERT_NE(nullptr, finish_update_4); + finish_update_3->complete(0); + finish_update_4->complete(0); + ASSERT_EQ(0, update_ctx3.wait()); + ASSERT_EQ(0, update_ctx4.wait()); + + C_SaferCond close_ctx; + mock_object_map->close(&close_ctx); + ASSERT_EQ(0, close_ctx.wait()); +} + +} // namespace librbd + |