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/internal.h"
10 #include "librbd/ObjectMap.h"
11 #include "librbd/operation/ResizeRequest.h"
12 #include "librbd/operation/TrimRequest.h"
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
20 class TrimRequest
<MockImageCtx
> {
22 static TrimRequest
*s_instance
;
23 static TrimRequest
*create(MockImageCtx
&image_ctx
, Context
*on_finish
,
24 uint64_t original_size
, uint64_t new_size
,
25 ProgressContext
&prog_ctx
) {
26 assert(s_instance
!= nullptr);
27 s_instance
->on_finish
= on_finish
;
31 Context
*on_finish
= nullptr;
37 MOCK_METHOD0(send
, void());
40 TrimRequest
<MockImageCtx
> *TrimRequest
<MockImageCtx
>::s_instance
= nullptr;
42 } // namespace operation
45 // template definitions
46 #include "librbd/operation/ResizeRequest.cc"
47 #include "librbd/operation/TrimRequest.cc"
53 using ::testing::DoAll
;
54 using ::testing::InSequence
;
55 using ::testing::Return
;
56 using ::testing::StrEq
;
57 using ::testing::WithArg
;
59 class TestMockOperationResizeRequest
: public TestMockFixture
{
61 typedef ResizeRequest
<MockImageCtx
> MockResizeRequest
;
62 typedef TrimRequest
<MockImageCtx
> MockTrimRequest
;
64 void expect_block_writes(MockImageCtx
&mock_image_ctx
, int r
) {
65 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, block_writes(_
))
66 .WillOnce(CompleteContext(r
, mock_image_ctx
.image_ctx
->op_work_queue
));
69 void expect_unblock_writes(MockImageCtx
&mock_image_ctx
) {
70 EXPECT_CALL(*mock_image_ctx
.io_work_queue
, unblock_writes())
74 void expect_is_lock_owner(MockImageCtx
&mock_image_ctx
) {
75 if (mock_image_ctx
.exclusive_lock
!= nullptr) {
76 EXPECT_CALL(*mock_image_ctx
.exclusive_lock
, is_lock_owner())
77 .WillOnce(Return(true));
81 void expect_grow_object_map(MockImageCtx
&mock_image_ctx
) {
82 if (mock_image_ctx
.object_map
!= nullptr) {
83 expect_is_lock_owner(mock_image_ctx
);
84 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(_
, _
, _
))
85 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
89 void expect_shrink_object_map(MockImageCtx
&mock_image_ctx
) {
90 if (mock_image_ctx
.object_map
!= nullptr) {
91 expect_is_lock_owner(mock_image_ctx
);
92 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(_
, _
, _
))
93 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
97 void expect_update_header(MockImageCtx
&mock_image_ctx
, int r
) {
98 if (mock_image_ctx
.old_format
) {
99 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
100 write(mock_image_ctx
.header_oid
, _
, _
, _
, _
))
101 .WillOnce(Return(r
));
103 expect_is_lock_owner(mock_image_ctx
);
104 EXPECT_CALL(get_mock_io_ctx(mock_image_ctx
.md_ctx
),
105 exec(mock_image_ctx
.header_oid
, _
, StrEq("rbd"), StrEq("set_size"), _
, _
, _
))
106 .WillOnce(Return(r
));
110 void expect_trim(MockImageCtx
&mock_image_ctx
,
111 MockTrimRequest
&mock_trim_request
, int r
) {
112 EXPECT_CALL(mock_trim_request
, send())
113 .WillOnce(FinishRequest(&mock_trim_request
, r
, &mock_image_ctx
));
116 void expect_flush_cache(MockImageCtx
&mock_image_ctx
, int r
) {
117 if (!mock_image_ctx
.image_ctx
->cache
) {
120 EXPECT_CALL(mock_image_ctx
, flush_cache(_
))
121 .WillOnce(CompleteContext(r
, static_cast<ContextWQ
*>(NULL
)));
122 expect_op_work_queue(mock_image_ctx
);
125 void expect_invalidate_cache(MockImageCtx
&mock_image_ctx
, int r
) {
126 if (!mock_image_ctx
.image_ctx
->cache
) {
129 EXPECT_CALL(mock_image_ctx
, invalidate_cache(false, _
))
130 .WillOnce(WithArg
<1>(CompleteContext(r
, static_cast<ContextWQ
*>(NULL
))));
131 expect_op_work_queue(mock_image_ctx
);
134 void expect_resize_object_map(MockImageCtx
&mock_image_ctx
,
136 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(new_size
, _
, _
))
137 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
140 int when_resize(MockImageCtx
&mock_image_ctx
, uint64_t new_size
,
141 bool allow_shrink
, uint64_t journal_op_tid
,
142 bool disable_journal
) {
143 C_SaferCond cond_ctx
;
144 librbd::NoOpProgressContext prog_ctx
;
145 MockResizeRequest
*req
= new MockResizeRequest(
146 mock_image_ctx
, &cond_ctx
, new_size
, allow_shrink
, prog_ctx
,
147 journal_op_tid
, disable_journal
);
149 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
152 return cond_ctx
.wait();
156 TEST_F(TestMockOperationResizeRequest
, NoOpSuccess
) {
157 librbd::ImageCtx
*ictx
;
158 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
160 MockImageCtx
mock_image_ctx(*ictx
);
161 MockExclusiveLock mock_exclusive_lock
;
162 MockJournal mock_journal
;
163 MockObjectMap mock_object_map
;
164 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
168 expect_block_writes(mock_image_ctx
, 0);
169 expect_append_op_event(mock_image_ctx
, true, 0);
170 expect_unblock_writes(mock_image_ctx
);
171 expect_commit_op_event(mock_image_ctx
, 0);
172 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
175 TEST_F(TestMockOperationResizeRequest
, GrowSuccess
) {
176 librbd::ImageCtx
*ictx
;
177 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
179 MockImageCtx
mock_image_ctx(*ictx
);
180 MockExclusiveLock mock_exclusive_lock
;
181 MockJournal mock_journal
;
182 MockObjectMap mock_object_map
;
183 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
187 expect_block_writes(mock_image_ctx
, 0);
188 expect_append_op_event(mock_image_ctx
, true, 0);
189 expect_unblock_writes(mock_image_ctx
);
190 expect_grow_object_map(mock_image_ctx
);
191 expect_block_writes(mock_image_ctx
, 0);
192 expect_update_header(mock_image_ctx
, 0);
193 expect_unblock_writes(mock_image_ctx
);
194 expect_commit_op_event(mock_image_ctx
, 0);
195 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
198 TEST_F(TestMockOperationResizeRequest
, ShrinkSuccess
) {
199 librbd::ImageCtx
*ictx
;
200 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
202 MockImageCtx
mock_image_ctx(*ictx
);
203 MockExclusiveLock mock_exclusive_lock
;
204 MockJournal mock_journal
;
205 MockObjectMap mock_object_map
;
206 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
210 expect_block_writes(mock_image_ctx
, 0);
211 expect_append_op_event(mock_image_ctx
, true, 0);
212 expect_unblock_writes(mock_image_ctx
);
214 MockTrimRequest mock_trim_request
;
215 expect_flush_cache(mock_image_ctx
, 0);
216 expect_invalidate_cache(mock_image_ctx
, 0);
217 expect_trim(mock_image_ctx
, mock_trim_request
, 0);
218 expect_block_writes(mock_image_ctx
, 0);
219 expect_update_header(mock_image_ctx
, 0);
220 expect_shrink_object_map(mock_image_ctx
);
221 expect_unblock_writes(mock_image_ctx
);
222 expect_commit_op_event(mock_image_ctx
, 0);
223 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
226 TEST_F(TestMockOperationResizeRequest
, ShrinkError
) {
227 librbd::ImageCtx
*ictx
;
228 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
230 MockImageCtx
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
,
238 expect_block_writes(mock_image_ctx
, -EINVAL
);
239 expect_unblock_writes(mock_image_ctx
);
240 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, false, 0, false));
243 TEST_F(TestMockOperationResizeRequest
, PreBlockWritesError
) {
244 librbd::ImageCtx
*ictx
;
245 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
247 MockImageCtx
mock_image_ctx(*ictx
);
248 MockExclusiveLock mock_exclusive_lock
;
249 MockJournal mock_journal
;
250 MockObjectMap mock_object_map
;
251 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
255 expect_block_writes(mock_image_ctx
, -EINVAL
);
256 expect_unblock_writes(mock_image_ctx
);
257 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
260 TEST_F(TestMockOperationResizeRequest
, TrimError
) {
261 librbd::ImageCtx
*ictx
;
262 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
264 MockImageCtx
mock_image_ctx(*ictx
);
265 MockExclusiveLock mock_exclusive_lock
;
266 MockJournal mock_journal
;
267 MockObjectMap mock_object_map
;
268 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
272 expect_block_writes(mock_image_ctx
, 0);
273 expect_append_op_event(mock_image_ctx
, true, 0);
274 expect_unblock_writes(mock_image_ctx
);
276 MockTrimRequest mock_trim_request
;
277 expect_flush_cache(mock_image_ctx
, 0);
278 expect_invalidate_cache(mock_image_ctx
, -EBUSY
);
279 expect_trim(mock_image_ctx
, mock_trim_request
, -EINVAL
);
280 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
281 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
284 TEST_F(TestMockOperationResizeRequest
, FlushCacheError
) {
285 librbd::ImageCtx
*ictx
;
286 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
287 REQUIRE(ictx
->cache
);
289 MockImageCtx
mock_image_ctx(*ictx
);
290 MockExclusiveLock mock_exclusive_lock
;
291 MockJournal mock_journal
;
292 MockObjectMap mock_object_map
;
293 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
297 expect_block_writes(mock_image_ctx
, 0);
298 expect_append_op_event(mock_image_ctx
, true, 0);
299 expect_unblock_writes(mock_image_ctx
);
301 MockTrimRequest mock_trim_request
;
302 expect_flush_cache(mock_image_ctx
, -EINVAL
);
303 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
304 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
307 TEST_F(TestMockOperationResizeRequest
, InvalidateCacheError
) {
308 librbd::ImageCtx
*ictx
;
309 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
310 REQUIRE(ictx
->cache
);
312 MockImageCtx
mock_image_ctx(*ictx
);
313 MockExclusiveLock mock_exclusive_lock
;
314 MockJournal mock_journal
;
315 MockObjectMap mock_object_map
;
316 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
320 expect_block_writes(mock_image_ctx
, 0);
321 expect_append_op_event(mock_image_ctx
, true, 0);
322 expect_unblock_writes(mock_image_ctx
);
324 MockTrimRequest mock_trim_request
;
325 expect_flush_cache(mock_image_ctx
, 0);
326 expect_invalidate_cache(mock_image_ctx
, -EINVAL
);
327 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
328 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
331 TEST_F(TestMockOperationResizeRequest
, PostBlockWritesError
) {
332 librbd::ImageCtx
*ictx
;
333 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
335 MockImageCtx
mock_image_ctx(*ictx
);
336 MockExclusiveLock mock_exclusive_lock
;
337 MockJournal mock_journal
;
338 MockObjectMap mock_object_map
;
339 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
343 expect_block_writes(mock_image_ctx
, 0);
344 expect_append_op_event(mock_image_ctx
, true, 0);
345 expect_unblock_writes(mock_image_ctx
);
346 expect_grow_object_map(mock_image_ctx
);
347 expect_block_writes(mock_image_ctx
, -EINVAL
);
348 expect_unblock_writes(mock_image_ctx
);
349 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
350 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
353 TEST_F(TestMockOperationResizeRequest
, UpdateHeaderError
) {
354 librbd::ImageCtx
*ictx
;
355 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
357 MockImageCtx
mock_image_ctx(*ictx
);
358 MockExclusiveLock mock_exclusive_lock
;
359 MockJournal mock_journal
;
360 MockObjectMap mock_object_map
;
361 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
365 expect_block_writes(mock_image_ctx
, 0);
366 expect_append_op_event(mock_image_ctx
, true, 0);
367 expect_unblock_writes(mock_image_ctx
);
368 expect_grow_object_map(mock_image_ctx
);
369 expect_block_writes(mock_image_ctx
, 0);
370 expect_update_header(mock_image_ctx
, -EINVAL
);
371 expect_unblock_writes(mock_image_ctx
);
372 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
373 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
376 TEST_F(TestMockOperationResizeRequest
, JournalAppendError
) {
377 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
379 librbd::ImageCtx
*ictx
;
380 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
382 MockImageCtx
mock_image_ctx(*ictx
);
383 MockExclusiveLock mock_exclusive_lock
;
384 MockJournal mock_journal
;
385 MockObjectMap mock_object_map
;
386 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
390 expect_block_writes(mock_image_ctx
, 0);
391 expect_append_op_event(mock_image_ctx
, true, -EINVAL
);
392 expect_unblock_writes(mock_image_ctx
);
393 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
396 TEST_F(TestMockOperationResizeRequest
, JournalDisabled
) {
397 librbd::ImageCtx
*ictx
;
398 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
400 MockImageCtx
mock_image_ctx(*ictx
);
401 MockExclusiveLock mock_exclusive_lock
;
402 MockJournal mock_journal
;
403 MockObjectMap mock_object_map
;
404 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
408 expect_block_writes(mock_image_ctx
, 0);
409 expect_unblock_writes(mock_image_ctx
);
410 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, true));
413 } // namespace operation
414 } // namespace librbd