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/librbd/mock/MockContextWQ.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "test/librados_test_stub/MockTestMemRadosClient.h"
10 #include "librbd/ImageState.h"
11 #include "librbd/internal.h"
12 #include "librbd/journal/RemoveRequest.h"
13 #include "librbd/Operations.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "librbd/image/TypeTraits.h"
16 #include "librbd/image/RemoveRequest.h"
17 #include "librbd/image/RefreshParentRequest.h"
18 #include "librbd/mirror/DisableRequest.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include <arpa/inet.h>
23 #include <boost/scope_exit.hpp>
28 struct TypeTraits
<MockImageCtx
> {
29 typedef librbd::MockContextWQ ContextWQ
;
36 class TrimRequest
<MockImageCtx
> {
38 static TrimRequest
*s_instance
;
39 static TrimRequest
*create(MockImageCtx
&image_ctx
, Context
*on_finish
,
40 uint64_t original_size
, uint64_t new_size
,
41 ProgressContext
&prog_ctx
) {
42 assert(s_instance
!= nullptr);
43 s_instance
->on_finish
= on_finish
;
47 Context
*on_finish
= nullptr;
53 MOCK_METHOD0(send
, void());
56 } // namespace operation
60 class RemoveRequest
<MockImageCtx
> {
62 typedef ::librbd::image::TypeTraits
<MockImageCtx
> TypeTraits
;
63 typedef typename
TypeTraits::ContextWQ ContextWQ
;
65 static RemoveRequest
*s_instance
;
66 static RemoveRequest
*create(IoCtx
&ioctx
, const std::string
&imageid
,
67 const std::string
&client_id
,
68 ContextWQ
*op_work_queue
, Context
*on_finish
) {
69 assert(s_instance
!= nullptr);
70 s_instance
->on_finish
= on_finish
;
74 Context
*on_finish
= nullptr;
80 MOCK_METHOD0(send
, void());
82 RemoveRequest
<MockImageCtx
> *RemoveRequest
<MockImageCtx
>::s_instance
= nullptr;
83 } // namespace journal
88 class DisableRequest
<MockImageCtx
> {
90 static DisableRequest
*s_instance
;
91 Context
*on_finish
= nullptr;
93 static DisableRequest
*create(MockImageCtx
*image_ctx
, bool force
,
94 bool remove
, Context
*on_finish
) {
95 assert(s_instance
!= nullptr);
96 s_instance
->on_finish
= on_finish
;
104 MOCK_METHOD0(send
, void());
107 DisableRequest
<MockImageCtx
> *DisableRequest
<MockImageCtx
>::s_instance
;
109 } // namespace mirror
110 } // namespace librbd
112 // template definitions
113 #include "librbd/image/RemoveRequest.cc"
114 template class librbd::image::RemoveRequest
<librbd::MockImageCtx
>;
116 ACTION_P(TestFeatures
, image_ctx
) {
117 return ((image_ctx
->features
& arg0
) != 0);
120 ACTION_P(ShutDownExclusiveLock
, image_ctx
) {
121 // shutting down exclusive lock will close object map and journal
122 image_ctx
->exclusive_lock
= nullptr;
123 image_ctx
->object_map
= nullptr;
124 image_ctx
->journal
= nullptr;
131 using ::testing::DoAll
;
132 using ::testing::DoDefault
;
133 using ::testing::Invoke
;
134 using ::testing::InSequence
;
135 using ::testing::Return
;
136 using ::testing::WithArg
;
137 using ::testing::SetArgPointee
;
138 using ::testing::StrEq
;
140 class TestMockImageRemoveRequest
: public TestMockFixture
{
142 typedef ::librbd::image::TypeTraits
<MockImageCtx
> TypeTraits
;
143 typedef typename
TypeTraits::ContextWQ ContextWQ
;
144 typedef RemoveRequest
<MockImageCtx
> MockRemoveRequest
;
145 typedef librbd::operation::TrimRequest
<MockImageCtx
> MockTrimRequest
;
146 typedef librbd::journal::RemoveRequest
<MockImageCtx
> MockJournalRemoveRequest
;
147 typedef librbd::mirror::DisableRequest
<MockImageCtx
> MockMirrorDisableRequest
;
149 librbd::ImageCtx
*m_test_imctx
= NULL
;
150 MockImageCtx
*m_mock_imctx
= NULL
;
153 void TestImageRemoveSetUp() {
154 ASSERT_EQ(0, open_image(m_image_name
, &m_test_imctx
));
155 m_mock_imctx
= new MockImageCtx(*m_test_imctx
);
156 librbd::MockImageCtx::s_instance
= m_mock_imctx
;
158 void TestImageRemoveTearDown() {
159 librbd::MockImageCtx::s_instance
= NULL
;
163 void expect_state_open(MockImageCtx
&mock_image_ctx
, int r
) {
164 EXPECT_CALL(*mock_image_ctx
.state
, open(_
, _
))
165 .WillOnce(Invoke([r
](bool open_parent
, Context
*on_ready
) {
166 on_ready
->complete(r
);
169 EXPECT_CALL(mock_image_ctx
, destroy());
173 void expect_state_close(MockImageCtx
&mock_image_ctx
) {
174 EXPECT_CALL(*mock_image_ctx
.state
, close(_
))
175 .WillOnce(Invoke([](Context
*on_ready
) {
176 on_ready
->complete(0);
178 EXPECT_CALL(mock_image_ctx
, destroy());
181 void expect_wq_queue(ContextWQ
&wq
, int r
) {
182 EXPECT_CALL(wq
, queue(_
, r
))
183 .WillRepeatedly(Invoke([](Context
*on_ready
, int r
) {
184 on_ready
->complete(r
);
188 void expect_get_group(MockImageCtx
&mock_image_ctx
, int r
) {
189 auto &expect
= EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
190 exec(mock_image_ctx
.header_oid
, _
, StrEq("rbd"),
191 StrEq("image_get_group"), _
, _
, _
));
193 expect
.WillOnce(Return(r
));
195 expect
.WillOnce(DoDefault());
199 void expect_trim(MockImageCtx
&mock_image_ctx
,
200 MockTrimRequest
&mock_trim_request
, int r
) {
201 EXPECT_CALL(mock_trim_request
, send())
202 .WillOnce(FinishRequest(&mock_trim_request
, r
, &mock_image_ctx
));
205 void expect_journal_remove(MockImageCtx
&mock_image_ctx
,
206 MockJournalRemoveRequest
&mock_journal_remove_request
, int r
) {
207 EXPECT_CALL(mock_journal_remove_request
, send())
208 .WillOnce(FinishRequest(&mock_journal_remove_request
, r
, &mock_image_ctx
));
211 void expect_mirror_disable(MockImageCtx
&mock_image_ctx
,
212 MockMirrorDisableRequest
&mock_mirror_disable_request
, int r
) {
213 EXPECT_CALL(mock_mirror_disable_request
, send())
214 .WillOnce(FinishRequest(&mock_mirror_disable_request
, r
, &mock_image_ctx
));
217 void expect_remove_child(MockImageCtx
&mock_image_ctx
, int r
) {
218 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
219 exec(RBD_CHILDREN
, _
, StrEq("rbd"), StrEq("remove_child"), _
,
221 .WillOnce(Return(r
));
224 void expect_remove_mirror_image(librados::IoCtx
&ioctx
, int r
) {
225 EXPECT_CALL(get_mock_io_ctx(ioctx
),
226 exec(StrEq("rbd_mirroring"), _
, StrEq("rbd"), StrEq("mirror_image_remove"),
228 .WillOnce(Return(r
));
231 void expect_mirror_image_get(MockImageCtx
&mock_image_ctx
, int r
) {
232 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
233 exec(RBD_MIRRORING
, _
, StrEq("rbd"), StrEq("mirror_image_get"),
235 .WillOnce(Return(r
));
238 void expect_dir_remove_image(librados::IoCtx
&ioctx
, int r
) {
239 EXPECT_CALL(get_mock_io_ctx(ioctx
),
240 exec(RBD_DIRECTORY
, _
, StrEq("rbd"), StrEq("dir_remove_image"),
242 .WillOnce(Return(r
));
245 void expect_test_features(MockImageCtx
&mock_image_ctx
) {
246 if (m_mock_imctx
->exclusive_lock
!= nullptr) {
247 EXPECT_CALL(mock_image_ctx
, test_features(_
))
248 .WillRepeatedly(TestFeatures(&mock_image_ctx
));
252 void expect_set_journal_policy(MockImageCtx
&mock_image_ctx
) {
253 if (m_test_imctx
->test_features(RBD_FEATURE_JOURNALING
)) {
254 EXPECT_CALL(mock_image_ctx
, set_journal_policy(_
))
255 .WillOnce(Invoke([](journal::Policy
* policy
) {
256 ASSERT_TRUE(policy
->journal_disabled());
262 void expect_shut_down_exclusive_lock(MockImageCtx
&mock_image_ctx
,
263 MockExclusiveLock
&mock_exclusive_lock
,
265 if (m_mock_imctx
->exclusive_lock
!= nullptr) {
266 EXPECT_CALL(mock_exclusive_lock
, shut_down(_
))
267 .WillOnce(DoAll(ShutDownExclusiveLock(&mock_image_ctx
),
268 CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
)));
274 TEST_F(TestMockImageRemoveRequest
, SuccessV1
) {
276 TestImageRemoveSetUp();
279 librbd::NoOpProgressContext no_op
;
280 ContextWQ op_work_queue
;
281 MockTrimRequest mock_trim_request
;
282 MockJournalRemoveRequest mock_journal_remove_request
;
285 expect_state_open(*m_mock_imctx
, 0);
286 expect_get_group(*m_mock_imctx
, 0);
287 expect_trim(*m_mock_imctx
, mock_trim_request
, 0);
288 expect_op_work_queue(*m_mock_imctx
);
289 expect_state_close(*m_mock_imctx
);
290 expect_wq_queue(op_work_queue
, 0);
292 MockRemoveRequest
*req
= MockRemoveRequest::create(m_ioctx
, m_image_name
, "",
293 true, false, no_op
, &op_work_queue
, &ctx
);
296 ASSERT_EQ(0, ctx
.wait());
298 TestImageRemoveTearDown();
301 TEST_F(TestMockImageRemoveRequest
, OpenFailV1
) {
303 TestImageRemoveSetUp();
306 librbd::NoOpProgressContext no_op
;
307 ContextWQ op_work_queue
;
308 MockTrimRequest mock_trim_request
;
311 expect_state_open(*m_mock_imctx
, -ENOENT
);
312 expect_wq_queue(op_work_queue
, 0);
314 MockRemoveRequest
*req
= MockRemoveRequest::create(m_ioctx
, m_image_name
, "",
315 true, false, no_op
, &op_work_queue
, &ctx
);
318 ASSERT_EQ(0, ctx
.wait());
320 TestImageRemoveTearDown();
323 TEST_F(TestMockImageRemoveRequest
, SuccessV2
) {
324 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
325 TestImageRemoveSetUp();
327 MockExclusiveLock
*mock_exclusive_lock
= nullptr;
328 if (m_test_imctx
->test_features(RBD_FEATURE_EXCLUSIVE_LOCK
)) {
329 mock_exclusive_lock
= new MockExclusiveLock();
330 m_mock_imctx
->exclusive_lock
= mock_exclusive_lock
;
334 librbd::NoOpProgressContext no_op
;
335 ContextWQ op_work_queue
;
336 MockTrimRequest mock_trim_request
;
337 MockJournalRemoveRequest mock_journal_remove_request
;
338 MockMirrorDisableRequest mock_mirror_disable_request
;
341 expect_state_open(*m_mock_imctx
, 0);
343 expect_test_features(*m_mock_imctx
);
344 expect_set_journal_policy(*m_mock_imctx
);
345 expect_shut_down_exclusive_lock(*m_mock_imctx
, *mock_exclusive_lock
, 0);
347 expect_mirror_image_get(*m_mock_imctx
, 0);
348 expect_get_group(*m_mock_imctx
, 0);
349 expect_trim(*m_mock_imctx
, mock_trim_request
, 0);
350 expect_op_work_queue(*m_mock_imctx
);
351 expect_remove_child(*m_mock_imctx
, 0);
352 expect_mirror_disable(*m_mock_imctx
, mock_mirror_disable_request
, 0);
353 expect_state_close(*m_mock_imctx
);
354 expect_wq_queue(op_work_queue
, 0);
355 expect_journal_remove(*m_mock_imctx
, mock_journal_remove_request
, 0);
356 expect_remove_mirror_image(m_ioctx
, 0);
357 expect_dir_remove_image(m_ioctx
, 0);
359 MockRemoveRequest
*req
= MockRemoveRequest::create(
360 m_ioctx
, m_image_name
, "", true, false, no_op
, &op_work_queue
, &ctx
);
363 ASSERT_EQ(0, ctx
.wait());
365 TestImageRemoveTearDown();
368 TEST_F(TestMockImageRemoveRequest
, NotExistsV2
) {
369 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
370 TestImageRemoveSetUp();
372 MockExclusiveLock
*mock_exclusive_lock
= nullptr;
373 if (m_test_imctx
->test_features(RBD_FEATURE_EXCLUSIVE_LOCK
)) {
374 mock_exclusive_lock
= new MockExclusiveLock();
375 m_mock_imctx
->exclusive_lock
= mock_exclusive_lock
;
379 librbd::NoOpProgressContext no_op
;
380 ContextWQ op_work_queue
;
381 MockTrimRequest mock_trim_request
;
382 MockJournalRemoveRequest mock_journal_remove_request
;
383 MockMirrorDisableRequest mock_mirror_disable_request
;
386 expect_state_open(*m_mock_imctx
, 0);
388 expect_test_features(*m_mock_imctx
);
389 expect_set_journal_policy(*m_mock_imctx
);
390 expect_shut_down_exclusive_lock(*m_mock_imctx
, *mock_exclusive_lock
, 0);
392 expect_mirror_image_get(*m_mock_imctx
, 0);
393 expect_get_group(*m_mock_imctx
, 0);
394 expect_trim(*m_mock_imctx
, mock_trim_request
, 0);
395 expect_op_work_queue(*m_mock_imctx
);
396 expect_remove_child(*m_mock_imctx
, 0);
397 expect_mirror_disable(*m_mock_imctx
, mock_mirror_disable_request
, 0);
398 expect_state_close(*m_mock_imctx
);
399 expect_wq_queue(op_work_queue
, 0);
400 expect_journal_remove(*m_mock_imctx
, mock_journal_remove_request
, 0);
401 expect_remove_mirror_image(m_ioctx
, 0);
402 expect_dir_remove_image(m_ioctx
, -ENOENT
);
404 MockRemoveRequest
*req
= MockRemoveRequest::create(
405 m_ioctx
, m_image_name
, "", true, false, no_op
, &op_work_queue
, &ctx
);
407 ASSERT_EQ(-ENOENT
, ctx
.wait());
409 TestImageRemoveTearDown();
413 } // namespace librbd