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_truncate(librbd::MockTestImageCtx *ictx,
74 const std::string &oid,
77 const ::SnapContext &snapc,
78 const ZTracer::Trace &parent_trace,
79 Context *completion) {
80 assert(s_instance != nullptr);
81 s_instance->on_finish = completion;
82 s_instance->construct_truncate();
86 static ObjectRequest* create_trim(librbd::MockTestImageCtx *ictx,
87 const std::string &oid,
89 const ::SnapContext &snapc,
90 bool post_object_map_update,
91 Context *completion) {
92 assert(s_instance != nullptr);
93 s_instance->on_finish = completion;
94 s_instance->construct_trim();
102 MOCK_METHOD0(construct_truncate, void());
103 MOCK_METHOD0(construct_trim, void());
104 MOCK_METHOD0(send, void());
105 MOCK_METHOD1(complete, void(int));
108 ObjectRequest<librbd::MockTestImageCtx>* ObjectRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
111 } // namespace librbd
113 // template definitions
114 #include "librbd/AsyncObjectThrottle.cc"
115 #include "librbd/operation/TrimRequest.cc"
118 namespace operation {
121 using ::testing::DoAll;
122 using ::testing::InSequence;
123 using ::testing::Invoke;
124 using ::testing::Return;
125 using ::testing::StrEq;
126 using ::testing::WithArg;
128 class TestMockOperationTrimRequest : public TestMockFixture {
130 typedef TrimRequest<MockTestImageCtx> MockTrimRequest;
131 typedef librbd::io::ObjectRequest<MockTestImageCtx> MockObjectRequest;
133 int create_snapshot(const char *snap_name) {
134 librbd::ImageCtx *ictx;
135 int r = open_image(m_image_name, &ictx);
140 r = snap_create(*ictx, snap_name);
145 r = snap_protect(*ictx, snap_name);
153 void expect_is_lock_owner(MockTestImageCtx &mock_image_ctx) {
154 if (mock_image_ctx.exclusive_lock != nullptr) {
155 EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
156 .WillRepeatedly(Return(true));
160 void expect_object_map_update(MockTestImageCtx &mock_image_ctx,
161 uint64_t start_object, uint64_t end_object,
162 uint8_t state, uint8_t current_state,
163 bool updated, int ret_val) {
164 if (mock_image_ctx.object_map != nullptr) {
165 EXPECT_CALL(*mock_image_ctx.object_map,
166 aio_update(CEPH_NOSNAP, start_object, end_object, state,
167 boost::optional<uint8_t>(current_state), _, _))
168 .WillOnce(WithArg<6>(Invoke([&mock_image_ctx, updated, ret_val](Context *ctx) {
170 mock_image_ctx.op_work_queue->queue(ctx, ret_val);
177 void expect_get_parent_overlap(MockTestImageCtx &mock_image_ctx,
179 EXPECT_CALL(mock_image_ctx, get_parent_overlap(CEPH_NOSNAP, _))
180 .WillOnce(WithArg<1>(Invoke([overlap](uint64_t *o) {
186 void expect_object_may_exist(MockTestImageCtx &mock_image_ctx,
187 uint64_t object_no, bool exists) {
188 if (mock_image_ctx.object_map != nullptr) {
189 EXPECT_CALL(*mock_image_ctx.object_map, object_may_exist(object_no))
190 .WillOnce(Return(exists));
194 void expect_get_object_name(MockTestImageCtx &mock_image_ctx,
195 uint64_t object_no, const std::string& oid) {
196 EXPECT_CALL(mock_image_ctx, get_object_name(object_no))
197 .WillOnce(Return(oid));
200 void expect_aio_remove(MockTestImageCtx &mock_image_ctx,
201 const std::string& oid, int ret_val) {
202 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx), remove(oid, _))
203 .WillOnce(Return(ret_val));
206 void expect_object_trim(MockImageCtx &mock_image_ctx,
207 MockObjectRequest &mock_object_request, int ret_val) {
208 EXPECT_CALL(mock_object_request, construct_trim());
209 EXPECT_CALL(mock_object_request, send())
210 .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, ret_val]() {
211 mock_image_ctx.op_work_queue->queue(mock_object_request.on_finish, ret_val);
215 void expect_object_truncate(MockImageCtx &mock_image_ctx,
216 MockObjectRequest &mock_object_request,
218 EXPECT_CALL(mock_object_request, construct_truncate());
219 EXPECT_CALL(mock_object_request, send())
220 .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, ret_val]() {
221 mock_image_ctx.op_work_queue->queue(mock_object_request.on_finish, ret_val);
226 TEST_F(TestMockOperationTrimRequest, SuccessRemove) {
227 librbd::ImageCtx *ictx;
228 ASSERT_EQ(0, open_image(m_image_name, &ictx));
230 MockTestImageCtx mock_image_ctx(*ictx);
231 MockExclusiveLock mock_exclusive_lock;
232 MockJournal mock_journal;
233 MockObjectMap mock_object_map;
234 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
236 expect_op_work_queue(mock_image_ctx);
237 expect_is_lock_owner(mock_image_ctx);
240 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
241 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
244 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
248 expect_get_parent_overlap(mock_image_ctx, 0);
251 expect_object_may_exist(mock_image_ctx, 0, true);
252 expect_get_object_name(mock_image_ctx, 0, "object0");
253 expect_aio_remove(mock_image_ctx, "object0", 0);
256 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_NONEXISTENT,
257 OBJECT_PENDING, true, 0);
259 C_SaferCond cond_ctx;
260 librbd::NoOpProgressContext progress_ctx;
261 MockTrimRequest *req = new MockTrimRequest(
262 mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
264 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
267 ASSERT_EQ(0, cond_ctx.wait());
270 TEST_F(TestMockOperationTrimRequest, SuccessCopyUp) {
271 REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
272 ASSERT_EQ(0, create_snapshot("snap1"));
276 ASSERT_TRUE(::get_features(&features));
277 std::string clone_name = get_temp_image_name();
278 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
279 clone_name.c_str(), features, &order, 0, 0));
281 librbd::ImageCtx *ictx;
282 ASSERT_EQ(0, open_image(clone_name, &ictx));
283 ASSERT_EQ(0, snap_create(*ictx, "snap"));
285 MockTestImageCtx mock_image_ctx(*ictx);
286 MockExclusiveLock mock_exclusive_lock;
287 MockJournal mock_journal;
288 MockObjectMap mock_object_map;
289 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
291 expect_op_work_queue(mock_image_ctx);
292 expect_is_lock_owner(mock_image_ctx);
295 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
296 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
299 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
303 expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
304 expect_get_object_name(mock_image_ctx, 0, "object0");
306 MockObjectRequest mock_object_request;
307 expect_object_trim(mock_image_ctx, mock_object_request, 0);
310 expect_object_may_exist(mock_image_ctx, 1, true);
311 expect_get_object_name(mock_image_ctx, 1, "object1");
312 expect_aio_remove(mock_image_ctx, "object1", 0);
315 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_NONEXISTENT,
316 OBJECT_PENDING, true, 0);
318 C_SaferCond cond_ctx;
319 librbd::NoOpProgressContext progress_ctx;
320 MockTrimRequest *req = new MockTrimRequest(
321 mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
323 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
326 ASSERT_EQ(0, cond_ctx.wait());
329 TEST_F(TestMockOperationTrimRequest, SuccessBoundary) {
330 librbd::ImageCtx *ictx;
331 ASSERT_EQ(0, open_image(m_image_name, &ictx));
333 MockTestImageCtx mock_image_ctx(*ictx);
334 MockExclusiveLock mock_exclusive_lock;
335 MockJournal mock_journal;
336 MockObjectMap mock_object_map;
337 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
339 expect_op_work_queue(mock_image_ctx);
340 expect_is_lock_owner(mock_image_ctx);
343 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
344 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
347 MockObjectRequest mock_object_request;
348 expect_object_truncate(mock_image_ctx, mock_object_request, 0);
350 C_SaferCond cond_ctx;
351 librbd::NoOpProgressContext progress_ctx;
352 MockTrimRequest *req = new MockTrimRequest(
353 mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
355 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
358 ASSERT_EQ(0, cond_ctx.wait());
361 TEST_F(TestMockOperationTrimRequest, SuccessNoOp) {
362 librbd::ImageCtx *ictx;
363 ASSERT_EQ(0, open_image(m_image_name, &ictx));
365 MockTestImageCtx mock_image_ctx(*ictx);
366 MockExclusiveLock mock_exclusive_lock;
367 MockJournal mock_journal;
368 MockObjectMap mock_object_map;
369 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
373 TEST_F(TestMockOperationTrimRequest, RemoveError) {
374 librbd::ImageCtx *ictx;
375 ASSERT_EQ(0, open_image(m_image_name, &ictx));
377 MockTestImageCtx mock_image_ctx(*ictx);
378 MockExclusiveLock mock_exclusive_lock;
379 MockJournal mock_journal;
380 MockObjectMap mock_object_map;
381 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
383 expect_op_work_queue(mock_image_ctx);
384 expect_is_lock_owner(mock_image_ctx);
387 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
388 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
391 expect_object_map_update(mock_image_ctx, 0, 1, OBJECT_PENDING, OBJECT_EXISTS,
395 expect_get_parent_overlap(mock_image_ctx, 0);
398 expect_object_may_exist(mock_image_ctx, 0, true);
399 expect_get_object_name(mock_image_ctx, 0, "object0");
400 expect_aio_remove(mock_image_ctx, "object0", -EPERM);
402 C_SaferCond cond_ctx;
403 librbd::NoOpProgressContext progress_ctx;
404 MockTrimRequest *req = new MockTrimRequest(
405 mock_image_ctx, &cond_ctx, m_image_size, 0, progress_ctx);
407 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
410 ASSERT_EQ(-EPERM, cond_ctx.wait());
413 TEST_F(TestMockOperationTrimRequest, CopyUpError) {
414 REQUIRE_FEATURE(RBD_FEATURE_LAYERING)
415 ASSERT_EQ(0, create_snapshot("snap1"));
419 ASSERT_TRUE(::get_features(&features));
420 std::string clone_name = get_temp_image_name();
421 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
422 clone_name.c_str(), features, &order, 0, 0));
424 librbd::ImageCtx *ictx;
425 ASSERT_EQ(0, open_image(clone_name, &ictx));
426 ASSERT_EQ(0, snap_create(*ictx, "snap"));
428 MockTestImageCtx mock_image_ctx(*ictx);
429 MockExclusiveLock mock_exclusive_lock;
430 MockJournal mock_journal;
431 MockObjectMap mock_object_map;
432 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
434 expect_op_work_queue(mock_image_ctx);
435 expect_is_lock_owner(mock_image_ctx);
438 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
439 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
442 expect_object_map_update(mock_image_ctx, 0, 2, OBJECT_PENDING, OBJECT_EXISTS,
446 expect_get_parent_overlap(mock_image_ctx, ictx->get_object_size());
447 expect_get_object_name(mock_image_ctx, 0, "object0");
449 MockObjectRequest mock_object_request;
450 expect_object_trim(mock_image_ctx, mock_object_request, -EINVAL);
452 C_SaferCond cond_ctx;
453 librbd::NoOpProgressContext progress_ctx;
454 MockTrimRequest *req = new MockTrimRequest(
455 mock_image_ctx, &cond_ctx, 2 * ictx->get_object_size(), 0, progress_ctx);
457 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
460 ASSERT_EQ(-EINVAL, cond_ctx.wait());
463 TEST_F(TestMockOperationTrimRequest, BoundaryError) {
464 librbd::ImageCtx *ictx;
465 ASSERT_EQ(0, open_image(m_image_name, &ictx));
467 MockTestImageCtx mock_image_ctx(*ictx);
468 MockExclusiveLock mock_exclusive_lock;
469 MockJournal mock_journal;
470 MockObjectMap mock_object_map;
471 initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
473 expect_op_work_queue(mock_image_ctx);
474 expect_is_lock_owner(mock_image_ctx);
477 EXPECT_CALL(mock_image_ctx, get_stripe_period()).WillOnce(Return(ictx->get_object_size()));
478 EXPECT_CALL(mock_image_ctx, get_stripe_count()).WillOnce(Return(ictx->get_stripe_count()));
481 MockObjectRequest mock_object_request;
482 expect_object_truncate(mock_image_ctx, mock_object_request, -EINVAL);
484 C_SaferCond cond_ctx;
485 librbd::NoOpProgressContext progress_ctx;
486 MockTrimRequest *req = new MockTrimRequest(
487 mock_image_ctx, &cond_ctx, ictx->get_object_size(), 1, progress_ctx);
489 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
492 ASSERT_EQ(-EINVAL, cond_ctx.wait());
495 } // namespace operation
496 } // namespace librbd