From: Jason Dillaman Date: Tue, 18 Aug 2015 00:51:50 +0000 (-0400) Subject: librados_test_stub: add mock class for IoCtx operations X-Git-Tag: v9.1.0~172^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=0d18f9b83a361e61c78906897d04c3bb88eba053;p=ceph.git librados_test_stub: add mock class for IoCtx operations Unit tests can now use gmock to simulate responses from the OSDs via a mocked librados library. Signed-off-by: Jason Dillaman --- diff --git a/src/test/librados_test_stub/LibradosTestStub.cc b/src/test/librados_test_stub/LibradosTestStub.cc index 7b70cfaf462..1aa256ff18a 100644 --- a/src/test/librados_test_stub/LibradosTestStub.cc +++ b/src/test/librados_test_stub/LibradosTestStub.cc @@ -1,6 +1,7 @@ // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab +#include "test/librados_test_stub/LibradosTestStub.h" #include "include/rados/librados.hpp" #include "common/ceph_argparse.h" #include "common/common_init.h" @@ -25,15 +26,7 @@ namespace { -static void DeallocateRadosClient(librados::TestRadosClient* client) -{ - client->put(); -} - -} // anonymous namespace - - -static librados::TestClassHandler *get_class_handler() { +librados::TestClassHandler *get_class_handler() { static boost::shared_ptr s_class_handler; if (!s_class_handler) { s_class_handler.reset(new librados::TestClassHandler()); @@ -42,23 +35,7 @@ static librados::TestClassHandler *get_class_handler() { return s_class_handler.get(); } -static librados::TestRadosClient *get_rados_client() { - // TODO: use factory to allow tests to swap out impl - static boost::shared_ptr s_rados_client; - if (!s_rados_client) { - CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT); - CephContext *cct = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0); - cct->_conf->parse_env(); - cct->_conf->apply_changes(NULL); - s_rados_client.reset(new librados::TestMemRadosClient(cct), - &DeallocateRadosClient); - cct->put(); - } - s_rados_client->get(); - return s_rados_client.get(); -} - -static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen) { +void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen) { if (outbuf) { if (outbl.length() > 0) { *outbuf = (char *)malloc(outbl.length()); @@ -72,7 +49,7 @@ static void do_out_buffer(bufferlist& outbl, char **outbuf, size_t *outbuflen) { } } -static void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen) { +void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen) { if (outbuf) { if (outbl.length() > 0) { *outbuf = (char *)malloc(outbl.length()); @@ -86,6 +63,40 @@ static void do_out_buffer(string& outbl, char **outbuf, size_t *outbuflen) { } } +} // anonymous namespace + +namespace librados_test_stub { + +TestRadosClientPtr *rados_client() { + // force proper destruction order by delaying construction + static TestRadosClientPtr s_rados_client; + return &s_rados_client; +} + +void set_rados_client( + const boost::shared_ptr &new_client) { + assert(new_client.get() != nullptr); + *rados_client() = new_client; +} + +TestRadosClientPtr get_rados_client() { + // TODO: use factory to allow tests to swap out impl + TestRadosClientPtr *client = rados_client(); + if (client->get() == nullptr) { + CephInitParameters iparams(CEPH_ENTITY_TYPE_CLIENT); + CephContext *cct = common_preinit(iparams, CODE_ENVIRONMENT_LIBRARY, 0); + cct->_conf->parse_env(); + cct->_conf->apply_changes(NULL); + client->reset(new librados::TestMemRadosClient(cct), + &librados::TestRadosClient::Deallocate); + cct->put(); + } + (*client)->get(); + return *client; +} + +} // namespace librados_test_stub + extern "C" int rados_aio_create_completion(void *cb_arg, rados_callback_t cb_complete, rados_callback_t cb_safe, @@ -158,7 +169,7 @@ extern "C" int rados_connect(rados_t cluster) { } extern "C" int rados_create(rados_t *cluster, const char * const id) { - *cluster = get_rados_client(); + *cluster = librados_test_stub::get_rados_client().get(); return 0; } @@ -409,7 +420,7 @@ void IoCtx::dup(const IoCtx& rhs) { int IoCtx::exec(const std::string& oid, const char *cls, const char *method, bufferlist& inbl, bufferlist& outbl) { TestIoCtxImpl *ctx = reinterpret_cast(io_ctx_impl); - return ctx->exec(oid, *get_class_handler(), cls, method, inbl, &outbl, + return ctx->exec(oid, get_class_handler(), cls, method, inbl, &outbl, ctx->get_snap_context()); } @@ -601,8 +612,7 @@ void ObjectOperation::exec(const char *cls, const char *method, bufferlist& inbl) { TestObjectOperationImpl *o = reinterpret_cast(impl); o->ops.push_back(boost::bind(&TestIoCtxImpl::exec, _1, _2, - boost::ref(*get_class_handler()), - cls, method, inbl, _3, _4)); + get_class_handler(), cls, method, inbl, _3, _4)); } void ObjectOperation::set_op_flags2(int flags) { diff --git a/src/test/librados_test_stub/LibradosTestStub.h b/src/test/librados_test_stub/LibradosTestStub.h new file mode 100644 index 00000000000..9fed68df24b --- /dev/null +++ b/src/test/librados_test_stub/LibradosTestStub.h @@ -0,0 +1,23 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef LIBRADOS_TEST_STUB_H +#define LIBRADOS_TEST_STUB_H + +#include + +namespace librados { +class TestRadosClient; +} + +namespace librados_test_stub { + +typedef boost::shared_ptr TestRadosClientPtr; + +void set_rados_client(const TestRadosClientPtr &rados_client); + +TestRadosClientPtr get_rados_client(); + +} // namespace librados_test_stub + +#endif // LIBRADOS_TEST_STUB_H diff --git a/src/test/librados_test_stub/MockTestMemIoCtxImpl.h b/src/test/librados_test_stub/MockTestMemIoCtxImpl.h new file mode 100644 index 00000000000..198db6ccb68 --- /dev/null +++ b/src/test/librados_test_stub/MockTestMemIoCtxImpl.h @@ -0,0 +1,101 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H +#define LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H + +#include "test/librados_test_stub/TestMemIoCtxImpl.h" +#include "gmock/gmock.h" + +namespace librados { + +class MockTestMemRadosClient; + +class MockTestMemIoCtxImpl : public TestMemIoCtxImpl { +public: + MockTestMemIoCtxImpl(MockTestMemRadosClient *mock_client, + TestMemRadosClient *client, int64_t pool_id, + const std::string& pool_name, + TestMemRadosClient::Pool *pool) + : TestMemIoCtxImpl(client, pool_id, pool_name, pool), + m_mock_client(mock_client), m_client(client) { + default_to_parent(); + } + + MockTestMemRadosClient *get_mock_rados_client() { + return m_mock_client; + } + + virtual TestIoCtxImpl *clone() { + TestIoCtxImpl *io_ctx_impl = new ::testing::NiceMock( + m_mock_client, m_client, get_pool_id(), get_pool_name(), get_pool()); + io_ctx_impl->set_snap_read(get_snap_read()); + io_ctx_impl->set_snap_context(get_snap_context()); + return io_ctx_impl; + } + + MOCK_METHOD7(exec, int(const std::string& oid, + TestClassHandler *handler, + const char *cls, + const char *method, + bufferlist& inbl, + bufferlist* outbl, + const SnapContext &snapc)); + int do_exec(const std::string& oid, TestClassHandler *handler, + const char *cls, const char *method, bufferlist& inbl, + bufferlist* outbl, const SnapContext &snapc) { + return TestMemIoCtxImpl::exec(oid, handler, cls, method, inbl, outbl, + snapc); + } + + MOCK_METHOD4(read, int(const std::string& oid, + size_t len, + uint64_t off, + bufferlist *bl)); + int do_read(const std::string& oid, size_t len, uint64_t off, + bufferlist *bl) { + return TestMemIoCtxImpl::read(oid, len, off, bl); + } + + MOCK_METHOD1(remove, int(const std::string& oid)); + int do_remove(const std::string& oid) { + return TestMemIoCtxImpl::remove(oid); + } + + MOCK_METHOD1(selfmanaged_snap_create, int(uint64_t *snap_id)); + int do_selfmanaged_snap_create(uint64_t *snap_id) { + return TestMemIoCtxImpl::selfmanaged_snap_create(snap_id); + } + + MOCK_METHOD1(selfmanaged_snap_remove, int(uint64_t snap_id)); + int do_selfmanaged_snap_remove(uint64_t snap_id) { + return TestMemIoCtxImpl::selfmanaged_snap_remove(snap_id); + } + + MOCK_METHOD3(write_full, int(const std::string& oid, + bufferlist& bl, + const SnapContext &snapc)); + int do_write_full(const std::string& oid, bufferlist& bl, + const SnapContext &snapc) { + return TestMemIoCtxImpl::write_full(oid, bl, snapc); + } + + void default_to_parent() { + using namespace ::testing; + + ON_CALL(*this, exec(_, _, _, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_exec)); + ON_CALL(*this, read(_, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_read)); + ON_CALL(*this, remove(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_remove)); + ON_CALL(*this, selfmanaged_snap_create(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_create)); + ON_CALL(*this, selfmanaged_snap_remove(_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_remove)); + ON_CALL(*this, write_full(_, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_write_full)); + } + +private: + MockTestMemRadosClient *m_mock_client; + TestMemRadosClient *m_client; +}; + +} // namespace librados + +#endif // LIBRADOS_TEST_STUB_MOCK_TEST_MEM_IO_CTX_IMPL_H diff --git a/src/test/librados_test_stub/MockTestMemRadosClient.h b/src/test/librados_test_stub/MockTestMemRadosClient.h new file mode 100644 index 00000000000..1d0b994afa6 --- /dev/null +++ b/src/test/librados_test_stub/MockTestMemRadosClient.h @@ -0,0 +1,36 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H +#define LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H + +#include "test/librados_test_stub/TestMemRadosClient.h" +#include "test/librados_test_stub/MockTestMemIoCtxImpl.h" +#include "gmock/gmock.h" + +namespace librados { + +class MockTestMemRadosClient : public TestMemRadosClient { +public: + MockTestMemRadosClient(CephContext *cct) : TestMemRadosClient(cct) { + default_to_dispatch(); + } + + MOCK_METHOD2(create_ioctx, TestIoCtxImpl *(int64_t pool_id, + const std::string &pool_name)); + TestIoCtxImpl *do_create_ioctx(int64_t pool_id, + const std::string &pool_name) { + return new ::testing::NiceMock( + this, this, pool_id, pool_name, get_pool(pool_name)); + } + + void default_to_dispatch() { + using namespace ::testing; + + ON_CALL(*this, create_ioctx(_, _)).WillByDefault(Invoke(this, &MockTestMemRadosClient::do_create_ioctx)); + } +}; + +} // namespace librados + +#endif // LIBRADOS_TEST_STUB_MOCK_TEST_MEM_RADOS_CLIENT_H diff --git a/src/test/librados_test_stub/TestClassHandler.cc b/src/test/librados_test_stub/TestClassHandler.cc index 1ac29cd3569..4f66e1effa8 100644 --- a/src/test/librados_test_stub/TestClassHandler.cc +++ b/src/test/librados_test_stub/TestClassHandler.cc @@ -2,10 +2,12 @@ // vim: ts=8 sw=2 smarttab #include "test/librados_test_stub/TestClassHandler.h" +#include "test/librados_test_stub/TestIoCtxImpl.h" #include #include #include #include "common/debug.h" +#include "include/assert.h" #define dout_subsys ceph_subsys_rados @@ -106,10 +108,16 @@ TestClassHandler::SharedMethodContext TestClassHandler::get_method_context( TestIoCtxImpl *io_ctx_impl, const std::string &oid, const SnapContext &snapc) { SharedMethodContext ctx(new MethodContext()); - ctx->io_ctx_impl = io_ctx_impl; + + // clone to ioctx to provide a firewall for gmock expectations + ctx->io_ctx_impl = io_ctx_impl->clone(); ctx->oid = oid; ctx->snapc = snapc; return ctx; } +TestClassHandler::MethodContext::~MethodContext() { + io_ctx_impl->put(); +} + } // namespace librados diff --git a/src/test/librados_test_stub/TestClassHandler.h b/src/test/librados_test_stub/TestClassHandler.h index 97062cec691..e25db273ae2 100644 --- a/src/test/librados_test_stub/TestClassHandler.h +++ b/src/test/librados_test_stub/TestClassHandler.h @@ -23,6 +23,8 @@ public: ~TestClassHandler(); struct MethodContext { + ~MethodContext(); + TestIoCtxImpl *io_ctx_impl; std::string oid; SnapContext snapc; diff --git a/src/test/librados_test_stub/TestIoCtxImpl.cc b/src/test/librados_test_stub/TestIoCtxImpl.cc index 1611a60fa21..e7582b46a4c 100644 --- a/src/test/librados_test_stub/TestIoCtxImpl.cc +++ b/src/test/librados_test_stub/TestIoCtxImpl.cc @@ -18,9 +18,9 @@ TestIoCtxImpl::TestIoCtxImpl() : m_client(NULL) { get(); } -TestIoCtxImpl::TestIoCtxImpl(TestRadosClient &client, int64_t pool_id, +TestIoCtxImpl::TestIoCtxImpl(TestRadosClient *client, int64_t pool_id, const std::string& pool_name) - : m_client(&client), m_pool_id(pool_id), m_pool_name(pool_name), + : m_client(client), m_pool_id(pool_id), m_pool_name(pool_name), m_snap_seq(CEPH_NOSNAP) { m_client->get(); @@ -113,17 +113,17 @@ int TestIoCtxImpl::aio_operate_read(const std::string& oid, return 0; } -int TestIoCtxImpl::exec(const std::string& oid, TestClassHandler &handler, +int TestIoCtxImpl::exec(const std::string& oid, TestClassHandler *handler, const char *cls, const char *method, bufferlist& inbl, bufferlist* outbl, const SnapContext &snapc) { - cls_method_cxx_call_t call = handler.get_method(cls, method); + cls_method_cxx_call_t call = handler->get_method(cls, method); if (call == NULL) { return -ENOSYS; } return (*call)(reinterpret_cast( - handler.get_method_context(this, oid, snapc).get()), &inbl, outbl); + handler->get_method_context(this, oid, snapc).get()), &inbl, outbl); } int TestIoCtxImpl::list_watchers(const std::string& o, diff --git a/src/test/librados_test_stub/TestIoCtxImpl.h b/src/test/librados_test_stub/TestIoCtxImpl.h index 3a715823888..1cb6213ff9d 100644 --- a/src/test/librados_test_stub/TestIoCtxImpl.h +++ b/src/test/librados_test_stub/TestIoCtxImpl.h @@ -36,7 +36,7 @@ class TestIoCtxImpl { public: TestIoCtxImpl(); - explicit TestIoCtxImpl(TestRadosClient &client, int64_t m_pool_id, + explicit TestIoCtxImpl(TestRadosClient *client, int64_t m_pool_id, const std::string& pool_name); TestRadosClient *get_rados_client() { @@ -46,6 +46,10 @@ public: void get(); void put(); + inline int64_t get_pool_id() const { + return m_pool_id; + } + virtual TestIoCtxImpl *clone() = 0; virtual uint64_t get_instance_id() const; @@ -56,6 +60,9 @@ public: return m_snap_seq; } + inline void set_snap_context(const SnapContext& snapc) { + m_snapc = snapc; + } const SnapContext &get_snap_context() const { return m_snapc; } @@ -73,7 +80,7 @@ public: virtual int assert_exists(const std::string &oid) = 0; virtual int create(const std::string& oid, bool exclusive) = 0; - virtual int exec(const std::string& oid, TestClassHandler &handler, + virtual int exec(const std::string& oid, TestClassHandler *handler, const char *cls, const char *method, bufferlist& inbl, bufferlist* outbl, const SnapContext &snapc); diff --git a/src/test/librados_test_stub/TestMemIoCtxImpl.cc b/src/test/librados_test_stub/TestMemIoCtxImpl.cc index bb39b49d62d..37fa6124e16 100644 --- a/src/test/librados_test_stub/TestMemIoCtxImpl.cc +++ b/src/test/librados_test_stub/TestMemIoCtxImpl.cc @@ -27,10 +27,10 @@ TestMemIoCtxImpl::TestMemIoCtxImpl(const TestMemIoCtxImpl& rhs) m_pool->get(); } -TestMemIoCtxImpl::TestMemIoCtxImpl(TestMemRadosClient &client, int64_t pool_id, +TestMemIoCtxImpl::TestMemIoCtxImpl(TestMemRadosClient *client, int64_t pool_id, const std::string& pool_name, TestMemRadosClient::Pool *pool) - : TestIoCtxImpl(client, pool_id, pool_name), m_client(&client), + : TestIoCtxImpl(client, pool_id, pool_name), m_client(client), m_pool(pool) { m_pool->get(); } diff --git a/src/test/librados_test_stub/TestMemIoCtxImpl.h b/src/test/librados_test_stub/TestMemIoCtxImpl.h index e58b97a3fec..aa6541530b5 100644 --- a/src/test/librados_test_stub/TestMemIoCtxImpl.h +++ b/src/test/librados_test_stub/TestMemIoCtxImpl.h @@ -12,7 +12,7 @@ namespace librados { class TestMemIoCtxImpl : public TestIoCtxImpl { public: TestMemIoCtxImpl(); - TestMemIoCtxImpl(TestMemRadosClient &client, int64_t m_pool_id, + TestMemIoCtxImpl(TestMemRadosClient *client, int64_t m_pool_id, const std::string& pool_name, TestMemRadosClient::Pool *pool); virtual ~TestMemIoCtxImpl(); @@ -56,6 +56,11 @@ public: bufferlist& bl); virtual int zero(const std::string& oid, uint64_t off, uint64_t len); +protected: + TestMemRadosClient::Pool *get_pool() { + return m_pool; + } + private: TestMemIoCtxImpl(const TestMemIoCtxImpl&); diff --git a/src/test/librados_test_stub/TestMemRadosClient.cc b/src/test/librados_test_stub/TestMemRadosClient.cc index b8bdf0777c3..6492d25b76b 100644 --- a/src/test/librados_test_stub/TestMemRadosClient.cc +++ b/src/test/librados_test_stub/TestMemRadosClient.cc @@ -38,10 +38,7 @@ TestMemRadosClient::Pool::Pool() TestIoCtxImpl *TestMemRadosClient::create_ioctx(int64_t pool_id, const std::string &pool_name) { - Pools::iterator iter = m_pools.find(pool_name); - assert(iter != m_pools.end()); - - return new TestMemIoCtxImpl(*this, pool_id, pool_name, iter->second); + return new TestMemIoCtxImpl(this, pool_id, pool_name, get_pool(pool_name)); } void TestMemRadosClient::object_list(int64_t pool_id, @@ -125,4 +122,11 @@ int TestMemRadosClient::blacklist_add(const std::string& client_address, return 0; } +TestMemRadosClient::Pool *TestMemRadosClient::get_pool( + const std::string &pool_name) { + Pools::iterator iter = m_pools.find(pool_name); + assert(iter != m_pools.end()); + return iter->second; +} + } // namespace librados diff --git a/src/test/librados_test_stub/TestMemRadosClient.h b/src/test/librados_test_stub/TestMemRadosClient.h index e0afacb3a70..dada74ec62a 100644 --- a/src/test/librados_test_stub/TestMemRadosClient.h +++ b/src/test/librados_test_stub/TestMemRadosClient.h @@ -87,6 +87,8 @@ public: protected: ~TestMemRadosClient(); + Pool *get_pool(const std::string &pool_name); + private: typedef std::map Pools; diff --git a/src/test/librados_test_stub/TestRadosClient.h b/src/test/librados_test_stub/TestRadosClient.h index a0611051cac..e811bafaa16 100644 --- a/src/test/librados_test_stub/TestRadosClient.h +++ b/src/test/librados_test_stub/TestRadosClient.h @@ -25,6 +25,11 @@ class TestIoCtxImpl; class TestRadosClient { public: + static void Deallocate(librados::TestRadosClient* client) + { + client->put(); + } + typedef boost::function AioFunction; struct Object {