]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
test/rbd: new unit test cases for clone request state machine 19724/head
authorJason Dillaman <dillaman@redhat.com>
Wed, 20 Dec 2017 20:53:06 +0000 (15:53 -0500)
committerJason Dillaman <dillaman@redhat.com>
Tue, 2 Jan 2018 15:12:01 +0000 (10:12 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/librbd/image/CloneRequest.cc
src/test/librbd/CMakeLists.txt
src/test/librbd/image/test_mock_CloneRequest.cc [new file with mode: 0644]
src/test/librbd/image/test_mock_RemoveRequest.cc

index 5ececb2c8c684e1a2406a4e714843071ae95dd5f..d1ab9e4f19e9c6a55f547c2a66c51a40554890db 100644 (file)
@@ -70,13 +70,15 @@ void CloneRequest<I>::validate_options() {
   m_opts.get(RBD_IMAGE_OPTION_FORMAT, &format);
   if (format < 2) {
     lderr(m_cct) << "format 2 or later required for clone" << dendl;
-    return complete(-EINVAL);
+    complete(-EINVAL);
+    return;
   }
 
   if (m_opts.get(RBD_IMAGE_OPTION_FEATURES, &m_features) == 0) {
     if (m_features & ~RBD_FEATURES_ALL) {
       lderr(m_cct) << "librbd does not support requested features" << dendl;
-      return complete(-ENOSYS);
+      complete(-ENOSYS);
+      return;
     }
     m_use_p_features = false;
   }
@@ -90,12 +92,14 @@ void CloneRequest<I>::send_validate_parent() {
 
   if (m_p_imctx->snap_id == CEPH_NOSNAP) {
     lderr(m_cct) << "image to be cloned must be a snapshot" << dendl;
-    return complete(-EINVAL);
+    complete(-EINVAL);
+    return;
   }
 
   if (m_p_imctx->old_format) {
     lderr(m_cct) << "parent image must be in new format" << dendl;
-    return complete(-EINVAL);
+    complete(-EINVAL);
+    return;
   }
 
   int r = 0;
@@ -108,17 +112,20 @@ void CloneRequest<I>::send_validate_parent() {
 
   if ((m_p_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
     lderr(m_cct) << "parent image must support layering" << dendl;
-    return complete(-ENOSYS);
+    complete(-ENOSYS);
+    return;
   }
 
   if (r < 0) {
     lderr(m_cct) << "unable to locate parent's snapshot" << dendl;
-    return complete(r);
+    complete(r);
+    return;
   }
 
   if (!snap_protected) {
     lderr(m_cct) << "parent snapshot must be protected" << dendl;
-    return complete(-EINVAL);
+    complete(-EINVAL);
+    return;
   }
 
   send_validate_child();
@@ -145,7 +152,8 @@ void CloneRequest<I>::handle_validate_child(int r) {
 
   if (r != -ENOENT) {
     lderr(m_cct) << "rbd image " << m_name << " already exists" << dendl;
-    return complete(r);
+    complete(r);
+    return;
   }
 
   send_create();
@@ -165,7 +173,8 @@ void CloneRequest<I>::send_create() {
   }
   if ((m_features & RBD_FEATURE_LAYERING) != RBD_FEATURE_LAYERING) {
     lderr(m_cct) << "cloning image must support layering" << dendl;
-    return complete(-ENOSYS);
+    complete(-ENOSYS);
+    return;
   }
   m_opts.set(RBD_IMAGE_OPTION_FEATURES, m_features);
 
@@ -185,7 +194,8 @@ void CloneRequest<I>::handle_create(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "error creating child: " << cpp_strerror(r) << dendl;
-    return complete(r);
+    complete(r);
+    return;
   }
   send_open();
 }
@@ -209,7 +219,8 @@ void CloneRequest<I>::handle_open(int r) {
   if (r < 0) {
     lderr(m_cct) << "Error opening new image: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    return send_remove();
+    send_remove();
+    return;
   }
 
   send_set_parent();
@@ -238,7 +249,8 @@ void CloneRequest<I>::handle_set_parent(int r) {
   if (r < 0) {
     lderr(m_cct) << "couldn't set parent: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    return send_close();
+    send_close();
+    return;
   }
 
   send_add_child();
@@ -266,7 +278,8 @@ void CloneRequest<I>::handle_add_child(int r) {
   if (r < 0) {
     lderr(m_cct) << "couldn't add child: " << cpp_strerror(r) << dendl;
     m_r_saved = r;
-    return send_close();
+    send_close();
+    return;
   }
 
   send_refresh();
@@ -296,7 +309,8 @@ void CloneRequest<I>::handle_refresh(int r) {
 
   if (r < 0 || !snap_protected) {
     m_r_saved = -EINVAL;
-    return send_close();
+    send_remove_child();
+    return;
   }
 
   send_metadata_list();
@@ -338,7 +352,6 @@ void CloneRequest<I>::handle_metadata_list(int r) {
       m_r_saved = r;
       send_remove_child();
     }
-
     return;
   }
 
@@ -356,6 +369,11 @@ void CloneRequest<I>::handle_metadata_list(int r) {
 
 template <typename I>
 void CloneRequest<I>::send_metadata_set() {
+  if (m_pairs.empty()) {
+    get_mirror_mode();
+    return;
+  }
+
   ldout(m_cct, 20) << this << " " << __func__ << dendl;
 
   librados::ObjectWriteOperation op;
@@ -478,7 +496,8 @@ void CloneRequest<I>::handle_close(int r) {
 
   if (r < 0) {
     lderr(m_cct) << "couldn't close image: " << cpp_strerror(r) << dendl;
-    return complete(r);
+    complete(r);
+    return;
   }
 
   if (m_r_saved == 0) {
@@ -498,7 +517,7 @@ void CloneRequest<I>::send_remove_child() {
   using klass = CloneRequest<I>;
   librados::AioCompletion *comp =
     create_rados_callback<klass, &klass::handle_remove_child>(this);
-  int r = m_p_imctx->md_ctx.aio_operate(RBD_CHILDREN, comp, &op);
+  int r = m_ioctx.aio_operate(RBD_CHILDREN, comp, &op);
   assert(r == 0);
   comp->release();
 }
@@ -522,7 +541,7 @@ void CloneRequest<I>::send_remove() {
   using klass = CloneRequest<I>;
   Context *ctx = create_context_callback<klass, &klass::handle_remove>(this);
 
-  librbd::image::RemoveRequest<> *req = librbd::image::RemoveRequest<>::create(
+  auto req = librbd::image::RemoveRequest<I>::create(
    m_ioctx, m_name, m_id, false, false, m_no_op, m_op_work_queue, ctx);
   req->send();
 }
@@ -535,7 +554,7 @@ void CloneRequest<I>::handle_remove(int r) {
     lderr(m_cct) << "Error removing failed clone: "
                 << cpp_strerror(r) << dendl;
   }
-  complete(r);
+  complete(m_r_saved);
 }
 
 template <typename I>
index 2c22f0829e1d47808aa5fd346689f138b023be35..cad751fe1c5f744bb5c75e8804c75ac76507b849 100644 (file)
@@ -43,6 +43,7 @@ set(unittest_librbd_srcs
   exclusive_lock/test_mock_PreAcquireRequest.cc
   exclusive_lock/test_mock_PostAcquireRequest.cc
   exclusive_lock/test_mock_PreReleaseRequest.cc
+  image/test_mock_CloneRequest.cc
   image/test_mock_RefreshRequest.cc
   image/test_mock_RemoveRequest.cc
   io/test_mock_ImageRequest.cc
diff --git a/src/test/librbd/image/test_mock_CloneRequest.cc b/src/test/librbd/image/test_mock_CloneRequest.cc
new file mode 100644 (file)
index 0000000..0515db7
--- /dev/null
@@ -0,0 +1,763 @@
+// -*- 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 "test/librbd/mock/MockContextWQ.h"
+#include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "test/librados_test_stub/MockTestMemRadosClient.h"
+#include "librbd/ImageState.h"
+#include "librbd/Operations.h"
+#include "librbd/image/TypeTraits.h"
+#include "librbd/image/CreateRequest.h"
+#include "librbd/image/RemoveRequest.h"
+#include "librbd/image/RefreshRequest.h"
+#include "librbd/mirror/EnableRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  static MockTestImageCtx* s_instance;
+  static MockTestImageCtx* create(const std::string &image_name,
+                                  const std::string &image_id,
+                                  const char *snap, librados::IoCtx& p,
+                                  bool read_only) {
+    assert(s_instance != nullptr);
+    return s_instance;
+  }
+
+  MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+    s_instance = this;
+  }
+};
+
+MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
+
+} // anonymous namespace
+
+namespace image {
+
+template <>
+struct CreateRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static CreateRequest* s_instance;
+  static CreateRequest* create(IoCtx &ioctx, const std::string &image_name,
+                               const std::string &image_id, uint64_t size,
+                               const ImageOptions &image_options,
+                               const std::string &non_primary_global_image_id,
+                               const std::string &primary_mirror_uuid,
+                               bool skip_mirror_enable,
+                               ContextWQ *op_work_queue,
+                               Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  CreateRequest() {
+    s_instance = this;
+  }
+};
+
+CreateRequest<MockTestImageCtx>* CreateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct RefreshRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static RefreshRequest* s_instance;
+  static RefreshRequest* create(MockTestImageCtx &image_ctx,
+                                bool acquiring_lock, bool skip_open_parent,
+                                Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  RefreshRequest() {
+    s_instance = this;
+  }
+};
+
+RefreshRequest<MockTestImageCtx>* RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
+
+template <>
+struct RemoveRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static RemoveRequest* s_instance;
+  static RemoveRequest* create(librados::IoCtx &ioctx,
+                               const std::string &image_name,
+                               const std::string &image_id,
+                               bool force, bool from_trash_remove,
+                               ProgressContext &prog_ctx,
+                               ContextWQ *op_work_queue,
+                               Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  RemoveRequest() {
+    s_instance = this;
+  }
+};
+
+RemoveRequest<MockTestImageCtx>* RemoveRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace image
+
+namespace mirror {
+
+template <>
+struct EnableRequest<MockTestImageCtx> {
+  Context* on_finish = nullptr;
+  static EnableRequest* s_instance;
+  static EnableRequest* create(librados::IoCtx &io_ctx,
+                               const std::string &image_id,
+                               const std::string &non_primary_global_image_id,
+                               MockContextWQ *op_work_queue,
+                               Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  EnableRequest() {
+    s_instance = this;
+  }
+};
+
+EnableRequest<MockTestImageCtx>* EnableRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace mirror
+} // namespace librbd
+
+// template definitions
+#include "librbd/image/CloneRequest.cc"
+
+namespace librbd {
+namespace image {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+
+class TestMockImageCloneRequest : public TestMockFixture {
+public:
+  typedef CloneRequest<MockTestImageCtx> MockCloneRequest;
+  typedef CreateRequest<MockTestImageCtx> MockCreateRequest;
+  typedef RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+  typedef RemoveRequest<MockTestImageCtx> MockRemoveRequest;
+  typedef mirror::EnableRequest<MockTestImageCtx> MockMirrorEnableRequest;
+
+  void SetUp() override {
+    TestMockFixture::SetUp();
+
+    ASSERT_EQ(0, open_image(m_image_name, &image_ctx));
+    ASSERT_EQ(0, image_ctx->operations->snap_create(
+                   cls::rbd::UserSnapshotNamespace{}, "snap"));
+    if (is_feature_enabled(RBD_FEATURE_LAYERING)) {
+      ASSERT_EQ(0, image_ctx->operations->snap_protect(
+                     cls::rbd::UserSnapshotNamespace{}, "snap"));
+
+      C_SaferCond ctx;
+      image_ctx->state->snap_set(cls::rbd::UserSnapshotNamespace{}, "snap",
+                                 &ctx);
+      ASSERT_EQ(0, ctx.wait());
+    }
+  }
+
+  void expect_get_image_size(MockTestImageCtx &mock_image_ctx, uint64_t snap_id,
+                             uint64_t size) {
+    EXPECT_CALL(mock_image_ctx, get_image_size(snap_id))
+      .WillOnce(Return(size));
+  }
+
+  void expect_is_snap_protected(MockImageCtx &mock_image_ctx, bool is_protected,
+                                int r) {
+    EXPECT_CALL(mock_image_ctx, is_snap_protected(_, _))
+      .WillOnce(WithArg<1>(Invoke([is_protected, r](bool* is_prot) {
+                             *is_prot = is_protected;
+                             return r;
+                           })));
+  }
+
+  void expect_create(MockCreateRequest& mock_create_request, int r) {
+    EXPECT_CALL(mock_create_request, send())
+      .WillOnce(Invoke([this, &mock_create_request, r]() {
+                  image_ctx->op_work_queue->queue(mock_create_request.on_finish, r);
+                }));
+  }
+
+  void expect_open(MockImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(*mock_image_ctx.state, open(true, _))
+      .WillOnce(WithArg<1>(Invoke([this, r](Context* ctx) {
+                             image_ctx->op_work_queue->queue(ctx, r);
+                           })));
+  }
+
+  void expect_set_parent(MockImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"),
+                     StrEq("set_parent"), _, _, _))
+      .WillOnce(InvokeWithoutArgs([r]() {
+                  return r;
+                }));
+  }
+
+  void expect_add_child(librados::IoCtx& io_ctx, int r) {
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("add_child"), _, _, _))
+      .WillOnce(Return(r));
+  }
+
+  void expect_refresh(MockRefreshRequest& mock_refresh_request, int r) {
+    EXPECT_CALL(mock_refresh_request, send())
+      .WillOnce(Invoke([this, &mock_refresh_request, r]() {
+                  image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
+                }));
+  }
+
+  void expect_metadata_list(MockTestImageCtx &mock_image_ctx,
+                            const std::map<std::string, bufferlist>& metadata,
+                            int r) {
+    bufferlist out_bl;
+    ::encode(metadata, out_bl);
+
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("metadata_list"), _, _, _))
+      .WillOnce(WithArg<5>(Invoke([out_bl, r](bufferlist *out) {
+                             *out = out_bl;
+                             return r;
+                           })));
+  }
+
+  void expect_metadata_set(librados::IoCtx& io_ctx,
+                           MockTestImageCtx& mock_image_ctx,
+                           const std::map<std::string, bufferlist>& metadata,
+                           int r) {
+    bufferlist in_bl;
+    ::encode(metadata, in_bl);
+
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("metadata_set"),
+                     ContentsEqual(in_bl), _, _))
+      .WillOnce(Return(r));
+  }
+
+  void expect_test_features(MockTestImageCtx &mock_image_ctx,
+                            uint64_t features, bool enabled) {
+    EXPECT_CALL(mock_image_ctx, test_features(features))
+      .WillOnce(Return(enabled));
+  }
+
+  void expect_mirror_mode_get(MockTestImageCtx &mock_image_ctx,
+                              cls::rbd::MirrorMode mirror_mode, int r) {
+    bufferlist out_bl;
+    ::encode(static_cast<uint32_t>(mirror_mode), out_bl);
+
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+                exec(RBD_MIRRORING, _, StrEq("rbd"), StrEq("mirror_mode_get"),
+                     _, _, _))
+      .WillOnce(WithArg<5>(Invoke([out_bl, r](bufferlist* out) {
+                             *out = out_bl;
+                             return r;
+                           })));
+  }
+
+  void expect_mirror_enable(MockMirrorEnableRequest& mock_mirror_enable_request,
+                            int r) {
+    EXPECT_CALL(mock_mirror_enable_request, send())
+      .WillOnce(Invoke([this, &mock_mirror_enable_request, r]() {
+                  image_ctx->op_work_queue->queue(mock_mirror_enable_request.on_finish, r);
+                }));
+  }
+
+  void expect_close(MockImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(*mock_image_ctx.state, close(_))
+      .WillOnce(Invoke([this, r](Context* ctx) {
+                  image_ctx->op_work_queue->queue(ctx, r);
+                }));
+    EXPECT_CALL(mock_image_ctx, destroy());
+  }
+
+  void expect_remove_child(librados::IoCtx& io_ctx, int r) {
+    EXPECT_CALL(get_mock_io_ctx(io_ctx),
+                exec(RBD_CHILDREN, _, StrEq("rbd"), StrEq("remove_child"), _, _, _))
+      .WillOnce(Return(r));
+  }
+
+  void expect_remove(MockRemoveRequest& mock_remove_request, int r) {
+    EXPECT_CALL(mock_remove_request, send())
+      .WillOnce(Invoke([this, &mock_remove_request, r]() {
+                  image_ctx->op_work_queue->queue(mock_remove_request.on_finish, r);
+                }));
+  }
+
+  librbd::ImageCtx *image_ctx;
+};
+
+TEST_F(TestMockImageCloneRequest, Success) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0);
+  expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, 0);
+
+  MockMirrorEnableRequest mock_mirror_enable_request;
+  if (is_feature_enabled(RBD_FEATURE_JOURNALING)) {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+    expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+    expect_mirror_enable(mock_mirror_enable_request, 0);
+  } else {
+    expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+  }
+
+  expect_close(mock_image_ctx, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(0, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CreateError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, -EINVAL);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, OpenError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, -EINVAL);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, SetParentError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, -EINVAL);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, AddChildError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, -EINVAL);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, RefreshError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, -EINVAL);
+
+  expect_remove_child(m_ioctx, 0);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, MetadataListError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {{"key", {}}}, -EINVAL);
+
+  expect_remove_child(m_ioctx, 0);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, MetadataSetError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {{"key", {}}}, 0);
+  expect_metadata_set(m_ioctx, mock_image_ctx, {{"key", {}}}, -EINVAL);
+
+  expect_remove_child(m_ioctx, 0);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, GetMirrorModeError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {}, 0);
+
+  expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+  expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, -EINVAL);
+
+  expect_remove_child(m_ioctx, 0);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, MirrorEnableError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING | RBD_FEATURE_JOURNALING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {}, 0);
+
+  expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, true);
+  expect_mirror_mode_get(mock_image_ctx, cls::rbd::MIRROR_MODE_POOL, 0);
+
+  MockMirrorEnableRequest mock_mirror_enable_request;
+  expect_mirror_enable(mock_mirror_enable_request, -EINVAL);
+
+  expect_remove_child(m_ioctx, 0);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, CloseError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, 0);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  expect_metadata_list(mock_image_ctx, {}, 0);
+  expect_test_features(mock_image_ctx, RBD_FEATURE_JOURNALING, false);
+
+  expect_close(mock_image_ctx, -EINVAL);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, RemoveChildError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, 0);
+  expect_set_parent(mock_image_ctx, 0);
+  expect_add_child(m_ioctx, 0);
+
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_refresh_request, -EINVAL);
+
+  expect_remove_child(m_ioctx, -EPERM);
+  expect_close(mock_image_ctx, 0);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, 0);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+TEST_F(TestMockImageCloneRequest, RemoveError) {
+  REQUIRE_FEATURE(RBD_FEATURE_LAYERING);
+
+  MockTestImageCtx mock_image_ctx(*image_ctx);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_get_image_size(mock_image_ctx, mock_image_ctx.snaps.front(), 123);
+  expect_is_snap_protected(mock_image_ctx, true, 0);
+
+  MockCreateRequest mock_create_request;
+  expect_create(mock_create_request, 0);
+
+  expect_open(mock_image_ctx, -EINVAL);
+
+  MockRemoveRequest mock_remove_request;
+  expect_remove(mock_remove_request, -EPERM);
+
+  C_SaferCond ctx;
+  ImageOptions clone_opts;
+  auto req = new MockCloneRequest(&mock_image_ctx, m_ioctx, "clone name",
+                                  "clone id", clone_opts, "", "",
+                                  image_ctx->op_work_queue, &ctx);
+  req->send();
+  ASSERT_EQ(-EINVAL, ctx.wait());
+}
+
+} // namespace image
+} // namespace librbd
index 77c536d03a8712e46cc04c6797f9ad8a08f20042..8dc2945c29cfc2b4e4097981704135949fc7d6c7 100644 (file)
@@ -1,4 +1,4 @@
-// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// -*- 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"