]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librados_test_stub: add mock class for IoCtx operations
authorJason Dillaman <dillaman@redhat.com>
Tue, 18 Aug 2015 00:51:50 +0000 (20:51 -0400)
committerJason Dillaman <dillaman@redhat.com>
Fri, 4 Sep 2015 17:30:45 +0000 (13:30 -0400)
Unit tests can now use gmock to simulate responses from the OSDs
via a mocked librados library.

Signed-off-by: Jason Dillaman <dillaman@redhat.com>
13 files changed:
src/test/librados_test_stub/LibradosTestStub.cc
src/test/librados_test_stub/LibradosTestStub.h [new file with mode: 0644]
src/test/librados_test_stub/MockTestMemIoCtxImpl.h [new file with mode: 0644]
src/test/librados_test_stub/MockTestMemRadosClient.h [new file with mode: 0644]
src/test/librados_test_stub/TestClassHandler.cc
src/test/librados_test_stub/TestClassHandler.h
src/test/librados_test_stub/TestIoCtxImpl.cc
src/test/librados_test_stub/TestIoCtxImpl.h
src/test/librados_test_stub/TestMemIoCtxImpl.cc
src/test/librados_test_stub/TestMemIoCtxImpl.h
src/test/librados_test_stub/TestMemRadosClient.cc
src/test/librados_test_stub/TestMemRadosClient.h
src/test/librados_test_stub/TestRadosClient.h

index 7b70cfaf462f00c792b7f37164fa4241e4950b0a..1aa256ff18a2d805e2a99ae26d3656c7f0c2c3a3 100644 (file)
@@ -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"
 
 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<librados::TestClassHandler> 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<librados::TestRadosClient> 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<librados::TestRadosClient> &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<TestIoCtxImpl*>(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<TestObjectOperationImpl*>(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 (file)
index 0000000..9fed68d
--- /dev/null
@@ -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 <boost/shared_ptr.hpp>
+
+namespace librados {
+class TestRadosClient;
+}
+
+namespace librados_test_stub {
+
+typedef boost::shared_ptr<librados::TestRadosClient> 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 (file)
index 0000000..198db6c
--- /dev/null
@@ -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<MockTestMemIoCtxImpl>(
+      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 (file)
index 0000000..1d0b994
--- /dev/null
@@ -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<MockTestMemIoCtxImpl>(
+      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
index 1ac29cd35694ee70d05e64ec3869f75bb1c9404b..4f66e1effa8b6fe518220550467dc5327e28a1b2 100644 (file)
@@ -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 <boost/algorithm/string/predicate.hpp>
 #include <dlfcn.h>
 #include <errno.h>
 #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
index 97062cec6917b325c349f9d9bfb092be7e8216a8..e25db273ae23ce9b97db58254f15a447aaf7712f 100644 (file)
@@ -23,6 +23,8 @@ public:
   ~TestClassHandler();
 
   struct MethodContext {
+    ~MethodContext();
+
     TestIoCtxImpl *io_ctx_impl;
     std::string oid;
     SnapContext snapc;
index 1611a60fa21f4ee4462f7a895de6908c4809ff08..e7582b46a4ccc67a3864044d04ea2f0175b113b4 100644 (file)
@@ -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<cls_method_context_t>(
-    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,
index 3a7158238888396eda539ec403663cdd47cdfa1d..1cb6213ff9d8c7107c0461ebb5665ad6a27af1fe 100644 (file)
@@ -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);
index bb39b49d62db141dac2cb838bb0139c69b397126..37fa6124e16d10861c8063888fbd68aa974375f3 100644 (file)
@@ -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();
 }
index e58b97a3fecaef948aaeb845ac23690e3fb34432..aa6541530b5de125d87dd0d979829977f0ed6582 100644 (file)
@@ -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&);
 
index b8bdf0777c31f637b98a2f2dc21f16c8c85b9d28..6492d25b76b3d33f0f11ec174f8238a966ee87f0 100644 (file)
@@ -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
index e0afacb3a7054af98dc67aca94bb44bdabee5498..dada74ec62a1a9713124d38053b9326ab79a754e 100644 (file)
@@ -87,6 +87,8 @@ public:
 protected:
   ~TestMemRadosClient();
 
+  Pool *get_pool(const std::string &pool_name);
+
 private:
 
   typedef std::map<std::string, Pool*>         Pools;
index a0611051cac522b0f242a298d0588fecd2834288..e811bafaa16ad19f0e8dff6b6a1b0dcbf724cfbc 100644 (file)
@@ -25,6 +25,11 @@ class TestIoCtxImpl;
 class TestRadosClient {
 public:
 
+  static void Deallocate(librados::TestRadosClient* client)
+  {
+    client->put();
+  }
+
   typedef boost::function<int()> AioFunction;
 
   struct Object {