]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test: unit test cases for rbd::mirror::image_sync::ObjectCopyRequest
authorJason Dillaman <dillaman@redhat.com>
Sun, 6 Mar 2016 18:09:38 +0000 (13:09 -0500)
committerJason Dillaman <dillaman@redhat.com>
Sun, 13 Mar 2016 03:40:15 +0000 (22:40 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/Makefile-client.am
src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc [new file with mode: 0644]
src/test/rbd_mirror/test_fixture.cc [new file with mode: 0644]
src/test/rbd_mirror/test_fixture.h [new file with mode: 0644]
src/test/rbd_mirror/test_mock_fixture.cc [new file with mode: 0644]
src/test/rbd_mirror/test_mock_fixture.h [new file with mode: 0644]

index 3edca402abda14bc26bff77234d9632835e95cc8..e18c4ec923fec08d550a5e78651a0e78552b99f0 100644 (file)
@@ -441,12 +441,20 @@ noinst_HEADERS += \
 librbd_mirror_test_la_SOURCES = \
        test/rbd_mirror/test_ClusterWatcher.cc \
        test/rbd_mirror/test_PoolWatcher.cc \
-       test/rbd_mirror/test_ImageReplayer.cc
+       test/rbd_mirror/test_ImageReplayer.cc \
+       test/rbd_mirror/test_fixture.cc \
+       test/rbd_mirror/test_mock_fixture.cc
+
+noinst_HEADERS += \
+       test/rbd_mirror/test_fixture.h \
+       test/rbd_mirror/test_mock_fixture.h
+
 librbd_mirror_test_la_CXXFLAGS = $(UNITTEST_CXXFLAGS)
 noinst_LTLIBRARIES += librbd_mirror_test.la
 
 unittest_rbd_mirror_SOURCES = \
-       test/rbd_mirror/test_main.cc
+       test/rbd_mirror/test_main.cc \
+       test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc
 unittest_rbd_mirror_CXXFLAGS = $(UNITTEST_CXXFLAGS)
 unittest_rbd_mirror_LDADD = \
        librbd_mirror_test.la \
diff --git a/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc b/src/test/rbd_mirror/image_sync/test_mock_ObjectCopyRequest.cc
new file mode 100644 (file)
index 0000000..14997b4
--- /dev/null
@@ -0,0 +1,459 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "include/interval_set.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librbd/mock/MockImageCtx.h"
+#include "tools/rbd_mirror/image_sync/ObjectCopyRequest.h"
+
+// template definitions
+#include "tools/rbd_mirror/image_sync/ObjectCopyRequest.cc"
+template class rbd::mirror::image_sync::ObjectCopyRequest<librbd::MockImageCtx>;
+
+namespace rbd {
+namespace mirror {
+namespace image_sync {
+
+using ::testing::_;
+using ::testing::DoDefault;
+using ::testing::InSequence;
+using ::testing::Return;
+
+namespace {
+
+void scribble(librados::IoCtx &io_ctx, const std::string &oid, int num_ops,
+              size_t max_size, interval_set<uint64_t> *what)
+{
+  uint64_t object_size = 1 << 22;
+  librados::ObjectWriteOperation op;
+  for (int i=0; i<num_ops; i++) {
+    uint64_t off = rand() % (object_size - max_size + 1);
+    uint64_t len = 1 + rand() % max_size;
+
+    bufferlist bl;
+    bl.append(std::string(len, '1'));
+
+    op.write(off, bl);
+
+    interval_set<uint64_t> w;
+    w.insert(off, len);
+    what->union_of(w);
+  }
+
+  ASSERT_EQ(0, io_ctx.operate(oid, &op));
+  std::cout << " wrote " << *what << std::endl;
+}
+
+} // anonymous namespace
+
+class TestMockImageSyncObjectCopyRequest : public TestMockFixture {
+public:
+  typedef ObjectCopyRequest<librbd::MockImageCtx> MockObjectCopyRequest;
+
+  virtual void SetUp() {
+    TestMockFixture::SetUp();
+
+    librbd::RBD rbd;
+    ASSERT_EQ(0, create_image(rbd, m_remote_io_ctx, m_image_name, m_image_size));
+    ASSERT_EQ(0, open_image(m_remote_io_ctx, m_image_name, &m_remote_image_ctx));
+
+    ASSERT_EQ(0, create_image(rbd, m_local_io_ctx, m_image_name, m_image_size));
+    ASSERT_EQ(0, open_image(m_local_io_ctx, m_image_name, &m_local_image_ctx));
+  }
+
+  void expect_list_snaps(librbd::MockImageCtx &mock_image_ctx,
+                         librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) {
+    auto &expect = EXPECT_CALL(mock_io_ctx,
+                               list_snaps(mock_image_ctx.image_ctx->get_object_name(0),
+                                          _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_get_object_name(librbd::MockImageCtx &mock_image_ctx) {
+    EXPECT_CALL(mock_image_ctx, get_object_name(0))
+                  .WillOnce(Return(mock_image_ctx.image_ctx->get_object_name(0)));
+  }
+
+  MockObjectCopyRequest *create_request(librbd::MockImageCtx &mock_remote_image_ctx,
+                                        librbd::MockImageCtx &mock_local_image_ctx,
+                                        Context *on_finish) {
+    expect_get_object_name(mock_local_image_ctx);
+    expect_get_object_name(mock_remote_image_ctx);
+    return new MockObjectCopyRequest(&mock_local_image_ctx,
+                                     &mock_remote_image_ctx, &m_snap_map,
+                                     0, on_finish);
+  }
+
+  void expect_read(librados::MockTestMemIoCtxImpl &mock_io_ctx, uint64_t offset,
+                   uint64_t length, int r) {
+    auto &expect = EXPECT_CALL(mock_io_ctx, read(_, length, offset, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_read(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+                   const interval_set<uint64_t> &extents, int r) {
+    for (auto extent : extents) {
+      expect_read(mock_io_ctx, extent.first, extent.second, r);
+      if (r < 0) {
+        break;
+      }
+    }
+  }
+
+  void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+                    uint64_t offset, uint64_t length, int r) {
+    auto &expect = EXPECT_CALL(mock_io_ctx, write(_, _, length, offset, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_write(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+                    const interval_set<uint64_t> &extents, int r) {
+    for (auto extent : extents) {
+      expect_write(mock_io_ctx, extent.first, extent.second, r);
+      if (r < 0) {
+        break;
+      }
+    }
+  }
+
+  void expect_truncate(librados::MockTestMemIoCtxImpl &mock_io_ctx,
+                       uint64_t offset, int r) {
+    auto &expect = EXPECT_CALL(mock_io_ctx, truncate(_, offset, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  void expect_remove(librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) {
+    auto &expect = EXPECT_CALL(mock_io_ctx, remove(_, _));
+    if (r < 0) {
+      expect.WillOnce(Return(r));
+    } else {
+      expect.WillOnce(DoDefault());
+    }
+  }
+
+  int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
+                  librados::snap_t *snap_id) {
+    int r = image_ctx->operations->snap_create(snap_name);
+    if (r < 0) {
+      return r;
+    }
+
+    r = image_ctx->state->refresh();
+    if (r < 0) {
+      return r;
+    }
+
+    if (image_ctx->snap_ids.count(snap_name) == 0) {
+      return -ENOENT;
+    }
+    *snap_id = image_ctx->snap_ids[snap_name];
+    return 0;
+  }
+
+  int create_snap(const char* snap_name) {
+    librados::snap_t remote_snap_id;
+    int r = create_snap(m_remote_image_ctx, snap_name, &remote_snap_id);
+    if (r < 0) {
+      return r;
+    }
+
+    librados::snap_t local_snap_id;
+    r = create_snap(m_local_image_ctx, snap_name, &local_snap_id);
+    if (r < 0) {
+      return r;
+    }
+
+    // collection of all existing snaps in local image
+    MockObjectCopyRequest::SnapIds local_snap_ids({local_snap_id});
+    if (!m_snap_map.empty()) {
+      local_snap_ids.insert(local_snap_ids.end(),
+                            m_snap_map.rbegin()->second.begin(),
+                            m_snap_map.rbegin()->second.end());
+    }
+    m_snap_map[remote_snap_id] = local_snap_ids;
+    return 0;
+  }
+
+  int compare_objects() {
+    MockObjectCopyRequest::SnapMap snap_map(m_snap_map);
+    if (snap_map.empty()) {
+      return -ENOENT;
+    }
+
+    std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+    std::string local_oid(m_local_image_ctx->get_object_name(0));
+
+    librados::IoCtx remote_io_ctx;
+    remote_io_ctx.dup(m_remote_image_ctx->data_ctx);
+
+    librados::IoCtx local_io_ctx;
+    local_io_ctx.dup(m_local_image_ctx->data_ctx);
+
+    while (!snap_map.empty()) {
+      librados::snap_t remote_snap_id = snap_map.begin()->first;
+      librados::snap_t local_snap_id = *snap_map.begin()->second.begin();
+      snap_map.erase(snap_map.begin());
+      std::cout << "comparing " << remote_snap_id << " to " << local_snap_id
+                << std::endl;
+
+      remote_io_ctx.snap_set_read(remote_snap_id);
+      bufferlist remote_bl;
+      int remote_r = remote_io_ctx.read(remote_oid, remote_bl, 1<<22, 0);
+
+      local_io_ctx.snap_set_read(local_snap_id);
+      bufferlist local_bl;
+      int local_r = local_io_ctx.read(local_oid, local_bl, 1<<22, 0);
+
+      if (remote_r != local_r) {
+        return remote_r < 0 ? remote_r : local_r;
+      }
+
+      if (!remote_bl.contents_equal(local_bl)) {
+        return -EBADMSG;
+      }
+    }
+    return 0;
+  }
+
+  librbd::ImageCtx *m_remote_image_ctx;
+  librbd::ImageCtx *m_local_image_ctx;
+
+  MockObjectCopyRequest::SnapMap m_snap_map;
+
+};
+
+TEST_F(TestMockImageSyncObjectCopyRequest, DNE) {
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, -ENOENT);
+
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, Write) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 102400, &one);
+
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+  librados::MockTestMemIoCtxImpl &mock_local_io_ctx(get_mock_io_ctx(
+    request->get_local_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), 0);
+  expect_write(mock_local_io_ctx, 0, one.range_end(), 0);
+
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+  ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, ReadError) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 102400, &one);
+
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), -EINVAL);
+
+  request->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, WriteError) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 102400, &one);
+
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+  librados::MockTestMemIoCtxImpl &mock_local_io_ctx(get_mock_io_ctx(
+    request->get_local_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), 0);
+  expect_write(mock_local_io_ctx, 0, one.range_end(), -EINVAL);
+
+  request->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, WriteSnaps) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 1024, &one);
+  ASSERT_EQ(0, create_snap("one"));
+
+  interval_set<uint64_t> two;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 1024, &two);
+  ASSERT_EQ(0, create_snap("two"));
+
+  if (one.range_end() < two.range_end()) {
+    interval_set<uint64_t> resize_diff;
+    resize_diff.insert(one.range_end(), two.range_end() - one.range_end());
+    two.union_of(resize_diff);
+  }
+
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+  librados::MockTestMemIoCtxImpl &mock_local_io_ctx(get_mock_io_ctx(
+    request->get_local_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), 0);
+  expect_write(mock_local_io_ctx, 0, one.range_end(), 0);
+  expect_read(mock_remote_io_ctx, two, 0);
+  expect_write(mock_local_io_ctx, two, 0);
+
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+  ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, Trim) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 1024, &one);
+  ASSERT_EQ(0, create_snap("one"));
+
+  // trim the object
+  uint64_t trim_offset = rand() % one.range_end();
+  ASSERT_EQ(0, m_remote_image_ctx->data_ctx.trunc(remote_oid, trim_offset));
+  ASSERT_EQ(0, create_snap("sync"));
+
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+  librados::MockTestMemIoCtxImpl &mock_local_io_ctx(get_mock_io_ctx(
+    request->get_local_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), 0);
+  expect_write(mock_local_io_ctx, 0, one.range_end(), 0);
+  expect_truncate(mock_local_io_ctx, trim_offset, 0);
+
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+  ASSERT_EQ(0, compare_objects());
+}
+
+TEST_F(TestMockImageSyncObjectCopyRequest, Remove) {
+  // scribble some data
+  std::string remote_oid(m_remote_image_ctx->get_object_name(0));
+  interval_set<uint64_t> one;
+  scribble(m_remote_image_ctx->data_ctx, remote_oid, 10, 1024, &one);
+  ASSERT_EQ(0, create_snap("one"));
+
+  // remove the object
+  ASSERT_EQ(0, m_remote_image_ctx->data_ctx.remove(remote_oid));
+  ASSERT_EQ(0, create_snap("sync"));
+  librbd::MockImageCtx mock_remote_image_ctx(*m_remote_image_ctx);
+  librbd::MockImageCtx mock_local_image_ctx(*m_local_image_ctx);
+
+  C_SaferCond ctx;
+  MockObjectCopyRequest *request = create_request(mock_remote_image_ctx,
+                                                  mock_local_image_ctx, &ctx);
+
+  librados::MockTestMemIoCtxImpl &mock_remote_io_ctx(get_mock_io_ctx(
+    request->get_remote_io_ctx()));
+  librados::MockTestMemIoCtxImpl &mock_local_io_ctx(get_mock_io_ctx(
+    request->get_local_io_ctx()));
+
+  InSequence seq;
+  expect_list_snaps(mock_remote_image_ctx, mock_remote_io_ctx, 0);
+  expect_read(mock_remote_io_ctx, 0, one.range_end(), 0);
+  expect_write(mock_local_io_ctx, 0, one.range_end(), 0);
+  expect_remove(mock_local_io_ctx, 0);
+
+  request->send();
+  ASSERT_EQ(0, ctx.wait());
+  ASSERT_EQ(0, compare_objects());
+}
+
+} // namespace image_sync
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_fixture.cc b/src/test/rbd_mirror/test_fixture.cc
new file mode 100644 (file)
index 0000000..51d25c2
--- /dev/null
@@ -0,0 +1,72 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_fixture.h"
+#include "include/stringify.h"
+#include "include/rbd/librbd.hpp"
+#include "librbd/ImageCtx.h"
+#include "librbd/ImageState.h"
+#include "test/librados/test.h"
+
+namespace rbd {
+namespace mirror {
+
+std::string TestFixture::_local_pool_name;
+std::string TestFixture::_remote_pool_name;
+librados::Rados TestFixture::_rados;
+uint64_t TestFixture::_image_number = 0;
+
+TestFixture::TestFixture() {
+}
+
+void TestFixture::SetUpTestCase() {
+  _local_pool_name = get_temp_pool_name("test-rbd-mirror-");
+  ASSERT_EQ("", create_one_pool_pp(_local_pool_name, _rados));
+
+  _remote_pool_name = get_temp_pool_name("test-rbd-mirror-");
+  ASSERT_EQ("", create_one_pool_pp(_remote_pool_name, _rados));
+}
+
+void TestFixture::TearDownTestCase() {
+  ASSERT_EQ(0, _rados.pool_delete(_remote_pool_name.c_str()));
+  ASSERT_EQ(0, _rados.pool_delete(_local_pool_name.c_str()));
+  _rados.shutdown();
+}
+
+void TestFixture::SetUp() {
+  ASSERT_EQ(0, _rados.ioctx_create(_local_pool_name.c_str(), m_local_io_ctx));
+  ASSERT_EQ(0, _rados.ioctx_create(_remote_pool_name.c_str(), m_remote_io_ctx));
+  m_image_name = get_temp_image_name();
+}
+
+void TestFixture::TearDown() {
+  for (auto image_ctx : m_image_ctxs) {
+    image_ctx->state->close();
+  }
+
+  m_remote_io_ctx.close();
+  m_local_io_ctx.close();
+}
+
+int TestFixture::create_image(librbd::RBD &rbd, librados::IoCtx &ioctx,
+                              const std::string &name, uint64_t size) {
+  int order = 0;
+  return rbd.create2(ioctx, name.c_str(), size, RBD_FEATURES_ALL, &order);
+}
+
+int TestFixture::open_image(librados::IoCtx &io_ctx,
+                            const std::string &image_name,
+                            librbd::ImageCtx **image_ctx) {
+  *image_ctx = new librbd::ImageCtx(image_name.c_str(), "", NULL, io_ctx,
+                                    false);
+  m_image_ctxs.insert(*image_ctx);
+  return (*image_ctx)->state->open();
+}
+
+std::string TestFixture::get_temp_image_name() {
+  ++_image_number;
+  return "image" + stringify(_image_number);
+}
+
+} // namespace mirror
+} // namespace rbd
diff --git a/src/test/rbd_mirror/test_fixture.h b/src/test/rbd_mirror/test_fixture.h
new file mode 100644 (file)
index 0000000..4297615
--- /dev/null
@@ -0,0 +1,54 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
+#define CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
+
+#include "include/int_types.h"
+#include "include/rados/librados.hpp"
+#include <gtest/gtest.h>
+#include <set>
+
+namespace librbd {
+class ImageCtx;
+class RBD;
+}
+
+namespace rbd {
+namespace mirror {
+
+class TestFixture : public ::testing::Test {
+public:
+  TestFixture();
+
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+
+  virtual void SetUp();
+  virtual void TearDown();
+
+  librados::IoCtx m_local_io_ctx;
+  librados::IoCtx m_remote_io_ctx;
+
+  std::string m_image_name;
+  uint64_t m_image_size = 2 << 20;
+
+  std::set<librbd::ImageCtx *> m_image_ctxs;
+
+  int create_image(librbd::RBD &rbd, librados::IoCtx &ioctx,
+                   const std::string &name, uint64_t size);
+  int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
+                 librbd::ImageCtx **image_ctx);
+
+  static std::string get_temp_image_name();
+
+  static std::string _local_pool_name;
+  static std::string _remote_pool_name;
+  static librados::Rados _rados;
+  static uint64_t _image_number;
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#endif // CEPH_TEST_RBD_MIRROR_TEST_FIXTURE_H
diff --git a/src/test/rbd_mirror/test_mock_fixture.cc b/src/test/rbd_mirror/test_mock_fixture.cc
new file mode 100644 (file)
index 0000000..e64297b
--- /dev/null
@@ -0,0 +1,53 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "test/rbd_mirror/test_mock_fixture.h"
+#include "test/librados_test_stub/LibradosTestStub.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+
+namespace rbd {
+namespace mirror {
+
+TestMockFixture::TestRadosClientPtr TestMockFixture::s_test_rados_client;
+::testing::NiceMock<librados::MockTestMemRadosClient> *
+  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<librados::MockTestMemRadosClient>(
+      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);
+  s_test_rados_client->put();
+  s_test_rados_client.reset();
+}
+
+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();
+}
+
+librados::MockTestMemIoCtxImpl &TestMockFixture::get_mock_io_ctx(
+    librados::IoCtx &ioctx) {
+  librados::MockTestMemIoCtxImpl **mock =
+    reinterpret_cast<librados::MockTestMemIoCtxImpl **>(&ioctx);
+  return **mock;
+}
+
+} // namespace mirror
+} // namespace rbd
+
diff --git a/src/test/rbd_mirror/test_mock_fixture.h b/src/test/rbd_mirror/test_mock_fixture.h
new file mode 100644 (file)
index 0000000..9038c79
--- /dev/null
@@ -0,0 +1,43 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#ifndef CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H
+#define CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H
+
+#include "test/rbd_mirror/test_fixture.h"
+#include <boost/shared_ptr.hpp>
+#include <gmock/gmock.h>
+
+namespace librados {
+class TestRadosClient;
+class MockTestMemIoCtxImpl;
+class MockTestMemRadosClient;
+}
+
+namespace rbd {
+namespace mirror {
+
+class TestMockFixture : public TestFixture {
+public:
+  typedef boost::shared_ptr<librados::TestRadosClient> TestRadosClientPtr;
+
+  static void SetUpTestCase();
+  static void TearDownTestCase();
+
+  virtual void SetUp();
+  virtual void TearDown();
+
+  ::testing::NiceMock<librados::MockTestMemRadosClient> &get_mock_rados_client() {
+    return *s_mock_rados_client;
+  }
+  librados::MockTestMemIoCtxImpl &get_mock_io_ctx(librados::IoCtx &ioctx);
+
+private:
+  static TestRadosClientPtr s_test_rados_client;
+  static ::testing::NiceMock<librados::MockTestMemRadosClient> *s_mock_rados_client;
+};
+
+} // namespace mirror
+} // namespace rbd
+
+#endif // CEPH_TEST_RBD_MIRROR_TEST_MOCK_FIXTURE_H