From 2b66a3be085802bd1f9f766cf4f12fda5ac5d492 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Thu, 17 Dec 2015 15:29:40 -0500 Subject: [PATCH] tests: unit test cases for all available journal events Signed-off-by: Jason Dillaman --- src/test/Makefile-client.am | 2 + src/test/librbd/journal/test_Replay.cc | 4 +- src/test/librbd/journal/test_mock_Replay.cc | 452 ++++++++++++++++++++ src/test/librbd/mock/MockImageCtx.h | 6 +- src/test/librbd/mock/MockOperations.h | 34 ++ 5 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 src/test/librbd/journal/test_mock_Replay.cc create mode 100644 src/test/librbd/mock/MockOperations.h diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index 3b275d4fda493..72877d20a4625 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -365,6 +365,7 @@ unittest_librbd_SOURCES = \ test/librbd/test_mock_Journal.cc \ test/librbd/exclusive_lock/test_mock_AcquireRequest.cc \ test/librbd/exclusive_lock/test_mock_ReleaseRequest.cc \ + test/librbd/journal/test_mock_Replay.cc \ test/librbd/object_map/test_mock_InvalidateRequest.cc \ test/librbd/object_map/test_mock_LockRequest.cc \ test/librbd/object_map/test_mock_RefreshRequest.cc \ @@ -421,6 +422,7 @@ noinst_HEADERS += \ test/librbd/mock/MockImageWatcher.h \ test/librbd/mock/MockJournal.h \ test/librbd/mock/MockObjectMap.h \ + test/librbd/mock/MockOperations.h \ test/librbd/mock/MockReadahead.h \ test/librbd/object_map/mock/MockInvalidateRequest.h diff --git a/src/test/librbd/journal/test_Replay.cc b/src/test/librbd/journal/test_Replay.cc index 076596c738b1b..f0f0409418394 100644 --- a/src/test/librbd/journal/test_Replay.cc +++ b/src/test/librbd/journal/test_Replay.cc @@ -141,8 +141,8 @@ TEST_F(TestJournalReplay, AioFlushEvent) { librbd::AioCompletion *aio_comp = new librbd::AioCompletion(); { RWLock::RLocker owner_lock(ictx->owner_lock); - librbd::AioImageRequest::aio_write(ictx, aio_comp, 0, payload.size(), - payload.c_str(), 0); + librbd::AioImageRequest<>::aio_write(ictx, aio_comp, 0, payload.size(), + payload.c_str(), 0); } ictx->journal = journal; diff --git a/src/test/librbd/journal/test_mock_Replay.cc b/src/test/librbd/journal/test_mock_Replay.cc new file mode 100644 index 0000000000000..539a135d5ea56 --- /dev/null +++ b/src/test/librbd/journal/test_mock_Replay.cc @@ -0,0 +1,452 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "test/librbd/mock/MockImageCtx.h" +#include "librbd/AioImageRequest.h" +#include "librbd/journal/Entries.h" +#include "librbd/journal/Replay.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace librbd { + +template <> +struct AioImageRequest { + static AioImageRequest *s_instance; + + MOCK_METHOD5(aio_write, void(AioCompletion *c, uint64_t off, size_t len, + const char *buf, int op_flags)); + static void aio_write(MockImageCtx *ictx, AioCompletion *c, uint64_t off, + size_t len, const char *buf, int op_flags) { + assert(s_instance != nullptr); + s_instance->aio_write(c, off, len, buf, op_flags); + } + + MOCK_METHOD3(aio_discard, void(AioCompletion *c, uint64_t off, uint64_t len)); + static void aio_discard(MockImageCtx *ictx, AioCompletion *c, uint64_t off, + uint64_t len) { + assert(s_instance != nullptr); + s_instance->aio_discard(c, off, len); + } + + MOCK_METHOD1(aio_flush, void(AioCompletion *c)); + static void aio_flush(MockImageCtx *ictx, AioCompletion *c) { + assert(s_instance != nullptr); + s_instance->aio_flush(c); + } + + AioImageRequest() { + s_instance = this; + } +}; + +AioImageRequest *AioImageRequest::s_instance = nullptr; + +} + +// template definitions +#include "librbd/journal/Replay.cc" +template class librbd::journal::Replay; + +using ::testing::_; +using ::testing::DoAll; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::WithArgs; + +MATCHER_P(CStrEq, str, "") { + return (strncmp(arg, str, strlen(str)) == 0); +} + +namespace librbd { +namespace journal { + +class TestMockJournalReplay : public TestMockFixture { +public: + typedef AioImageRequest MockAioImageRequest; + typedef Replay MockJournalReplay; + + void expect_aio_discard(MockAioImageRequest &mock_aio_image_request, + AioCompletion **aio_comp, uint64_t off, + uint64_t len) { + EXPECT_CALL(mock_aio_image_request, aio_discard(_, off, len)) + .WillOnce(SaveArg<0>(aio_comp)); + } + + void expect_aio_flush(MockAioImageRequest &mock_aio_image_request, + AioCompletion **aio_comp) { + EXPECT_CALL(mock_aio_image_request, aio_flush(_)) + .WillOnce(SaveArg<0>(aio_comp)); + } + + void expect_aio_write(MockAioImageRequest &mock_aio_image_request, + AioCompletion **aio_comp, uint64_t off, + uint64_t len, const char *data) { + EXPECT_CALL(mock_aio_image_request, + aio_write(_, off, len, CStrEq(data), _)) + .WillOnce(SaveArg<0>(aio_comp)); + } + + void expect_flatten(MockImageCtx &mock_image_ctx, Context **on_finish) { + EXPECT_CALL(*mock_image_ctx.operations, flatten(_, _)) + .WillOnce(SaveArg<1>(on_finish)); + } + + 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)); + } + + void expect_resize(MockImageCtx &mock_image_ctx, Context **on_finish, + uint64_t size) { + EXPECT_CALL(*mock_image_ctx.operations, resize(size, _, _)) + .WillOnce(SaveArg<2>(on_finish)); + } + + void expect_snap_create(MockImageCtx &mock_image_ctx, + Context **on_finish, const char *snap_name) { + EXPECT_CALL(*mock_image_ctx.operations, snap_create(CStrEq(snap_name), _)) + .WillOnce(SaveArg<1>(on_finish)); + } + + 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)); + } + + 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)); + } + + 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)); + } + + 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)); + } + + 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)); + } + + void when_process(MockJournalReplay &mock_journal_replay, + EventEntry &&event_entry, Context *on_safe) { + bufferlist bl; + ::encode(event_entry, bl); + + bufferlist::iterator it = bl.begin(); + mock_journal_replay.process(it, on_safe); + } + + void when_complete(MockImageCtx &mock_image_ctx, AioCompletion *aio_comp, + int r) { + aio_comp->get(); + aio_comp->set_request_count(mock_image_ctx.cct, 1); + aio_comp->complete_request(mock_image_ctx.cct, r); + } + + bufferlist to_bl(const std::string &str) { + bufferlist bl; + bl.append(str); + return bl; + } +}; + +TEST_F(TestMockJournalReplay, AioDiscard) { + 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); + MockAioImageRequest mock_aio_image_request; + + InSequence seq; + AioCompletion *aio_comp; + C_SaferCond on_safe; + expect_aio_discard(mock_aio_image_request, &aio_comp, 123, 456); + when_process(mock_journal_replay, + EventEntry{AioDiscardEvent(123, 456)}, + &on_safe); + + when_complete(mock_image_ctx, aio_comp, 0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, AioWrite) { + 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); + MockAioImageRequest mock_aio_image_request; + + InSequence seq; + AioCompletion *aio_comp; + C_SaferCond on_safe; + expect_aio_write(mock_aio_image_request, &aio_comp, 123, 456, "test"); + when_process(mock_journal_replay, + EventEntry{AioWriteEvent(123, 456, to_bl("test"))}, + &on_safe); + + when_complete(mock_image_ctx, aio_comp, 0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, AioFlush) { + 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); + MockAioImageRequest mock_aio_image_request; + + InSequence seq; + AioCompletion *aio_comp; + C_SaferCond on_safe; + expect_aio_flush(mock_aio_image_request, &aio_comp); + when_process(mock_journal_replay, EventEntry{AioFlushEvent()}, &on_safe); + + when_complete(mock_image_ctx, aio_comp, 0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, IOError) { + 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); + MockAioImageRequest mock_aio_image_request; + + InSequence seq; + AioCompletion *aio_comp; + C_SaferCond on_safe; + expect_aio_discard(mock_aio_image_request, &aio_comp, 123, 456); + when_process(mock_journal_replay, + EventEntry{AioDiscardEvent(123, 456)}, + &on_safe); + + when_complete(mock_image_ctx, aio_comp, -EINVAL); + ASSERT_EQ(-EINVAL, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SynchronousUntilFlush) { + REQUIRE_FEATURE(RBD_FEATURE_JOURNALING); + // TODO +} + +TEST_F(TestMockJournalReplay, SnapCreateEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_create(mock_image_ctx, &on_finish, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{SnapCreateEvent(123, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SnapRemoveEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_remove(mock_image_ctx, &on_finish, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{SnapRemoveEvent(123, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SnapRenameEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_rename(mock_image_ctx, &on_finish, 234, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, + EventEntry{SnapRenameEvent(123, 234, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SnapProtectEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_protect(mock_image_ctx, &on_finish, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{SnapProtectEvent(123, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SnapUnprotectEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_unprotect(mock_image_ctx, &on_finish, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{SnapUnprotectEvent(123, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, SnapRollbackEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_snap_rollback(mock_image_ctx, &on_finish, "snap"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{SnapRollbackEvent(123, "snap")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, RenameEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_rename(mock_image_ctx, &on_finish, "image"); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{RenameEvent(123, "image")}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, ResizeEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_resize(mock_image_ctx, &on_finish, 234); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{ResizeEvent(123, 234)}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +TEST_F(TestMockJournalReplay, FlattenEvent) { + 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); + + InSequence seq; + Context *on_finish; + expect_flatten(mock_image_ctx, &on_finish); + + C_SaferCond on_safe; + when_process(mock_journal_replay, EventEntry{FlattenEvent(123)}, + &on_safe); + + on_finish->complete(0); + ASSERT_EQ(0, on_safe.wait()); +} + +} // namespace journal +} // namespace librbd diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h index d75364138ae49..1883819a6d851 100644 --- a/src/test/librbd/mock/MockImageCtx.h +++ b/src/test/librbd/mock/MockImageCtx.h @@ -10,6 +10,7 @@ #include "test/librbd/mock/MockImageWatcher.h" #include "test/librbd/mock/MockJournal.h" #include "test/librbd/mock/MockObjectMap.h" +#include "test/librbd/mock/MockOperations.h" #include "test/librbd/mock/MockReadahead.h" #include "common/RWLock.h" #include "common/WorkQueue.h" @@ -46,7 +47,8 @@ struct MockImageCtx { layout(image_ctx.layout), aio_work_queue(new MockAioImageRequestWQ()), op_work_queue(new MockContextWQ()), - parent(NULL), image_watcher(NULL), object_map(NULL), + parent(NULL), operations(new MockOperations()), + image_watcher(NULL), object_map(NULL), exclusive_lock(NULL), journal(NULL), concurrent_management_ops(image_ctx.concurrent_management_ops), blacklist_on_break_lock(image_ctx.blacklist_on_break_lock), @@ -72,6 +74,7 @@ struct MockImageCtx { image_ctx->md_ctx.aio_flush(); image_ctx->data_ctx.aio_flush(); image_ctx->op_work_queue->drain(); + delete operations; delete image_watcher; delete op_work_queue; delete aio_work_queue; @@ -161,6 +164,7 @@ struct MockImageCtx { MockReadahead readahead; MockImageCtx *parent; + MockOperations *operations; MockImageWatcher *image_watcher; MockObjectMap *object_map; diff --git a/src/test/librbd/mock/MockOperations.h b/src/test/librbd/mock/MockOperations.h new file mode 100644 index 0000000000000..b3dde8cd151a6 --- /dev/null +++ b/src/test/librbd/mock/MockOperations.h @@ -0,0 +1,34 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H +#define CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H + +#include "include/int_types.h" +#include "gmock/gmock.h" + +class Context; + +namespace librbd { + +struct MockOperations { + MOCK_METHOD2(flatten, void(ProgressContext &prog_ctx, Context *on_finish)); + MOCK_METHOD2(rebuild_object_map, void(ProgressContext &prog_ctx, + Context *on_finish)); + MOCK_METHOD2(rename, void(const char *dstname, Context *on_finish)); + MOCK_METHOD3(resize, void(uint64_t size, ProgressContext &prog_ctx, + Context *on_finish)); + MOCK_METHOD2(snap_create, void(const char *snap_name, Context *on_finish)); + MOCK_METHOD2(snap_remove, void(const char *snap_name, Context *on_finish)); + MOCK_METHOD3(snap_rename, void(uint64_t src_snap_id, const char *snap_name, + Context *on_finish)); + MOCK_METHOD3(snap_rollback, void(const char *snap_name, + ProgressContext &prog_ctx, + Context *on_finish)); + MOCK_METHOD2(snap_protect, void(const char *snap_name, Context *on_finish)); + MOCK_METHOD2(snap_unprotect, void(const char *snap_name, Context *on_finish)); +}; + +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_OPERATIONS_H -- 2.39.5