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