Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / rbd_mirror / test_mock_ImageSync.cc
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/rbd/librbd.hpp"
6 #include "librbd/journal/Types.h"
7 #include "librbd/journal/TypeTraits.h"
8 #include "test/journal/mock/MockJournaler.h"
9 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
10 #include "test/librbd/mock/MockImageCtx.h"
11 #include "test/librbd/mock/MockObjectMap.h"
12 #include "tools/rbd_mirror/ImageSync.h"
13 #include "tools/rbd_mirror/Threads.h"
14 #include "tools/rbd_mirror/image_sync/ImageCopyRequest.h"
15 #include "tools/rbd_mirror/image_sync/SnapshotCopyRequest.h"
16 #include "tools/rbd_mirror/image_sync/SyncPointCreateRequest.h"
17 #include "tools/rbd_mirror/image_sync/SyncPointPruneRequest.h"
18
19 namespace librbd {
20
21 namespace {
22
23 struct MockTestImageCtx : public librbd::MockImageCtx {
24   MockTestImageCtx(librbd::ImageCtx &image_ctx)
25     : librbd::MockImageCtx(image_ctx) {
26   }
27 };
28
29 } // anonymous namespace
30
31 namespace journal {
32
33 template <>
34 struct TypeTraits<librbd::MockTestImageCtx> {
35   typedef ::journal::MockJournaler Journaler;
36 };
37
38 } // namespace journal
39 } // namespace librbd
40
41 // template definitions
42 template class rbd::mirror::ImageSync<librbd::MockTestImageCtx>;
43 #include "tools/rbd_mirror/ImageSync.cc"
44
45 namespace rbd {
46 namespace mirror {
47
48 template<>
49 struct InstanceWatcher<librbd::MockTestImageCtx> {
50   MOCK_METHOD2(notify_sync_request, void(const std::string, Context *));
51   MOCK_METHOD1(cancel_sync_request, bool(const std::string &));
52   MOCK_METHOD1(notify_sync_complete, void(const std::string &));
53 };
54
55 namespace image_sync {
56
57 template <>
58 class ImageCopyRequest<librbd::MockTestImageCtx> {
59 public:
60   static ImageCopyRequest* s_instance;
61   Context *on_finish;
62
63   static ImageCopyRequest* create(librbd::MockTestImageCtx *local_image_ctx,
64                                   librbd::MockTestImageCtx *remote_image_ctx,
65                                   SafeTimer *timer, Mutex *timer_lock,
66                                   journal::MockJournaler *journaler,
67                                   librbd::journal::MirrorPeerClientMeta *client_meta,
68                                   librbd::journal::MirrorPeerSyncPoint *sync_point,
69                                   Context *on_finish,
70                                   rbd::mirror::ProgressContext *progress_ctx = nullptr) {
71     assert(s_instance != nullptr);
72     s_instance->on_finish = on_finish;
73     return s_instance;
74   }
75
76   ImageCopyRequest() {
77     s_instance = this;
78   }
79
80   void put() {
81   }
82
83   void get() {
84   }
85
86   MOCK_METHOD0(cancel, void());
87   MOCK_METHOD0(send, void());
88 };
89
90 template <>
91 class SnapshotCopyRequest<librbd::MockTestImageCtx> {
92 public:
93   static SnapshotCopyRequest* s_instance;
94   Context *on_finish;
95
96   static SnapshotCopyRequest* create(librbd::MockTestImageCtx *local_image_ctx,
97                                      librbd::MockTestImageCtx *remote_image_ctx,
98                                      SnapshotCopyRequest<librbd::ImageCtx>::SnapMap *snap_map,
99                                      journal::MockJournaler *journaler,
100                                      librbd::journal::MirrorPeerClientMeta *client_meta,
101                                      ContextWQ *work_queue,
102                                      Context *on_finish) {
103     assert(s_instance != nullptr);
104     s_instance->on_finish = on_finish;
105     return s_instance;
106   }
107
108   SnapshotCopyRequest() {
109     s_instance = this;
110   }
111
112   void put() {
113   }
114
115   void get() {
116   }
117
118   MOCK_METHOD0(send, void());
119   MOCK_METHOD0(cancel, void());
120 };
121
122 template <>
123 class SyncPointCreateRequest<librbd::MockTestImageCtx> {
124 public:
125   static SyncPointCreateRequest *s_instance;
126   Context *on_finish;
127
128   static SyncPointCreateRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
129                                         const std::string &mirror_uuid,
130                                         journal::MockJournaler *journaler,
131                                         librbd::journal::MirrorPeerClientMeta *client_meta,
132                                         Context *on_finish) {
133     assert(s_instance != nullptr);
134     s_instance->on_finish = on_finish;
135     return s_instance;
136   }
137
138   SyncPointCreateRequest() {
139     s_instance = this;
140   }
141   MOCK_METHOD0(send, void());
142 };
143
144 template <>
145 class SyncPointPruneRequest<librbd::MockTestImageCtx> {
146 public:
147   static SyncPointPruneRequest *s_instance;
148   Context *on_finish;
149   bool sync_complete;
150
151   static SyncPointPruneRequest* create(librbd::MockTestImageCtx *remote_image_ctx,
152                                        bool sync_complete,
153                                        journal::MockJournaler *journaler,
154                                        librbd::journal::MirrorPeerClientMeta *client_meta,
155                                        Context *on_finish) {
156     assert(s_instance != nullptr);
157     s_instance->on_finish = on_finish;
158     s_instance->sync_complete = sync_complete;
159     return s_instance;
160   }
161
162   SyncPointPruneRequest() {
163     s_instance = this;
164   }
165   MOCK_METHOD0(send, void());
166 };
167
168 ImageCopyRequest<librbd::MockTestImageCtx>* ImageCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
169 SnapshotCopyRequest<librbd::MockTestImageCtx>* SnapshotCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
170 SyncPointCreateRequest<librbd::MockTestImageCtx>* SyncPointCreateRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
171 SyncPointPruneRequest<librbd::MockTestImageCtx>* SyncPointPruneRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
172
173 } // namespace image_sync
174
175 using ::testing::_;
176 using ::testing::InSequence;
177 using ::testing::Invoke;
178 using ::testing::Return;
179 using ::testing::ReturnNew;
180 using ::testing::WithArg;
181 using ::testing::InvokeWithoutArgs;
182
183 class TestMockImageSync : public TestMockFixture {
184 public:
185   typedef ImageSync<librbd::MockTestImageCtx> MockImageSync;
186   typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
187   typedef image_sync::ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
188   typedef image_sync::SnapshotCopyRequest<librbd::MockTestImageCtx> MockSnapshotCopyRequest;
189   typedef image_sync::SyncPointCreateRequest<librbd::MockTestImageCtx> MockSyncPointCreateRequest;
190   typedef image_sync::SyncPointPruneRequest<librbd::MockTestImageCtx> MockSyncPointPruneRequest;
191
192   void SetUp() override {
193     TestMockFixture::SetUp();
194
195     librbd::RBD rbd;
196     ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
197     ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
198
199     ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
200     ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
201   }
202
203   void expect_start_op(librbd::MockExclusiveLock &mock_exclusive_lock) {
204     EXPECT_CALL(mock_exclusive_lock, start_op()).WillOnce(
205       ReturnNew<FunctionContext>([](int) {}));
206   }
207
208   void expect_notify_sync_request(MockInstanceWatcher &mock_instance_watcher,
209                                   const std::string &sync_id, int r) {
210     EXPECT_CALL(mock_instance_watcher, notify_sync_request(sync_id, _))
211       .WillOnce(Invoke([this, r](const std::string &, Context *on_sync_start) {
212             m_threads->work_queue->queue(on_sync_start, r);
213           }));
214   }
215
216   void expect_cancel_sync_request(MockInstanceWatcher &mock_instance_watcher,
217                                   const std::string &sync_id, bool canceled) {
218     EXPECT_CALL(mock_instance_watcher, cancel_sync_request(sync_id))
219       .WillOnce(Return(canceled));
220   }
221
222   void expect_notify_sync_complete(MockInstanceWatcher &mock_instance_watcher,
223                                    const std::string &sync_id) {
224     EXPECT_CALL(mock_instance_watcher, notify_sync_complete(sync_id));
225   }
226
227   void expect_create_sync_point(librbd::MockTestImageCtx &mock_local_image_ctx,
228                                 MockSyncPointCreateRequest &mock_sync_point_create_request,
229                                 int r) {
230     EXPECT_CALL(mock_sync_point_create_request, send())
231       .WillOnce(Invoke([this, &mock_local_image_ctx, &mock_sync_point_create_request, r]() {
232           if (r == 0) {
233             mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(),
234                                            "snap1"}] = 123;
235             m_client_meta.sync_points.emplace_back(cls::rbd::UserSnapshotNamespace(),
236                                                    "snap1",
237                                                    boost::none);
238           }
239           m_threads->work_queue->queue(mock_sync_point_create_request.on_finish, r);
240         }));
241   }
242
243   void expect_copy_snapshots(MockSnapshotCopyRequest &mock_snapshot_copy_request, int r) {
244     EXPECT_CALL(mock_snapshot_copy_request, send())
245       .WillOnce(Invoke([this, &mock_snapshot_copy_request, r]() {
246           m_threads->work_queue->queue(mock_snapshot_copy_request.on_finish, r);
247         }));
248   }
249
250   void expect_copy_image(MockImageCopyRequest &mock_image_copy_request, int r) {
251     EXPECT_CALL(mock_image_copy_request, send())
252       .WillOnce(Invoke([this, &mock_image_copy_request, r]() {
253           m_threads->work_queue->queue(mock_image_copy_request.on_finish, r);
254         }));
255   }
256
257   void expect_rollback_object_map(librbd::MockObjectMap &mock_object_map, int r) {
258     if ((m_local_image_ctx->features & RBD_FEATURE_OBJECT_MAP) != 0) {
259       EXPECT_CALL(mock_object_map, rollback(_, _))
260         .WillOnce(WithArg<1>(Invoke([this, r](Context *ctx) {
261             m_threads->work_queue->queue(ctx, r);
262           })));
263     }
264   }
265
266   void expect_create_object_map(librbd::MockTestImageCtx &mock_image_ctx,
267                                 librbd::MockObjectMap *mock_object_map) {
268     EXPECT_CALL(mock_image_ctx, create_object_map(CEPH_NOSNAP))
269       .WillOnce(Return(mock_object_map));
270   }
271
272   void expect_open_object_map(librbd::MockTestImageCtx &mock_image_ctx,
273                               librbd::MockObjectMap &mock_object_map) {
274     EXPECT_CALL(mock_object_map, open(_))
275       .WillOnce(Invoke([this](Context *ctx) {
276           m_threads->work_queue->queue(ctx, 0);
277         }));
278   }
279
280   void expect_prune_sync_point(MockSyncPointPruneRequest &mock_sync_point_prune_request,
281                                bool sync_complete, int r) {
282     EXPECT_CALL(mock_sync_point_prune_request, send())
283       .WillOnce(Invoke([this, &mock_sync_point_prune_request, sync_complete, r]() {
284           ASSERT_EQ(sync_complete, mock_sync_point_prune_request.sync_complete);
285           if (r == 0 && !m_client_meta.sync_points.empty()) {
286             if (sync_complete) {
287               m_client_meta.sync_points.pop_front();
288             } else {
289               while (m_client_meta.sync_points.size() > 1) {
290                 m_client_meta.sync_points.pop_back();
291               }
292             }
293           }
294           m_threads->work_queue->queue(mock_sync_point_prune_request.on_finish, r);
295         }));
296   }
297
298   MockImageSync *create_request(librbd::MockTestImageCtx &mock_remote_image_ctx,
299                                 librbd::MockTestImageCtx &mock_local_image_ctx,
300                                 journal::MockJournaler &mock_journaler,
301                                 MockInstanceWatcher &mock_instance_watcher,
302                                 Context *ctx) {
303     return new MockImageSync(&mock_local_image_ctx, &mock_remote_image_ctx,
304                              m_threads->timer, &m_threads->timer_lock,
305                              "mirror-uuid", &mock_journaler, &m_client_meta,
306                              m_threads->work_queue, &mock_instance_watcher,
307                              ctx);
308   }
309
310   librbd::ImageCtx *m_remote_image_ctx;
311   librbd::ImageCtx *m_local_image_ctx;
312   librbd::journal::MirrorPeerClientMeta m_client_meta;
313 };
314
315 TEST_F(TestMockImageSync, SimpleSync) {
316   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
317   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
318   journal::MockJournaler mock_journaler;
319   MockInstanceWatcher mock_instance_watcher;
320   MockImageCopyRequest mock_image_copy_request;
321   MockSnapshotCopyRequest mock_snapshot_copy_request;
322   MockSyncPointCreateRequest mock_sync_point_create_request;
323   MockSyncPointPruneRequest mock_sync_point_prune_request;
324
325   librbd::MockExclusiveLock mock_exclusive_lock;
326   mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
327
328   librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
329   mock_local_image_ctx.object_map = mock_object_map;
330   expect_test_features(mock_local_image_ctx);
331
332   InSequence seq;
333   expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
334   expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
335   expect_copy_snapshots(mock_snapshot_copy_request, 0);
336   expect_copy_image(mock_image_copy_request, 0);
337   expect_start_op(mock_exclusive_lock);
338   expect_rollback_object_map(*mock_object_map, 0);
339   expect_create_object_map(mock_local_image_ctx, mock_object_map);
340   expect_open_object_map(mock_local_image_ctx, *mock_object_map);
341   expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
342   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
343
344   C_SaferCond ctx;
345   MockImageSync *request = create_request(mock_remote_image_ctx,
346                                           mock_local_image_ctx, mock_journaler,
347                                           mock_instance_watcher, &ctx);
348   request->send();
349   ASSERT_EQ(0, ctx.wait());
350 }
351
352 TEST_F(TestMockImageSync, RestartSync) {
353   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
354   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
355   journal::MockJournaler mock_journaler;
356   MockInstanceWatcher mock_instance_watcher;
357   MockImageCopyRequest mock_image_copy_request;
358   MockSnapshotCopyRequest mock_snapshot_copy_request;
359   MockSyncPointCreateRequest mock_sync_point_create_request;
360   MockSyncPointPruneRequest mock_sync_point_prune_request;
361
362   m_client_meta.sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", boost::none},
363                                {cls::rbd::UserSnapshotNamespace(), "snap2", "snap1", boost::none}};
364   mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap1"}] = 123;
365   mock_local_image_ctx.snap_ids[{cls::rbd::UserSnapshotNamespace(), "snap2"}] = 234;
366
367   librbd::MockExclusiveLock mock_exclusive_lock;
368   mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
369
370   librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
371   mock_local_image_ctx.object_map = mock_object_map;
372   expect_test_features(mock_local_image_ctx);
373
374   InSequence seq;
375   expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
376   expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
377   expect_copy_snapshots(mock_snapshot_copy_request, 0);
378   expect_copy_image(mock_image_copy_request, 0);
379   expect_start_op(mock_exclusive_lock);
380   expect_rollback_object_map(*mock_object_map, 0);
381   expect_create_object_map(mock_local_image_ctx, mock_object_map);
382   expect_open_object_map(mock_local_image_ctx, *mock_object_map);
383   expect_prune_sync_point(mock_sync_point_prune_request, true, 0);
384   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
385
386   C_SaferCond ctx;
387   MockImageSync *request = create_request(mock_remote_image_ctx,
388                                           mock_local_image_ctx, mock_journaler,
389                                           mock_instance_watcher, &ctx);
390   request->send();
391   ASSERT_EQ(0, ctx.wait());
392 }
393
394 TEST_F(TestMockImageSync, CancelNotifySyncRequest) {
395   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
396   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
397   journal::MockJournaler mock_journaler;
398   MockInstanceWatcher mock_instance_watcher;
399
400   InSequence seq;
401   Context *on_sync_start = nullptr;
402   C_SaferCond notify_sync_ctx;
403   EXPECT_CALL(mock_instance_watcher,
404               notify_sync_request(mock_local_image_ctx.id, _))
405     .WillOnce(Invoke([this, &on_sync_start, &notify_sync_ctx](
406                          const std::string &, Context *ctx) {
407                        on_sync_start = ctx;
408                        notify_sync_ctx.complete(0);
409                      }));
410   EXPECT_CALL(mock_instance_watcher,
411               cancel_sync_request(mock_local_image_ctx.id))
412     .WillOnce(Invoke([this, &on_sync_start](const std::string &) {
413           EXPECT_NE(nullptr, on_sync_start);
414           on_sync_start->complete(-ECANCELED);
415           return true;
416         }));
417
418   C_SaferCond ctx;
419   MockImageSync *request = create_request(mock_remote_image_ctx,
420                                           mock_local_image_ctx, mock_journaler,
421                                           mock_instance_watcher, &ctx);
422   request->get();
423   request->send();
424
425   // cancel the notify sync request once it starts
426   ASSERT_EQ(0, notify_sync_ctx.wait());
427   request->cancel();
428   request->put();
429
430   ASSERT_EQ(-ECANCELED, ctx.wait());
431 }
432
433 TEST_F(TestMockImageSync, CancelImageCopy) {
434   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
435   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
436   journal::MockJournaler mock_journaler;
437   MockInstanceWatcher mock_instance_watcher;
438   MockImageCopyRequest mock_image_copy_request;
439   MockSnapshotCopyRequest mock_snapshot_copy_request;
440   MockSyncPointCreateRequest mock_sync_point_create_request;
441   MockSyncPointPruneRequest mock_sync_point_prune_request;
442
443   librbd::MockExclusiveLock mock_exclusive_lock;
444   mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
445
446   m_client_meta.sync_points = {{cls::rbd::UserSnapshotNamespace(), "snap1", boost::none}};
447
448   InSequence seq;
449   expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
450   expect_prune_sync_point(mock_sync_point_prune_request, false, 0);
451   expect_copy_snapshots(mock_snapshot_copy_request, 0);
452
453   C_SaferCond image_copy_ctx;
454   EXPECT_CALL(mock_image_copy_request, send())
455     .WillOnce(Invoke([&image_copy_ctx]() {
456         image_copy_ctx.complete(0);
457       }));
458   expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
459                              false);
460   EXPECT_CALL(mock_image_copy_request, cancel());
461   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
462
463   C_SaferCond ctx;
464   MockImageSync *request = create_request(mock_remote_image_ctx,
465                                           mock_local_image_ctx, mock_journaler,
466                                           mock_instance_watcher, &ctx);
467   request->get();
468   request->send();
469
470   // cancel the image copy once it starts
471   ASSERT_EQ(0, image_copy_ctx.wait());
472   request->cancel();
473   request->put();
474   m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
475
476   ASSERT_EQ(-ECANCELED, ctx.wait());
477 }
478
479 TEST_F(TestMockImageSync, CancelAfterCopySnapshots) {
480   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
481   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
482   journal::MockJournaler mock_journaler;
483   MockInstanceWatcher mock_instance_watcher;
484   MockSnapshotCopyRequest mock_snapshot_copy_request;
485   MockSyncPointCreateRequest mock_sync_point_create_request;
486
487   librbd::MockExclusiveLock mock_exclusive_lock;
488   mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
489
490   librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
491   mock_local_image_ctx.object_map = mock_object_map;
492   expect_test_features(mock_local_image_ctx);
493
494   C_SaferCond ctx;
495   MockImageSync *request = create_request(mock_remote_image_ctx,
496                                           mock_local_image_ctx, mock_journaler,
497                                           mock_instance_watcher, &ctx);
498   InSequence seq;
499   expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
500   expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
501   EXPECT_CALL(mock_snapshot_copy_request, send())
502     .WillOnce((DoAll(InvokeWithoutArgs([request]() {
503               request->cancel();
504             }),
505           Invoke([this, &mock_snapshot_copy_request]() {
506               m_threads->work_queue->queue(mock_snapshot_copy_request.on_finish, 0);
507             }))));
508   expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
509                              false);
510   EXPECT_CALL(mock_snapshot_copy_request, cancel());
511   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
512
513   request->send();
514   ASSERT_EQ(-ECANCELED, ctx.wait());
515 }
516
517 TEST_F(TestMockImageSync, CancelAfterCopyImage) {
518   librbd::MockTestImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
519   librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
520   journal::MockJournaler mock_journaler;
521   MockInstanceWatcher mock_instance_watcher;
522   MockImageCopyRequest mock_image_copy_request;
523   MockSnapshotCopyRequest mock_snapshot_copy_request;
524   MockSyncPointCreateRequest mock_sync_point_create_request;
525   MockSyncPointPruneRequest mock_sync_point_prune_request;
526
527   librbd::MockExclusiveLock mock_exclusive_lock;
528   mock_local_image_ctx.exclusive_lock = &mock_exclusive_lock;
529
530   librbd::MockObjectMap *mock_object_map = new librbd::MockObjectMap();
531   mock_local_image_ctx.object_map = mock_object_map;
532   expect_test_features(mock_local_image_ctx);
533
534   C_SaferCond ctx;
535   MockImageSync *request = create_request(mock_remote_image_ctx,
536                                           mock_local_image_ctx, mock_journaler,
537                                           mock_instance_watcher, &ctx);
538   InSequence seq;
539   expect_notify_sync_request(mock_instance_watcher, mock_local_image_ctx.id, 0);
540   expect_create_sync_point(mock_local_image_ctx, mock_sync_point_create_request, 0);
541   expect_copy_snapshots(mock_snapshot_copy_request, 0);
542   EXPECT_CALL(mock_image_copy_request, send())
543     .WillOnce((DoAll(InvokeWithoutArgs([request]() {
544               request->cancel();
545             }),
546           Invoke([this, &mock_image_copy_request]() {
547               m_threads->work_queue->queue(mock_image_copy_request.on_finish, 0);
548             }))));
549   expect_cancel_sync_request(mock_instance_watcher, mock_local_image_ctx.id,
550                              false);
551   EXPECT_CALL(mock_image_copy_request, cancel());
552   expect_notify_sync_complete(mock_instance_watcher, mock_local_image_ctx.id);
553
554   request->send();
555   ASSERT_EQ(-ECANCELED, ctx.wait());
556 }
557
558 } // namespace mirror
559 } // namespace rbd