]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
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/MockJournal.h" | |
8 | #include "test/librbd/mock/cache/MockImageCache.h" | |
9 | #include "librbd/io/ImageRequest.h" | |
10 | #include "librbd/io/ObjectRequest.h" | |
11 | ||
12 | namespace librbd { | |
13 | namespace { | |
14 | ||
15 | struct MockTestImageCtx : public MockImageCtx { | |
16 | MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { | |
17 | } | |
18 | }; | |
19 | ||
20 | } // anonymous namespace | |
21 | ||
22 | namespace util { | |
23 | ||
24 | inline ImageCtx *get_image_ctx(MockTestImageCtx *image_ctx) { | |
25 | return image_ctx->image_ctx; | |
26 | } | |
27 | ||
28 | } // namespace util | |
29 | ||
30 | namespace io { | |
31 | ||
32 | template <> | |
33 | struct ObjectRequest<librbd::MockTestImageCtx> : public ObjectRequestHandle { | |
34 | static ObjectRequest* s_instance; | |
35 | Context *on_finish = nullptr; | |
36 | ||
37 | static ObjectRequest* create_remove(librbd::MockTestImageCtx *ictx, | |
38 | const std::string &oid, | |
39 | uint64_t object_no, | |
40 | const ::SnapContext &snapc, | |
41 | Context *completion) { | |
42 | assert(s_instance != nullptr); | |
43 | s_instance->on_finish = completion; | |
44 | return s_instance; | |
45 | } | |
46 | ||
47 | static ObjectRequest* create_truncate(librbd::MockTestImageCtx *ictx, | |
48 | const std::string &oid, | |
49 | uint64_t object_no, | |
50 | uint64_t object_off, | |
51 | const ::SnapContext &snapc, | |
52 | Context *completion) { | |
53 | assert(s_instance != nullptr); | |
54 | s_instance->on_finish = completion; | |
55 | return s_instance; | |
56 | } | |
57 | ||
58 | static ObjectRequest* create_write(librbd::MockTestImageCtx *ictx, | |
59 | const std::string &oid, | |
60 | uint64_t object_no, | |
61 | uint64_t object_off, | |
62 | const ceph::bufferlist &data, | |
63 | const ::SnapContext &snapc, | |
64 | Context *completion, int op_flags) { | |
65 | assert(s_instance != nullptr); | |
66 | s_instance->on_finish = completion; | |
67 | return s_instance; | |
68 | } | |
69 | ||
70 | static ObjectRequest* create_zero(librbd::MockTestImageCtx *ictx, | |
71 | const std::string &oid, | |
72 | uint64_t object_no, uint64_t object_off, | |
73 | uint64_t object_len, | |
74 | const ::SnapContext &snapc, | |
75 | Context *completion) { | |
76 | assert(s_instance != nullptr); | |
77 | s_instance->on_finish = completion; | |
78 | return s_instance; | |
79 | } | |
80 | ||
81 | static ObjectRequest* create_writesame(librbd::MockTestImageCtx *ictx, | |
82 | const std::string &oid, | |
83 | uint64_t object_no, | |
84 | uint64_t object_off, | |
85 | uint64_t object_len, | |
86 | const ceph::bufferlist &data, | |
87 | const ::SnapContext &snapc, | |
88 | Context *completion, int op_flags) { | |
89 | assert(s_instance != nullptr); | |
90 | s_instance->on_finish = completion; | |
91 | return s_instance; | |
92 | } | |
93 | ||
94 | ObjectRequest() { | |
95 | assert(s_instance == nullptr); | |
96 | s_instance = this; | |
97 | } | |
98 | ~ObjectRequest() override { | |
99 | s_instance = nullptr; | |
100 | } | |
101 | ||
102 | MOCK_METHOD1(complete, void(int)); | |
103 | MOCK_METHOD0(send, void()); | |
104 | }; | |
105 | ||
106 | template <> | |
107 | struct ObjectReadRequest<librbd::MockTestImageCtx> : public ObjectRequest<librbd::MockTestImageCtx> { | |
108 | typedef std::vector<std::pair<uint64_t, uint64_t> > Extents; | |
109 | typedef std::map<uint64_t, uint64_t> ExtentMap; | |
110 | ||
111 | static ObjectReadRequest* s_instance; | |
112 | ||
113 | static ObjectReadRequest* create(librbd::MockTestImageCtx *ictx, | |
114 | const std::string &oid, | |
115 | uint64_t objectno, uint64_t offset, | |
116 | uint64_t len, Extents &buffer_extents, | |
117 | librados::snap_t snap_id, bool sparse, | |
118 | Context *completion, int op_flags) { | |
119 | assert(s_instance != nullptr); | |
120 | s_instance->on_finish = completion; | |
121 | return s_instance; | |
122 | } | |
123 | ||
124 | ObjectReadRequest() { | |
125 | assert(s_instance == nullptr); | |
126 | s_instance = this; | |
127 | } | |
128 | ~ObjectReadRequest() override { | |
129 | s_instance = nullptr; | |
130 | } | |
131 | ||
132 | MOCK_CONST_METHOD0(get_offset, uint64_t()); | |
133 | MOCK_CONST_METHOD0(get_length, uint64_t()); | |
134 | MOCK_METHOD0(data, ceph::bufferlist &()); | |
135 | MOCK_CONST_METHOD0(get_buffer_extents, const Extents &()); | |
136 | MOCK_METHOD0(get_extent_map, ExtentMap &()); | |
137 | ||
138 | }; | |
139 | ||
140 | ObjectRequest<librbd::MockTestImageCtx>* ObjectRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
141 | ObjectReadRequest<librbd::MockTestImageCtx>* ObjectReadRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
142 | ||
143 | } // namespace io | |
144 | } // namespace librbd | |
145 | ||
146 | #include "librbd/io/ImageRequest.cc" | |
147 | ||
148 | namespace librbd { | |
149 | namespace io { | |
150 | ||
151 | using ::testing::_; | |
152 | using ::testing::InSequence; | |
153 | using ::testing::Invoke; | |
154 | using ::testing::Return; | |
155 | using ::testing::WithArg; | |
156 | ||
157 | struct TestMockIoImageRequest : public TestMockFixture { | |
158 | typedef ImageRequest<librbd::MockTestImageCtx> MockImageRequest; | |
159 | typedef ImageWriteRequest<librbd::MockTestImageCtx> MockImageWriteRequest; | |
160 | typedef ImageDiscardRequest<librbd::MockTestImageCtx> MockImageDiscardRequest; | |
161 | typedef ImageFlushRequest<librbd::MockTestImageCtx> MockImageFlushRequest; | |
162 | typedef ImageWriteSameRequest<librbd::MockTestImageCtx> MockImageWriteSameRequest; | |
163 | typedef ObjectRequest<librbd::MockTestImageCtx> MockObjectRequest; | |
164 | typedef ObjectReadRequest<librbd::MockTestImageCtx> MockObjectReadRequest; | |
165 | ||
166 | void expect_is_journal_appending(MockJournal &mock_journal, bool appending) { | |
167 | EXPECT_CALL(mock_journal, is_journal_appending()) | |
168 | .WillOnce(Return(appending)); | |
169 | } | |
170 | ||
171 | void expect_write_to_cache(MockImageCtx &mock_image_ctx, | |
172 | const object_t &object, | |
173 | uint64_t offset, uint64_t length, | |
174 | uint64_t journal_tid, int r) { | |
175 | EXPECT_CALL(mock_image_ctx, write_to_cache(object, _, length, offset, _, _, | |
176 | journal_tid)) | |
177 | .WillOnce(WithArg<4>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue))); | |
178 | } | |
179 | ||
180 | void expect_object_request_send(MockImageCtx &mock_image_ctx, | |
181 | MockObjectRequest &mock_object_request, | |
182 | int r) { | |
183 | EXPECT_CALL(mock_object_request, send()) | |
184 | .WillOnce(Invoke([&mock_image_ctx, &mock_object_request, r]() { | |
185 | mock_image_ctx.image_ctx->op_work_queue->queue( | |
186 | mock_object_request.on_finish, r); | |
187 | })); | |
188 | } | |
189 | ||
190 | void expect_user_flushed(MockImageCtx &mock_image_ctx) { | |
191 | EXPECT_CALL(mock_image_ctx, user_flushed()); | |
192 | } | |
193 | ||
194 | void expect_flush(MockImageCtx &mock_image_ctx, int r) { | |
195 | EXPECT_CALL(mock_image_ctx, flush(_)) | |
196 | .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)); | |
197 | } | |
198 | }; | |
199 | ||
200 | TEST_F(TestMockIoImageRequest, AioWriteJournalAppendDisabled) { | |
201 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
202 | ||
203 | librbd::ImageCtx *ictx; | |
204 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
205 | ||
206 | MockObjectRequest mock_aio_object_request; | |
207 | MockTestImageCtx mock_image_ctx(*ictx); | |
208 | MockJournal mock_journal; | |
209 | mock_image_ctx.journal = &mock_journal; | |
210 | ||
211 | InSequence seq; | |
212 | expect_is_journal_appending(mock_journal, false); | |
213 | expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0), | |
214 | 0, 1, 0, 0); | |
215 | ||
216 | C_SaferCond aio_comp_ctx; | |
217 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
218 | &aio_comp_ctx, ictx, AIO_TYPE_WRITE); | |
219 | ||
220 | bufferlist bl; | |
221 | bl.append("1"); | |
222 | MockImageWriteRequest mock_aio_image_write(mock_image_ctx, aio_comp, | |
223 | {{0, 1}}, std::move(bl), 0); | |
224 | { | |
225 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
226 | mock_aio_image_write.send(); | |
227 | } | |
228 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
229 | } | |
230 | ||
231 | TEST_F(TestMockIoImageRequest, AioDiscardJournalAppendDisabled) { | |
232 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
233 | ||
234 | librbd::ImageCtx *ictx; | |
235 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
236 | ||
237 | MockObjectRequest mock_aio_object_request; | |
238 | MockTestImageCtx mock_image_ctx(*ictx); | |
239 | MockJournal mock_journal; | |
240 | mock_image_ctx.journal = &mock_journal; | |
241 | ||
242 | InSequence seq; | |
243 | expect_is_journal_appending(mock_journal, false); | |
244 | if (!ictx->skip_partial_discard) { | |
245 | expect_object_request_send(mock_image_ctx, mock_aio_object_request, 0); | |
246 | } | |
247 | ||
248 | C_SaferCond aio_comp_ctx; | |
249 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
250 | &aio_comp_ctx, ictx, AIO_TYPE_DISCARD); | |
251 | MockImageDiscardRequest mock_aio_image_discard(mock_image_ctx, aio_comp, | |
252 | 0, 1, ictx->skip_partial_discard); | |
253 | { | |
254 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
255 | mock_aio_image_discard.send(); | |
256 | } | |
257 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
258 | } | |
259 | ||
260 | TEST_F(TestMockIoImageRequest, AioFlushJournalAppendDisabled) { | |
261 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
262 | ||
263 | librbd::ImageCtx *ictx; | |
264 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
265 | ||
266 | MockTestImageCtx mock_image_ctx(*ictx); | |
267 | MockJournal mock_journal; | |
268 | mock_image_ctx.journal = &mock_journal; | |
269 | ||
270 | InSequence seq; | |
271 | expect_user_flushed(mock_image_ctx); | |
272 | expect_is_journal_appending(mock_journal, false); | |
273 | expect_flush(mock_image_ctx, 0); | |
274 | ||
275 | C_SaferCond aio_comp_ctx; | |
276 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
277 | &aio_comp_ctx, ictx, AIO_TYPE_FLUSH); | |
278 | MockImageFlushRequest mock_aio_image_flush(mock_image_ctx, aio_comp); | |
279 | { | |
280 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
281 | mock_aio_image_flush.send(); | |
282 | } | |
283 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
284 | } | |
285 | ||
286 | TEST_F(TestMockIoImageRequest, AioWriteSameJournalAppendDisabled) { | |
287 | REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); | |
288 | ||
289 | librbd::ImageCtx *ictx; | |
290 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
291 | ||
292 | MockObjectRequest mock_aio_object_request; | |
293 | MockTestImageCtx mock_image_ctx(*ictx); | |
294 | MockJournal mock_journal; | |
295 | mock_image_ctx.journal = &mock_journal; | |
296 | ||
297 | InSequence seq; | |
298 | expect_is_journal_appending(mock_journal, false); | |
299 | expect_write_to_cache(mock_image_ctx, ictx->get_object_name(0), | |
300 | 0, 1, 0, 0); | |
301 | ||
302 | C_SaferCond aio_comp_ctx; | |
303 | AioCompletion *aio_comp = AioCompletion::create_and_start( | |
304 | &aio_comp_ctx, ictx, AIO_TYPE_WRITESAME); | |
305 | ||
306 | bufferlist bl; | |
307 | bl.append("1"); | |
308 | MockImageWriteSameRequest mock_aio_image_writesame(mock_image_ctx, aio_comp, | |
309 | 0, 1, std::move(bl), 0); | |
310 | { | |
311 | RWLock::RLocker owner_locker(mock_image_ctx.owner_lock); | |
312 | mock_aio_image_writesame.send(); | |
313 | } | |
314 | ASSERT_EQ(0, aio_comp_ctx.wait()); | |
315 | } | |
316 | ||
317 | } // namespace io | |
318 | } // namespace librbd |