diff options
Diffstat (limited to 'src/test/librbd/object_map/test_mock_DiffRequest.cc')
-rw-r--r-- | src/test/librbd/object_map/test_mock_DiffRequest.cc | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/src/test/librbd/object_map/test_mock_DiffRequest.cc b/src/test/librbd/object_map/test_mock_DiffRequest.cc new file mode 100644 index 000000000..c25ae4a95 --- /dev/null +++ b/src/test/librbd/object_map/test_mock_DiffRequest.cc @@ -0,0 +1,493 @@ +// -*- 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/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "include/rbd_types.h" +#include "common/ceph_mutex.h" +#include "librbd/object_map/DiffRequest.h" +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace +} // namespace librbd + +#include "librbd/object_map/DiffRequest.cc" + +using ::testing::_; +using ::testing::Invoke; +using ::testing::InSequence; +using ::testing::StrEq; +using ::testing::WithArg; + +namespace librbd { +namespace object_map { + +class TestMockObjectMapDiffRequest : public TestMockFixture { +public: + typedef DiffRequest<MockTestImageCtx> MockDiffRequest; + + void SetUp() override { + TestMockFixture::SetUp(); + + ASSERT_EQ(0, open_image(m_image_name, &m_image_ctx)); + } + + void expect_get_flags(MockTestImageCtx& mock_image_ctx, uint64_t snap_id, + int32_t flags, int r) { + EXPECT_CALL(mock_image_ctx, get_flags(snap_id, _)) + .WillOnce(WithArg<1>(Invoke([flags, r](uint64_t *out_flags) { + *out_flags = flags; + return r; + }))); + } + + template <typename Lambda> + void expect_load_map(MockTestImageCtx& mock_image_ctx, uint64_t snap_id, + const BitVector<2>& object_map, int r, + Lambda&& lambda) { + std::string snap_oid(ObjectMap<>::object_map_name(mock_image_ctx.id, + snap_id)); + EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx), + exec(snap_oid, _, StrEq("rbd"), StrEq("object_map_load"), _, + _, _, _)) + .WillOnce(WithArg<5>(Invoke([object_map, r, lambda=std::move(lambda)] + (bufferlist* out_bl) { + lambda(); + + auto out_object_map{object_map}; + out_object_map.set_crc_enabled(false); + encode(out_object_map, *out_bl); + return r; + }))); + } + + void expect_load_map(MockTestImageCtx& mock_image_ctx, uint64_t snap_id, + const BitVector<2>& object_map, int r) { + expect_load_map(mock_image_ctx, snap_id, object_map, r, [](){}); + } + + librbd::ImageCtx* m_image_ctx = nullptr; + BitVector<2> m_object_diff_state; +}; + +TEST_F(TestMockObjectMapDiffRequest, InvalidStartSnap) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, CEPH_NOSNAP, 0, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, StartEndSnapEqual) { + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 1, 1, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + ASSERT_EQ(0U, m_object_diff_state.size()); +} + +TEST_F(TestMockObjectMapDiffRequest, FastDiffDisabled) { + // negative test -- object-map implicitly enables fast-diff + REQUIRE(!is_feature_enabled(RBD_FEATURE_OBJECT_MAP)); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, FastDiffInvalid) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, {}, {}, {}, {}, {}}} + }; + + InSequence seq; + expect_get_flags(mock_image_ctx, 1U, RBD_FLAG_FAST_DIFF_INVALID, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, FullDelta) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}}, + {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + object_map_1.resize(object_count); + object_map_1[1] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, 1U, object_map_1, 0); + + expect_get_flags(mock_image_ctx, 2U, 0, 0); + + BitVector<2> object_map_2; + object_map_2.resize(object_count); + object_map_2[1] = OBJECT_EXISTS_CLEAN; + object_map_2[2] = OBJECT_EXISTS; + object_map_2[3] = OBJECT_EXISTS; + expect_load_map(mock_image_ctx, 2U, object_map_2, 0); + + expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0); + + BitVector<2> object_map_head; + object_map_head.resize(object_count); + object_map_head[1] = OBJECT_EXISTS_CLEAN; + object_map_head[2] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + BitVector<2> expected_diff_state; + expected_diff_state.resize(object_count); + expected_diff_state[1] = DIFF_STATE_DATA_UPDATED; + expected_diff_state[2] = DIFF_STATE_DATA_UPDATED; + expected_diff_state[3] = DIFF_STATE_HOLE_UPDATED; + ASSERT_EQ(expected_diff_state, m_object_diff_state); +} + +TEST_F(TestMockObjectMapDiffRequest, IntermediateDelta) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}}, + {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + object_map_1.resize(object_count); + object_map_1[1] = OBJECT_EXISTS; + object_map_1[2] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, 1U, object_map_1, 0); + + expect_get_flags(mock_image_ctx, 2U, 0, 0); + + BitVector<2> object_map_2; + object_map_2.resize(object_count); + object_map_2[1] = OBJECT_EXISTS_CLEAN; + object_map_2[2] = OBJECT_EXISTS; + object_map_2[3] = OBJECT_EXISTS; + expect_load_map(mock_image_ctx, 2U, object_map_2, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 1, 2, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + BitVector<2> expected_diff_state; + expected_diff_state.resize(object_count); + expected_diff_state[1] = DIFF_STATE_DATA; + expected_diff_state[2] = DIFF_STATE_DATA_UPDATED; + expected_diff_state[3] = DIFF_STATE_DATA_UPDATED; + ASSERT_EQ(expected_diff_state, m_object_diff_state); +} + +TEST_F(TestMockObjectMapDiffRequest, EndDelta) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}}, + {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 2U, 0, 0); + + BitVector<2> object_map_2; + object_map_2.resize(object_count); + object_map_2[1] = OBJECT_EXISTS_CLEAN; + object_map_2[2] = OBJECT_EXISTS; + object_map_2[3] = OBJECT_EXISTS; + expect_load_map(mock_image_ctx, 2U, object_map_2, 0); + + expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0); + + BitVector<2> object_map_head; + object_map_head.resize(object_count); + object_map_head[1] = OBJECT_EXISTS_CLEAN; + object_map_head[2] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 2, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + BitVector<2> expected_diff_state; + expected_diff_state.resize(object_count); + expected_diff_state[1] = DIFF_STATE_DATA; + expected_diff_state[2] = DIFF_STATE_DATA; + expected_diff_state[3] = DIFF_STATE_HOLE_UPDATED; + ASSERT_EQ(expected_diff_state, m_object_diff_state); +} + +TEST_F(TestMockObjectMapDiffRequest, StartSnapDNE) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 1, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, EndSnapDNE) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + object_map_1.resize(object_count); + expect_load_map(mock_image_ctx, 1U, object_map_1, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 1, 2, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, IntermediateSnapDNE) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}}, + {2U, {"snap2", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + object_map_1.resize(object_count); + object_map_1[1] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, 1U, object_map_1, 0, + [&mock_image_ctx]() { mock_image_ctx.snap_info.erase(2); }); + + expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0); + + BitVector<2> object_map_head; + object_map_head.resize(object_count); + object_map_head[1] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + BitVector<2> expected_diff_state; + expected_diff_state.resize(object_count); + expected_diff_state[1] = DIFF_STATE_DATA_UPDATED; + ASSERT_EQ(expected_diff_state, m_object_diff_state); +} + +TEST_F(TestMockObjectMapDiffRequest, LoadObjectMapDNE) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + + InSequence seq; + + expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0); + + BitVector<2> object_map_head; + expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, -ENOENT); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-ENOENT, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, LoadIntermediateObjectMapDNE) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + expect_load_map(mock_image_ctx, 1U, object_map_1, -ENOENT); + + expect_get_flags(mock_image_ctx, CEPH_NOSNAP, 0, 0); + + BitVector<2> object_map_head; + object_map_head.resize(object_count); + object_map_head[1] = OBJECT_EXISTS_CLEAN; + expect_load_map(mock_image_ctx, CEPH_NOSNAP, object_map_head, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(0, ctx.wait()); + + BitVector<2> expected_diff_state; + expected_diff_state.resize(object_count); + expected_diff_state[1] = DIFF_STATE_DATA_UPDATED; + ASSERT_EQ(expected_diff_state, m_object_diff_state); +} + +TEST_F(TestMockObjectMapDiffRequest, LoadObjectMapError) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + expect_load_map(mock_image_ctx, 1U, object_map_1, -EPERM); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-EPERM, ctx.wait()); +} + +TEST_F(TestMockObjectMapDiffRequest, ObjectMapTooSmall) { + REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF); + + uint32_t object_count = 5; + m_image_ctx->size = object_count * (1 << m_image_ctx->order); + + MockTestImageCtx mock_image_ctx(*m_image_ctx); + mock_image_ctx.snap_info = { + {1U, {"snap1", {cls::rbd::UserSnapshotNamespace{}}, mock_image_ctx.size, {}, + {}, {}, {}}} + }; + + InSequence seq; + + expect_get_flags(mock_image_ctx, 1U, 0, 0); + + BitVector<2> object_map_1; + expect_load_map(mock_image_ctx, 1U, object_map_1, 0); + + C_SaferCond ctx; + auto req = new MockDiffRequest(&mock_image_ctx, 0, CEPH_NOSNAP, + &m_object_diff_state, &ctx); + req->send(); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +} // namespace object_map +} // librbd |