From 404dd16d4b1930ebb3e918221d0bb751f5ffdce7 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Fri, 4 Sep 2015 15:01:38 -0400 Subject: [PATCH] tests: base gmock class support for librbd Created mock classes to represent a few central librbd classes and a basic gmock test fixture for future gmock-based unit tests. Signed-off-by: Jason Dillaman --- src/test/Makefile-client.am | 12 ++- src/test/Makefile.am | 2 - src/test/librbd/mock/MockContextWQ.h | 17 ++++ src/test/librbd/mock/MockImageCtx.h | 112 ++++++++++++++++++++++++ src/test/librbd/mock/MockImageWatcher.h | 19 ++++ src/test/librbd/mock/MockObjectMap.h | 20 +++++ src/test/librbd/test_mock_fixture.cc | 68 ++++++++++++++ src/test/librbd/test_mock_fixture.h | 64 ++++++++++++++ 8 files changed, 311 insertions(+), 3 deletions(-) create mode 100644 src/test/librbd/mock/MockContextWQ.h create mode 100644 src/test/librbd/mock/MockImageCtx.h create mode 100644 src/test/librbd/mock/MockImageWatcher.h create mode 100644 src/test/librbd/mock/MockObjectMap.h create mode 100644 src/test/librbd/test_mock_fixture.cc create mode 100644 src/test/librbd/test_mock_fixture.h diff --git a/src/test/Makefile-client.am b/src/test/Makefile-client.am index 0ea7b80ad3a..49e17f6e61d 100644 --- a/src/test/Makefile-client.am +++ b/src/test/Makefile-client.am @@ -321,7 +321,8 @@ librbd_test_la_CXXFLAGS = $(UNITTEST_CXXFLAGS) noinst_LTLIBRARIES += librbd_test.la unittest_librbd_SOURCES = \ - test/librbd/test_main.cc + test/librbd/test_main.cc \ + test/librbd/test_mock_fixture.cc unittest_librbd_CXXFLAGS = $(UNITTEST_CXXFLAGS) -DTEST_LIBRBD_INTERNALS unittest_librbd_LDADD = \ librbd_test.la librbd_api.la librbd_internal.la $(LIBRBD_TYPES) \ @@ -351,6 +352,15 @@ ceph_test_librbd_api_LDADD = \ $(LIBRBD) $(LIBRADOS) $(LIBCOMMON) $(UNITTEST_LDADD) $(RADOS_TEST_LDADD) bin_DEBUGPROGRAMS += ceph_test_librbd_api +noinst_HEADERS += \ + test/librbd/test_fixture.h \ + test/librbd/test_mock_fixture.h \ + test/librbd/test_support.h \ + test/librbd/mock/MockContextWQ.h \ + test/librbd/mock/MockImageCtx.h \ + test/librbd/mock/MockImageWatcher.h \ + test/librbd/mock/MockObjectMap.h + if WITH_LTTNG unittest_librbd_LDADD += $(LIBRBD_TP) ceph_test_librbd_LDADD += $(LIBRBD_TP) diff --git a/src/test/Makefile.am b/src/test/Makefile.am index f6373c76d6d..73c2da1a15f 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -449,8 +449,6 @@ noinst_HEADERS += \ test/librados/test.h \ test/librados/TestCase.h \ test/libradosstriper/TestCase.h \ - test/librbd/test_fixture.h \ - test/librbd/test_support.h \ test/ObjectMap/KeyValueDBMemory.h \ test/omap_bench.h \ test/osdc/FakeWriteback.h \ diff --git a/src/test/librbd/mock/MockContextWQ.h b/src/test/librbd/mock/MockContextWQ.h new file mode 100644 index 00000000000..a690d4eeb82 --- /dev/null +++ b/src/test/librbd/mock/MockContextWQ.h @@ -0,0 +1,17 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H +#define CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H + +#include "gmock/gmock.h" + +namespace librbd { + +struct MockContextWQ { + MOCK_METHOD2(queue, void(Context *, int r)); +}; + +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_CONTEXT_WQ_H diff --git a/src/test/librbd/mock/MockImageCtx.h b/src/test/librbd/mock/MockImageCtx.h new file mode 100644 index 00000000000..53d6fd0d01e --- /dev/null +++ b/src/test/librbd/mock/MockImageCtx.h @@ -0,0 +1,112 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H +#define CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H + +#include "test/librbd/mock/MockContextWQ.h" +#include "test/librbd/mock/MockImageWatcher.h" +#include "test/librbd/mock/MockObjectMap.h" +#include "common/RWLock.h" +#include "librbd/ImageCtx.h" +#include "gmock/gmock.h" + +namespace librbd { + +struct MockImageCtx { + MockImageCtx(librbd::ImageCtx &image_ctx) + : image_ctx(&image_ctx), + cct(image_ctx.cct), + snapc(image_ctx.snapc), + snaps(image_ctx.snaps), + snap_info(image_ctx.snap_info), + old_format(image_ctx.old_format), + read_only(image_ctx.read_only), + owner_lock("owner_lock"), + md_lock("md_lock"), + snap_lock("snap_lock"), + parent_lock("parent_lock"), + object_map_lock("object_map_lock"), + async_ops_lock("async_ops_lock"), + size(image_ctx.size), + features(image_ctx.features), + header_oid(image_ctx.header_oid), + id(image_ctx.id), + parent_md(image_ctx.parent_md), + aio_work_queue(new MockContextWQ()), + op_work_queue(new MockContextWQ()), + image_watcher(NULL), + concurrent_management_ops(image_ctx.concurrent_management_ops) + { + md_ctx.dup(image_ctx.md_ctx); + data_ctx.dup(image_ctx.data_ctx); + + if (image_ctx.image_watcher != NULL) { + image_watcher = new MockImageWatcher(); + } + } + + ~MockImageCtx() { + delete image_watcher; + delete op_work_queue; + delete aio_work_queue; + } + + MOCK_CONST_METHOD1(get_snap_id, librados::snap_t(std::string in_snap_name)); + MOCK_CONST_METHOD1(get_snap_info, const SnapInfo*(librados::snap_t)); + MOCK_CONST_METHOD2(get_parent_spec, int(librados::snap_t in_snap_id, + parent_spec *pspec)); + + MOCK_CONST_METHOD2(is_snap_protected, int(librados::snap_t in_snap_id, + bool *is_protected)); + MOCK_CONST_METHOD2(is_snap_unprotected, int(librados::snap_t in_snap_id, + bool *is_unprotected)); + + MOCK_METHOD6(add_snap, void(std::string in_snap_name, librados::snap_t id, + uint64_t in_size, parent_info parent, + uint8_t protection_status, uint64_t flags)); + MOCK_METHOD2(rm_snap, void(std::string in_snap_name, librados::snap_t id)); + MOCK_METHOD1(flush, void(Context *)); + + ImageCtx *image_ctx; + CephContext *cct; + + ::SnapContext snapc; + std::vector snaps; + std::map snap_info; + + + bool old_format; + bool read_only; + + librados::IoCtx md_ctx; + librados::IoCtx data_ctx; + + RWLock owner_lock; + RWLock md_lock; + RWLock snap_lock; + RWLock parent_lock; + RWLock object_map_lock; + Mutex async_ops_lock; + + uint64_t size; + uint64_t features; + std::string header_oid; + std::string id; + parent_info parent_md; + + xlist*> async_requests; + Cond async_requests_cond; + + MockContextWQ *aio_work_queue; + MockContextWQ *op_work_queue; + + MockImageWatcher *image_watcher; + MockObjectMap object_map; + + int concurrent_management_ops; +}; + +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_CTX_H diff --git a/src/test/librbd/mock/MockImageWatcher.h b/src/test/librbd/mock/MockImageWatcher.h new file mode 100644 index 00000000000..1c339bceb61 --- /dev/null +++ b/src/test/librbd/mock/MockImageWatcher.h @@ -0,0 +1,19 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H +#define CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H + +#include "gmock/gmock.h" + +namespace librbd { + +struct MockImageWatcher { + MOCK_CONST_METHOD0(is_lock_owner, bool()); + MOCK_CONST_METHOD1(is_lock_supported, bool(const RWLock &)); + MOCK_METHOD1(assert_header_locked, void (librados::ObjectWriteOperation *)); +}; + +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_IMAGE_WATCHER_H diff --git a/src/test/librbd/mock/MockObjectMap.h b/src/test/librbd/mock/MockObjectMap.h new file mode 100644 index 00000000000..7f2f84bbde7 --- /dev/null +++ b/src/test/librbd/mock/MockObjectMap.h @@ -0,0 +1,20 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H +#define CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H + +#include "gmock/gmock.h" + +namespace librbd { + +struct MockObjectMap { + MOCK_CONST_METHOD1(enabled, bool(const RWLock &object_map_lock)); + + MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish)); + MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish)); +}; + +} // namespace librbd + +#endif // CEPH_TEST_LIBRBD_MOCK_OBJECT_MAP_H diff --git a/src/test/librbd/test_mock_fixture.cc b/src/test/librbd/test_mock_fixture.cc new file mode 100644 index 00000000000..1839b9150ec --- /dev/null +++ b/src/test/librbd/test_mock_fixture.cc @@ -0,0 +1,68 @@ +// -*- 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/mock/MockImageCtx.h" +#include "test/librados_test_stub/LibradosTestStub.h" +#include "test/librados_test_stub/MockTestMemRadosClient.h" + +// template definitions +#include "librbd/AsyncRequest.cc" +#include "librbd/AsyncObjectThrottle.cc" + +template class librbd::AsyncRequest; +template class librbd::AsyncObjectThrottle; + +using ::testing::_; +using ::testing::DoDefault; + +TestMockFixture::TestRadosClientPtr TestMockFixture::s_test_rados_client; +::testing::NiceMock * + TestMockFixture::s_mock_rados_client = NULL; + +void TestMockFixture::SetUpTestCase() { + s_test_rados_client = librados_test_stub::get_rados_client(); + + // use a mock version of the in-memory rados client + s_mock_rados_client = new ::testing::NiceMock( + s_test_rados_client->cct()); + librados_test_stub::set_rados_client(TestRadosClientPtr(s_mock_rados_client)); + TestFixture::SetUpTestCase(); +} + +void TestMockFixture::TearDownTestCase() { + TestFixture::TearDownTestCase(); + librados_test_stub::set_rados_client(s_test_rados_client); +} + +void TestMockFixture::SetUp() { + TestFixture::SetUp(); +} + +void TestMockFixture::TearDown() { + TestFixture::TearDown(); + + // Mock rados client lives across tests -- reset it to initial state + ::testing::Mock::VerifyAndClear(s_mock_rados_client); + s_mock_rados_client->default_to_dispatch(); +} + +void TestMockFixture::expect_unlock_exclusive_lock(librbd::ImageCtx &ictx) { + EXPECT_CALL(get_mock_io_ctx(ictx.md_ctx), + exec(_, _, "lock", "unlock", _, _, _)) + .WillRepeatedly(DoDefault()); +} + +void TestMockFixture::expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx) { + EXPECT_CALL(*mock_image_ctx.op_work_queue, queue(_, _)) + .WillRepeatedly(DispatchContext( + mock_image_ctx.image_ctx->op_work_queue)); +} + +librados::MockTestMemIoCtxImpl &TestMockFixture::get_mock_io_ctx( + librados::IoCtx &ioctx) { + // TODO become friend of IoCtx so that we can cleanly extract io_ctx_impl + librados::MockTestMemIoCtxImpl **mock = + reinterpret_cast(&ioctx); + return **mock; +} diff --git a/src/test/librbd/test_mock_fixture.h b/src/test/librbd/test_mock_fixture.h new file mode 100644 index 00000000000..150e312f259 --- /dev/null +++ b/src/test/librbd/test_mock_fixture.h @@ -0,0 +1,64 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H +#define CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H + +#include "test/librbd/test_fixture.h" +#include "common/WorkQueue.h" +#include +#include + +namespace librados { +class TestRadosClient; +class MockTestMemIoCtxImpl; +class MockTestMemRadosClient; +} +namespace librbd { +class MockImageCtx; +} + +ACTION_P2(CompleteContext, r, wq) { + ContextWQ *context_wq = reinterpret_cast(wq); + if (context_wq != NULL) { + context_wq->queue(arg0, r); + } else { + arg0->complete(r); + } +} + +ACTION_P(DispatchContext, wq) { + wq->queue(arg0, arg1); +} + +ACTION_P(GetReference, ref_object) { + ref_object->get(); +} + +MATCHER_P(ContentsEqual, bl, "") { + // TODO fix const-correctness of bufferlist + return const_cast(arg).contents_equal( + const_cast(bl)); +} + +class TestMockFixture : public TestFixture { +public: + typedef boost::shared_ptr TestRadosClientPtr; + + static void SetUpTestCase(); + static void TearDownTestCase(); + + virtual void SetUp(); + virtual void TearDown(); + + librados::MockTestMemIoCtxImpl &get_mock_io_ctx(librados::IoCtx &ioctx); + + void expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx); + void expect_unlock_exclusive_lock(librbd::ImageCtx &ictx); + +private: + static TestRadosClientPtr s_test_rados_client; + static ::testing::NiceMock *s_mock_rados_client; +}; + +#endif // CEPH_TEST_LIBRBD_TEST_MOCK_FIXTURE_H -- 2.47.3