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/librbd/mock/MockImageCtx.h"
7 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
8 #include "common/bit_vector.hpp"
9 #include "librbd/AsyncRequest.h"
10 #include "librbd/internal.h"
11 #include "librbd/ObjectMap.h"
12 #include "librbd/Utils.h"
13 #include "librbd/io/ObjectRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
21 struct MockTestImageCtx
: public MockImageCtx
{
22 MockTestImageCtx(ImageCtx
&image_ctx
) : MockImageCtx(image_ctx
) {
26 } // anonymous namespace
29 struct AsyncRequest
<librbd::MockTestImageCtx
> {
30 librbd::MockTestImageCtx
& m_image_ctx
;
33 AsyncRequest(librbd::MockTestImageCtx
& image_ctx
, Context
* on_finish
)
34 : m_image_ctx(image_ctx
), on_finish(on_finish
) {
36 virtual ~AsyncRequest() {
39 Context
* create_callback_context() {
40 return util::create_context_callback(this);
43 Context
* create_async_callback_context() {
44 return util::create_context_callback
<AsyncRequest
,
45 &AsyncRequest::async_complete
>(this);
48 void complete(int r
) {
49 if (should_complete(r
)) {
54 void async_complete(int r
) {
55 on_finish
->complete(r
);
58 bool is_canceled() const {
62 virtual void send() = 0;
63 virtual bool should_complete(int r
) = 0;
69 struct ObjectRequest
<librbd::MockTestImageCtx
> : public ObjectRequestHandle
{
70 static ObjectRequest
* s_instance
;
71 Context
*on_finish
= nullptr;
73 static ObjectRequest
* create_discard(librbd::MockTestImageCtx
*ictx
,
74 const std::string
&oid
,
78 const ::SnapContext
&snapc
,
79 bool disable_remove_on_clone
,
80 bool update_object_map
,
81 const ZTracer::Trace
&parent_trace
,
82 Context
*completion
) {
83 assert(s_instance
!= nullptr);
84 EXPECT_FALSE(disable_remove_on_clone
);
85 s_instance
->on_finish
= completion
;
86 s_instance
->construct(object_off
, object_len
, update_object_map
);
94 MOCK_METHOD3(construct
, void(uint64_t, uint64_t, bool));
95 MOCK_METHOD0(send
, void());
96 MOCK_METHOD1(fail
, void(int));
99 ObjectRequest
<librbd::MockTestImageCtx
>* ObjectRequest
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
102 } // namespace librbd
104 // template definitions
105 #include "librbd/AsyncObjectThrottle.cc"
106 #include "librbd/operation/TrimRequest.cc"
109 namespace operation
{
112 using ::testing::DoAll
;
113 using ::testing::InSequence
;
114 using ::testing::Invoke
;
115 using ::testing::Return
;
116 using ::testing::StrEq
;
117 using ::testing::WithArg
;
119 class TestMockOperationTrimRequest
: public TestMockFixture
{
121 typedef TrimRequest
<MockTestImageCtx
> MockTrimRequest
;
122 typedef librbd::io::ObjectRequest
<MockTestImageCtx
> MockObjectRequest
;
124 int create_snapshot(const char *snap_name
) {
125 librbd::ImageCtx
*ictx
;
126 int r
= open_image(m_image_name
, &ictx
);
131 r
= snap_create(*ictx
, snap_name
);
136 r
= snap_protect(*ictx
, snap_name
);
144 void expect_is_lock_owner(MockTestImageCtx
&mock_image_ctx
) {
145 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
146 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
147 .WillRepeatedly(Return(true));
151 void expect_object_map_update(MockTestImageCtx
&mock_image_ctx
,
152 uint64_t start_object
, uint64_t end_object
,
153 uint8_t state
, uint8_t current_state
,
154 bool updated
, int ret_val
) {
155 if (mock_image_ctx
.object_map
!= nullptr) {
156 EXPECT_CALL(*mock_image_ctx
.object_map
,
157 aio_update(CEPH_NOSNAP
, start_object
, end_object
, state
,
158 boost::optional
<uint8_t>(current_state
), _
, _
))
159 .WillOnce(WithArg
<6>(Invoke([&mock_image_ctx
, updated
, ret_val
](Context
*ctx
) {
161 mock_image_ctx
.op_work_queue
->queue(ctx
, ret_val
);
168 void expect_get_parent_overlap(MockTestImageCtx
&mock_image_ctx
,
170 EXPECT_CALL(mock_image_ctx
, get_parent_overlap(CEPH_NOSNAP
, _
))
171 .WillOnce(WithArg
<1>(Invoke([overlap
](uint64_t *o
) {
177 void expect_object_may_exist(MockTestImageCtx
&mock_image_ctx
,
178 uint64_t object_no
, bool exists
) {
179 if (mock_image_ctx
.object_map
!= nullptr) {
180 EXPECT_CALL(*mock_image_ctx
.object_map
, object_may_exist(object_no
))
181 .WillOnce(Return(exists
));
185 void expect_get_object_name(MockTestImageCtx
&mock_image_ctx
,
186 uint64_t object_no
, const std::string
& oid
) {
187 EXPECT_CALL(mock_image_ctx
, get_object_name(object_no
))
188 .WillOnce(Return(oid
));
191 void expect_aio_remove(MockTestImageCtx
&mock_image_ctx
,
192 const std::string
& oid
, int ret_val
) {
193 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.data_ctx
), remove(oid
, _
))
194 .WillOnce(Return(ret_val
));
197 void expect_object_discard(MockImageCtx
&mock_image_ctx
,
198 MockObjectRequest
&mock_object_request
,
199 uint64_t offset
, uint64_t length
,
200 bool update_object_map
, int ret_val
) {
201 EXPECT_CALL(mock_object_request
, construct(offset
, length
,
203 EXPECT_CALL(mock_object_request
, send())
204 .WillOnce(Invoke([&mock_image_ctx
, &mock_object_request
, ret_val
]() {
205 mock_image_ctx
.op_work_queue
->queue(mock_object_request
.on_finish
, ret_val
);
210 TEST_F(TestMockOperationTrimRequest
, SuccessRemove
) {
211 librbd::ImageCtx
*ictx
;
212 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
214 MockTestImageCtx
mock_image_ctx(*ictx
);
215 MockExclusiveLock mock_exclusive_lock
;
216 MockJournal mock_journal
;
217 MockObjectMap mock_object_map
;
218 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
220 expect_op_work_queue(mock_image_ctx
);
221 expect_is_lock_owner(mock_image_ctx
);
224 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
225 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
228 expect_object_map_update(mock_image_ctx
, 0, 1, OBJECT_PENDING
, OBJECT_EXISTS
,
232 expect_get_parent_overlap(mock_image_ctx
, 0);
235 expect_object_may_exist(mock_image_ctx
, 0, true);
236 expect_get_object_name(mock_image_ctx
, 0, "object0");
237 expect_aio_remove(mock_image_ctx
, "object0", 0);
240 expect_object_map_update(mock_image_ctx
, 0, 1, OBJECT_NONEXISTENT
,
241 OBJECT_PENDING
, true, 0);
243 C_SaferCond cond_ctx
;
244 librbd::NoOpProgressContext progress_ctx
;
245 MockTrimRequest
*req
= new MockTrimRequest(
246 mock_image_ctx
, &cond_ctx
, m_image_size
, 0, progress_ctx
);
248 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
251 ASSERT_EQ(0, cond_ctx
.wait());
254 TEST_F(TestMockOperationTrimRequest
, SuccessCopyUp
) {
255 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
)
256 ASSERT_EQ(0, create_snapshot("snap1"));
260 ASSERT_TRUE(::get_features(&features
));
261 std::string clone_name
= get_temp_image_name();
262 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
263 clone_name
.c_str(), features
, &order
, 0, 0));
265 librbd::ImageCtx
*ictx
;
266 ASSERT_EQ(0, open_image(clone_name
, &ictx
));
267 ASSERT_EQ(0, snap_create(*ictx
, "snap"));
269 MockTestImageCtx
mock_image_ctx(*ictx
);
270 MockExclusiveLock mock_exclusive_lock
;
271 MockJournal mock_journal
;
272 MockObjectMap mock_object_map
;
273 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
275 expect_op_work_queue(mock_image_ctx
);
276 expect_is_lock_owner(mock_image_ctx
);
279 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
280 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
283 expect_object_map_update(mock_image_ctx
, 0, 2, OBJECT_PENDING
, OBJECT_EXISTS
,
287 expect_get_parent_overlap(mock_image_ctx
, ictx
->get_object_size());
288 expect_get_object_name(mock_image_ctx
, 0, "object0");
290 MockObjectRequest mock_object_request
;
291 expect_object_discard(mock_image_ctx
, mock_object_request
, 0,
292 ictx
->get_object_size(), false, 0);
295 expect_object_may_exist(mock_image_ctx
, 1, true);
296 expect_get_object_name(mock_image_ctx
, 1, "object1");
297 expect_aio_remove(mock_image_ctx
, "object1", 0);
300 expect_object_map_update(mock_image_ctx
, 0, 2, OBJECT_NONEXISTENT
,
301 OBJECT_PENDING
, true, 0);
303 C_SaferCond cond_ctx
;
304 librbd::NoOpProgressContext progress_ctx
;
305 MockTrimRequest
*req
= new MockTrimRequest(
306 mock_image_ctx
, &cond_ctx
, 2 * ictx
->get_object_size(), 0, progress_ctx
);
308 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
311 ASSERT_EQ(0, cond_ctx
.wait());
314 TEST_F(TestMockOperationTrimRequest
, SuccessBoundary
) {
315 librbd::ImageCtx
*ictx
;
316 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
318 MockTestImageCtx
mock_image_ctx(*ictx
);
319 MockExclusiveLock mock_exclusive_lock
;
320 MockJournal mock_journal
;
321 MockObjectMap mock_object_map
;
322 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
324 expect_op_work_queue(mock_image_ctx
);
325 expect_is_lock_owner(mock_image_ctx
);
328 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
329 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
332 MockObjectRequest mock_object_request
;
333 expect_object_discard(mock_image_ctx
, mock_object_request
, 1,
334 ictx
->get_object_size() - 1, true, 0);
336 C_SaferCond cond_ctx
;
337 librbd::NoOpProgressContext progress_ctx
;
338 MockTrimRequest
*req
= new MockTrimRequest(
339 mock_image_ctx
, &cond_ctx
, ictx
->get_object_size(), 1, progress_ctx
);
341 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
344 ASSERT_EQ(0, cond_ctx
.wait());
347 TEST_F(TestMockOperationTrimRequest
, SuccessNoOp
) {
348 librbd::ImageCtx
*ictx
;
349 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
351 MockTestImageCtx
mock_image_ctx(*ictx
);
352 MockExclusiveLock mock_exclusive_lock
;
353 MockJournal mock_journal
;
354 MockObjectMap mock_object_map
;
355 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
359 TEST_F(TestMockOperationTrimRequest
, RemoveError
) {
360 librbd::ImageCtx
*ictx
;
361 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
363 MockTestImageCtx
mock_image_ctx(*ictx
);
364 MockExclusiveLock mock_exclusive_lock
;
365 MockJournal mock_journal
;
366 MockObjectMap mock_object_map
;
367 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
369 expect_op_work_queue(mock_image_ctx
);
370 expect_is_lock_owner(mock_image_ctx
);
373 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
374 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
377 expect_object_map_update(mock_image_ctx
, 0, 1, OBJECT_PENDING
, OBJECT_EXISTS
,
381 expect_get_parent_overlap(mock_image_ctx
, 0);
384 expect_object_may_exist(mock_image_ctx
, 0, true);
385 expect_get_object_name(mock_image_ctx
, 0, "object0");
386 expect_aio_remove(mock_image_ctx
, "object0", -EPERM
);
388 C_SaferCond cond_ctx
;
389 librbd::NoOpProgressContext progress_ctx
;
390 MockTrimRequest
*req
= new MockTrimRequest(
391 mock_image_ctx
, &cond_ctx
, m_image_size
, 0, progress_ctx
);
393 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
396 ASSERT_EQ(-EPERM
, cond_ctx
.wait());
399 TEST_F(TestMockOperationTrimRequest
, CopyUpError
) {
400 REQUIRE_FEATURE(RBD_FEATURE_LAYERING
)
401 ASSERT_EQ(0, create_snapshot("snap1"));
405 ASSERT_TRUE(::get_features(&features
));
406 std::string clone_name
= get_temp_image_name();
407 ASSERT_EQ(0, librbd::clone(m_ioctx
, m_image_name
.c_str(), "snap1", m_ioctx
,
408 clone_name
.c_str(), features
, &order
, 0, 0));
410 librbd::ImageCtx
*ictx
;
411 ASSERT_EQ(0, open_image(clone_name
, &ictx
));
412 ASSERT_EQ(0, snap_create(*ictx
, "snap"));
414 MockTestImageCtx
mock_image_ctx(*ictx
);
415 MockExclusiveLock mock_exclusive_lock
;
416 MockJournal mock_journal
;
417 MockObjectMap mock_object_map
;
418 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
420 expect_op_work_queue(mock_image_ctx
);
421 expect_is_lock_owner(mock_image_ctx
);
424 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
425 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
428 expect_object_map_update(mock_image_ctx
, 0, 2, OBJECT_PENDING
, OBJECT_EXISTS
,
432 expect_get_parent_overlap(mock_image_ctx
, ictx
->get_object_size());
433 expect_get_object_name(mock_image_ctx
, 0, "object0");
435 MockObjectRequest mock_object_request
;
436 expect_object_discard(mock_image_ctx
, mock_object_request
, 0,
437 ictx
->get_object_size(), false, -EINVAL
);
439 C_SaferCond cond_ctx
;
440 librbd::NoOpProgressContext progress_ctx
;
441 MockTrimRequest
*req
= new MockTrimRequest(
442 mock_image_ctx
, &cond_ctx
, 2 * ictx
->get_object_size(), 0, progress_ctx
);
444 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
447 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
450 TEST_F(TestMockOperationTrimRequest
, BoundaryError
) {
451 librbd::ImageCtx
*ictx
;
452 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
454 MockTestImageCtx
mock_image_ctx(*ictx
);
455 MockExclusiveLock mock_exclusive_lock
;
456 MockJournal mock_journal
;
457 MockObjectMap mock_object_map
;
458 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
460 expect_op_work_queue(mock_image_ctx
);
461 expect_is_lock_owner(mock_image_ctx
);
464 EXPECT_CALL(mock_image_ctx
, get_stripe_period()).WillOnce(Return(ictx
->get_object_size()));
465 EXPECT_CALL(mock_image_ctx
, get_stripe_count()).WillOnce(Return(ictx
->get_stripe_count()));
468 MockObjectRequest mock_object_request
;
469 expect_object_discard(mock_image_ctx
, mock_object_request
, 1,
470 ictx
->get_object_size() - 1, true, -EINVAL
);
472 C_SaferCond cond_ctx
;
473 librbd::NoOpProgressContext progress_ctx
;
474 MockTrimRequest
*req
= new MockTrimRequest(
475 mock_image_ctx
, &cond_ctx
, ictx
->get_object_size(), 1, progress_ctx
);
477 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
480 ASSERT_EQ(-EINVAL
, cond_ctx
.wait());
483 } // namespace operation
484 } // namespace librbd