1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
7 #include "include/rbd_types.h"
8 #include "common/ceph_mutex.h"
9 #include "librbd/object_map/DiffRequest.h"
10 #include "gtest/gtest.h"
11 #include "gmock/gmock.h"
16 struct MockTestImageCtx
: public MockImageCtx
{
17 MockTestImageCtx(ImageCtx
&image_ctx
) : MockImageCtx(image_ctx
) {
21 } // anonymous namespace
24 #include "librbd/object_map/DiffRequest.cc"
27 using ::testing::Invoke
;
28 using ::testing::InSequence
;
29 using ::testing::StrEq
;
30 using ::testing::WithArg
;
33 namespace object_map
{
35 class TestMockObjectMapDiffRequest
: public TestMockFixture
{
37 typedef DiffRequest
<MockTestImageCtx
> MockDiffRequest
;
39 void SetUp() override
{
40 TestMockFixture::SetUp();
42 ASSERT_EQ(0, open_image(m_image_name
, &m_image_ctx
));
45 void expect_get_flags(MockTestImageCtx
& mock_image_ctx
, uint64_t snap_id
,
46 int32_t flags
, int r
) {
47 EXPECT_CALL(mock_image_ctx
, get_flags(snap_id
, _
))
48 .WillOnce(WithArg
<1>(Invoke([flags
, r
](uint64_t *out_flags
) {
54 template <typename Lambda
>
55 void expect_load_map(MockTestImageCtx
& mock_image_ctx
, uint64_t snap_id
,
56 const BitVector
<2>& object_map
, int r
,
58 std::string
snap_oid(ObjectMap
<>::object_map_name(mock_image_ctx
.id
,
60 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
61 exec(snap_oid
, _
, StrEq("rbd"), StrEq("object_map_load"), _
,
63 .WillOnce(WithArg
<5>(Invoke([object_map
, r
, lambda
=std::move(lambda
)]
64 (bufferlist
* out_bl
) {
67 auto out_object_map
{object_map
};
68 out_object_map
.set_crc_enabled(false);
69 encode(out_object_map
, *out_bl
);
74 void expect_load_map(MockTestImageCtx
& mock_image_ctx
, uint64_t snap_id
,
75 const BitVector
<2>& object_map
, int r
) {
76 expect_load_map(mock_image_ctx
, snap_id
, object_map
, r
, [](){});
79 librbd::ImageCtx
* m_image_ctx
= nullptr;
80 BitVector
<2> m_object_diff_state
;
83 TEST_F(TestMockObjectMapDiffRequest
, InvalidStartSnap
) {
84 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
89 auto req
= new MockDiffRequest(&mock_image_ctx
, CEPH_NOSNAP
, 0,
90 &m_object_diff_state
, &ctx
);
92 ASSERT_EQ(-EINVAL
, ctx
.wait());
95 TEST_F(TestMockObjectMapDiffRequest
, StartEndSnapEqual
) {
96 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
101 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, 1,
102 &m_object_diff_state
, &ctx
);
104 ASSERT_EQ(0, ctx
.wait());
105 ASSERT_EQ(0U, m_object_diff_state
.size());
108 TEST_F(TestMockObjectMapDiffRequest
, FastDiffDisabled
) {
109 // negative test -- object-map implicitly enables fast-diff
110 REQUIRE(!is_feature_enabled(RBD_FEATURE_OBJECT_MAP
));
112 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
117 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
118 &m_object_diff_state
, &ctx
);
120 ASSERT_EQ(-EINVAL
, ctx
.wait());
123 TEST_F(TestMockObjectMapDiffRequest
, FastDiffInvalid
) {
124 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
126 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
127 mock_image_ctx
.snap_info
= {
128 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, {}, {}, {}, {}, {}}}
132 expect_get_flags(mock_image_ctx
, 1U, RBD_FLAG_FAST_DIFF_INVALID
, 0);
135 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
136 &m_object_diff_state
, &ctx
);
138 ASSERT_EQ(-EINVAL
, ctx
.wait());
141 TEST_F(TestMockObjectMapDiffRequest
, FullDelta
) {
142 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
144 uint32_t object_count
= 5;
145 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
147 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
148 mock_image_ctx
.snap_info
= {
149 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
151 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
157 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
159 BitVector
<2> object_map_1
;
160 object_map_1
.resize(object_count
);
161 object_map_1
[1] = OBJECT_EXISTS_CLEAN
;
162 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
164 expect_get_flags(mock_image_ctx
, 2U, 0, 0);
166 BitVector
<2> object_map_2
;
167 object_map_2
.resize(object_count
);
168 object_map_2
[1] = OBJECT_EXISTS_CLEAN
;
169 object_map_2
[2] = OBJECT_EXISTS
;
170 object_map_2
[3] = OBJECT_EXISTS
;
171 expect_load_map(mock_image_ctx
, 2U, object_map_2
, 0);
173 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
175 BitVector
<2> object_map_head
;
176 object_map_head
.resize(object_count
);
177 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
178 object_map_head
[2] = OBJECT_EXISTS_CLEAN
;
179 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
182 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
183 &m_object_diff_state
, &ctx
);
185 ASSERT_EQ(0, ctx
.wait());
187 BitVector
<2> expected_diff_state
;
188 expected_diff_state
.resize(object_count
);
189 expected_diff_state
[1] = DIFF_STATE_UPDATED
;
190 expected_diff_state
[2] = DIFF_STATE_UPDATED
;
191 expected_diff_state
[3] = DIFF_STATE_HOLE
;
192 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
195 TEST_F(TestMockObjectMapDiffRequest
, IntermediateDelta
) {
196 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
198 uint32_t object_count
= 5;
199 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
201 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
202 mock_image_ctx
.snap_info
= {
203 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
205 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
211 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
213 BitVector
<2> object_map_1
;
214 object_map_1
.resize(object_count
);
215 object_map_1
[1] = OBJECT_EXISTS_CLEAN
;
216 object_map_1
[2] = OBJECT_EXISTS_CLEAN
;
217 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
219 expect_get_flags(mock_image_ctx
, 2U, 0, 0);
221 BitVector
<2> object_map_2
;
222 object_map_2
.resize(object_count
);
223 object_map_2
[1] = OBJECT_EXISTS_CLEAN
;
224 object_map_2
[2] = OBJECT_EXISTS
;
225 object_map_2
[3] = OBJECT_EXISTS
;
226 expect_load_map(mock_image_ctx
, 2U, object_map_2
, 0);
229 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, 2,
230 &m_object_diff_state
, &ctx
);
232 ASSERT_EQ(0, ctx
.wait());
234 BitVector
<2> expected_diff_state
;
235 expected_diff_state
.resize(object_count
);
236 expected_diff_state
[2] = DIFF_STATE_UPDATED
;
237 expected_diff_state
[3] = DIFF_STATE_UPDATED
;
238 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
241 TEST_F(TestMockObjectMapDiffRequest
, EndDelta
) {
242 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
244 uint32_t object_count
= 5;
245 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
247 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
248 mock_image_ctx
.snap_info
= {
249 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
251 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
257 expect_get_flags(mock_image_ctx
, 2U, 0, 0);
259 BitVector
<2> object_map_2
;
260 object_map_2
.resize(object_count
);
261 object_map_2
[1] = OBJECT_EXISTS_CLEAN
;
262 object_map_2
[2] = OBJECT_EXISTS
;
263 object_map_2
[3] = OBJECT_EXISTS
;
264 expect_load_map(mock_image_ctx
, 2U, object_map_2
, 0);
266 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
268 BitVector
<2> object_map_head
;
269 object_map_head
.resize(object_count
);
270 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
271 object_map_head
[2] = OBJECT_EXISTS_CLEAN
;
272 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
275 auto req
= new MockDiffRequest(&mock_image_ctx
, 2, CEPH_NOSNAP
,
276 &m_object_diff_state
, &ctx
);
278 ASSERT_EQ(0, ctx
.wait());
280 BitVector
<2> expected_diff_state
;
281 expected_diff_state
.resize(object_count
);
282 expected_diff_state
[3] = DIFF_STATE_HOLE
;
283 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
286 TEST_F(TestMockObjectMapDiffRequest
, StartSnapDNE
) {
287 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
289 uint32_t object_count
= 5;
290 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
292 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
293 mock_image_ctx
.snap_info
= {
294 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
301 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, CEPH_NOSNAP
,
302 &m_object_diff_state
, &ctx
);
304 ASSERT_EQ(-ENOENT
, ctx
.wait());
307 TEST_F(TestMockObjectMapDiffRequest
, EndSnapDNE
) {
308 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
310 uint32_t object_count
= 5;
311 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
313 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
314 mock_image_ctx
.snap_info
= {
315 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
321 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
323 BitVector
<2> object_map_1
;
324 object_map_1
.resize(object_count
);
325 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
328 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, 2,
329 &m_object_diff_state
, &ctx
);
331 ASSERT_EQ(-ENOENT
, ctx
.wait());
334 TEST_F(TestMockObjectMapDiffRequest
, IntermediateSnapDNE
) {
335 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
337 uint32_t object_count
= 5;
338 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
340 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
341 mock_image_ctx
.snap_info
= {
342 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
344 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
350 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
352 BitVector
<2> object_map_1
;
353 object_map_1
.resize(object_count
);
354 object_map_1
[1] = OBJECT_EXISTS_CLEAN
;
355 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0,
356 [&mock_image_ctx
]() { mock_image_ctx
.snap_info
.erase(2); });
358 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
360 BitVector
<2> object_map_head
;
361 object_map_head
.resize(object_count
);
362 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
363 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
366 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
367 &m_object_diff_state
, &ctx
);
369 ASSERT_EQ(0, ctx
.wait());
371 BitVector
<2> expected_diff_state
;
372 expected_diff_state
.resize(object_count
);
373 expected_diff_state
[1] = DIFF_STATE_UPDATED
;
374 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
377 TEST_F(TestMockObjectMapDiffRequest
, LoadObjectMapDNE
) {
378 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
380 uint32_t object_count
= 5;
381 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
383 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
387 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
389 BitVector
<2> object_map_head
;
390 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, -ENOENT
);
393 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
394 &m_object_diff_state
, &ctx
);
396 ASSERT_EQ(-ENOENT
, ctx
.wait());
399 TEST_F(TestMockObjectMapDiffRequest
, LoadIntermediateObjectMapDNE
) {
400 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
402 uint32_t object_count
= 5;
403 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
405 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
406 mock_image_ctx
.snap_info
= {
407 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
413 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
415 BitVector
<2> object_map_1
;
416 expect_load_map(mock_image_ctx
, 1U, object_map_1
, -ENOENT
);
418 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
420 BitVector
<2> object_map_head
;
421 object_map_head
.resize(object_count
);
422 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
423 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
426 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
427 &m_object_diff_state
, &ctx
);
429 ASSERT_EQ(0, ctx
.wait());
431 BitVector
<2> expected_diff_state
;
432 expected_diff_state
.resize(object_count
);
433 expected_diff_state
[1] = DIFF_STATE_UPDATED
;
434 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
437 TEST_F(TestMockObjectMapDiffRequest
, LoadObjectMapError
) {
438 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
440 uint32_t object_count
= 5;
441 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
443 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
444 mock_image_ctx
.snap_info
= {
445 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
451 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
453 BitVector
<2> object_map_1
;
454 expect_load_map(mock_image_ctx
, 1U, object_map_1
, -EPERM
);
457 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
458 &m_object_diff_state
, &ctx
);
460 ASSERT_EQ(-EPERM
, ctx
.wait());
463 TEST_F(TestMockObjectMapDiffRequest
, ObjectMapTooSmall
) {
464 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
466 uint32_t object_count
= 5;
467 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
469 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
470 mock_image_ctx
.snap_info
= {
471 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
477 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
479 BitVector
<2> object_map_1
;
480 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
483 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
484 &m_object_diff_state
, &ctx
);
486 ASSERT_EQ(-EINVAL
, ctx
.wait());
489 } // namespace object_map