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/ImageState.h"
10 #include "librbd/internal.h"
11 #include "librbd/operation/SnapshotRemoveRequest.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
15 // template definitions
16 #include "librbd/operation/SnapshotRemoveRequest.cc"
22 using ::testing::DoAll;
23 using ::testing::DoDefault;
24 using ::testing::Return;
25 using ::testing::SetArgPointee;
26 using ::testing::StrEq;
27 using ::testing::WithArg;
29 class TestMockOperationSnapshotRemoveRequest : public TestMockFixture {
31 typedef SnapshotRemoveRequest<MockImageCtx> MockSnapshotRemoveRequest;
33 int create_snapshot(const char *snap_name) {
34 librbd::ImageCtx *ictx;
35 int r = open_image(m_image_name, &ictx);
40 r = snap_create(*ictx, snap_name);
45 r = snap_protect(*ictx, snap_name);
53 void expect_object_map_snap_remove(MockImageCtx &mock_image_ctx, int r) {
54 if (mock_image_ctx.object_map != nullptr) {
55 EXPECT_CALL(*mock_image_ctx.object_map, snapshot_remove(_, _))
56 .WillOnce(WithArg<1>(CompleteContext(
57 r, mock_image_ctx.image_ctx->op_work_queue)));
61 void expect_get_parent_spec(MockImageCtx &mock_image_ctx, int r) {
62 auto &expect = EXPECT_CALL(mock_image_ctx, get_parent_spec(_, _));
64 expect.WillOnce(Return(r));
66 ParentSpec &parent_spec = mock_image_ctx.snap_info.rbegin()->second.parent.spec;
67 expect.WillOnce(DoAll(SetArgPointee<1>(parent_spec),
72 void expect_remove_child(MockImageCtx &mock_image_ctx, int r) {
73 bool deep_flatten = mock_image_ctx.image_ctx->test_features(RBD_FEATURE_DEEP_FLATTEN);
74 auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
75 exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("remove_child"), _,
80 expect.WillOnce(Return(r));
84 void expect_verify_lock_ownership(MockImageCtx &mock_image_ctx) {
85 if (mock_image_ctx.old_format) {
89 if (mock_image_ctx.exclusive_lock != nullptr) {
90 EXPECT_CALL(*mock_image_ctx.exclusive_lock, is_lock_owner())
91 .WillRepeatedly(Return(false));
95 void expect_snap_remove(MockImageCtx &mock_image_ctx, int r) {
96 auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
97 exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
98 StrEq(mock_image_ctx.old_format ? "snap_remove" :
102 expect.WillOnce(Return(r));
104 expect.WillOnce(DoDefault());
108 void expect_rm_snap(MockImageCtx &mock_image_ctx) {
109 EXPECT_CALL(mock_image_ctx, rm_snap(_, _, _)).Times(1);
112 void expect_release_snap_id(MockImageCtx &mock_image_ctx) {
113 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
114 selfmanaged_snap_remove(_))
115 .WillOnce(DoDefault());
120 TEST_F(TestMockOperationSnapshotRemoveRequest, Success) {
121 librbd::ImageCtx *ictx;
122 ASSERT_EQ(0, open_image(m_image_name, &ictx));
123 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
124 ASSERT_EQ(0, ictx->state->refresh_if_required());
126 MockImageCtx mock_image_ctx(*ictx);
128 MockExclusiveLock mock_exclusive_lock;
129 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
130 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
133 MockObjectMap mock_object_map;
134 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
135 mock_image_ctx.object_map = &mock_object_map;
138 expect_op_work_queue(mock_image_ctx);
140 ::testing::InSequence seq;
141 uint64_t snap_id = ictx->snap_info.rbegin()->first;
142 expect_object_map_snap_remove(mock_image_ctx, 0);
143 expect_get_parent_spec(mock_image_ctx, 0);
144 expect_verify_lock_ownership(mock_image_ctx);
145 expect_snap_remove(mock_image_ctx, 0);
146 expect_rm_snap(mock_image_ctx);
147 expect_release_snap_id(mock_image_ctx);
149 C_SaferCond cond_ctx;
150 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
151 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
154 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
157 ASSERT_EQ(0, cond_ctx.wait());
160 TEST_F(TestMockOperationSnapshotRemoveRequest, FlattenedCloneRemovesChild) {
161 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
163 ASSERT_EQ(0, create_snapshot("snap1"));
167 ASSERT_TRUE(::get_features(&features));
168 std::string clone_name = get_temp_image_name();
169 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
170 clone_name.c_str(), features, &order, 0, 0));
172 librbd::ImageCtx *ictx;
173 ASSERT_EQ(0, open_image(clone_name, &ictx));
174 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
176 librbd::NoOpProgressContext prog_ctx;
177 ASSERT_EQ(0, flatten(*ictx, prog_ctx));
178 ASSERT_EQ(0, ictx->state->refresh_if_required());
180 MockImageCtx mock_image_ctx(*ictx);
182 MockExclusiveLock mock_exclusive_lock;
183 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
184 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
187 MockObjectMap mock_object_map;
188 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
189 mock_image_ctx.object_map = &mock_object_map;
192 expect_op_work_queue(mock_image_ctx);
194 uint64_t snap_id = ictx->snap_info.rbegin()->first;
195 expect_object_map_snap_remove(mock_image_ctx, 0);
196 expect_get_parent_spec(mock_image_ctx, 0);
197 expect_remove_child(mock_image_ctx, -ENOENT);
198 expect_verify_lock_ownership(mock_image_ctx);
199 expect_snap_remove(mock_image_ctx, 0);
200 expect_rm_snap(mock_image_ctx);
201 expect_release_snap_id(mock_image_ctx);
203 C_SaferCond cond_ctx;
204 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
205 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
208 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
211 ASSERT_EQ(0, cond_ctx.wait());
214 TEST_F(TestMockOperationSnapshotRemoveRequest, ObjectMapSnapRemoveError) {
215 REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
217 librbd::ImageCtx *ictx;
218 ASSERT_EQ(0, open_image(m_image_name, &ictx));
219 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
220 ASSERT_EQ(0, ictx->state->refresh_if_required());
222 MockImageCtx mock_image_ctx(*ictx);
224 MockObjectMap mock_object_map;
225 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
226 mock_image_ctx.object_map = &mock_object_map;
229 expect_op_work_queue(mock_image_ctx);
231 ::testing::InSequence seq;
232 uint64_t snap_id = ictx->snap_info.rbegin()->first;
233 expect_object_map_snap_remove(mock_image_ctx, -EINVAL);
235 C_SaferCond cond_ctx;
236 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
237 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
240 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
243 ASSERT_EQ(-EINVAL, cond_ctx.wait());
246 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildParentError) {
247 librbd::ImageCtx *ictx;
248 ASSERT_EQ(0, open_image(m_image_name, &ictx));
249 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
250 ASSERT_EQ(0, ictx->state->refresh_if_required());
252 MockImageCtx mock_image_ctx(*ictx);
254 MockObjectMap mock_object_map;
255 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
256 mock_image_ctx.object_map = &mock_object_map;
259 expect_op_work_queue(mock_image_ctx);
261 ::testing::InSequence seq;
262 uint64_t snap_id = ictx->snap_info.rbegin()->first;
263 expect_object_map_snap_remove(mock_image_ctx, 0);
264 expect_get_parent_spec(mock_image_ctx, -ENOENT);
266 C_SaferCond cond_ctx;
267 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
268 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
271 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
274 ASSERT_EQ(-ENOENT, cond_ctx.wait());
277 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveChildError) {
278 REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
280 ASSERT_EQ(0, create_snapshot("snap1"));
284 ASSERT_TRUE(::get_features(&features));
285 std::string clone_name = get_temp_image_name();
286 ASSERT_EQ(0, librbd::clone(m_ioctx, m_image_name.c_str(), "snap1", m_ioctx,
287 clone_name.c_str(), features, &order, 0, 0));
289 librbd::ImageCtx *ictx;
290 ASSERT_EQ(0, open_image(clone_name, &ictx));
291 if (ictx->test_features(RBD_FEATURE_DEEP_FLATTEN)) {
292 std::cout << "SKIPPING" << std::endl;
296 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
298 librbd::NoOpProgressContext prog_ctx;
299 ASSERT_EQ(0, flatten(*ictx, prog_ctx));
300 ASSERT_EQ(0, ictx->state->refresh_if_required());
302 MockImageCtx mock_image_ctx(*ictx);
304 MockObjectMap mock_object_map;
305 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
306 mock_image_ctx.object_map = &mock_object_map;
309 expect_op_work_queue(mock_image_ctx);
311 uint64_t snap_id = ictx->snap_info.rbegin()->first;
312 expect_object_map_snap_remove(mock_image_ctx, 0);
313 expect_get_parent_spec(mock_image_ctx, 0);
314 expect_remove_child(mock_image_ctx, -EINVAL);
316 C_SaferCond cond_ctx;
317 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
318 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
321 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
324 ASSERT_EQ(-EINVAL, cond_ctx.wait());
327 TEST_F(TestMockOperationSnapshotRemoveRequest, RemoveSnapError) {
328 librbd::ImageCtx *ictx;
329 ASSERT_EQ(0, open_image(m_image_name, &ictx));
330 ASSERT_EQ(0, snap_create(*ictx, "snap1"));
331 ASSERT_EQ(0, ictx->state->refresh_if_required());
333 MockImageCtx mock_image_ctx(*ictx);
335 MockExclusiveLock mock_exclusive_lock;
336 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
337 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
340 MockObjectMap mock_object_map;
341 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
342 mock_image_ctx.object_map = &mock_object_map;
345 expect_op_work_queue(mock_image_ctx);
347 ::testing::InSequence seq;
348 uint64_t snap_id = ictx->snap_info.rbegin()->first;
349 expect_object_map_snap_remove(mock_image_ctx, 0);
350 expect_get_parent_spec(mock_image_ctx, 0);
351 expect_verify_lock_ownership(mock_image_ctx);
352 expect_snap_remove(mock_image_ctx, -ENOENT);
354 C_SaferCond cond_ctx;
355 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
356 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
359 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
362 ASSERT_EQ(-ENOENT, cond_ctx.wait());
365 TEST_F(TestMockOperationSnapshotRemoveRequest, MissingSnap) {
366 librbd::ImageCtx *ictx;
367 ASSERT_EQ(0, open_image(m_image_name, &ictx));
369 MockImageCtx mock_image_ctx(*ictx);
371 MockExclusiveLock mock_exclusive_lock;
372 if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
373 mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
376 MockObjectMap mock_object_map;
377 if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
378 mock_image_ctx.object_map = &mock_object_map;
381 expect_op_work_queue(mock_image_ctx);
383 ::testing::InSequence seq;
384 uint64_t snap_id = 456;
386 C_SaferCond cond_ctx;
387 MockSnapshotRemoveRequest *req = new MockSnapshotRemoveRequest(
388 mock_image_ctx, &cond_ctx, cls::rbd::UserSnapshotNamespace(), "snap1",
391 RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
394 ASSERT_EQ(-ENOENT, cond_ctx.wait());
397 } // namespace operation
398 } // namespace librbd