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 EXPECT_CALL(mock_image_ctx
, flush_cache(_
))
118 .WillOnce(CompleteContext(r
, static_cast<ContextWQ
*>(NULL
)));
119 expect_op_work_queue(mock_image_ctx
);
122 void expect_invalidate_cache(MockImageCtx
&mock_image_ctx
, int r
) {
123 EXPECT_CALL(mock_image_ctx
, invalidate_cache(false, _
))
124 .WillOnce(WithArg
<1>(CompleteContext(r
, static_cast<ContextWQ
*>(NULL
))));
125 expect_op_work_queue(mock_image_ctx
);
128 void expect_resize_object_map(MockImageCtx
&mock_image_ctx
,
130 EXPECT_CALL(*mock_image_ctx
.object_map
, aio_resize(new_size
, _
, _
))
131 .WillOnce(WithArg
<2>(CompleteContext(0, mock_image_ctx
.image_ctx
->op_work_queue
)));
134 int when_resize(MockImageCtx
&mock_image_ctx
, uint64_t new_size
,
135 bool allow_shrink
, uint64_t journal_op_tid
,
136 bool disable_journal
) {
137 C_SaferCond cond_ctx
;
138 librbd::NoOpProgressContext prog_ctx
;
139 MockResizeRequest
*req
= new MockResizeRequest(
140 mock_image_ctx
, &cond_ctx
, new_size
, allow_shrink
, prog_ctx
,
141 journal_op_tid
, disable_journal
);
143 RWLock::RLocker
owner_locker(mock_image_ctx
.owner_lock
);
146 return cond_ctx
.wait();
150 TEST_F(TestMockOperationResizeRequest
, NoOpSuccess
) {
151 librbd::ImageCtx
*ictx
;
152 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
154 MockImageCtx
mock_image_ctx(*ictx
);
155 MockExclusiveLock mock_exclusive_lock
;
156 MockJournal mock_journal
;
157 MockObjectMap mock_object_map
;
158 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
162 expect_block_writes(mock_image_ctx
, 0);
163 expect_append_op_event(mock_image_ctx
, true, 0);
164 expect_unblock_writes(mock_image_ctx
);
165 expect_commit_op_event(mock_image_ctx
, 0);
166 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
169 TEST_F(TestMockOperationResizeRequest
, GrowSuccess
) {
170 librbd::ImageCtx
*ictx
;
171 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
173 MockImageCtx
mock_image_ctx(*ictx
);
174 MockExclusiveLock mock_exclusive_lock
;
175 MockJournal mock_journal
;
176 MockObjectMap mock_object_map
;
177 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
181 expect_block_writes(mock_image_ctx
, 0);
182 expect_append_op_event(mock_image_ctx
, true, 0);
183 expect_unblock_writes(mock_image_ctx
);
184 expect_grow_object_map(mock_image_ctx
);
185 expect_block_writes(mock_image_ctx
, 0);
186 expect_update_header(mock_image_ctx
, 0);
187 expect_unblock_writes(mock_image_ctx
);
188 expect_commit_op_event(mock_image_ctx
, 0);
189 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
192 TEST_F(TestMockOperationResizeRequest
, ShrinkSuccess
) {
193 librbd::ImageCtx
*ictx
;
194 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
196 MockImageCtx
mock_image_ctx(*ictx
);
197 MockExclusiveLock mock_exclusive_lock
;
198 MockJournal mock_journal
;
199 MockObjectMap mock_object_map
;
200 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
204 expect_block_writes(mock_image_ctx
, 0);
205 expect_append_op_event(mock_image_ctx
, true, 0);
206 expect_unblock_writes(mock_image_ctx
);
208 MockTrimRequest mock_trim_request
;
209 expect_flush_cache(mock_image_ctx
, 0);
210 expect_invalidate_cache(mock_image_ctx
, 0);
211 expect_trim(mock_image_ctx
, mock_trim_request
, 0);
212 expect_block_writes(mock_image_ctx
, 0);
213 expect_update_header(mock_image_ctx
, 0);
214 expect_shrink_object_map(mock_image_ctx
);
215 expect_unblock_writes(mock_image_ctx
);
216 expect_commit_op_event(mock_image_ctx
, 0);
217 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
220 TEST_F(TestMockOperationResizeRequest
, ShrinkError
) {
221 librbd::ImageCtx
*ictx
;
222 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
224 MockImageCtx
mock_image_ctx(*ictx
);
225 MockExclusiveLock mock_exclusive_lock
;
226 MockJournal mock_journal
;
227 MockObjectMap mock_object_map
;
228 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
232 expect_block_writes(mock_image_ctx
, -EINVAL
);
233 expect_unblock_writes(mock_image_ctx
);
234 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, false, 0, false));
237 TEST_F(TestMockOperationResizeRequest
, PreBlockWritesError
) {
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
, -EINVAL
);
250 expect_unblock_writes(mock_image_ctx
);
251 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
254 TEST_F(TestMockOperationResizeRequest
, TrimError
) {
255 librbd::ImageCtx
*ictx
;
256 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
258 MockImageCtx
mock_image_ctx(*ictx
);
259 MockExclusiveLock mock_exclusive_lock
;
260 MockJournal mock_journal
;
261 MockObjectMap mock_object_map
;
262 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
266 expect_block_writes(mock_image_ctx
, 0);
267 expect_append_op_event(mock_image_ctx
, true, 0);
268 expect_unblock_writes(mock_image_ctx
);
270 MockTrimRequest mock_trim_request
;
271 expect_flush_cache(mock_image_ctx
, 0);
272 expect_invalidate_cache(mock_image_ctx
, -EBUSY
);
273 expect_trim(mock_image_ctx
, mock_trim_request
, -EINVAL
);
274 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
275 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
278 TEST_F(TestMockOperationResizeRequest
, FlushCacheError
) {
279 librbd::ImageCtx
*ictx
;
280 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
282 MockImageCtx
mock_image_ctx(*ictx
);
283 MockExclusiveLock mock_exclusive_lock
;
284 MockJournal mock_journal
;
285 MockObjectMap mock_object_map
;
286 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
290 expect_block_writes(mock_image_ctx
, 0);
291 expect_append_op_event(mock_image_ctx
, true, 0);
292 expect_unblock_writes(mock_image_ctx
);
294 MockTrimRequest mock_trim_request
;
295 expect_flush_cache(mock_image_ctx
, -EINVAL
);
296 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
297 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
300 TEST_F(TestMockOperationResizeRequest
, InvalidateCacheError
) {
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 expect_flush_cache(mock_image_ctx
, 0);
318 expect_invalidate_cache(mock_image_ctx
, -EINVAL
);
319 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
320 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
/ 2, true, 0, false));
323 TEST_F(TestMockOperationResizeRequest
, PostBlockWritesError
) {
324 librbd::ImageCtx
*ictx
;
325 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
327 MockImageCtx
mock_image_ctx(*ictx
);
328 MockExclusiveLock mock_exclusive_lock
;
329 MockJournal mock_journal
;
330 MockObjectMap mock_object_map
;
331 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
335 expect_block_writes(mock_image_ctx
, 0);
336 expect_append_op_event(mock_image_ctx
, true, 0);
337 expect_unblock_writes(mock_image_ctx
);
338 expect_grow_object_map(mock_image_ctx
);
339 expect_block_writes(mock_image_ctx
, -EINVAL
);
340 expect_unblock_writes(mock_image_ctx
);
341 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
342 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
345 TEST_F(TestMockOperationResizeRequest
, UpdateHeaderError
) {
346 librbd::ImageCtx
*ictx
;
347 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
349 MockImageCtx
mock_image_ctx(*ictx
);
350 MockExclusiveLock mock_exclusive_lock
;
351 MockJournal mock_journal
;
352 MockObjectMap mock_object_map
;
353 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
357 expect_block_writes(mock_image_ctx
, 0);
358 expect_append_op_event(mock_image_ctx
, true, 0);
359 expect_unblock_writes(mock_image_ctx
);
360 expect_grow_object_map(mock_image_ctx
);
361 expect_block_writes(mock_image_ctx
, 0);
362 expect_update_header(mock_image_ctx
, -EINVAL
);
363 expect_unblock_writes(mock_image_ctx
);
364 expect_commit_op_event(mock_image_ctx
, -EINVAL
);
365 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
* 2, true, 0, false));
368 TEST_F(TestMockOperationResizeRequest
, JournalAppendError
) {
369 REQUIRE_FEATURE(RBD_FEATURE_JOURNALING
);
371 librbd::ImageCtx
*ictx
;
372 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
374 MockImageCtx
mock_image_ctx(*ictx
);
375 MockExclusiveLock mock_exclusive_lock
;
376 MockJournal mock_journal
;
377 MockObjectMap mock_object_map
;
378 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
382 expect_block_writes(mock_image_ctx
, 0);
383 expect_append_op_event(mock_image_ctx
, true, -EINVAL
);
384 expect_unblock_writes(mock_image_ctx
);
385 ASSERT_EQ(-EINVAL
, when_resize(mock_image_ctx
, ictx
->size
, true, 0, false));
388 TEST_F(TestMockOperationResizeRequest
, JournalDisabled
) {
389 librbd::ImageCtx
*ictx
;
390 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
392 MockImageCtx
mock_image_ctx(*ictx
);
393 MockExclusiveLock mock_exclusive_lock
;
394 MockJournal mock_journal
;
395 MockObjectMap mock_object_map
;
396 initialize_features(ictx
, mock_image_ctx
, mock_exclusive_lock
, mock_journal
,
400 expect_block_writes(mock_image_ctx
, 0);
401 expect_unblock_writes(mock_image_ctx
);
402 ASSERT_EQ(0, when_resize(mock_image_ctx
, ictx
->size
, true, 0, true));
405 } // namespace operation
406 } // namespace librbd