initial code repo
[stor4nfv.git] / src / ceph / src / test / librbd / test_mock_ObjectMap.cc
diff --git a/src/ceph/src/test/librbd/test_mock_ObjectMap.cc b/src/ceph/src/test/librbd/test_mock_ObjectMap.cc
new file mode 100644 (file)
index 0000000..7deaef0
--- /dev/null
@@ -0,0 +1,273 @@
+// -*- 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 "librbd/ObjectMap.h"
+#include "librbd/object_map/RefreshRequest.h"
+#include "librbd/object_map/UnlockRequest.h"
+#include "librbd/object_map/UpdateRequest.h"
+
+namespace librbd {
+
+namespace {
+
+struct MockTestImageCtx : public MockImageCtx {
+  MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) {
+  }
+};
+
+} // anonymous namespace
+
+namespace object_map {
+
+template <>
+struct RefreshRequest<MockTestImageCtx> {
+  Context *on_finish = nullptr;
+  ceph::BitVector<2u> *object_map = nullptr;
+  static RefreshRequest *s_instance;
+  static RefreshRequest *create(MockTestImageCtx &image_ctx,
+                                ceph::BitVector<2u> *object_map,
+                                uint64_t snap_id, Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    s_instance->object_map = object_map;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+
+  RefreshRequest() {
+    s_instance = this;
+  }
+};
+
+template <>
+struct UnlockRequest<MockTestImageCtx> {
+  Context *on_finish = nullptr;
+  static UnlockRequest *s_instance;
+  static UnlockRequest *create(MockTestImageCtx &image_ctx,
+                               Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  MOCK_METHOD0(send, void());
+  UnlockRequest() {
+    s_instance = this;
+  }
+};
+
+template <>
+struct UpdateRequest<MockTestImageCtx> {
+  Context *on_finish = nullptr;
+  static UpdateRequest *s_instance;
+  static UpdateRequest *create(MockTestImageCtx &image_ctx,
+                               ceph::BitVector<2u> *object_map,
+                               uint64_t snap_id,
+                               uint64_t start_object_no, uint64_t end_object_no,
+                               uint8_t new_state,
+                               const boost::optional<uint8_t> &current_state,
+                               const ZTracer::Trace &parent_trace,
+                               Context *on_finish) {
+    assert(s_instance != nullptr);
+    s_instance->on_finish = on_finish;
+    s_instance->construct(snap_id, start_object_no, end_object_no, new_state,
+                          current_state);
+    return s_instance;
+  }
+
+  MOCK_METHOD5(construct, void(uint64_t snap_id, uint64_t start_object_no,
+                               uint64_t end_object_no, uint8_t new_state,
+                               const boost::optional<uint8_t> &current_state));
+  MOCK_METHOD0(send, void());
+  UpdateRequest() {
+    s_instance = this;
+  }
+};
+
+RefreshRequest<MockTestImageCtx> *RefreshRequest<MockTestImageCtx>::s_instance = nullptr;
+UnlockRequest<MockTestImageCtx> *UnlockRequest<MockTestImageCtx>::s_instance = nullptr;
+UpdateRequest<MockTestImageCtx> *UpdateRequest<MockTestImageCtx>::s_instance = nullptr;
+
+} // namespace object_map
+} // namespace librbd
+
+#include "librbd/ObjectMap.cc"
+
+namespace librbd {
+
+using testing::_;
+using testing::InSequence;
+using testing::Invoke;
+
+class TestMockObjectMap : public TestMockFixture {
+public:
+  typedef ObjectMap<MockTestImageCtx> MockObjectMap;
+  typedef object_map::RefreshRequest<MockTestImageCtx> MockRefreshRequest;
+  typedef object_map::UnlockRequest<MockTestImageCtx> MockUnlockRequest;
+  typedef object_map::UpdateRequest<MockTestImageCtx> MockUpdateRequest;
+
+  void expect_refresh(MockTestImageCtx &mock_image_ctx,
+                      MockRefreshRequest &mock_refresh_request,
+                      const ceph::BitVector<2u> &object_map, int r) {
+    EXPECT_CALL(mock_refresh_request, send())
+      .WillOnce(Invoke([&mock_image_ctx, &mock_refresh_request, &object_map, r]() {
+          *mock_refresh_request.object_map = object_map;
+          mock_image_ctx.image_ctx->op_work_queue->queue(mock_refresh_request.on_finish, r);
+        }));
+  }
+
+  void expect_unlock(MockTestImageCtx &mock_image_ctx,
+                     MockUnlockRequest &mock_unlock_request, int r) {
+    EXPECT_CALL(mock_unlock_request, send())
+      .WillOnce(Invoke([&mock_image_ctx, &mock_unlock_request, r]() {
+          mock_image_ctx.image_ctx->op_work_queue->queue(mock_unlock_request.on_finish, r);
+        }));
+  }
+
+  void expect_update(MockTestImageCtx &mock_image_ctx,
+                     MockUpdateRequest &mock_update_request,
+                     uint64_t snap_id, uint64_t start_object_no,
+                     uint64_t end_object_no, uint8_t new_state,
+                     const boost::optional<uint8_t> &current_state,
+                     Context **on_finish) {
+    EXPECT_CALL(mock_update_request, construct(snap_id, start_object_no,
+                                               end_object_no, new_state,
+                                               current_state))
+      .Times(1);
+    EXPECT_CALL(mock_update_request, send())
+      .WillOnce(Invoke([&mock_image_ctx, &mock_update_request, on_finish]() {
+          *on_finish = mock_update_request.on_finish;
+        }));
+  }
+
+};
+
+TEST_F(TestMockObjectMap, NonDetainedUpdate) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+
+  InSequence seq;
+  ceph::BitVector<2u> object_map;
+  object_map.resize(4);
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
+
+  MockUpdateRequest mock_update_request;
+  Context *finish_update_1;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                0, 1, 1, {}, &finish_update_1);
+  Context *finish_update_2;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                1, 2, 1, {}, &finish_update_2);
+
+  MockUnlockRequest mock_unlock_request;
+  expect_unlock(mock_image_ctx, mock_unlock_request, 0);
+
+  MockObjectMap mock_object_map(mock_image_ctx, CEPH_NOSNAP);
+  C_SaferCond open_ctx;
+  mock_object_map.open(&open_ctx);
+  ASSERT_EQ(0, open_ctx.wait());
+
+  C_SaferCond update_ctx1;
+  C_SaferCond update_ctx2;
+  {
+    RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
+    RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
+    mock_object_map.aio_update(CEPH_NOSNAP, 0, 1, {}, {}, &update_ctx1);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 1, {}, {}, &update_ctx2);
+  }
+
+  finish_update_2->complete(0);
+  ASSERT_EQ(0, update_ctx2.wait());
+
+  finish_update_1->complete(0);
+  ASSERT_EQ(0, update_ctx1.wait());
+
+  C_SaferCond close_ctx;
+  mock_object_map.close(&close_ctx);
+  ASSERT_EQ(0, close_ctx.wait());
+}
+
+TEST_F(TestMockObjectMap, DetainedUpdate) {
+  REQUIRE_FEATURE(RBD_FEATURE_OBJECT_MAP);
+
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockTestImageCtx mock_image_ctx(*ictx);
+
+  InSequence seq;
+  ceph::BitVector<2u> object_map;
+  object_map.resize(4);
+  MockRefreshRequest mock_refresh_request;
+  expect_refresh(mock_image_ctx, mock_refresh_request, object_map, 0);
+
+  MockUpdateRequest mock_update_request;
+  Context *finish_update_1;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                1, 4, 1, {}, &finish_update_1);
+  Context *finish_update_2 = nullptr;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                1, 3, 1, {}, &finish_update_2);
+  Context *finish_update_3 = nullptr;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                2, 3, 1, {}, &finish_update_3);
+  Context *finish_update_4 = nullptr;
+  expect_update(mock_image_ctx, mock_update_request, CEPH_NOSNAP,
+                0, 2, 1, {}, &finish_update_4);
+
+  MockUnlockRequest mock_unlock_request;
+  expect_unlock(mock_image_ctx, mock_unlock_request, 0);
+
+  MockObjectMap mock_object_map(mock_image_ctx, CEPH_NOSNAP);
+  C_SaferCond open_ctx;
+  mock_object_map.open(&open_ctx);
+  ASSERT_EQ(0, open_ctx.wait());
+
+  C_SaferCond update_ctx1;
+  C_SaferCond update_ctx2;
+  C_SaferCond update_ctx3;
+  C_SaferCond update_ctx4;
+  {
+    RWLock::RLocker snap_locker(mock_image_ctx.snap_lock);
+    RWLock::WLocker object_map_locker(mock_image_ctx.object_map_lock);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 4, 1, {}, {}, &update_ctx1);
+    mock_object_map.aio_update(CEPH_NOSNAP, 1, 3, 1, {}, {}, &update_ctx2);
+    mock_object_map.aio_update(CEPH_NOSNAP, 2, 3, 1, {}, {}, &update_ctx3);
+    mock_object_map.aio_update(CEPH_NOSNAP, 0, 2, 1, {}, {}, &update_ctx4);
+  }
+
+  // updates 2, 3, and 4 are blocked on update 1
+  ASSERT_EQ(nullptr, finish_update_2);
+  finish_update_1->complete(0);
+  ASSERT_EQ(0, update_ctx1.wait());
+
+  // updates 3 and 4 are blocked on update 2
+  ASSERT_NE(nullptr, finish_update_2);
+  ASSERT_EQ(nullptr, finish_update_3);
+  ASSERT_EQ(nullptr, finish_update_4);
+  finish_update_2->complete(0);
+  ASSERT_EQ(0, update_ctx2.wait());
+
+  ASSERT_NE(nullptr, finish_update_3);
+  ASSERT_NE(nullptr, finish_update_4);
+  finish_update_3->complete(0);
+  finish_update_4->complete(0);
+  ASSERT_EQ(0, update_ctx3.wait());
+  ASSERT_EQ(0, update_ctx4.wait());
+
+  C_SaferCond close_ctx;
+  mock_object_map.close(&close_ctx);
+  ASSERT_EQ(0, close_ctx.wait());
+}
+
+} // namespace librbd
+