diff options
Diffstat (limited to 'src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc')
-rw-r--r-- | src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc | 706 |
1 files changed, 706 insertions, 0 deletions
diff --git a/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc b/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc new file mode 100644 index 000000000..4db576eb4 --- /dev/null +++ b/src/test/rbd_mirror/test_mock_MirrorStatusUpdater.cc @@ -0,0 +1,706 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/rbd_mirror/test_mock_fixture.h" +#include "include/stringify.h" +#include "tools/rbd_mirror/MirrorStatusUpdater.h" +#include "tools/rbd_mirror/MirrorStatusWatcher.h" +#include "tools/rbd_mirror/Threads.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "test/rbd_mirror/mock/MockContextWQ.h" +#include "test/rbd_mirror/mock/MockSafeTimer.h" +#include <map> +#include <string> +#include <utility> + +namespace librbd { +namespace { + +struct MockTestImageCtx : public MockImageCtx { + MockTestImageCtx(librbd::ImageCtx &image_ctx) + : librbd::MockImageCtx(image_ctx) { + } +}; + +} // anonymous namespace +} // namespace librbd + +namespace rbd { +namespace mirror { + +template <> +struct MirrorStatusWatcher<librbd::MockTestImageCtx> { + static MirrorStatusWatcher* s_instance; + static MirrorStatusWatcher* create(librados::IoCtx& io_ctx, + MockContextWQ* mock_context_wq) { + ceph_assert(s_instance != nullptr); + return s_instance; + } + + MOCK_METHOD1(init, void(Context*)); + MOCK_METHOD1(shut_down, void(Context*)); + + MirrorStatusWatcher() { + s_instance = this; + } +}; + +MirrorStatusWatcher<librbd::MockTestImageCtx>* MirrorStatusWatcher<librbd::MockTestImageCtx>::s_instance = nullptr; + +template <> +struct Threads<librbd::MockTestImageCtx> { + MockSafeTimer *timer; + ceph::mutex &timer_lock; + + MockContextWQ *work_queue; + + Threads(Threads<librbd::ImageCtx> *threads) + : timer(new MockSafeTimer()), + timer_lock(threads->timer_lock), + work_queue(new MockContextWQ()) { + } + ~Threads() { + delete timer; + delete work_queue; + } +}; + +} // namespace mirror +} // namespace rbd + +#include "tools/rbd_mirror/MirrorStatusUpdater.cc" + +namespace rbd { +namespace mirror { + +using ::testing::_; +using ::testing::DoDefault; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::StrEq; +using ::testing::Return; +using ::testing::WithArg; + +class TestMockMirrorStatusUpdater : public TestMockFixture { +public: + typedef MirrorStatusUpdater<librbd::MockTestImageCtx> MockMirrorStatusUpdater; + typedef MirrorStatusWatcher<librbd::MockTestImageCtx> MockMirrorStatusWatcher; + typedef Threads<librbd::MockTestImageCtx> MockThreads; + + typedef std::map<std::string, cls::rbd::MirrorImageSiteStatus> + MirrorImageSiteStatuses; + + void SetUp() override { + TestMockFixture::SetUp(); + + m_mock_local_io_ctx = &get_mock_io_ctx(m_local_io_ctx); + m_mock_threads = new MockThreads(m_threads); + } + + void TearDown() override { + delete m_mock_threads; + TestMockFixture::TearDown(); + } + + void expect_timer_add_event(Context** timer_event) { + EXPECT_CALL(*m_mock_threads->timer, add_event_after(_, _)) + .WillOnce(WithArg<1>(Invoke([timer_event](Context *ctx) { + *timer_event = ctx; + return ctx; + }))); + } + + void expect_timer_cancel_event() { + EXPECT_CALL(*m_mock_threads->timer, cancel_event(_)) + .WillOnce(Invoke([](Context* ctx) { + delete ctx; + return false; + })); + } + + void expect_work_queue(bool async) { + EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _)) + .WillOnce(Invoke([this, async](Context *ctx, int r) { + if (async) { + m_threads->work_queue->queue(ctx, r); + } else { + ctx->complete(r); + } + })); + } + + void expect_mirror_status_watcher_init( + MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) { + EXPECT_CALL(*mock_mirror_status_watcher.s_instance, init(_)) + .WillOnce(Invoke([this, r](Context* ctx) { + m_threads->work_queue->queue(ctx, r); + })); + } + + void expect_mirror_status_watcher_shut_down( + MockMirrorStatusWatcher& mock_mirror_status_watcher, int r) { + EXPECT_CALL(*mock_mirror_status_watcher.s_instance, shut_down(_)) + .WillOnce(Invoke([this, r](Context* ctx) { + m_threads->work_queue->queue(ctx, r); + })); + } + + void expect_mirror_status_update( + const std::string& global_image_id, + const cls::rbd::MirrorImageSiteStatus& mirror_image_status, int r) { + EXPECT_CALL(*m_mock_local_io_ctx, + exec(RBD_MIRRORING, _, StrEq("rbd"), + StrEq("mirror_image_status_set"), _, _, _, _)) + .WillOnce(WithArg<4>(Invoke( + [r, global_image_id, mirror_image_status](bufferlist& in_bl) { + auto bl_it = in_bl.cbegin(); + std::string decode_global_image_id; + decode(decode_global_image_id, bl_it); + EXPECT_EQ(global_image_id, decode_global_image_id); + + cls::rbd::MirrorImageSiteStatus decode_mirror_image_status; + decode(decode_mirror_image_status, bl_it); + EXPECT_EQ(mirror_image_status, decode_mirror_image_status); + return r; + }))); + } + + void expect_mirror_status_update( + const MirrorImageSiteStatuses& mirror_image_site_statuses, + const std::string& mirror_uuid, int r) { + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke([this](auto&&... args) { + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + return r; + })); + + for (auto [global_image_id, mirror_image_status] : + mirror_image_site_statuses) { + mirror_image_status.mirror_uuid = mirror_uuid; + expect_mirror_status_update(global_image_id, mirror_image_status, r); + if (r < 0) { + break; + } + } + } + + void expect_mirror_status_remove(const std::string& global_image_id, int r) { + EXPECT_CALL(*m_mock_local_io_ctx, + exec(RBD_MIRRORING, _, StrEq("rbd"), + StrEq("mirror_image_status_remove"), _, _, _, _)) + .WillOnce(WithArg<4>(Invoke( + [r, global_image_id](bufferlist& in_bl) { + auto bl_it = in_bl.cbegin(); + std::string decode_global_image_id; + decode(decode_global_image_id, bl_it); + EXPECT_EQ(global_image_id, decode_global_image_id); + + return r; + }))); + } + + void expect_mirror_status_removes(const std::set<std::string>& mirror_images, + int r) { + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke([this](auto&&... args) { + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + return r; + })); + + for (auto global_image_id : mirror_images) { + expect_mirror_status_remove(global_image_id, r); + if (r < 0) { + break; + } + } + } + + void fire_timer_event(Context** timer_event, + Context** update_task) { + expect_timer_add_event(timer_event); + + // timer queues the update task + EXPECT_CALL(*m_mock_threads->work_queue, queue(_, _)) + .WillOnce(WithArg<0>(Invoke([update_task](Context* ctx) mutable { + *update_task = ctx; + }))); + + // fire the timer task + { + std::lock_guard timer_locker{m_mock_threads->timer_lock}; + ceph_assert(*timer_event != nullptr); + (*timer_event)->complete(0); + } + } + + void init_mirror_status_updater( + MockMirrorStatusUpdater& mock_mirror_status_updater, + MockMirrorStatusWatcher& mock_mirror_status_watcher, + Context** timer_event) { + expect_timer_add_event(timer_event); + expect_mirror_status_watcher_init(mock_mirror_status_watcher, 0); + expect_work_queue(true); + + C_SaferCond ctx; + mock_mirror_status_updater.init(&ctx); + ASSERT_EQ(0, ctx.wait()); + } + + void shut_down_mirror_status_updater( + MockMirrorStatusUpdater& mock_mirror_status_updater, + MockMirrorStatusWatcher& mock_mirror_status_watcher) { + expect_timer_cancel_event(); + expect_mirror_status_watcher_shut_down(mock_mirror_status_watcher, 0); + expect_work_queue(true); + + C_SaferCond ctx; + mock_mirror_status_updater.shut_down(&ctx); + ASSERT_EQ(0, ctx.wait()); + } + + librados::MockTestMemIoCtxImpl* m_mock_local_io_ctx = nullptr; + MockThreads* m_mock_threads = nullptr; +}; + +TEST_F(TestMockMirrorStatusUpdater, InitShutDown) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, InitStatusWatcherError) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + Context* timer_event = nullptr; + expect_timer_add_event(&timer_event); + expect_mirror_status_watcher_init(*mock_mirror_status_watcher, -EINVAL); + expect_timer_cancel_event(); + expect_work_queue(true); + + C_SaferCond ctx; + mock_mirror_status_updater.init(&ctx); + ASSERT_EQ(-EINVAL, ctx.wait()); +} + +TEST_F(TestMockMirrorStatusUpdater, ShutDownStatusWatcherError) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + C_SaferCond on_shutdown; + expect_timer_cancel_event(); + expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, -EINVAL); + expect_work_queue(true); + mock_mirror_status_updater.shut_down(&on_shutdown); + + ASSERT_EQ(-EINVAL, on_shutdown.wait()); +} + +TEST_F(TestMockMirrorStatusUpdater, SmallBatch) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + MirrorImageSiteStatuses mirror_image_site_statuses; + for (auto i = 0; i < 100; ++i) { + auto pair = mirror_image_site_statuses.emplace( + stringify(i), cls::rbd::MirrorImageSiteStatus{}); + mock_mirror_status_updater.set_mirror_image_status(pair.first->first, + pair.first->second, + false); + } + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + expect_mirror_status_update(mirror_image_site_statuses, "", 0); + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, LargeBatch) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + MirrorImageSiteStatuses mirror_image_site_statuses; + for (auto i = 0; i < 200; ++i) { + auto pair = mirror_image_site_statuses.emplace( + stringify(i), cls::rbd::MirrorImageSiteStatus{}); + mock_mirror_status_updater.set_mirror_image_status(pair.first->first, + pair.first->second, + false); + } + + auto it_1 = mirror_image_site_statuses.begin(); + auto it_2 = mirror_image_site_statuses.begin(); + std::advance(it_2, 100); + MirrorImageSiteStatuses mirror_image_site_statuses_1{it_1, it_2}; + + it_1 = it_2; + std::advance(it_2, 100); + MirrorImageSiteStatuses mirror_image_site_statuses_2{it_1, it_2}; + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + expect_mirror_status_update(mirror_image_site_statuses_1, "", 0); + expect_mirror_status_update(mirror_image_site_statuses_2, "", 0); + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, OverwriteStatus) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + mock_mirror_status_updater.set_mirror_image_status( + "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}, + false); + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + expect_mirror_status_update( + {{"1", cls::rbd::MirrorImageSiteStatus{ + "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, + "", 0); + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, RemoveStatus) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + C_SaferCond ctx; + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + expect_work_queue(false); + mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx); + ASSERT_EQ(0, ctx.wait()); + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + C_SaferCond remove_flush_ctx; + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke([this, &remove_flush_ctx](auto&&... args) { + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + remove_flush_ctx.complete(r); + return r; + })); + expect_mirror_status_remove("1", 0); + update_task->complete(0); + ASSERT_EQ(0, remove_flush_ctx.wait()); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, OverwriteRemoveStatus) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + C_SaferCond ctx; + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + expect_work_queue(false); + mock_mirror_status_updater.remove_mirror_image_status("1", false, &ctx); + ASSERT_EQ(0, ctx.wait()); + mock_mirror_status_updater.set_mirror_image_status( + "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}, + false); + + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + expect_mirror_status_update( + {{"1", cls::rbd::MirrorImageSiteStatus{ + "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, + "", 0); + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, OverwriteStatusInFlight) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke([this, &mock_mirror_status_updater](auto&&... args) { + mock_mirror_status_updater.set_mirror_image_status( + "1", {"", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, + "description"}, + true); + + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + return r; + })); + expect_mirror_status_update("1", cls::rbd::MirrorImageSiteStatus{}, 0); + expect_work_queue(false); + expect_mirror_status_update( + {{"1", cls::rbd::MirrorImageSiteStatus{ + "", cls::rbd::MIRROR_IMAGE_STATUS_STATE_REPLAYING, "description"}}}, + "", 0); + + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, ImmediateUpdate) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + expect_work_queue(false); + expect_mirror_status_update({{"1", cls::rbd::MirrorImageSiteStatus{}}}, + "", 0); + mock_mirror_status_updater.set_mirror_image_status("1", {}, true); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, RemoveImmediateUpdate) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + + C_SaferCond ctx; + expect_work_queue(false); + expect_mirror_status_removes({"1"}, 0); + expect_work_queue(false); + mock_mirror_status_updater.remove_mirror_image_status("1", true, &ctx); + ASSERT_EQ(0, ctx.wait()); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshIdleStatus) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + + C_SaferCond ctx; + expect_work_queue(true); + mock_mirror_status_updater.remove_refresh_mirror_image_status("1", &ctx); + ASSERT_EQ(0, ctx.wait()); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, RemoveRefreshInFlightStatus) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + C_SaferCond on_removed; + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke( + [this, &mock_mirror_status_updater, &on_removed](auto&&... args) { + mock_mirror_status_updater.remove_refresh_mirror_image_status( + "1", &on_removed); + + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + return r; + })); + update_task->complete(0); + ASSERT_EQ(0, on_removed.wait()); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +TEST_F(TestMockMirrorStatusUpdater, ShutDownWhileUpdating) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, ""); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + mock_mirror_status_updater.set_mirror_image_status("1", {}, false); + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + C_SaferCond on_shutdown; + EXPECT_CALL(*m_mock_local_io_ctx, aio_operate(_, _, _, _, _)) + .WillOnce(Invoke( + [this, &mock_mirror_status_updater, &on_shutdown](auto&&... args) { + mock_mirror_status_updater.shut_down(&on_shutdown); + m_threads->work_queue->drain(); + + int r = m_mock_local_io_ctx->do_aio_operate(decltype(args)(args)...); + m_mock_local_io_ctx->aio_flush(); + return r; + })); + + expect_timer_cancel_event(); + expect_mirror_status_watcher_shut_down(*mock_mirror_status_watcher, 0); + + update_task->complete(0); + ASSERT_EQ(0, on_shutdown.wait()); +} + +TEST_F(TestMockMirrorStatusUpdater, MirrorPeerSitePing) { + MockMirrorStatusUpdater mock_mirror_status_updater(m_local_io_ctx, + m_mock_threads, + "mirror uuid"); + MockMirrorStatusWatcher* mock_mirror_status_watcher = + new MockMirrorStatusWatcher(); + + InSequence seq; + + Context* timer_event = nullptr; + init_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher, &timer_event); + + MirrorImageSiteStatuses mirror_image_site_statuses; + for (auto i = 0; i < 100; ++i) { + auto pair = mirror_image_site_statuses.emplace( + stringify(i), cls::rbd::MirrorImageSiteStatus{}); + mock_mirror_status_updater.set_mirror_image_status(pair.first->first, + pair.first->second, + false); + } + + Context* update_task = nullptr; + fire_timer_event(&timer_event, &update_task); + + expect_mirror_status_update(mirror_image_site_statuses, "mirror uuid", 0); + update_task->complete(0); + + shut_down_mirror_status_updater(mock_mirror_status_updater, + *mock_mirror_status_watcher); +} + +} // namespace mirror +} // namespace rbd |