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_DATA_UPDATED
;
190 expected_diff_state
[2] = DIFF_STATE_DATA_UPDATED
;
191 expected_diff_state
[3] = DIFF_STATE_HOLE_UPDATED
;
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
[1] = DIFF_STATE_DATA
;
237 expected_diff_state
[2] = DIFF_STATE_DATA_UPDATED
;
238 expected_diff_state
[3] = DIFF_STATE_DATA_UPDATED
;
239 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
242 TEST_F(TestMockObjectMapDiffRequest
, EndDelta
) {
243 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
245 uint32_t object_count
= 5;
246 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
248 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
249 mock_image_ctx
.snap_info
= {
250 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
252 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
258 expect_get_flags(mock_image_ctx
, 2U, 0, 0);
260 BitVector
<2> object_map_2
;
261 object_map_2
.resize(object_count
);
262 object_map_2
[1] = OBJECT_EXISTS_CLEAN
;
263 object_map_2
[2] = OBJECT_EXISTS
;
264 object_map_2
[3] = OBJECT_EXISTS
;
265 expect_load_map(mock_image_ctx
, 2U, object_map_2
, 0);
267 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
269 BitVector
<2> object_map_head
;
270 object_map_head
.resize(object_count
);
271 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
272 object_map_head
[2] = OBJECT_EXISTS_CLEAN
;
273 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
276 auto req
= new MockDiffRequest(&mock_image_ctx
, 2, CEPH_NOSNAP
,
277 &m_object_diff_state
, &ctx
);
279 ASSERT_EQ(0, ctx
.wait());
281 BitVector
<2> expected_diff_state
;
282 expected_diff_state
.resize(object_count
);
283 expected_diff_state
[1] = DIFF_STATE_DATA
;
284 expected_diff_state
[2] = DIFF_STATE_DATA_UPDATED
;
285 expected_diff_state
[3] = DIFF_STATE_HOLE_UPDATED
;
286 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
289 TEST_F(TestMockObjectMapDiffRequest
, StartSnapDNE
) {
290 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
292 uint32_t object_count
= 5;
293 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
295 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
296 mock_image_ctx
.snap_info
= {
297 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
304 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, CEPH_NOSNAP
,
305 &m_object_diff_state
, &ctx
);
307 ASSERT_EQ(-ENOENT
, ctx
.wait());
310 TEST_F(TestMockObjectMapDiffRequest
, EndSnapDNE
) {
311 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
313 uint32_t object_count
= 5;
314 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
316 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
317 mock_image_ctx
.snap_info
= {
318 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
324 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
326 BitVector
<2> object_map_1
;
327 object_map_1
.resize(object_count
);
328 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
331 auto req
= new MockDiffRequest(&mock_image_ctx
, 1, 2,
332 &m_object_diff_state
, &ctx
);
334 ASSERT_EQ(-ENOENT
, ctx
.wait());
337 TEST_F(TestMockObjectMapDiffRequest
, IntermediateSnapDNE
) {
338 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
340 uint32_t object_count
= 5;
341 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
343 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
344 mock_image_ctx
.snap_info
= {
345 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
347 {2U, {"snap2", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
353 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
355 BitVector
<2> object_map_1
;
356 object_map_1
.resize(object_count
);
357 object_map_1
[1] = OBJECT_EXISTS_CLEAN
;
358 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0,
359 [&mock_image_ctx
]() { mock_image_ctx
.snap_info
.erase(2); });
361 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
363 BitVector
<2> object_map_head
;
364 object_map_head
.resize(object_count
);
365 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
366 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
369 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
370 &m_object_diff_state
, &ctx
);
372 ASSERT_EQ(0, ctx
.wait());
374 BitVector
<2> expected_diff_state
;
375 expected_diff_state
.resize(object_count
);
376 expected_diff_state
[1] = DIFF_STATE_DATA_UPDATED
;
377 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
380 TEST_F(TestMockObjectMapDiffRequest
, LoadObjectMapDNE
) {
381 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
383 uint32_t object_count
= 5;
384 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
386 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
390 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
392 BitVector
<2> object_map_head
;
393 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, -ENOENT
);
396 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
397 &m_object_diff_state
, &ctx
);
399 ASSERT_EQ(-ENOENT
, ctx
.wait());
402 TEST_F(TestMockObjectMapDiffRequest
, LoadIntermediateObjectMapDNE
) {
403 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
405 uint32_t object_count
= 5;
406 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
408 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
409 mock_image_ctx
.snap_info
= {
410 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
416 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
418 BitVector
<2> object_map_1
;
419 expect_load_map(mock_image_ctx
, 1U, object_map_1
, -ENOENT
);
421 expect_get_flags(mock_image_ctx
, CEPH_NOSNAP
, 0, 0);
423 BitVector
<2> object_map_head
;
424 object_map_head
.resize(object_count
);
425 object_map_head
[1] = OBJECT_EXISTS_CLEAN
;
426 expect_load_map(mock_image_ctx
, CEPH_NOSNAP
, object_map_head
, 0);
429 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
430 &m_object_diff_state
, &ctx
);
432 ASSERT_EQ(0, ctx
.wait());
434 BitVector
<2> expected_diff_state
;
435 expected_diff_state
.resize(object_count
);
436 expected_diff_state
[1] = DIFF_STATE_DATA_UPDATED
;
437 ASSERT_EQ(expected_diff_state
, m_object_diff_state
);
440 TEST_F(TestMockObjectMapDiffRequest
, LoadObjectMapError
) {
441 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
443 uint32_t object_count
= 5;
444 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
446 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
447 mock_image_ctx
.snap_info
= {
448 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
454 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
456 BitVector
<2> object_map_1
;
457 expect_load_map(mock_image_ctx
, 1U, object_map_1
, -EPERM
);
460 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
461 &m_object_diff_state
, &ctx
);
463 ASSERT_EQ(-EPERM
, ctx
.wait());
466 TEST_F(TestMockObjectMapDiffRequest
, ObjectMapTooSmall
) {
467 REQUIRE_FEATURE(RBD_FEATURE_FAST_DIFF
);
469 uint32_t object_count
= 5;
470 m_image_ctx
->size
= object_count
* (1 << m_image_ctx
->order
);
472 MockTestImageCtx
mock_image_ctx(*m_image_ctx
);
473 mock_image_ctx
.snap_info
= {
474 {1U, {"snap1", {cls::rbd::UserSnapshotNamespace
{}}, mock_image_ctx
.size
, {},
480 expect_get_flags(mock_image_ctx
, 1U, 0, 0);
482 BitVector
<2> object_map_1
;
483 expect_load_map(mock_image_ctx
, 1U, object_map_1
, 0);
486 auto req
= new MockDiffRequest(&mock_image_ctx
, 0, CEPH_NOSNAP
,
487 &m_object_diff_state
, &ctx
);
489 ASSERT_EQ(-EINVAL
, ctx
.wait());
492 } // namespace object_map