return (strncmp(arg, str, strlen(str)) == 0);
}
+ACTION_P2(NotifyInvoke, lock, cond) {
+ Mutex::Locker locker(*lock);
+ cond->Signal();
+}
+
ACTION_P2(CompleteAioCompletion, r, image_ctx) {
CephContext *cct = image_ctx->cct;
image_ctx->op_work_queue->queue(new FunctionContext([cct, arg0](int r) {
typedef AioImageRequest<MockImageCtx> MockAioImageRequest;
typedef Replay<MockImageCtx> MockJournalReplay;
+ TestMockJournalReplay() : m_invoke_lock("m_invoke_lock") {
+ }
+
void expect_aio_discard(MockAioImageRequest &mock_aio_image_request,
AioCompletion **aio_comp, uint64_t off,
uint64_t len) {
void expect_flatten(MockImageCtx &mock_image_ctx, Context **on_finish) {
EXPECT_CALL(*mock_image_ctx.operations, flatten(_, _))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_rename(MockImageCtx &mock_image_ctx, Context **on_finish,
const char *image_name) {
EXPECT_CALL(*mock_image_ctx.operations, rename(CStrEq(image_name), _))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_resize(MockImageCtx &mock_image_ctx, Context **on_finish,
uint64_t size, uint64_t op_tid) {
EXPECT_CALL(*mock_image_ctx.operations, resize(size, _, _, op_tid))
- .WillOnce(SaveArg<2>(on_finish));
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_create(MockImageCtx &mock_image_ctx,
uint64_t op_tid) {
EXPECT_CALL(*mock_image_ctx.operations, snap_create(CStrEq(snap_name), _,
op_tid))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_remove(MockImageCtx &mock_image_ctx,
Context **on_finish, const char *snap_name) {
EXPECT_CALL(*mock_image_ctx.operations, snap_remove(CStrEq(snap_name), _))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_rename(MockImageCtx &mock_image_ctx,
Context **on_finish, uint64_t snap_id,
const char *snap_name) {
EXPECT_CALL(*mock_image_ctx.operations, snap_rename(snap_id, CStrEq(snap_name), _))
- .WillOnce(SaveArg<2>(on_finish));
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_protect(MockImageCtx &mock_image_ctx,
Context **on_finish, const char *snap_name) {
EXPECT_CALL(*mock_image_ctx.operations, snap_protect(CStrEq(snap_name), _))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_unprotect(MockImageCtx &mock_image_ctx,
Context **on_finish, const char *snap_name) {
EXPECT_CALL(*mock_image_ctx.operations, snap_unprotect(CStrEq(snap_name), _))
- .WillOnce(SaveArg<1>(on_finish));
+ .WillOnce(DoAll(SaveArg<1>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void expect_snap_rollback(MockImageCtx &mock_image_ctx,
Context **on_finish, const char *snap_name) {
EXPECT_CALL(*mock_image_ctx.operations, snap_rollback(CStrEq(snap_name), _, _))
- .WillOnce(SaveArg<2>(on_finish));
+ .WillOnce(DoAll(SaveArg<2>(on_finish),
+ NotifyInvoke(&m_invoke_lock, &m_invoke_cond)));
}
void when_process(MockJournalReplay &mock_journal_replay,
mock_journal_replay.replay_op_ready(op_tid, on_resume);
}
+ void wait_for_op_invoked(Context **on_finish, int r) {
+ {
+ Mutex::Locker locker(m_invoke_lock);
+ while (*on_finish == nullptr) {
+ m_invoke_cond.Wait(m_invoke_lock);
+ }
+ }
+ (*on_finish)->complete(r);
+ }
+
bufferlist to_bl(const std::string &str) {
bufferlist bl;
bl.append(str);
return bl;
}
+
+ Mutex m_invoke_lock;
+ Cond m_invoke_cond;
};
TEST_F(TestMockJournalReplay, AioDiscard) {
ASSERT_EQ(0, on_safe.wait());
}
+TEST_F(TestMockJournalReplay, OpFinishError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapRemoveEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, -EIO)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(-EIO, on_start_safe.wait());
+ ASSERT_EQ(-EIO, on_finish_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+}
+
+TEST_F(TestMockJournalReplay, BlockedOpFinishError) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapCreateEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+
+ C_SaferCond on_resume;
+ when_replay_op_ready(mock_journal_replay, 123, &on_resume);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, -EBADMSG)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(-EBADMSG, on_resume.wait());
+ on_finish->complete(-ESTALE);
+
+ ASSERT_EQ(-ESTALE, on_start_safe.wait());
+ ASSERT_EQ(-ESTALE, on_finish_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+}
+
TEST_F(TestMockJournalReplay, MissingOpFinishEvent) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(-ERESTART, on_safe.wait());
}
-TEST_F(TestMockJournalReplay, MissingCompleteOpFinishEvent) {
- // TODO
-}
-
TEST_F(TestMockJournalReplay, UnknownOpFinishEvent) {
librbd::ImageCtx *ictx;
ASSERT_EQ(0, open_image(m_image_name, &ictx));
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, SnapCreateEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish = nullptr;
+ expect_snap_create(mock_image_ctx, &on_finish, "snap", 123);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapCreateEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+
+ wait_for_op_invoked(&on_finish, -EEXIST);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, SnapRemoveEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, SnapRemoveEventDNE) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_snap_remove(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapRemoveEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-ENOENT);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, SnapRenameEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, SnapRenameEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay,
+ EventEntry{SnapRenameEvent(123, 234, "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-EEXIST);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, SnapProtectEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, SnapProtectEventBusy) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_snap_protect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapProtectEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-EBUSY);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, SnapUnprotectEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, SnapUnprotectEventInvalid) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_snap_unprotect(mock_image_ctx, &on_finish, "snap");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{SnapUnprotectEvent(123, "snap")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-EINVAL);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, SnapRollbackEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, RenameEventExists) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_rename(mock_image_ctx, &on_finish, "image");
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-EEXIST);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, ResizeEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
ASSERT_EQ(0, on_finish_safe.wait());
}
+TEST_F(TestMockJournalReplay, FlattenEventInvalid) {
+ REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);
+
+ librbd::ImageCtx *ictx;
+ ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+ MockImageCtx mock_image_ctx(*ictx);
+ MockJournalReplay mock_journal_replay(mock_image_ctx);
+ expect_op_work_queue(mock_image_ctx);
+
+ InSequence seq;
+ Context *on_finish;
+ expect_flatten(mock_image_ctx, &on_finish);
+
+ C_SaferCond on_start_ready;
+ C_SaferCond on_start_safe;
+ when_process(mock_journal_replay, EventEntry{FlattenEvent(123)},
+ &on_start_ready, &on_start_safe);
+ ASSERT_EQ(0, on_start_ready.wait());
+
+ C_SaferCond on_finish_ready;
+ C_SaferCond on_finish_safe;
+ when_process(mock_journal_replay, EventEntry{OpFinishEvent(123, 0)},
+ &on_finish_ready, &on_finish_safe);
+
+ on_finish->complete(-EINVAL);
+ ASSERT_EQ(0, on_start_safe.wait());
+ ASSERT_EQ(0, on_finish_ready.wait());
+ ASSERT_EQ(0, on_finish_safe.wait());
+}
+
TEST_F(TestMockJournalReplay, UnknownEvent) {
REQUIRE_FEATURE(RBD_FEATURE_JOURNALING);