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/io/MockObjectDispatch.h"
8 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
9 #include "common/bit_vector.hpp"
10 #include "librbd/internal.h"
11 #include "librbd/ObjectMap.h"
12 #include "librbd/io/ImageDispatchSpec.h"
13 #include "librbd/operation/ResizeRequest.h"
14 #include "librbd/operation/TrimRequest.h"
15 #include "gmock/gmock.h"
16 #include "gtest/gtest.h"
22 inline ImageCtx
* get_image_ctx(MockImageCtx
* image_ctx
) {
23 return image_ctx
->image_ctx
;
31 struct ImageDispatchSpec
<MockImageCtx
> {
32 static ImageDispatchSpec
* s_instance
;
33 AioCompletion
*aio_comp
= nullptr;
35 static ImageDispatchSpec
* create_flush_request(
36 MockImageCtx
&image_ctx
, AioCompletion
*aio_comp
,
37 FlushSource flush_source
, const ZTracer::Trace
&parent_trace
) {
38 ceph_assert(s_instance
!= nullptr);
39 s_instance
->aio_comp
= aio_comp
;
43 MOCK_CONST_METHOD0(send
, void());
50 ImageDispatchSpec
<MockImageCtx
>* ImageDispatchSpec
<MockImageCtx
>::s_instance
= nullptr;
57 class TrimRequest
<MockImageCtx
> {
59 static TrimRequest
*s_instance
;
60 static TrimRequest
*create(MockImageCtx
&image_ctx
, Context
*on_finish
,
61 uint64_t original_size
, uint64_t new_size
,
62 ProgressContext
&prog_ctx
) {
63 ceph_assert(s_instance
!= nullptr);
64 s_instance
->on_finish
= on_finish
;
68 Context
*on_finish
= nullptr;
74 MOCK_METHOD0(send
, void());
77 TrimRequest
<MockImageCtx
> *TrimRequest
<MockImageCtx
>::s_instance
= nullptr;
79 } // namespace operation
82 // template definitions
83 #include "librbd/operation/ResizeRequest.cc"
89 using ::testing::DoAll
;
90 using ::testing::Invoke
;
91 using ::testing::InSequence
;
92 using ::testing::Return
;
93 using ::testing::StrEq
;
94 using ::testing::WithArg
;
96 class TestMockOperationResizeRequest
: public TestMockFixture
{
98 typedef ResizeRequest
<MockImageCtx
> MockResizeRequest
;
99 typedef TrimRequest
<MockImageCtx
> MockTrimRequest
;
100 typedef io::ImageDispatchSpec
<MockImageCtx
> MockIoImageDispatchSpec
;
102 void expect_block_writes(MockImageCtx
&mock_image_ctx
, int r
) {
103 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, block_writes(_
))
104 .WillOnce(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
));
107 void expect_unblock_writes(MockImageCtx
&mock_image_ctx
) {
108 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, unblock_writes())
112 void expect_is_lock_owner(MockImageCtx
&mock_image_ctx
) {
113 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
114 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
115 .WillOnce(Return(true));
119 void expect_grow_object_map(MockImageCtx
&mock_image_ctx
) {
120 if (mock_image_ctx
.object_map
!= nullptr) {
121 expect_is_lock_owner(mock_image_ctx
);
122 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(_
, _
, _
))
123 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
127 void expect_shrink_object_map(MockImageCtx
&mock_image_ctx
) {
128 if (mock_image_ctx
.object_map
!= nullptr) {
129 expect_is_lock_owner(mock_image_ctx
);
130 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(_
, _
, _
))
131 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
135 void expect_update_header(MockImageCtx
&mock_image_ctx
, int r
) {
136 if (mock_image_ctx
.old_format
) {
137 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
138 write(mock_image_ctx
.header_oid
, _
, _
, _
, _
))
139 .WillOnce(Return(r
));
141 expect_is_lock_owner(mock_image_ctx
);
142 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
143 exec(mock_image_ctx
.header_oid
, _
, StrEq("rbd"), StrEq("set_size"), _
, _
, _
))
144 .WillOnce(Return(r
));
148 void expect_trim(MockImageCtx
&mock_image_ctx
,
149 MockTrimRequest
&mock_trim_request
, int r
) {
150 EXPECT_CALL(mock_trim_request
, send())
151 .WillOnce(FinishRequest(&mock_trim_request
, r
, &mock_image_ctx
));
154 void expect_flush_cache(MockImageCtx
&mock_image_ctx
,
155 MockIoImageDispatchSpec
& mock_io_image_dispatch_spec
,
157 EXPECT_CALL(mock_io_image_dispatch_spec
, send())
158 .WillOnce(Invoke([&mock_image_ctx
, &mock_io_image_dispatch_spec
, r
]() {
159 auto aio_comp
= mock_io_image_dispatch_spec
.s_instance
->aio_comp
;
160 auto ctx
= new FunctionContext([aio_comp
](int r
) {
164 mock_image_ctx
.image_ctx
->op_work_queue
->queue(ctx
, r
);
168 void expect_invalidate_cache(MockImageCtx
&mock_image_ctx
,
170 EXPECT_CALL(*mock_image_ctx
.io_object_dispatcher
, invalidate_cache(_
))
171 .WillOnce(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
));
172 expect_op_work_queue(mock_image_ctx
);
175 void expect_resize_object_map(MockImageCtx
&mock_image_ctx
,
177 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(new_size
, _
, _
))
178 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
181 int when_resize(MockImageCtx
&mock_image_ctx
, uint64_t new_size
,
182 bool allow_shrink
, uint64_t journal_op_tid
,
183 bool disable_journal
) {
184 C_SaferCond cond_ctx
;
185 librbd::NoOpProgressContext prog_ctx
;
186 MockResizeRequest
*req
= new MockResizeRequest(
187 mock_image_ctx
, &cond_ctx
, new_size
, allow_shrink
, prog_ctx
,
188 journal_op_tid
, disable_journal
);
190 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
193 return cond_ctx
.wait();
197 TEST_F(TestMockOperationResizeRequest
, NoOpSuccess
) {
198 librbd::ImageCtx
*ictx
;
199 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
201 MockImageCtx
mock_image_ctx(*ictx
);
202 MockExclusiveLock mock_exclusive_lock
;
203 MockJournal mock_journal
;
204 MockObjectMap mock_object_map
;
205 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
209 expect_block_writes(mock_image_ctx
, 0);
210 expect_append_op_event(mock_image_ctx
, true, 0);
211 expect_unblock_writes(mock_image_ctx
);
212 expect_commit_op_event(mock_image_ctx
, 0);
213 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
216 TEST_F(TestMockOperationResizeRequest
, GrowSuccess
) {
217 librbd::ImageCtx
*ictx
;
218 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
220 MockImageCtx
mock_image_ctx(*ictx
);
221 MockExclusiveLock mock_exclusive_lock
;
222 MockJournal mock_journal
;
223 MockObjectMap mock_object_map
;
224 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
228 expect_block_writes(mock_image_ctx
, 0);
229 expect_append_op_event(mock_image_ctx
, true, 0);
230 expect_grow_object_map(mock_image_ctx
);
231 expect_update_header(mock_image_ctx
, 0);
232 expect_unblock_writes(mock_image_ctx
);
233 expect_commit_op_event(mock_image_ctx
, 0);
234 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
237 TEST_F(TestMockOperationResizeRequest
, ShrinkSuccess
) {
238 librbd::ImageCtx
*ictx
;
239 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
241 MockImageCtx
mock_image_ctx(*ictx
);
242 MockExclusiveLock mock_exclusive_lock
;
243 MockJournal mock_journal
;
244 MockObjectMap mock_object_map
;
245 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
249 expect_block_writes(mock_image_ctx
, 0);
250 expect_append_op_event(mock_image_ctx
, true, 0);
251 expect_unblock_writes(mock_image_ctx
);
253 MockTrimRequest mock_trim_request
;
254 auto mock_io_image_dispatch_spec
= new MockIoImageDispatchSpec();
255 expect_flush_cache(mock_image_ctx
, *mock_io_image_dispatch_spec
, 0);
256 expect_invalidate_cache(mock_image_ctx
, 0);
257 expect_trim(mock_image_ctx
, mock_trim_request
, 0);
258 expect_block_writes(mock_image_ctx
, 0);
259 expect_update_header(mock_image_ctx
, 0);
260 expect_shrink_object_map(mock_image_ctx
);
261 expect_unblock_writes(mock_image_ctx
);
262 expect_commit_op_event(mock_image_ctx
, 0);
263 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
266 TEST_F(TestMockOperationResizeRequest
, ShrinkError
) {
267 librbd::ImageCtx
*ictx
;
268 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
270 MockImageCtx
mock_image_ctx(*ictx
);
271 MockExclusiveLock mock_exclusive_lock
;
272 MockJournal mock_journal
;
273 MockObjectMap mock_object_map
;
274 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
278 expect_block_writes(mock_image_ctx
, -EINVAL
);
279 expect_unblock_writes(mock_image_ctx
);
280 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, false, 0, false));
283 TEST_F(TestMockOperationResizeRequest
, PreBlockWritesError
) {
284 librbd::ImageCtx
*ictx
;
285 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
287 MockImageCtx
mock_image_ctx(*ictx
);
288 MockExclusiveLock mock_exclusive_lock
;
289 MockJournal mock_journal
;
290 MockObjectMap mock_object_map
;
291 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
295 expect_block_writes(mock_image_ctx
, -EINVAL
);
296 expect_unblock_writes(mock_image_ctx
);
297 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
300 TEST_F(TestMockOperationResizeRequest
, TrimError
) {
301 librbd::ImageCtx
*ictx
;
302 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
304 MockImageCtx
mock_image_ctx(*ictx
);
305 MockExclusiveLock mock_exclusive_lock
;
306 MockJournal mock_journal
;
307 MockObjectMap mock_object_map
;
308 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
312 expect_block_writes(mock_image_ctx
, 0);
313 expect_append_op_event(mock_image_ctx
, true, 0);
314 expect_unblock_writes(mock_image_ctx
);
316 MockTrimRequest mock_trim_request
;
317 auto mock_io_image_dispatch_spec
= new MockIoImageDispatchSpec();
318 expect_flush_cache(mock_image_ctx
, *mock_io_image_dispatch_spec
, 0);
319 expect_invalidate_cache(mock_image_ctx
, -EBUSY
);
320 expect_trim(mock_image_ctx
, mock_trim_request
, -EINVAL
);
321 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
322 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
325 TEST_F(TestMockOperationResizeRequest
, FlushCacheError
) {
326 librbd::ImageCtx
*ictx
;
327 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
328 REQUIRE(ictx
->cache
);
330 MockImageCtx
mock_image_ctx(*ictx
);
331 MockExclusiveLock mock_exclusive_lock
;
332 MockJournal mock_journal
;
333 MockObjectMap mock_object_map
;
334 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
338 expect_block_writes(mock_image_ctx
, 0);
339 expect_append_op_event(mock_image_ctx
, true, 0);
340 expect_unblock_writes(mock_image_ctx
);
342 MockTrimRequest mock_trim_request
;
343 auto mock_io_image_dispatch_spec
= new MockIoImageDispatchSpec();
344 expect_flush_cache(mock_image_ctx
, *mock_io_image_dispatch_spec
, -EINVAL
);
345 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
346 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
349 TEST_F(TestMockOperationResizeRequest
, InvalidateCacheError
) {
350 librbd::ImageCtx
*ictx
;
351 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
352 REQUIRE(ictx
->cache
);
354 MockImageCtx
mock_image_ctx(*ictx
);
355 MockExclusiveLock mock_exclusive_lock
;
356 MockJournal mock_journal
;
357 MockObjectMap mock_object_map
;
358 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
362 expect_block_writes(mock_image_ctx
, 0);
363 expect_append_op_event(mock_image_ctx
, true, 0);
364 expect_unblock_writes(mock_image_ctx
);
366 MockTrimRequest mock_trim_request
;
367 auto mock_io_image_dispatch_spec
= new MockIoImageDispatchSpec();
368 expect_flush_cache(mock_image_ctx
, *mock_io_image_dispatch_spec
, 0);
369 expect_invalidate_cache(mock_image_ctx
, -EINVAL
);
370 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
371 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
374 TEST_F(TestMockOperationResizeRequest
, PostBlockWritesError
) {
375 librbd::ImageCtx
*ictx
;
376 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
378 MockImageCtx
mock_image_ctx(*ictx
);
379 MockExclusiveLock mock_exclusive_lock
;
380 MockJournal mock_journal
;
381 MockObjectMap mock_object_map
;
382 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
386 expect_block_writes(mock_image_ctx
, 0);
387 expect_append_op_event(mock_image_ctx
, true, 0);
388 expect_unblock_writes(mock_image_ctx
);
390 MockTrimRequest mock_trim_request
;
391 auto mock_io_image_dispatch_spec
= new MockIoImageDispatchSpec();
392 expect_flush_cache(mock_image_ctx
, *mock_io_image_dispatch_spec
, 0);
393 expect_invalidate_cache(mock_image_ctx
, 0);
394 expect_trim(mock_image_ctx
, mock_trim_request
, 0);
395 expect_block_writes(mock_image_ctx
, -EINVAL
);
396 expect_unblock_writes(mock_image_ctx
);
397 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
398 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0,
402 TEST_F(TestMockOperationResizeRequest
, UpdateHeaderError
) {
403 librbd::ImageCtx
*ictx
;
404 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
406 MockImageCtx
mock_image_ctx(*ictx
);
407 MockExclusiveLock mock_exclusive_lock
;
408 MockJournal mock_journal
;
409 MockObjectMap mock_object_map
;
410 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
414 expect_block_writes(mock_image_ctx
, 0);
415 expect_append_op_event(mock_image_ctx
, true, 0);
416 expect_grow_object_map(mock_image_ctx
);
417 expect_update_header(mock_image_ctx
, -EINVAL
);
418 expect_unblock_writes(mock_image_ctx
);
419 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
420 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
423 TEST_F(TestMockOperationResizeRequest
, JournalAppendError
) {
424 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
426 librbd::ImageCtx
*ictx
;
427 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
429 MockImageCtx
mock_image_ctx(*ictx
);
430 MockExclusiveLock mock_exclusive_lock
;
431 MockJournal mock_journal
;
432 MockObjectMap mock_object_map
;
433 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
437 expect_block_writes(mock_image_ctx
, 0);
438 expect_append_op_event(mock_image_ctx
, true, -EINVAL
);
439 expect_unblock_writes(mock_image_ctx
);
440 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
443 TEST_F(TestMockOperationResizeRequest
, JournalDisabled
) {
444 librbd::ImageCtx
*ictx
;
445 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
447 MockImageCtx
mock_image_ctx(*ictx
);
448 MockExclusiveLock mock_exclusive_lock
;
449 MockJournal mock_journal
;
450 MockObjectMap mock_object_map
;
451 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
455 expect_block_writes(mock_image_ctx
, 0);
456 expect_unblock_writes(mock_image_ctx
);
457 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, true));
460 } // namespace operation
461 } // namespace librbd