#include "tools/rbd_mirror/ImageDeleter.h"
#include "tools/rbd_mirror/ImageReplayer.h"
#include "tools/rbd_mirror/InstanceWatcher.h"
+#include "tools/rbd_mirror/Threads.h"
#include "tools/rbd_mirror/image_replayer/BootstrapRequest.h"
#include "tools/rbd_mirror/image_replayer/CloseImageRequest.h"
#include "tools/rbd_mirror/image_replayer/EventPreprocessor.h"
#include "test/journal/mock/MockJournaler.h"
#include "test/librbd/mock/MockImageCtx.h"
#include "test/librbd/mock/MockJournal.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
namespace librbd {
namespace rbd {
namespace mirror {
+template <>
+struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
+ 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;
+ }
+};
+
template <>
struct ImageDeleter<librbd::MockTestImageCtx> {
MOCK_METHOD4(schedule_image_delete, void(RadosRef, int64_t,
const std::string &global_image_id,
std::string *local_image_id,
std::string *tag_owner,
- ContextWQ *work_queue,
+ MockContextWQ *work_queue,
Context *on_finish) {
assert(s_instance != nullptr);
s_instance->local_image_id = local_image_id;
rbd::mirror::InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher,
librbd::MockTestImageCtx **local_image_ctx,
const std::string &local_image_name, const std::string &remote_image_id,
- const std::string &global_image_id, ContextWQ *work_queue,
- SafeTimer *timer, Mutex *timer_lock, const std::string &local_mirror_uuid,
+ const std::string &global_image_id, MockContextWQ *work_queue,
+ MockSafeTimer *timer, Mutex *timer_lock,
+ const std::string &local_mirror_uuid,
const std::string &remote_mirror_uuid,
::journal::MockJournalerProxy *journaler,
librbd::journal::MirrorPeerClientMeta *client_meta,
::journal::MockJournalerProxy &remote_journaler,
const std::string &local_mirror_uuid,
librbd::journal::MirrorPeerClientMeta *client_meta,
- ContextWQ *work_queue) {
+ MockContextWQ *work_queue) {
assert(s_instance != nullptr);
return s_instance;
}
class TestMockImageReplayer : public TestMockFixture {
public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter;
typedef BootstrapRequest<librbd::MockTestImageCtx> MockBootstrapRequest;
typedef CloseImageRequest<librbd::MockTestImageCtx> MockCloseImageRequest;
ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
}
+ void expect_work_queue_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillRepeatedly(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
+ }
+
+ void expect_add_event_after_repeatedly(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillRepeatedly(
+ Invoke([this](double seconds, Context *ctx) {
+ m_threads->timer->add_event_after(seconds, ctx);
+ }));
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillRepeatedly(
+ Invoke([this](Context *ctx) {
+ return m_threads->timer->cancel_event(ctx);
+ }));
+ }
+
void expect_wait_for_scheduled_deletion(MockImageDeleter& mock_image_deleter,
const std::string& global_image_id,
int r) {
WithArg<2>(CompleteContext(on_commit_r))));
}
- void create_image_replayer(MockImageDeleter &mock_image_deleter) {
+ void create_image_replayer(MockThreads &mock_threads,
+ MockImageDeleter &mock_image_deleter) {
m_image_replayer = new MockImageReplayer(
- m_threads, &mock_image_deleter, &m_instance_watcher,
+ &mock_threads, &mock_image_deleter, &m_instance_watcher,
rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx)),
"local_mirror_uuid", m_local_io_ctx.get_id(), "global image id");
m_image_replayer->add_peer("peer_uuid", m_remote_io_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _));
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
create_local_image();
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockReplayStatusFormatter mock_replay_status_formatter;
expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id,
"", 0);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, remove_listener(_));
expect_shut_down(mock_remote_journaler, 0);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
create_local_image();
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockReplayStatusFormatter mock_replay_status_formatter;
expect_send(mock_prepare_local_image_request, mock_local_image_ctx.id,
"remote mirror uuid", -EINVAL);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
create_local_image();
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
expect_send(mock_prepare_remote_image_request, "remote mirror uuid",
"", -ENOENT);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
create_local_image();
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
expect_send(mock_prepare_remote_image_request, "remote mirror uuid",
"", -ENOENT);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
create_local_image();
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
expect_send(mock_prepare_remote_image_request, "remote mirror uuid",
m_remote_image_ctx->id, -EINVAL);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
librbd::MockTestImageCtx mock_local_image_ctx(*m_local_image_ctx);
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, remove_listener(_));
expect_shut_down(mock_remote_journaler, 0);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, remove_listener(_));
expect_shut_down(mock_remote_journaler, 0);
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _));
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _));
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _));
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
mock_local_image_ctx.journal = &mock_local_journal;
journal::MockJournaler mock_remote_journaler;
+ MockThreads mock_threads(m_threads);
+ expect_work_queue_repeatedly(mock_threads);
+ expect_add_event_after_repeatedly(mock_threads);
+
MockImageDeleter mock_image_deleter;
MockPrepareLocalImageRequest mock_prepare_local_image_request;
MockPrepareRemoteImageRequest mock_prepare_remote_image_request;
EXPECT_CALL(mock_remote_journaler, start_live_replay(_, _));
- create_image_replayer(mock_image_deleter);
+ create_image_replayer(mock_threads, mock_image_deleter);
C_SaferCond start_ctx;
m_image_replayer->start(&start_ctx);
#include "test/librbd/mock/MockImageCtx.h"
#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/rbd_mirror/mock/MockContextWQ.h"
+#include "test/rbd_mirror/mock/MockSafeTimer.h"
#include "tools/rbd_mirror/ImageDeleter.h"
#include "tools/rbd_mirror/ImageReplayer.h"
#include "tools/rbd_mirror/InstanceWatcher.h"
template <>
struct Threads<librbd::MockTestImageCtx> {
+ MockSafeTimer *timer;
Mutex &timer_lock;
- SafeTimer *timer;
- ContextWQ *work_queue;
+ Cond timer_cond;
+
+ MockContextWQ *work_queue;
Threads(Threads<librbd::ImageCtx> *threads)
- : timer_lock(threads->timer_lock), timer(threads->timer),
- work_queue(threads->work_queue) {
+ : timer(new MockSafeTimer()),
+ timer_lock(threads->timer_lock),
+ work_queue(new MockContextWQ()) {
+ }
+ ~Threads() {
+ delete timer;
+ delete work_queue;
}
};
class TestMockInstanceReplayer : public TestMockFixture {
public:
+ typedef Threads<librbd::MockTestImageCtx> MockThreads;
typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter;
typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer;
typedef InstanceReplayer<librbd::MockTestImageCtx> MockInstanceReplayer;
typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher;
typedef ServiceDaemon<librbd::MockTestImageCtx> MockServiceDaemon;
- typedef Threads<librbd::MockTestImageCtx> MockThreads;
-
- void SetUp() override {
- TestMockFixture::SetUp();
- m_mock_threads = new MockThreads(m_threads);
+ void expect_work_queue(MockThreads &mock_threads) {
+ EXPECT_CALL(*mock_threads.work_queue, queue(_, _))
+ .WillOnce(Invoke([this](Context *ctx, int r) {
+ m_threads->work_queue->queue(ctx, r);
+ }));
}
- void TearDown() override {
- delete m_mock_threads;
- TestMockFixture::TearDown();
+ void expect_add_event_after(MockThreads &mock_threads,
+ Context** timer_ctx = nullptr) {
+ EXPECT_CALL(*mock_threads.timer, add_event_after(_, _))
+ .WillOnce(WithArg<1>(
+ Invoke([this, &mock_threads, timer_ctx](Context *ctx) {
+ assert(mock_threads.timer_lock.is_locked());
+ if (timer_ctx != nullptr) {
+ *timer_ctx = ctx;
+ mock_threads.timer_cond.SignalOne();
+ } else {
+ m_threads->work_queue->queue(
+ new FunctionContext([&mock_threads, ctx](int) {
+ Mutex::Locker timer_lock(mock_threads.timer_lock);
+ ctx->complete(0);
+ }), 0);
+ }
+ })));
}
- MockThreads *m_mock_threads;
+ void expect_cancel_event(MockThreads &mock_threads, bool canceled) {
+ EXPECT_CALL(*mock_threads.timer, cancel_event(_))
+ .WillOnce(Return(canceled));
+ }
};
TEST_F(TestMockInstanceReplayer, AcquireReleaseImage) {
+ MockThreads mock_threads(m_threads);
MockServiceDaemon mock_service_daemon;
MockImageDeleter mock_image_deleter;
MockInstanceWatcher mock_instance_watcher;
MockImageReplayer mock_image_replayer;
MockInstanceReplayer instance_replayer(
- m_mock_threads, &mock_service_daemon, &mock_image_deleter,
+ &mock_threads, &mock_service_daemon, &mock_image_deleter,
rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx)),
"local_mirror_uuid", m_local_io_ctx.get_id());
-
std::string global_image_id("global_image_id");
EXPECT_CALL(mock_image_replayer, get_global_image_id())
.WillRepeatedly(ReturnRef(global_image_id));
- EXPECT_CALL(mock_image_replayer, is_blacklisted())
- .WillRepeatedly(Return(false));
InSequence seq;
-
+ expect_work_queue(mock_threads);
+ Context *timer_ctx = nullptr;
+ expect_add_event_after(mock_threads, &timer_ctx);
instance_replayer.init();
instance_replayer.add_peer("peer_uuid", m_remote_io_ctx);
// Acquire
C_SaferCond on_acquire;
-
EXPECT_CALL(mock_image_replayer, add_peer("peer_uuid", _));
- EXPECT_CALL(mock_image_replayer, is_stopped())
- .WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_stopped()).WillOnce(Return(true));
+ EXPECT_CALL(mock_image_replayer, is_blacklisted()).WillOnce(Return(false));
EXPECT_CALL(mock_image_replayer, start(nullptr, false));
+ expect_work_queue(mock_threads);
instance_replayer.acquire_image(&mock_instance_watcher, global_image_id,
&on_acquire);
.WillOnce(Return(false));
EXPECT_CALL(mock_image_replayer, is_running())
.WillOnce(Return(false));
+ expect_work_queue(mock_threads);
+ expect_add_event_after(mock_threads);
+ expect_work_queue(mock_threads);
EXPECT_CALL(mock_image_replayer, is_stopped())
.WillOnce(Return(false));
EXPECT_CALL(mock_image_replayer, is_running())
.WillOnce(Return(true));
EXPECT_CALL(mock_image_replayer, stop(_, false))
.WillOnce(CompleteContext(0));
+ expect_work_queue(mock_threads);
EXPECT_CALL(mock_image_replayer, is_stopped())
.WillOnce(Return(true));
+ expect_work_queue(mock_threads);
EXPECT_CALL(mock_image_replayer, destroy());
instance_replayer.release_image("global_image_id", &on_release);
ASSERT_EQ(0, on_release.wait());
+ expect_work_queue(mock_threads);
+ expect_cancel_event(mock_threads, true);
+ expect_work_queue(mock_threads);
instance_replayer.shut_down();
+ ASSERT_TRUE(timer_ctx != nullptr);
+ delete timer_ctx;
}
} // namespace mirror