]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
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/rbd_mirror/test_mock_fixture.h" | |
5 | #include "include/stringify.h" | |
6 | #include "tools/rbd_mirror/MirrorStatusUpdater.h" | |
7 | #include "tools/rbd_mirror/MirrorStatusWatcher.h" | |
8 | #include "tools/rbd_mirror/Threads.h" | |
9 | #include "test/librados_test_stub/MockTestMemIoCtxImpl.h" | |
10 | #include "test/librbd/mock/MockImageCtx.h" | |
11 | #include "test/rbd_mirror/mock/MockContextWQ.h" | |
12 | #include "test/rbd_mirror/mock/MockSafeTimer.h" | |
13 | #include <map> | |
14 | #include <string> | |
15 | #include <utility> | |
16 | ||
17 | namespace librbd { | |
18 | namespace { | |
19 | ||
20 | struct MockTestImageCtx : public MockImageCtx { | |
21 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
22 | : librbd::MockImageCtx(image_ctx) { | |
23 | } | |
24 | }; | |
25 | ||
26 | } // anonymous namespace | |
27 | } // namespace librbd | |
28 | ||
29 | namespace rbd { | |
30 | namespace mirror { | |
31 | ||
32 | template <> | |
33 | struct MirrorStatusWatcher<librbd::MockTestImageCtx> { | |
34 | static MirrorStatusWatcher* s_instance; | |
35 | static MirrorStatusWatcher* create(librados::IoCtx& io_ctx, | |
36 | MockContextWQ* mock_context_wq) { | |
37 | ceph_assert(s_instance != nullptr); | |
38 | return s_instance; | |
39 | } | |
40 | ||
41 | MOCK_METHOD1(init, void(Context*)); | |
42 | MOCK_METHOD1(shut_down, void(Context*)); | |
43 | ||
44 | MirrorStatusWatcher() { | |
45 | s_instance = this; | |
46 | } | |
47 | }; | |
48 | ||
49 | MirrorStatusWatcher<librbd::MockTestImageCtx>* MirrorStatusWatcher<librbd::MockTestImageCtx>::s_instance = nullptr; | |
50 | ||
51 | template <> | |
52 | struct Threads<librbd::MockTestImageCtx> { | |
53 | MockSafeTimer *timer; | |
54 | ceph::mutex &timer_lock; | |
55 | ||
56 | MockContextWQ *work_queue; | |
57 | ||
58 | Threads(Threads<librbd::ImageCtx> *threads) | |
59 | : timer(new MockSafeTimer()), | |
60 | timer_lock(threads->timer_lock), | |
61 | work_queue(new MockContextWQ()) { | |
62 | } | |
63 | ~Threads() { | |
64 | delete timer; | |
65 | delete work_queue; | |
66 | } | |
67 | }; | |
68 | ||
69 | } // namespace mirror | |
70 | } // namespace rbd | |
71 | ||
72 | #include "tools/rbd_mirror/MirrorStatusUpdater.cc" | |
73 | ||
74 | namespace rbd { | |
75 | namespace mirror { | |
76 | ||
77 | using ::testing::_; | |
78 | using ::testing::DoDefault; | |
79 | using ::testing::InSequence; | |
80 | using ::testing::Invoke; | |
81 | using ::testing::StrEq; | |
82 | using ::testing::Return; | |
83 | using ::testing::WithArg; | |
84 | ||
85 | class TestMockMirrorStatusUpdater : public TestMockFixture { | |
86 | public: | |
87 | typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater; | |
88 | typedef MirrorStatusWatcher<librbd::MockTestImageCtx> MockMirrorStatusWatcher; | |
89 | typedef Threads<librbd::MockTestImageCtx> MockThreads; | |
90 | ||
91 | typedef std::map<std::string, cls::rbd::MirrorImageSiteStatus> | |
92 | MirrorImageSiteStatuses; | |
93 | ||
94 | void SetUp() override { | |
95 | TestMockFixture::SetUp(); | |
96 | ||
97 | m_mock_local_io_ctx = &get_mock_io_ctx(m_local_io_ctx); | |
98 | m_mock_threads = new MockThreads(m_threads); | |
99 | } | |
100 | ||
101 | void TearDown() override { | |
102 | delete m_mock_threads; | |
103 | TestMockFixture::TearDown(); | |
104 | } | |
105 | ||
106 | void expect_timer_add_event(Context** timer_event) { | |
107 | EXPECT_CALL(*m_mock_threads->timer, add_event_after(_, _)) | |
108 | .WillOnce(WithArg<1>(Invoke([timer_event](Context *ctx) { | |
109 | *timer_event = ctx; | |
110 | return ctx; | |
111 | }))); | |
112 | } | |
113 | ||
114 | void expect_timer_cancel_event() { | |
115 | EXPECT_CALL(*m_mock_threads->timer, cancel_event(_)) | |
116 | .WillOnce(Invoke([](Context* ctx) { | |
117 | delete ctx; | |
118 | return false; | |
119 | })); | |
120 | } | |
121 | ||
122 | void expect_work_queue(bool async) { | |
123 | EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _)) | |
124 | .WillOnce(Invoke([this, async](Context *ctx, int r) { | |
125 | if (async) { | |
126 | m_threads->work_queue->queue(ctx, r); | |
127 | } else { | |
128 | ctx->complete(r); | |
129 | } | |
130 | })); | |
131 | } | |
132 | ||
133 | void expect_mirror_status_watcher_init( | |
134 | MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) { | |
135 | EXPECT_CALL(*mock_mirror_status_watcher.s_instance, init(_)) | |
136 | .WillOnce(Invoke([this, r](Context* ctx) { | |
137 | m_threads->work_queue->queue(ctx, r); | |
138 | })); | |
139 | } | |
140 | ||
141 | void expect_mirror_status_watcher_shut_down( | |
142 | MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) { | |
143 | EXPECT_CALL(*mock_mirror_status_watcher.s_instance, shut_down(_)) | |
144 | .WillOnce(Invoke([this, r](Context* ctx) { | |
145 | m_threads->work_queue->queue(ctx, r); | |
146 | })); | |
147 | } | |
148 | ||
149 | void expect_mirror_status_update( | |
150 | const std::string& global_image_id, | |
151 | const cls::rbd::MirrorImageSiteStatus& mirror_image_status, int r) { | |
152 | EXPECT_CALL(*m_mock_local_io_ctx, | |
153 | exec(RBD_MIRRORING, _, StrEq("rbd"), | |
f67539c2 | 154 | StrEq("mirror_image_status_set"), _, _, _, _)) |
9f95a23c TL |
155 | .WillOnce(WithArg<4>(Invoke( |
156 | [r, global_image_id, mirror_image_status](bufferlist& in_bl) { | |
157 | auto bl_it = in_bl.cbegin(); | |
158 | std::string decode_global_image_id; | |
159 | decode(decode_global_image_id, bl_it); | |
160 | EXPECT_EQ(global_image_id, decode_global_image_id); | |
161 | ||
162 | cls::rbd::MirrorImageSiteStatus decode_mirror_image_status; | |
163 | decode(decode_mirror_image_status, bl_it); | |
164 | EXPECT_EQ(mirror_image_status, decode_mirror_image_status); | |
165 | return r; | |
166 | }))); | |
167 | } | |
168 | ||
169 | void expect_mirror_status_update( | |
170 | const MirrorImageSiteStatuses& mirror_image_site_statuses, | |
171 | const std::string& mirror_uuid, int r) { | |
172 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
173 | .WillOnce(Invoke([this](auto&&... args) { | |
174 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
175 | m_mock_local_io_ctx->aio_flush(); | |
176 | return r; | |
177 | })); | |
178 | ||
179 | for (auto [global_image_id, mirror_image_status] : | |
180 | mirror_image_site_statuses) { | |
181 | mirror_image_status.mirror_uuid = mirror_uuid; | |
182 | expect_mirror_status_update(global_image_id, mirror_image_status, r); | |
183 | if (r < 0) { | |
184 | break; | |
185 | } | |
186 | } | |
187 | } | |
188 | ||
a4b75251 TL |
189 | void expect_mirror_status_remove(const std::string& global_image_id, int r) { |
190 | EXPECT_CALL(*m_mock_local_io_ctx, | |
191 | exec(RBD_MIRRORING, _, StrEq("rbd"), | |
192 | StrEq("mirror_image_status_remove"), _, _, _, _)) | |
193 | .WillOnce(WithArg<4>(Invoke( | |
194 | [r, global_image_id](bufferlist& in_bl) { | |
195 | auto bl_it = in_bl.cbegin(); | |
196 | std::string decode_global_image_id; | |
197 | decode(decode_global_image_id, bl_it); | |
198 | EXPECT_EQ(global_image_id, decode_global_image_id); | |
199 | ||
200 | return r; | |
201 | }))); | |
202 | } | |
203 | ||
204 | void expect_mirror_status_removes(const std::set<std::string>& mirror_images, | |
205 | int r) { | |
206 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
207 | .WillOnce(Invoke([this](auto&&... args) { | |
208 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
209 | m_mock_local_io_ctx->aio_flush(); | |
210 | return r; | |
211 | })); | |
212 | ||
213 | for (auto global_image_id : mirror_images) { | |
214 | expect_mirror_status_remove(global_image_id, r); | |
215 | if (r < 0) { | |
216 | break; | |
217 | } | |
218 | } | |
219 | } | |
220 | ||
9f95a23c TL |
221 | void fire_timer_event(Context** timer_event, |
222 | Context** update_task) { | |
223 | expect_timer_add_event(timer_event); | |
224 | ||
225 | // timer queues the update task | |
226 | EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _)) | |
227 | .WillOnce(WithArg<0>(Invoke([update_task](Context* ctx) mutable { | |
228 | *update_task = ctx; | |
229 | }))); | |
230 | ||
231 | // fire the timer task | |
232 | { | |
233 | std::lock_guard timer_locker{m_mock_threads->timer_lock}; | |
234 | ceph_assert(*timer_event != nullptr); | |
235 | (*timer_event)->complete(0); | |
236 | } | |
237 | } | |
238 | ||
239 | void init_mirror_status_updater( | |
240 | MockMirrorStatusUpdater& mock_mirror_status_updater, | |
241 | MockMirrorStatusWatcher& mock_mirror_status_watcher, | |
242 | Context** timer_event) { | |
243 | expect_timer_add_event(timer_event); | |
244 | expect_mirror_status_watcher_init(mock_mirror_status_watcher, 0); | |
245 | expect_work_queue(true); | |
246 | ||
247 | C_SaferCond ctx; | |
248 | mock_mirror_status_updater.init(&ctx); | |
249 | ASSERT_EQ(0, ctx.wait()); | |
250 | } | |
251 | ||
252 | void shut_down_mirror_status_updater( | |
253 | MockMirrorStatusUpdater& mock_mirror_status_updater, | |
254 | MockMirrorStatusWatcher& mock_mirror_status_watcher) { | |
255 | expect_timer_cancel_event(); | |
256 | expect_mirror_status_watcher_shut_down(mock_mirror_status_watcher, 0); | |
257 | expect_work_queue(true); | |
258 | ||
259 | C_SaferCond ctx; | |
260 | mock_mirror_status_updater.shut_down(&ctx); | |
261 | ASSERT_EQ(0, ctx.wait()); | |
262 | } | |
263 | ||
264 | librados::MockTestMemIoCtxImpl* m_mock_local_io_ctx = nullptr; | |
265 | MockThreads* m_mock_threads = nullptr; | |
266 | }; | |
267 | ||
268 | TEST_F(TestMockMirrorStatusUpdater, InitShutDown) { | |
269 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
270 | m_mock_threads, ""); | |
271 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
272 | new MockMirrorStatusWatcher(); | |
273 | ||
274 | Context* timer_event = nullptr; | |
275 | init_mirror_status_updater(mock_mirror_status_updater, | |
276 | *mock_mirror_status_watcher, &timer_event); | |
277 | ||
278 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
279 | *mock_mirror_status_watcher); | |
280 | } | |
281 | ||
282 | TEST_F(TestMockMirrorStatusUpdater, InitStatusWatcherError) { | |
283 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
284 | m_mock_threads, ""); | |
285 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
286 | new MockMirrorStatusWatcher(); | |
287 | ||
288 | Context* timer_event = nullptr; | |
289 | expect_timer_add_event(&timer_event); | |
290 | expect_mirror_status_watcher_init(*mock_mirror_status_watcher, -EINVAL); | |
291 | expect_timer_cancel_event(); | |
292 | expect_work_queue(true); | |
293 | ||
294 | C_SaferCond ctx; | |
295 | mock_mirror_status_updater.init(&ctx); | |
296 | ASSERT_EQ(-EINVAL, ctx.wait()); | |
297 | } | |
298 | ||
299 | TEST_F(TestMockMirrorStatusUpdater, ShutDownStatusWatcherError) { | |
300 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
301 | m_mock_threads, ""); | |
302 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
303 | new MockMirrorStatusWatcher(); | |
304 | ||
305 | Context* timer_event = nullptr; | |
306 | init_mirror_status_updater(mock_mirror_status_updater, | |
307 | *mock_mirror_status_watcher, &timer_event); | |
308 | ||
309 | C_SaferCond on_shutdown; | |
310 | expect_timer_cancel_event(); | |
311 | expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, -EINVAL); | |
312 | expect_work_queue(true); | |
313 | mock_mirror_status_updater.shut_down(&on_shutdown); | |
314 | ||
315 | ASSERT_EQ(-EINVAL, on_shutdown.wait()); | |
316 | } | |
317 | ||
318 | TEST_F(TestMockMirrorStatusUpdater, SmallBatch) { | |
319 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
320 | m_mock_threads, ""); | |
321 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
322 | new MockMirrorStatusWatcher(); | |
323 | ||
324 | InSequence seq; | |
325 | ||
326 | Context* timer_event = nullptr; | |
327 | init_mirror_status_updater(mock_mirror_status_updater, | |
328 | *mock_mirror_status_watcher, &timer_event); | |
329 | ||
330 | MirrorImageSiteStatuses mirror_image_site_statuses; | |
331 | for (auto i = 0; i < 100; ++i) { | |
332 | auto pair = mirror_image_site_statuses.emplace( | |
333 | stringify(i), cls::rbd::MirrorImageSiteStatus{}); | |
334 | mock_mirror_status_updater.set_mirror_image_status(pair.first->first, | |
335 | pair.first->second, | |
336 | false); | |
337 | } | |
338 | ||
339 | Context* update_task = nullptr; | |
340 | fire_timer_event(&timer_event, &update_task); | |
341 | ||
342 | expect_mirror_status_update(mirror_image_site_statuses, "", 0); | |
343 | update_task->complete(0); | |
344 | ||
345 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
346 | *mock_mirror_status_watcher); | |
347 | } | |
348 | ||
349 | TEST_F(TestMockMirrorStatusUpdater, LargeBatch) { | |
350 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
351 | m_mock_threads, ""); | |
352 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
353 | new MockMirrorStatusWatcher(); | |
354 | ||
355 | InSequence seq; | |
356 | ||
357 | Context* timer_event = nullptr; | |
358 | init_mirror_status_updater(mock_mirror_status_updater, | |
359 | *mock_mirror_status_watcher, &timer_event); | |
360 | ||
361 | MirrorImageSiteStatuses mirror_image_site_statuses; | |
362 | for (auto i = 0; i < 200; ++i) { | |
363 | auto pair = mirror_image_site_statuses.emplace( | |
364 | stringify(i), cls::rbd::MirrorImageSiteStatus{}); | |
365 | mock_mirror_status_updater.set_mirror_image_status(pair.first->first, | |
366 | pair.first->second, | |
367 | false); | |
368 | } | |
369 | ||
370 | auto it_1 = mirror_image_site_statuses.begin(); | |
371 | auto it_2 = mirror_image_site_statuses.begin(); | |
372 | std::advance(it_2, 100); | |
373 | MirrorImageSiteStatuses mirror_image_site_statuses_1{it_1, it_2}; | |
374 | ||
375 | it_1 = it_2; | |
376 | std::advance(it_2, 100); | |
377 | MirrorImageSiteStatuses mirror_image_site_statuses_2{it_1, it_2}; | |
378 | ||
379 | Context* update_task = nullptr; | |
380 | fire_timer_event(&timer_event, &update_task); | |
381 | ||
382 | expect_mirror_status_update(mirror_image_site_statuses_1, "", 0); | |
383 | expect_mirror_status_update(mirror_image_site_statuses_2, "", 0); | |
384 | update_task->complete(0); | |
385 | ||
386 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
387 | *mock_mirror_status_watcher); | |
388 | } | |
389 | ||
390 | TEST_F(TestMockMirrorStatusUpdater, OverwriteStatus) { | |
391 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
392 | m_mock_threads, ""); | |
393 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
394 | new MockMirrorStatusWatcher(); | |
395 | ||
396 | InSequence seq; | |
397 | ||
398 | Context* timer_event = nullptr; | |
399 | init_mirror_status_updater(mock_mirror_status_updater, | |
400 | *mock_mirror_status_watcher, &timer_event); | |
401 | ||
402 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
403 | mock_mirror_status_updater.set_mirror_image_status( | |
404 | "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}, | |
405 | false); | |
406 | ||
407 | Context* update_task = nullptr; | |
408 | fire_timer_event(&timer_event, &update_task); | |
409 | ||
410 | expect_mirror_status_update( | |
411 | {{"1", cls::rbd::MirrorImageSiteStatus{ | |
412 | "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, | |
413 | "", 0); | |
414 | update_task->complete(0); | |
415 | ||
416 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
417 | *mock_mirror_status_watcher); | |
418 | } | |
419 | ||
a4b75251 TL |
420 | TEST_F(TestMockMirrorStatusUpdater, RemoveStatus) { |
421 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
422 | m_mock_threads, ""); | |
423 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
424 | new MockMirrorStatusWatcher(); | |
425 | ||
426 | InSequence seq; | |
427 | ||
428 | Context* timer_event = nullptr; | |
429 | init_mirror_status_updater(mock_mirror_status_updater, | |
430 | *mock_mirror_status_watcher, &timer_event); | |
431 | ||
432 | C_SaferCond ctx; | |
433 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
434 | expect_work_queue(false); | |
435 | mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx); | |
436 | ASSERT_EQ(0, ctx.wait()); | |
437 | ||
438 | Context* update_task = nullptr; | |
439 | fire_timer_event(&timer_event, &update_task); | |
440 | ||
441 | C_SaferCond remove_flush_ctx; | |
442 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
443 | .WillOnce(Invoke([this, &remove_flush_ctx](auto&&... args) { | |
444 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
445 | m_mock_local_io_ctx->aio_flush(); | |
446 | remove_flush_ctx.complete(r); | |
447 | return r; | |
448 | })); | |
449 | expect_mirror_status_remove("1", 0); | |
450 | update_task->complete(0); | |
451 | ASSERT_EQ(0, remove_flush_ctx.wait()); | |
452 | ||
453 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
454 | *mock_mirror_status_watcher); | |
455 | } | |
456 | ||
457 | TEST_F(TestMockMirrorStatusUpdater, OverwriteRemoveStatus) { | |
458 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
459 | m_mock_threads, ""); | |
460 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
461 | new MockMirrorStatusWatcher(); | |
462 | ||
463 | InSequence seq; | |
464 | ||
465 | Context* timer_event = nullptr; | |
466 | init_mirror_status_updater(mock_mirror_status_updater, | |
467 | *mock_mirror_status_watcher, &timer_event); | |
468 | ||
469 | C_SaferCond ctx; | |
470 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
471 | expect_work_queue(false); | |
472 | mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx); | |
473 | ASSERT_EQ(0, ctx.wait()); | |
474 | mock_mirror_status_updater.set_mirror_image_status( | |
475 | "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}, | |
476 | false); | |
477 | ||
478 | ||
479 | Context* update_task = nullptr; | |
480 | fire_timer_event(&timer_event, &update_task); | |
481 | ||
482 | expect_mirror_status_update( | |
483 | {{"1", cls::rbd::MirrorImageSiteStatus{ | |
484 | "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, | |
485 | "", 0); | |
486 | update_task->complete(0); | |
487 | ||
488 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
489 | *mock_mirror_status_watcher); | |
490 | } | |
491 | ||
9f95a23c TL |
492 | TEST_F(TestMockMirrorStatusUpdater, OverwriteStatusInFlight) { |
493 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
494 | m_mock_threads, ""); | |
495 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
496 | new MockMirrorStatusWatcher(); | |
497 | ||
498 | InSequence seq; | |
499 | ||
500 | Context* timer_event = nullptr; | |
501 | init_mirror_status_updater(mock_mirror_status_updater, | |
502 | *mock_mirror_status_watcher, &timer_event); | |
503 | ||
504 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
505 | ||
506 | Context* update_task = nullptr; | |
507 | fire_timer_event(&timer_event, &update_task); | |
508 | ||
509 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
510 | .WillOnce(Invoke([this, &mock_mirror_status_updater](auto&&... args) { | |
511 | mock_mirror_status_updater.set_mirror_image_status( | |
512 | "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, | |
513 | "description"}, | |
514 | true); | |
515 | ||
516 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
517 | m_mock_local_io_ctx->aio_flush(); | |
518 | return r; | |
519 | })); | |
520 | expect_mirror_status_update("1", cls::rbd::MirrorImageSiteStatus{}, 0); | |
521 | expect_work_queue(false); | |
522 | expect_mirror_status_update( | |
523 | {{"1", cls::rbd::MirrorImageSiteStatus{ | |
524 | "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, | |
525 | "", 0); | |
526 | ||
527 | update_task->complete(0); | |
528 | ||
529 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
530 | *mock_mirror_status_watcher); | |
531 | } | |
532 | ||
533 | TEST_F(TestMockMirrorStatusUpdater, ImmediateUpdate) { | |
534 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
535 | m_mock_threads, ""); | |
536 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
537 | new MockMirrorStatusWatcher(); | |
538 | ||
539 | InSequence seq; | |
540 | ||
541 | Context* timer_event = nullptr; | |
542 | init_mirror_status_updater(mock_mirror_status_updater, | |
543 | *mock_mirror_status_watcher, &timer_event); | |
544 | ||
545 | expect_work_queue(false); | |
546 | expect_mirror_status_update({{"1", cls::rbd::MirrorImageSiteStatus{}}}, | |
547 | "", 0); | |
548 | mock_mirror_status_updater.set_mirror_image_status("1", {}, true); | |
549 | ||
550 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
551 | *mock_mirror_status_watcher); | |
552 | } | |
553 | ||
a4b75251 TL |
554 | TEST_F(TestMockMirrorStatusUpdater, RemoveImmediateUpdate) { |
555 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
556 | m_mock_threads, ""); | |
557 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
558 | new MockMirrorStatusWatcher(); | |
559 | ||
560 | InSequence seq; | |
561 | ||
562 | Context* timer_event = nullptr; | |
563 | init_mirror_status_updater(mock_mirror_status_updater, | |
564 | *mock_mirror_status_watcher, &timer_event); | |
565 | ||
566 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
567 | ||
568 | C_SaferCond ctx; | |
20effc67 | 569 | expect_work_queue(false); |
a4b75251 | 570 | expect_mirror_status_removes({"1"}, 0); |
20effc67 | 571 | expect_work_queue(false); |
a4b75251 TL |
572 | mock_mirror_status_updater.remove_mirror_image_status("1", true, &ctx); |
573 | ASSERT_EQ(0, ctx.wait()); | |
574 | ||
575 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
576 | *mock_mirror_status_watcher); | |
577 | } | |
578 | ||
579 | TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshIdleStatus) { | |
9f95a23c TL |
580 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, |
581 | m_mock_threads, ""); | |
582 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
583 | new MockMirrorStatusWatcher(); | |
584 | ||
585 | InSequence seq; | |
586 | ||
587 | Context* timer_event = nullptr; | |
588 | init_mirror_status_updater(mock_mirror_status_updater, | |
589 | *mock_mirror_status_watcher, &timer_event); | |
590 | ||
591 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
592 | ||
593 | C_SaferCond ctx; | |
594 | expect_work_queue(true); | |
a4b75251 | 595 | mock_mirror_status_updater.remove_refresh_mirror_image_status("1", &ctx); |
9f95a23c TL |
596 | ASSERT_EQ(0, ctx.wait()); |
597 | ||
598 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
599 | *mock_mirror_status_watcher); | |
600 | } | |
601 | ||
a4b75251 | 602 | TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshInFlightStatus) { |
9f95a23c TL |
603 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, |
604 | m_mock_threads, ""); | |
605 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
606 | new MockMirrorStatusWatcher(); | |
607 | ||
608 | InSequence seq; | |
609 | ||
610 | Context* timer_event = nullptr; | |
611 | init_mirror_status_updater(mock_mirror_status_updater, | |
612 | *mock_mirror_status_watcher, &timer_event); | |
613 | ||
614 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
615 | ||
616 | Context* update_task = nullptr; | |
617 | fire_timer_event(&timer_event, &update_task); | |
618 | ||
619 | C_SaferCond on_removed; | |
620 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
621 | .WillOnce(Invoke( | |
622 | [this, &mock_mirror_status_updater, &on_removed](auto&&... args) { | |
a4b75251 TL |
623 | mock_mirror_status_updater.remove_refresh_mirror_image_status( |
624 | "1", &on_removed); | |
9f95a23c TL |
625 | |
626 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
627 | m_mock_local_io_ctx->aio_flush(); | |
628 | return r; | |
629 | })); | |
630 | update_task->complete(0); | |
631 | ASSERT_EQ(0, on_removed.wait()); | |
632 | ||
633 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
634 | *mock_mirror_status_watcher); | |
635 | } | |
636 | ||
637 | TEST_F(TestMockMirrorStatusUpdater, ShutDownWhileUpdating) { | |
638 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
639 | m_mock_threads, ""); | |
640 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
641 | new MockMirrorStatusWatcher(); | |
642 | ||
643 | InSequence seq; | |
644 | ||
645 | Context* timer_event = nullptr; | |
646 | init_mirror_status_updater(mock_mirror_status_updater, | |
647 | *mock_mirror_status_watcher, &timer_event); | |
648 | ||
649 | mock_mirror_status_updater.set_mirror_image_status("1", {}, false); | |
650 | ||
651 | Context* update_task = nullptr; | |
652 | fire_timer_event(&timer_event, &update_task); | |
653 | ||
654 | C_SaferCond on_shutdown; | |
655 | EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) | |
656 | .WillOnce(Invoke( | |
657 | [this, &mock_mirror_status_updater, &on_shutdown](auto&&... args) { | |
658 | mock_mirror_status_updater.shut_down(&on_shutdown); | |
659 | m_threads->work_queue->drain(); | |
660 | ||
661 | int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); | |
662 | m_mock_local_io_ctx->aio_flush(); | |
663 | return r; | |
664 | })); | |
665 | ||
666 | expect_timer_cancel_event(); | |
667 | expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, 0); | |
668 | ||
669 | update_task->complete(0); | |
670 | ASSERT_EQ(0, on_shutdown.wait()); | |
671 | } | |
672 | ||
673 | TEST_F(TestMockMirrorStatusUpdater, MirrorPeerSitePing) { | |
674 | MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, | |
675 | m_mock_threads, | |
676 | "mirror uuid"); | |
677 | MockMirrorStatusWatcher* mock_mirror_status_watcher = | |
678 | new MockMirrorStatusWatcher(); | |
679 | ||
680 | InSequence seq; | |
681 | ||
682 | Context* timer_event = nullptr; | |
683 | init_mirror_status_updater(mock_mirror_status_updater, | |
684 | *mock_mirror_status_watcher, &timer_event); | |
685 | ||
686 | MirrorImageSiteStatuses mirror_image_site_statuses; | |
687 | for (auto i = 0; i < 100; ++i) { | |
688 | auto pair = mirror_image_site_statuses.emplace( | |
689 | stringify(i), cls::rbd::MirrorImageSiteStatus{}); | |
690 | mock_mirror_status_updater.set_mirror_image_status(pair.first->first, | |
691 | pair.first->second, | |
692 | false); | |
693 | } | |
694 | ||
695 | Context* update_task = nullptr; | |
696 | fire_timer_event(&timer_event, &update_task); | |
697 | ||
698 | expect_mirror_status_update(mirror_image_site_statuses, "mirror uuid", 0); | |
699 | update_task->complete(0); | |
700 | ||
701 | shut_down_mirror_status_updater(mock_mirror_status_updater, | |
702 | *mock_mirror_status_watcher); | |
703 | } | |
704 | ||
705 | } // namespace mirror | |
706 | } // namespace rbd |