]>
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 "cls/journal/cls_journal_types.h" | |
7c673cae | 5 | #include "librbd/journal/Types.h" |
9f95a23c | 6 | #include "librbd/journal/TypeTraits.h" |
c07f9fc5 | 7 | #include "tools/rbd_mirror/ImageDeleter.h" |
7c673cae | 8 | #include "tools/rbd_mirror/ImageReplayer.h" |
31f18b77 | 9 | #include "tools/rbd_mirror/InstanceWatcher.h" |
9f95a23c | 10 | #include "tools/rbd_mirror/MirrorStatusUpdater.h" |
d2e6a577 | 11 | #include "tools/rbd_mirror/Threads.h" |
7c673cae | 12 | #include "tools/rbd_mirror/image_replayer/BootstrapRequest.h" |
9f95a23c TL |
13 | #include "tools/rbd_mirror/image_replayer/Replayer.h" |
14 | #include "tools/rbd_mirror/image_replayer/ReplayerListener.h" | |
15 | #include "tools/rbd_mirror/image_replayer/StateBuilder.h" | |
16 | #include "tools/rbd_mirror/image_replayer/Utils.h" | |
7c673cae | 17 | #include "test/rbd_mirror/test_mock_fixture.h" |
7c673cae | 18 | #include "test/librbd/mock/MockImageCtx.h" |
d2e6a577 FG |
19 | #include "test/rbd_mirror/mock/MockContextWQ.h" |
20 | #include "test/rbd_mirror/mock/MockSafeTimer.h" | |
7c673cae FG |
21 | |
22 | namespace librbd { | |
23 | ||
24 | namespace { | |
25 | ||
7c673cae FG |
26 | struct MockTestImageCtx : public MockImageCtx { |
27 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
28 | : librbd::MockImageCtx(image_ctx) { | |
29 | } | |
7c673cae FG |
30 | }; |
31 | ||
32 | } // anonymous namespace | |
33 | ||
7c673cae FG |
34 | } // namespace librbd |
35 | ||
36 | namespace rbd { | |
37 | namespace mirror { | |
38 | ||
11fdf7f2 TL |
39 | template <> |
40 | struct ImageDeleter<librbd::MockTestImageCtx> { | |
41 | static ImageDeleter* s_instance; | |
42 | ||
43 | static void trash_move(librados::IoCtx& local_io_ctx, | |
44 | const std::string& global_image_id, bool resync, | |
45 | MockContextWQ* work_queue, Context* on_finish) { | |
46 | ceph_assert(s_instance != nullptr); | |
47 | s_instance->trash_move(global_image_id, resync, on_finish); | |
48 | } | |
49 | ||
50 | MOCK_METHOD3(trash_move, void(const std::string&, bool, Context*)); | |
51 | ||
52 | ImageDeleter() { | |
53 | s_instance = this; | |
54 | } | |
55 | }; | |
56 | ||
57 | ImageDeleter<librbd::MockTestImageCtx>* ImageDeleter<librbd::MockTestImageCtx>::s_instance = nullptr; | |
58 | ||
9f95a23c TL |
59 | template <> |
60 | struct MirrorStatusUpdater<librbd::MockTestImageCtx> { | |
61 | ||
62 | MOCK_METHOD1(exists, bool(const std::string&)); | |
63 | MOCK_METHOD3(set_mirror_image_status, | |
64 | void(const std::string&, const cls::rbd::MirrorImageSiteStatus&, | |
65 | bool)); | |
a4b75251 TL |
66 | MOCK_METHOD2(remove_refresh_mirror_image_status, void(const std::string&, |
67 | Context*)); | |
68 | MOCK_METHOD3(remove_mirror_image_status, void(const std::string&, bool, | |
69 | Context*)); | |
9f95a23c TL |
70 | }; |
71 | ||
d2e6a577 FG |
72 | template <> |
73 | struct Threads<librbd::MockTestImageCtx> { | |
74 | MockSafeTimer *timer; | |
9f95a23c | 75 | ceph::mutex &timer_lock; |
d2e6a577 FG |
76 | |
77 | MockContextWQ *work_queue; | |
78 | ||
79 | Threads(Threads<librbd::ImageCtx> *threads) | |
80 | : timer(new MockSafeTimer()), | |
81 | timer_lock(threads->timer_lock), | |
82 | work_queue(new MockContextWQ()) { | |
83 | } | |
84 | ~Threads() { | |
85 | delete timer; | |
86 | delete work_queue; | |
87 | } | |
88 | }; | |
89 | ||
7c673cae | 90 | template<> |
31f18b77 | 91 | class InstanceWatcher<librbd::MockTestImageCtx> { |
7c673cae FG |
92 | }; |
93 | ||
94 | namespace image_replayer { | |
95 | ||
7c673cae FG |
96 | template<> |
97 | struct BootstrapRequest<librbd::MockTestImageCtx> { | |
98 | static BootstrapRequest* s_instance; | |
9f95a23c TL |
99 | |
100 | StateBuilder<librbd::MockTestImageCtx>** state_builder = nullptr; | |
7c673cae | 101 | bool *do_resync = nullptr; |
9f95a23c | 102 | Context *on_finish = nullptr; |
7c673cae | 103 | |
31f18b77 | 104 | static BootstrapRequest* create( |
11fdf7f2 | 105 | Threads<librbd::MockTestImageCtx>* threads, |
9f95a23c TL |
106 | librados::IoCtx &local_io_ctx, |
107 | librados::IoCtx& remote_io_ctx, | |
31f18b77 | 108 | rbd::mirror::InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher, |
11fdf7f2 | 109 | const std::string &global_image_id, |
d2e6a577 | 110 | const std::string &local_mirror_uuid, |
9f95a23c TL |
111 | const RemotePoolMeta& remote_pool_meta, |
112 | ::journal::CacheManagerHandler *cache_manager_handler, | |
113 | PoolMetaCache* pool_meta_cache, | |
114 | rbd::mirror::ProgressContext *progress_ctx, | |
115 | StateBuilder<librbd::MockTestImageCtx>** state_builder, | |
116 | bool *do_resync, Context *on_finish) { | |
11fdf7f2 | 117 | ceph_assert(s_instance != nullptr); |
9f95a23c | 118 | s_instance->state_builder = state_builder; |
7c673cae | 119 | s_instance->do_resync = do_resync; |
9f95a23c | 120 | s_instance->on_finish = on_finish; |
7c673cae FG |
121 | return s_instance; |
122 | } | |
123 | ||
124 | BootstrapRequest() { | |
11fdf7f2 | 125 | ceph_assert(s_instance == nullptr); |
7c673cae FG |
126 | s_instance = this; |
127 | } | |
128 | ||
129 | ~BootstrapRequest() { | |
11fdf7f2 | 130 | ceph_assert(s_instance == this); |
7c673cae FG |
131 | s_instance = nullptr; |
132 | } | |
133 | ||
134 | void put() { | |
135 | } | |
136 | ||
137 | void get() { | |
138 | } | |
139 | ||
9f95a23c TL |
140 | std::string get_local_image_name() const { |
141 | return "local image name"; | |
142 | } | |
143 | ||
c07f9fc5 FG |
144 | inline bool is_syncing() const { |
145 | return false; | |
146 | } | |
147 | ||
7c673cae FG |
148 | MOCK_METHOD0(send, void()); |
149 | MOCK_METHOD0(cancel, void()); | |
150 | }; | |
151 | ||
9f95a23c TL |
152 | struct MockReplayer : public Replayer { |
153 | image_replayer::ReplayerListener* replayer_listener; | |
7c673cae | 154 | |
9f95a23c | 155 | MOCK_METHOD0(destroy, void()); |
7c673cae | 156 | |
9f95a23c TL |
157 | MOCK_METHOD1(init, void(Context*)); |
158 | MOCK_METHOD1(shut_down, void(Context*)); | |
159 | MOCK_METHOD1(flush, void(Context*)); | |
7c673cae | 160 | |
9f95a23c | 161 | MOCK_METHOD2(get_replay_status, bool(std::string*, Context*)); |
7c673cae | 162 | |
9f95a23c TL |
163 | MOCK_CONST_METHOD0(is_replaying, bool()); |
164 | MOCK_CONST_METHOD0(is_resync_requested, bool()); | |
165 | MOCK_CONST_METHOD0(get_error_code, int()); | |
166 | MOCK_CONST_METHOD0(get_error_description, std::string()); | |
7c673cae FG |
167 | }; |
168 | ||
9f95a23c TL |
169 | template <> |
170 | struct StateBuilder<librbd::MockTestImageCtx> { | |
171 | static StateBuilder* s_instance; | |
7c673cae | 172 | |
9f95a23c TL |
173 | librbd::MockTestImageCtx* local_image_ctx = nullptr; |
174 | std::string local_image_id; | |
175 | std::string remote_image_id; | |
7c673cae | 176 | |
9f95a23c | 177 | void destroy() { |
7c673cae FG |
178 | } |
179 | ||
9f95a23c TL |
180 | MOCK_METHOD1(close, void(Context*)); |
181 | MOCK_METHOD5(create_replayer, Replayer*(Threads<librbd::MockTestImageCtx>*, | |
182 | InstanceWatcher<librbd::MockTestImageCtx>*, | |
183 | const std::string&, PoolMetaCache*, | |
184 | ReplayerListener*)); | |
7c673cae | 185 | |
9f95a23c | 186 | StateBuilder() { |
7c673cae FG |
187 | s_instance = this; |
188 | } | |
7c673cae FG |
189 | }; |
190 | ||
191 | BootstrapRequest<librbd::MockTestImageCtx>* BootstrapRequest<librbd::MockTestImageCtx>::s_instance = nullptr; | |
9f95a23c | 192 | StateBuilder<librbd::MockTestImageCtx>* StateBuilder<librbd::MockTestImageCtx>::s_instance = nullptr; |
7c673cae FG |
193 | |
194 | } // namespace image_replayer | |
195 | } // namespace mirror | |
196 | } // namespace rbd | |
197 | ||
198 | // template definitions | |
199 | #include "tools/rbd_mirror/ImageReplayer.cc" | |
7c673cae FG |
200 | |
201 | namespace rbd { | |
202 | namespace mirror { | |
203 | ||
9f95a23c TL |
204 | using ::testing::_; |
205 | using ::testing::AtLeast; | |
206 | using ::testing::DoAll; | |
207 | using ::testing::InSequence; | |
208 | using ::testing::Invoke; | |
209 | using ::testing::MatcherCast; | |
210 | using ::testing::Return; | |
211 | using ::testing::ReturnArg; | |
212 | using ::testing::SetArgPointee; | |
213 | using ::testing::WithArg; | |
214 | ||
7c673cae FG |
215 | class TestMockImageReplayer : public TestMockFixture { |
216 | public: | |
d2e6a577 | 217 | typedef Threads<librbd::MockTestImageCtx> MockThreads; |
c07f9fc5 | 218 | typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter; |
9f95a23c TL |
219 | typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater; |
220 | typedef image_replayer::BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest; | |
221 | typedef image_replayer::StateBuilder<librbd::MockTestImageCtx> MockStateBuilder; | |
222 | typedef image_replayer::MockReplayer MockReplayer; | |
7c673cae | 223 | typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer; |
31f18b77 | 224 | typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher; |
7c673cae FG |
225 | |
226 | void SetUp() override { | |
227 | TestMockFixture::SetUp(); | |
228 | ||
229 | librbd::RBD rbd; | |
230 | ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size)); | |
231 | ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx)); | |
7c673cae FG |
232 | } |
233 | ||
234 | void TearDown() override { | |
235 | delete m_image_replayer; | |
236 | ||
237 | TestMockFixture::TearDown(); | |
238 | } | |
239 | ||
240 | void create_local_image() { | |
241 | librbd::RBD rbd; | |
242 | ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size)); | |
243 | ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx)); | |
244 | } | |
245 | ||
d2e6a577 FG |
246 | void expect_work_queue_repeatedly(MockThreads &mock_threads) { |
247 | EXPECT_CALL(*mock_threads.work_queue, queue(_, _)) | |
248 | .WillRepeatedly(Invoke([this](Context *ctx, int r) { | |
249 | m_threads->work_queue->queue(ctx, r); | |
250 | })); | |
251 | } | |
252 | ||
253 | void expect_add_event_after_repeatedly(MockThreads &mock_threads) { | |
254 | EXPECT_CALL(*mock_threads.timer, add_event_after(_, _)) | |
255 | .WillRepeatedly( | |
3efd9988 FG |
256 | DoAll(Invoke([this](double seconds, Context *ctx) { |
257 | m_threads->timer->add_event_after(seconds, ctx); | |
258 | }), | |
259 | ReturnArg<1>())); | |
d2e6a577 FG |
260 | EXPECT_CALL(*mock_threads.timer, cancel_event(_)) |
261 | .WillRepeatedly( | |
262 | Invoke([this](Context *ctx) { | |
263 | return m_threads->timer->cancel_event(ctx); | |
264 | })); | |
265 | } | |
266 | ||
11fdf7f2 TL |
267 | void expect_trash_move(MockImageDeleter& mock_image_deleter, |
268 | const std::string& global_image_id, | |
269 | bool ignore_orphan, int r) { | |
d2e6a577 | 270 | EXPECT_CALL(mock_image_deleter, |
11fdf7f2 TL |
271 | trash_move(global_image_id, ignore_orphan, _)) |
272 | .WillOnce(WithArg<2>(Invoke([this, r](Context* ctx) { | |
d2e6a577 FG |
273 | m_threads->work_queue->queue(ctx, r); |
274 | }))); | |
275 | } | |
276 | ||
7c673cae FG |
277 | bufferlist encode_tag_data(const librbd::journal::TagData &tag_data) { |
278 | bufferlist bl; | |
11fdf7f2 | 279 | encode(tag_data, bl); |
7c673cae FG |
280 | return bl; |
281 | } | |
282 | ||
9f95a23c TL |
283 | void expect_send(MockBootstrapRequest& mock_bootstrap_request, |
284 | MockStateBuilder& mock_state_builder, | |
285 | librbd::MockTestImageCtx& mock_local_image_ctx, | |
a4b75251 | 286 | bool do_resync, bool set_local_image, int r) { |
7c673cae | 287 | EXPECT_CALL(mock_bootstrap_request, send()) |
9f95a23c | 288 | .WillOnce(Invoke([this, &mock_bootstrap_request, &mock_state_builder, |
a4b75251 TL |
289 | &mock_local_image_ctx, set_local_image, do_resync, |
290 | r]() { | |
9f95a23c TL |
291 | if (r == 0 || r == -ENOLINK) { |
292 | mock_state_builder.local_image_id = mock_local_image_ctx.id; | |
293 | mock_state_builder.remote_image_id = m_remote_image_ctx->id; | |
294 | *mock_bootstrap_request.state_builder = &mock_state_builder; | |
295 | } | |
7c673cae | 296 | if (r == 0) { |
9f95a23c | 297 | mock_state_builder.local_image_ctx = &mock_local_image_ctx; |
7c673cae FG |
298 | *mock_bootstrap_request.do_resync = do_resync; |
299 | } | |
a4b75251 | 300 | if (r < 0 && r != -ENOENT) { |
9f95a23c TL |
301 | mock_state_builder.remote_image_id = ""; |
302 | } | |
a4b75251 TL |
303 | if (r == -ENOENT) { |
304 | *mock_bootstrap_request.state_builder = &mock_state_builder; | |
305 | } | |
306 | if (set_local_image) { | |
307 | mock_state_builder.local_image_id = mock_local_image_ctx.id; | |
308 | } | |
7c673cae FG |
309 | mock_bootstrap_request.on_finish->complete(r); |
310 | })); | |
311 | } | |
312 | ||
9f95a23c TL |
313 | void expect_create_replayer(MockStateBuilder& mock_state_builder, |
314 | MockReplayer& mock_replayer) { | |
315 | EXPECT_CALL(mock_state_builder, create_replayer(_, _, _, _, _)) | |
316 | .WillOnce(WithArg<4>( | |
317 | Invoke([&mock_replayer] | |
318 | (image_replayer::ReplayerListener* replayer_listener) { | |
319 | mock_replayer.replayer_listener = replayer_listener; | |
320 | return &mock_replayer; | |
321 | }))); | |
7c673cae FG |
322 | } |
323 | ||
9f95a23c TL |
324 | void expect_close(MockStateBuilder& mock_state_builder, int r) { |
325 | EXPECT_CALL(mock_state_builder, close(_)) | |
326 | .WillOnce(Invoke([this, r](Context* ctx) { | |
327 | m_threads->work_queue->queue(ctx, r); | |
328 | })); | |
7c673cae FG |
329 | } |
330 | ||
9f95a23c TL |
331 | void expect_init(MockReplayer& mock_replayer, int r) { |
332 | EXPECT_CALL(mock_replayer, init(_)) | |
333 | .WillOnce(Invoke([this, r](Context* ctx) { | |
334 | m_threads->work_queue->queue(ctx, r); | |
335 | })); | |
7c673cae FG |
336 | } |
337 | ||
9f95a23c TL |
338 | void expect_shut_down(MockReplayer& mock_replayer, int r) { |
339 | EXPECT_CALL(mock_replayer, shut_down(_)) | |
340 | .WillOnce(Invoke([this, r](Context* ctx) { | |
341 | m_threads->work_queue->queue(ctx, r); | |
342 | })); | |
343 | EXPECT_CALL(mock_replayer, destroy()); | |
7c673cae FG |
344 | } |
345 | ||
9f95a23c TL |
346 | void expect_get_replay_status(MockReplayer& mock_replayer) { |
347 | EXPECT_CALL(mock_replayer, get_replay_status(_, _)) | |
348 | .WillRepeatedly(DoAll(WithArg<1>(CompleteContext(-EEXIST)), | |
349 | Return(true))); | |
7c673cae FG |
350 | } |
351 | ||
9f95a23c TL |
352 | void expect_set_mirror_image_status_repeatedly() { |
353 | EXPECT_CALL(m_local_status_updater, set_mirror_image_status(_, _, _)) | |
354 | .WillRepeatedly(Invoke([](auto, auto, auto){})); | |
355 | EXPECT_CALL(m_remote_status_updater, set_mirror_image_status(_, _, _)) | |
356 | .WillRepeatedly(Invoke([](auto, auto, auto){})); | |
7c673cae FG |
357 | } |
358 | ||
9f95a23c TL |
359 | void expect_mirror_image_status_exists(bool exists) { |
360 | EXPECT_CALL(m_local_status_updater, exists(_)) | |
361 | .WillOnce(Return(exists)); | |
362 | EXPECT_CALL(m_remote_status_updater, exists(_)) | |
363 | .WillOnce(Return(exists)); | |
7c673cae FG |
364 | } |
365 | ||
11fdf7f2 | 366 | void create_image_replayer(MockThreads &mock_threads) { |
c07f9fc5 | 367 | m_image_replayer = new MockImageReplayer( |
9f95a23c TL |
368 | m_local_io_ctx, "local_mirror_uuid", "global image id", |
369 | &mock_threads, &m_instance_watcher, &m_local_status_updater, nullptr, | |
370 | nullptr); | |
371 | m_image_replayer->add_peer({"peer_uuid", m_remote_io_ctx, | |
372 | {"remote mirror uuid", | |
373 | "remote mirror peer uuid"}, | |
374 | &m_remote_status_updater}); | |
375 | } | |
376 | ||
377 | void wait_for_stopped() { | |
378 | for (int i = 0; i < 10000; i++) { | |
379 | if (m_image_replayer->is_stopped()) { | |
380 | break; | |
381 | } | |
382 | usleep(1000); | |
383 | } | |
384 | ASSERT_TRUE(m_image_replayer->is_stopped()); | |
c07f9fc5 FG |
385 | } |
386 | ||
7c673cae FG |
387 | librbd::ImageCtx *m_remote_image_ctx; |
388 | librbd::ImageCtx *m_local_image_ctx = nullptr; | |
31f18b77 | 389 | MockInstanceWatcher m_instance_watcher; |
9f95a23c TL |
390 | MockMirrorStatusUpdater m_local_status_updater; |
391 | MockMirrorStatusUpdater m_remote_status_updater; | |
c07f9fc5 | 392 | MockImageReplayer *m_image_replayer = nullptr; |
7c673cae FG |
393 | }; |
394 | ||
395 | TEST_F(TestMockImageReplayer, StartStop) { | |
396 | // START | |
397 | ||
398 | create_local_image(); | |
399 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
400 | ||
d2e6a577 FG |
401 | MockThreads mock_threads(m_threads); |
402 | expect_work_queue_repeatedly(mock_threads); | |
403 | expect_add_event_after_repeatedly(mock_threads); | |
404 | ||
405 | MockImageDeleter mock_image_deleter; | |
9f95a23c | 406 | MockReplayer mock_replayer; |
7c673cae | 407 | |
9f95a23c TL |
408 | expect_get_replay_status(mock_replayer); |
409 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
410 | |
411 | InSequence seq; | |
9f95a23c TL |
412 | MockBootstrapRequest mock_bootstrap_request; |
413 | MockStateBuilder mock_state_builder; | |
414 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 415 | false, false, 0); |
7c673cae | 416 | |
9f95a23c TL |
417 | expect_create_replayer(mock_state_builder, mock_replayer); |
418 | expect_init(mock_replayer, 0); | |
7c673cae | 419 | |
11fdf7f2 | 420 | create_image_replayer(mock_threads); |
c07f9fc5 | 421 | |
7c673cae FG |
422 | C_SaferCond start_ctx; |
423 | m_image_replayer->start(&start_ctx); | |
424 | ASSERT_EQ(0, start_ctx.wait()); | |
c07f9fc5 FG |
425 | ASSERT_EQ(image_replayer::HEALTH_STATE_OK, |
426 | m_image_replayer->get_health_state()); | |
7c673cae FG |
427 | |
428 | // STOP | |
9f95a23c TL |
429 | expect_shut_down(mock_replayer, 0); |
430 | expect_close(mock_state_builder, 0); | |
431 | expect_mirror_image_status_exists(false); | |
7c673cae | 432 | |
7c673cae FG |
433 | C_SaferCond stop_ctx; |
434 | m_image_replayer->stop(&stop_ctx); | |
435 | ASSERT_EQ(0, stop_ctx.wait()); | |
c07f9fc5 FG |
436 | ASSERT_EQ(image_replayer::HEALTH_STATE_OK, |
437 | m_image_replayer->get_health_state()); | |
7c673cae FG |
438 | } |
439 | ||
440 | TEST_F(TestMockImageReplayer, LocalImagePrimary) { | |
441 | create_local_image(); | |
442 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
443 | ||
d2e6a577 FG |
444 | MockThreads mock_threads(m_threads); |
445 | expect_work_queue_repeatedly(mock_threads); | |
446 | expect_add_event_after_repeatedly(mock_threads); | |
447 | ||
448 | MockImageDeleter mock_image_deleter; | |
7c673cae | 449 | MockBootstrapRequest mock_bootstrap_request; |
7c673cae | 450 | |
9f95a23c | 451 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
452 | |
453 | InSequence seq; | |
7c673cae | 454 | |
9f95a23c TL |
455 | MockStateBuilder mock_state_builder; |
456 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 TL |
457 | false, false, -ENOMSG); |
458 | ||
459 | expect_mirror_image_status_exists(false); | |
460 | ||
461 | create_image_replayer(mock_threads); | |
462 | ||
463 | C_SaferCond start_ctx; | |
464 | m_image_replayer->start(&start_ctx); | |
465 | ASSERT_EQ(0, start_ctx.wait()); | |
466 | } | |
467 | ||
468 | TEST_F(TestMockImageReplayer, MetadataCleanup) { | |
469 | // START | |
470 | ||
471 | create_local_image(); | |
472 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
473 | ||
474 | MockThreads mock_threads(m_threads); | |
475 | expect_work_queue_repeatedly(mock_threads); | |
476 | expect_add_event_after_repeatedly(mock_threads); | |
477 | ||
478 | MockImageDeleter mock_image_deleter; | |
479 | MockBootstrapRequest mock_bootstrap_request; | |
480 | MockReplayer mock_replayer; | |
481 | ||
482 | expect_get_replay_status(mock_replayer); | |
483 | expect_set_mirror_image_status_repeatedly(); | |
484 | ||
485 | InSequence seq; | |
486 | ||
487 | MockStateBuilder mock_state_builder; | |
488 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
489 | false, true, -ENOLINK); | |
9f95a23c | 490 | |
a4b75251 TL |
491 | expect_close(mock_state_builder, 0); |
492 | expect_trash_move(mock_image_deleter, "global image id", false, 0); | |
9f95a23c | 493 | expect_mirror_image_status_exists(false); |
7c673cae | 494 | |
11fdf7f2 | 495 | create_image_replayer(mock_threads); |
c07f9fc5 | 496 | |
7c673cae FG |
497 | C_SaferCond start_ctx; |
498 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 499 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae FG |
500 | } |
501 | ||
9f95a23c | 502 | TEST_F(TestMockImageReplayer, BootstrapRemoteDeleted) { |
7c673cae FG |
503 | create_local_image(); |
504 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
505 | ||
d2e6a577 FG |
506 | MockThreads mock_threads(m_threads); |
507 | expect_work_queue_repeatedly(mock_threads); | |
508 | expect_add_event_after_repeatedly(mock_threads); | |
509 | ||
510 | MockImageDeleter mock_image_deleter; | |
7c673cae | 511 | |
9f95a23c | 512 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
513 | |
514 | InSequence seq; | |
d2e6a577 | 515 | |
9f95a23c TL |
516 | MockBootstrapRequest mock_bootstrap_request; |
517 | MockStateBuilder mock_state_builder; | |
518 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 519 | false, false, -ENOLINK); |
d2e6a577 | 520 | |
9f95a23c | 521 | expect_close(mock_state_builder, 0); |
d2e6a577 | 522 | |
11fdf7f2 | 523 | expect_trash_move(mock_image_deleter, "global image id", false, 0); |
9f95a23c | 524 | expect_mirror_image_status_exists(false); |
d2e6a577 | 525 | |
11fdf7f2 | 526 | create_image_replayer(mock_threads); |
d2e6a577 FG |
527 | |
528 | C_SaferCond start_ctx; | |
529 | m_image_replayer->start(&start_ctx); | |
530 | ASSERT_EQ(0, start_ctx.wait()); | |
531 | } | |
532 | ||
9f95a23c | 533 | TEST_F(TestMockImageReplayer, BootstrapResyncRequested) { |
d2e6a577 FG |
534 | create_local_image(); |
535 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
536 | ||
537 | MockThreads mock_threads(m_threads); | |
538 | expect_work_queue_repeatedly(mock_threads); | |
539 | expect_add_event_after_repeatedly(mock_threads); | |
540 | ||
541 | MockImageDeleter mock_image_deleter; | |
d2e6a577 | 542 | |
9f95a23c | 543 | expect_set_mirror_image_status_repeatedly(); |
d2e6a577 FG |
544 | |
545 | InSequence seq; | |
d2e6a577 | 546 | |
9f95a23c TL |
547 | MockBootstrapRequest mock_bootstrap_request; |
548 | MockStateBuilder mock_state_builder; | |
549 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 550 | true, false, 0); |
d2e6a577 | 551 | |
9f95a23c | 552 | expect_close(mock_state_builder, 0); |
d2e6a577 | 553 | |
9f95a23c TL |
554 | expect_trash_move(mock_image_deleter, "global image id", true, 0); |
555 | expect_mirror_image_status_exists(false); | |
d2e6a577 | 556 | |
11fdf7f2 | 557 | create_image_replayer(mock_threads); |
c07f9fc5 | 558 | |
7c673cae FG |
559 | C_SaferCond start_ctx; |
560 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 561 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae FG |
562 | } |
563 | ||
564 | TEST_F(TestMockImageReplayer, BootstrapError) { | |
7c673cae FG |
565 | create_local_image(); |
566 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
567 | ||
d2e6a577 FG |
568 | MockThreads mock_threads(m_threads); |
569 | expect_work_queue_repeatedly(mock_threads); | |
570 | expect_add_event_after_repeatedly(mock_threads); | |
571 | ||
572 | MockImageDeleter mock_image_deleter; | |
7c673cae | 573 | MockBootstrapRequest mock_bootstrap_request; |
7c673cae | 574 | |
9f95a23c | 575 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
576 | |
577 | InSequence seq; | |
9f95a23c TL |
578 | MockStateBuilder mock_state_builder; |
579 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 580 | false, false, -EINVAL); |
7c673cae | 581 | |
9f95a23c | 582 | expect_mirror_image_status_exists(false); |
7c673cae | 583 | |
11fdf7f2 | 584 | create_image_replayer(mock_threads); |
c07f9fc5 | 585 | |
7c673cae FG |
586 | C_SaferCond start_ctx; |
587 | m_image_replayer->start(&start_ctx); | |
588 | ASSERT_EQ(-EINVAL, start_ctx.wait()); | |
589 | } | |
590 | ||
9f95a23c | 591 | TEST_F(TestMockImageReplayer, BootstrapCancel) { |
11fdf7f2 TL |
592 | create_local_image(); |
593 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
594 | ||
11fdf7f2 TL |
595 | MockThreads mock_threads(m_threads); |
596 | expect_work_queue_repeatedly(mock_threads); | |
597 | expect_add_event_after_repeatedly(mock_threads); | |
598 | ||
599 | MockImageDeleter mock_image_deleter; | |
11fdf7f2 | 600 | |
9f95a23c | 601 | expect_set_mirror_image_status_repeatedly(); |
11fdf7f2 TL |
602 | |
603 | InSequence seq; | |
11fdf7f2 TL |
604 | |
605 | create_image_replayer(mock_threads); | |
606 | ||
9f95a23c TL |
607 | MockBootstrapRequest mock_bootstrap_request; |
608 | MockStateBuilder mock_state_builder; | |
609 | EXPECT_CALL(mock_bootstrap_request, send()) | |
610 | .WillOnce(Invoke([this, &mock_bootstrap_request]() { | |
20effc67 | 611 | m_image_replayer->stop(nullptr); |
9f95a23c TL |
612 | mock_bootstrap_request.on_finish->complete(-ECANCELED); |
613 | })); | |
614 | EXPECT_CALL(mock_bootstrap_request, cancel()); | |
615 | ||
616 | expect_mirror_image_status_exists(false); | |
617 | ||
11fdf7f2 TL |
618 | C_SaferCond start_ctx; |
619 | m_image_replayer->start(&start_ctx); | |
620 | ASSERT_EQ(-ECANCELED, start_ctx.wait()); | |
621 | } | |
622 | ||
9f95a23c | 623 | TEST_F(TestMockImageReplayer, StopError) { |
7c673cae FG |
624 | // START |
625 | ||
626 | create_local_image(); | |
627 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
628 | ||
d2e6a577 FG |
629 | MockThreads mock_threads(m_threads); |
630 | expect_work_queue_repeatedly(mock_threads); | |
631 | expect_add_event_after_repeatedly(mock_threads); | |
632 | ||
633 | MockImageDeleter mock_image_deleter; | |
7c673cae | 634 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 635 | MockReplayer mock_replayer; |
7c673cae | 636 | |
9f95a23c TL |
637 | expect_get_replay_status(mock_replayer); |
638 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
639 | |
640 | InSequence seq; | |
9f95a23c TL |
641 | MockStateBuilder mock_state_builder; |
642 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 643 | false, false, 0); |
7c673cae | 644 | |
9f95a23c TL |
645 | expect_create_replayer(mock_state_builder, mock_replayer); |
646 | expect_init(mock_replayer, 0); | |
7c673cae | 647 | |
11fdf7f2 | 648 | create_image_replayer(mock_threads); |
c07f9fc5 | 649 | |
7c673cae FG |
650 | C_SaferCond start_ctx; |
651 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 652 | ASSERT_EQ(0, start_ctx.wait()); |
7c673cae | 653 | |
9f95a23c TL |
654 | // STOP (errors are ignored) |
655 | ||
656 | expect_shut_down(mock_replayer, -EINVAL); | |
657 | expect_close(mock_state_builder, -EINVAL); | |
658 | expect_mirror_image_status_exists(false); | |
659 | ||
660 | C_SaferCond stop_ctx; | |
661 | m_image_replayer->stop(&stop_ctx); | |
662 | ASSERT_EQ(0, stop_ctx.wait()); | |
663 | } | |
7c673cae | 664 | |
9f95a23c | 665 | TEST_F(TestMockImageReplayer, ReplayerError) { |
7c673cae FG |
666 | create_local_image(); |
667 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
668 | ||
d2e6a577 FG |
669 | MockThreads mock_threads(m_threads); |
670 | expect_work_queue_repeatedly(mock_threads); | |
671 | expect_add_event_after_repeatedly(mock_threads); | |
672 | ||
673 | MockImageDeleter mock_image_deleter; | |
7c673cae | 674 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 675 | MockReplayer mock_replayer; |
7c673cae | 676 | |
9f95a23c | 677 | expect_set_mirror_image_status_repeatedly(); |
7c673cae FG |
678 | |
679 | InSequence seq; | |
9f95a23c TL |
680 | MockStateBuilder mock_state_builder; |
681 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 682 | false, false, 0); |
7c673cae | 683 | |
9f95a23c TL |
684 | expect_create_replayer(mock_state_builder, mock_replayer); |
685 | expect_init(mock_replayer, -EINVAL); | |
686 | EXPECT_CALL(mock_replayer, get_error_description()) | |
687 | .WillOnce(Return("FAIL")); | |
7c673cae | 688 | |
9f95a23c TL |
689 | EXPECT_CALL(mock_replayer, destroy()); |
690 | expect_close(mock_state_builder, -EINVAL); | |
7c673cae | 691 | |
9f95a23c | 692 | expect_mirror_image_status_exists(false); |
11fdf7f2 | 693 | create_image_replayer(mock_threads); |
c07f9fc5 | 694 | |
7c673cae FG |
695 | C_SaferCond start_ctx; |
696 | m_image_replayer->start(&start_ctx); | |
9f95a23c | 697 | ASSERT_EQ(-EINVAL, start_ctx.wait()); |
7c673cae FG |
698 | } |
699 | ||
9f95a23c | 700 | TEST_F(TestMockImageReplayer, ReplayerResync) { |
7c673cae | 701 | // START |
7c673cae FG |
702 | create_local_image(); |
703 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
704 | ||
d2e6a577 FG |
705 | MockThreads mock_threads(m_threads); |
706 | expect_work_queue_repeatedly(mock_threads); | |
707 | expect_add_event_after_repeatedly(mock_threads); | |
708 | ||
709 | MockImageDeleter mock_image_deleter; | |
7c673cae | 710 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 711 | MockReplayer mock_replayer; |
7c673cae | 712 | |
9f95a23c TL |
713 | expect_get_replay_status(mock_replayer); |
714 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
715 | |
716 | InSequence seq; | |
9f95a23c TL |
717 | MockStateBuilder mock_state_builder; |
718 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 719 | false, false, 0); |
7c673cae | 720 | |
9f95a23c TL |
721 | expect_create_replayer(mock_state_builder, mock_replayer); |
722 | expect_init(mock_replayer, 0); | |
7c673cae | 723 | |
11fdf7f2 | 724 | create_image_replayer(mock_threads); |
c07f9fc5 | 725 | |
7c673cae FG |
726 | C_SaferCond start_ctx; |
727 | m_image_replayer->start(&start_ctx); | |
728 | ASSERT_EQ(0, start_ctx.wait()); | |
729 | ||
9f95a23c TL |
730 | // NOTIFY |
731 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
732 | .WillOnce(Return(true)); | |
733 | expect_shut_down(mock_replayer, 0); | |
734 | expect_close(mock_state_builder, 0); | |
735 | expect_trash_move(mock_image_deleter, "global image id", true, 0); | |
736 | expect_mirror_image_status_exists(false); | |
737 | mock_replayer.replayer_listener->handle_notification(); | |
738 | ASSERT_FALSE(m_image_replayer->is_running()); | |
739 | ||
740 | wait_for_stopped(); | |
7c673cae FG |
741 | } |
742 | ||
9f95a23c | 743 | TEST_F(TestMockImageReplayer, ReplayerInterrupted) { |
7c673cae | 744 | // START |
7c673cae FG |
745 | create_local_image(); |
746 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
747 | ||
d2e6a577 FG |
748 | MockThreads mock_threads(m_threads); |
749 | expect_work_queue_repeatedly(mock_threads); | |
750 | expect_add_event_after_repeatedly(mock_threads); | |
751 | ||
752 | MockImageDeleter mock_image_deleter; | |
7c673cae | 753 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 754 | MockReplayer mock_replayer; |
7c673cae | 755 | |
9f95a23c TL |
756 | expect_get_replay_status(mock_replayer); |
757 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
758 | |
759 | InSequence seq; | |
9f95a23c TL |
760 | MockStateBuilder mock_state_builder; |
761 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 762 | false, false, 0); |
7c673cae | 763 | |
9f95a23c TL |
764 | expect_create_replayer(mock_state_builder, mock_replayer); |
765 | expect_init(mock_replayer, 0); | |
7c673cae | 766 | |
11fdf7f2 | 767 | create_image_replayer(mock_threads); |
c07f9fc5 | 768 | |
7c673cae FG |
769 | C_SaferCond start_ctx; |
770 | m_image_replayer->start(&start_ctx); | |
771 | ASSERT_EQ(0, start_ctx.wait()); | |
772 | ||
9f95a23c TL |
773 | // NOTIFY |
774 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
775 | .WillOnce(Return(false)); | |
776 | EXPECT_CALL(mock_replayer, is_replaying()) | |
777 | .WillOnce(Return(false)); | |
778 | EXPECT_CALL(mock_replayer, get_error_code()) | |
7c673cae | 779 | .WillOnce(Return(-EINVAL)); |
9f95a23c TL |
780 | EXPECT_CALL(mock_replayer, get_error_description()) |
781 | .WillOnce(Return("INVALID")); | |
782 | expect_shut_down(mock_replayer, 0); | |
783 | expect_close(mock_state_builder, 0); | |
784 | expect_mirror_image_status_exists(false); | |
785 | mock_replayer.replayer_listener->handle_notification(); | |
786 | ASSERT_FALSE(m_image_replayer->is_running()); | |
787 | ||
788 | wait_for_stopped(); | |
7c673cae FG |
789 | } |
790 | ||
9f95a23c | 791 | TEST_F(TestMockImageReplayer, ReplayerRenamed) { |
7c673cae | 792 | // START |
7c673cae FG |
793 | create_local_image(); |
794 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
795 | ||
d2e6a577 FG |
796 | MockThreads mock_threads(m_threads); |
797 | expect_work_queue_repeatedly(mock_threads); | |
798 | expect_add_event_after_repeatedly(mock_threads); | |
799 | ||
800 | MockImageDeleter mock_image_deleter; | |
7c673cae | 801 | MockBootstrapRequest mock_bootstrap_request; |
9f95a23c | 802 | MockReplayer mock_replayer; |
7c673cae | 803 | |
9f95a23c TL |
804 | expect_get_replay_status(mock_replayer); |
805 | expect_set_mirror_image_status_repeatedly(); | |
7c673cae FG |
806 | |
807 | InSequence seq; | |
9f95a23c TL |
808 | MockStateBuilder mock_state_builder; |
809 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
a4b75251 | 810 | false, false, 0); |
7c673cae | 811 | |
9f95a23c TL |
812 | expect_create_replayer(mock_state_builder, mock_replayer); |
813 | expect_init(mock_replayer, 0); | |
7c673cae | 814 | |
11fdf7f2 | 815 | create_image_replayer(mock_threads); |
c07f9fc5 | 816 | |
7c673cae FG |
817 | C_SaferCond start_ctx; |
818 | m_image_replayer->start(&start_ctx); | |
819 | ASSERT_EQ(0, start_ctx.wait()); | |
820 | ||
9f95a23c TL |
821 | // NOTIFY |
822 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
823 | .WillOnce(Return(false)); | |
824 | EXPECT_CALL(mock_replayer, is_replaying()) | |
825 | .WillOnce(Return(true)); | |
826 | mock_local_image_ctx.name = "NEW NAME"; | |
827 | mock_replayer.replayer_listener->handle_notification(); | |
7c673cae FG |
828 | |
829 | // STOP | |
9f95a23c TL |
830 | expect_shut_down(mock_replayer, 0); |
831 | expect_close(mock_state_builder, 0); | |
832 | expect_mirror_image_status_exists(false); | |
7c673cae | 833 | |
7c673cae FG |
834 | C_SaferCond stop_ctx; |
835 | m_image_replayer->stop(&stop_ctx); | |
836 | ASSERT_EQ(0, stop_ctx.wait()); | |
7c673cae | 837 | |
9f95a23c TL |
838 | auto image_spec = image_replayer::util::compute_image_spec( |
839 | m_local_io_ctx, "NEW NAME"); | |
840 | ASSERT_EQ(image_spec, m_image_replayer->get_name()); | |
841 | } | |
d2e6a577 | 842 | |
20effc67 TL |
843 | TEST_F(TestMockImageReplayer, StopJoinInterruptedReplayer) { |
844 | // START | |
845 | create_local_image(); | |
846 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
847 | ||
848 | MockThreads mock_threads(m_threads); | |
849 | expect_work_queue_repeatedly(mock_threads); | |
850 | expect_add_event_after_repeatedly(mock_threads); | |
851 | ||
852 | MockReplayer mock_replayer; | |
853 | expect_get_replay_status(mock_replayer); | |
854 | expect_set_mirror_image_status_repeatedly(); | |
855 | ||
856 | InSequence seq; | |
857 | MockBootstrapRequest mock_bootstrap_request; | |
858 | MockStateBuilder mock_state_builder; | |
859 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
860 | false, false, 0); | |
861 | ||
862 | expect_create_replayer(mock_state_builder, mock_replayer); | |
863 | expect_init(mock_replayer, 0); | |
864 | ||
865 | create_image_replayer(mock_threads); | |
866 | ||
867 | C_SaferCond start_ctx; | |
868 | m_image_replayer->start(&start_ctx); | |
869 | ASSERT_EQ(0, start_ctx.wait()); | |
870 | ||
871 | // NOTIFY | |
872 | EXPECT_CALL(mock_replayer, is_resync_requested()) | |
873 | .WillOnce(Return(false)); | |
874 | EXPECT_CALL(mock_replayer, is_replaying()) | |
875 | .WillOnce(Return(false)); | |
876 | EXPECT_CALL(mock_replayer, get_error_code()) | |
877 | .WillOnce(Return(-EINVAL)); | |
878 | EXPECT_CALL(mock_replayer, get_error_description()) | |
879 | .WillOnce(Return("INVALID")); | |
880 | const double DELAY = 10; | |
881 | EXPECT_CALL(mock_replayer, shut_down(_)) | |
882 | .WillOnce(Invoke([this, DELAY](Context* ctx) { | |
883 | m_threads->timer->add_event_after(DELAY, ctx); | |
884 | })); | |
885 | EXPECT_CALL(mock_replayer, destroy()); | |
886 | expect_close(mock_state_builder, 0); | |
887 | expect_mirror_image_status_exists(false); | |
888 | ||
889 | mock_replayer.replayer_listener->handle_notification(); | |
890 | ASSERT_FALSE(m_image_replayer->is_running()); | |
891 | ||
892 | C_SaferCond stop_ctx; | |
893 | m_image_replayer->stop(&stop_ctx); | |
894 | ASSERT_EQ(ETIMEDOUT, stop_ctx.wait_for(DELAY * 3 / 4)); | |
895 | ASSERT_EQ(0, stop_ctx.wait_for(DELAY)); | |
896 | } | |
897 | ||
898 | TEST_F(TestMockImageReplayer, StopJoinRequestedStop) { | |
899 | // START | |
900 | create_local_image(); | |
901 | librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx); | |
902 | ||
903 | MockThreads mock_threads(m_threads); | |
904 | expect_work_queue_repeatedly(mock_threads); | |
905 | expect_add_event_after_repeatedly(mock_threads); | |
906 | ||
907 | MockReplayer mock_replayer; | |
908 | expect_get_replay_status(mock_replayer); | |
909 | expect_set_mirror_image_status_repeatedly(); | |
910 | ||
911 | InSequence seq; | |
912 | MockBootstrapRequest mock_bootstrap_request; | |
913 | MockStateBuilder mock_state_builder; | |
914 | expect_send(mock_bootstrap_request, mock_state_builder, mock_local_image_ctx, | |
915 | false, false, 0); | |
916 | ||
917 | expect_create_replayer(mock_state_builder, mock_replayer); | |
918 | expect_init(mock_replayer, 0); | |
919 | ||
920 | create_image_replayer(mock_threads); | |
921 | ||
922 | C_SaferCond start_ctx; | |
923 | m_image_replayer->start(&start_ctx); | |
924 | ASSERT_EQ(0, start_ctx.wait()); | |
925 | ||
926 | // STOP | |
927 | const double DELAY = 10; | |
928 | EXPECT_CALL(mock_replayer, shut_down(_)) | |
929 | .WillOnce(Invoke([this, DELAY](Context* ctx) { | |
930 | m_threads->timer->add_event_after(DELAY, ctx); | |
931 | })); | |
932 | EXPECT_CALL(mock_replayer, destroy()); | |
933 | expect_close(mock_state_builder, 0); | |
934 | expect_mirror_image_status_exists(false); | |
935 | ||
936 | C_SaferCond stop_ctx1; | |
937 | m_image_replayer->stop(&stop_ctx1); | |
938 | ||
939 | C_SaferCond stop_ctx2; | |
940 | m_image_replayer->stop(&stop_ctx2); | |
941 | ASSERT_EQ(ETIMEDOUT, stop_ctx2.wait_for(DELAY * 3 / 4)); | |
942 | ASSERT_EQ(0, stop_ctx2.wait_for(DELAY)); | |
943 | ||
944 | ASSERT_EQ(0, stop_ctx1.wait_for(0)); | |
945 | } | |
946 | ||
7c673cae FG |
947 | } // namespace mirror |
948 | } // namespace rbd |