]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
tests: new unit test for snap rollback state machine
authorJason Dillaman <dillaman@redhat.com>
Tue, 22 Dec 2015 18:40:51 +0000 (13:40 -0500)
committerJason Dillaman <dillaman@redhat.com>
Fri, 15 Jan 2016 15:40:29 +0000 (10:40 -0500)
Signed-off-by: Jason Dillaman <dillaman@redhat.com>
src/test/Makefile-client.am
src/test/librados_test_stub/MockTestMemIoCtxImpl.h
src/test/librbd/mock/MockImageCtx.h
src/test/librbd/mock/MockObjectMap.h
src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc [new file with mode: 0644]
src/test/librbd/test_mock_fixture.cc
src/test/librbd/test_mock_fixture.h

index 72877d20a462568f332ece38838de046789af072..967dde46baac337ffee4fb4c84e66ee45037083c 100644 (file)
@@ -379,6 +379,7 @@ unittest_librbd_SOURCES = \
        test/librbd/operation/test_mock_SnapshotCreateRequest.cc \
        test/librbd/operation/test_mock_SnapshotProtectRequest.cc \
        test/librbd/operation/test_mock_SnapshotRemoveRequest.cc \
+       test/librbd/operation/test_mock_SnapshotRollbackRequest.cc \
        test/librbd/operation/test_mock_SnapshotUnprotectRequest.cc
 unittest_librbd_CXXFLAGS = $(UNITTEST_CXXFLAGS) -DTEST_LIBRBD_INTERNALS
 unittest_librbd_LDADD = \
index 3bc957e373ae09e604c41d3962197ad909003f96..c9da11b1c04baa2ed0158e9a84f81b730ada2a37 100644 (file)
@@ -79,6 +79,12 @@ public:
     return TestMemIoCtxImpl::selfmanaged_snap_remove(snap_id);
   }
 
+  MOCK_METHOD2(selfmanaged_snap_rollback, int(const std::string& oid,
+                                              uint64_t snap_id));
+  int do_selfmanaged_snap_rollback(const std::string& oid, uint64_t snap_id) {
+    return TestMemIoCtxImpl::selfmanaged_snap_rollback(oid, snap_id);
+  }
+
   MOCK_METHOD3(truncate, int(const std::string& oid,
                              uint64_t size,
                              const SnapContext &snapc));
@@ -111,6 +117,7 @@ public:
     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, selfmanaged_snap_rollback(_, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_selfmanaged_snap_rollback));
     ON_CALL(*this, truncate(_,_,_)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_truncate));
     ON_CALL(*this, write(_, _, _, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_write));
     ON_CALL(*this, write_full(_, _, _)).WillByDefault(Invoke(this, &MockTestMemIoCtxImpl::do_write_full));
index 1883819a6d851fabe2c5ce0d5ee8620076106e17..7d198f5caba3267f3f404376f18965f9a9ad4dfa 100644 (file)
@@ -31,6 +31,7 @@ struct MockImageCtx {
       snapc(image_ctx.snapc),
       snaps(image_ctx.snaps),
       snap_info(image_ctx.snap_info),
+      object_cacher(image_ctx.object_cacher),
       old_format(image_ctx.old_format),
       read_only(image_ctx.read_only),
       owner_lock("owner_lock"),
@@ -94,6 +95,8 @@ struct MockImageCtx {
     ctx.wait();
   }
 
+  MOCK_CONST_METHOD1(get_object_name, std::string(uint64_t));
+  MOCK_CONST_METHOD0(get_current_size, uint64_t());
   MOCK_CONST_METHOD1(get_image_size, uint64_t(librados::snap_t));
   MOCK_CONST_METHOD1(get_snap_id, librados::snap_t(std::string in_snap_name));
   MOCK_CONST_METHOD1(get_snap_info, const SnapInfo*(librados::snap_t));
@@ -131,6 +134,7 @@ struct MockImageCtx {
   std::vector<librados::snap_t> snaps;
   std::map<librados::snap_t, SnapInfo> snap_info;
 
+  ObjectCacher *object_cacher;
 
   bool old_format;
   bool read_only;
index 6be9beaab32a9721785c93d121b2712e7c4892e8..5a0aeeca6f747b181d9a15dc704d77346affc169 100644 (file)
@@ -21,6 +21,7 @@ struct MockObjectMap {
 
   MOCK_METHOD2(snapshot_add, void(uint64_t snap_id, Context *on_finish));
   MOCK_METHOD2(snapshot_remove, void(uint64_t snap_id, Context *on_finish));
+  MOCK_METHOD2(rollback, void(uint64_t snap_id, Context *on_finish));
 };
 
 } // namespace librbd
diff --git a/src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc b/src/test/librbd/operation/test_mock_SnapshotRollbackRequest.cc
new file mode 100644 (file)
index 0000000..365d086
--- /dev/null
@@ -0,0 +1,310 @@
+// -*- 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/librados_test_stub/MockTestMemIoCtxImpl.h"
+#include "include/stringify.h"
+#include "common/bit_vector.hpp"
+#include "librbd/ImageState.h"
+#include "librbd/internal.h"
+#include "librbd/operation/SnapshotRollbackRequest.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace librbd {
+namespace operation {
+
+template <>
+struct ResizeRequest<MockImageCtx> {
+  static ResizeRequest *s_instance;
+  Context *on_finish = nullptr;
+
+  static ResizeRequest* create(MockImageCtx &image_ctx, Context *on_finish,
+                               uint64_t new_size, ProgressContext &prog_ctx,
+                               uint64_t journal_op_tid, bool disable_journal) {
+    assert(s_instance != nullptr);
+    assert(journal_op_tid == 0);
+    assert(disable_journal);
+    s_instance->on_finish = on_finish;
+    return s_instance;
+  }
+
+  ResizeRequest() {
+    s_instance = this;
+  }
+
+  MOCK_METHOD0(send, void());
+};
+
+ResizeRequest<MockImageCtx> *ResizeRequest<MockImageCtx>::s_instance = nullptr;
+
+} // namespace operation
+} // namespace librbd
+
+// template definitions
+#include "librbd/operation/SnapshotRollbackRequest.cc"
+
+namespace librbd {
+namespace operation {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Return;
+using ::testing::WithArg;
+
+class TestMockOperationSnapshotRollbackRequest : public TestMockFixture {
+public:
+  typedef SnapshotRollbackRequest<MockImageCtx> MockSnapshotRollbackRequest;
+  typedef ResizeRequest<MockImageCtx> MockResizeRequest;
+
+  void expect_block_writes(MockImageCtx &mock_image_ctx, int r) {
+    EXPECT_CALL(*mock_image_ctx.aio_work_queue, block_writes(_))
+                  .WillOnce(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_unblock_writes(MockImageCtx &mock_image_ctx) {
+    EXPECT_CALL(*mock_image_ctx.aio_work_queue, unblock_writes())
+                  .Times(1);
+  }
+
+  void expect_get_image_size(MockImageCtx &mock_image_ctx,
+                             uint64_t size) {
+    EXPECT_CALL(mock_image_ctx, get_image_size(CEPH_NOSNAP))
+                  .WillOnce(Return(size));
+  }
+
+  void expect_resize(MockImageCtx &mock_image_ctx,
+                     MockResizeRequest &mock_resize_request, int r) {
+    expect_get_image_size(mock_image_ctx, 123);
+    EXPECT_CALL(mock_resize_request, send())
+                  .WillOnce(FinishRequest(&mock_resize_request, r,
+                                          &mock_image_ctx));
+  }
+
+  void expect_rollback_object_map(MockImageCtx &mock_image_ctx,
+                                  MockObjectMap &mock_object_map) {
+    if (mock_image_ctx.object_map != nullptr) {
+      EXPECT_CALL(mock_object_map, rollback(_, _))
+                    .WillOnce(WithArg<1>(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue)));
+    }
+  }
+
+  void expect_get_object_name(MockImageCtx &mock_image_ctx,
+                              uint64_t object_num) {
+    EXPECT_CALL(mock_image_ctx, get_object_name(object_num))
+                  .WillOnce(Return("object-name-" + stringify(object_num)));
+  }
+
+  void expect_get_current_size(MockImageCtx &mock_image_ctx, uint64_t size) {
+    EXPECT_CALL(mock_image_ctx, get_current_size())
+                  .WillOnce(Return(size));
+  }
+
+  void expect_rollback_snap_id(MockImageCtx &mock_image_ctx,
+                               const std::string &oid, int r) {
+    EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.data_ctx),
+                selfmanaged_snap_rollback(oid, _))
+                  .WillOnce(Return(r));
+  }
+
+  void expect_rollback(MockImageCtx &mock_image_ctx, int r) {
+    expect_get_current_size(mock_image_ctx, 1);
+    expect_get_object_name(mock_image_ctx, 0);
+    expect_rollback_snap_id(mock_image_ctx, "object-name-0", r);
+  }
+
+  void expect_create_object_map(MockImageCtx &mock_image_ctx,
+                                MockObjectMap *mock_object_map) {
+    EXPECT_CALL(mock_image_ctx, create_object_map(_))
+                  .WillOnce(Return(mock_object_map));
+  }
+
+  void expect_open_object_map(MockImageCtx &mock_image_ctx,
+                              MockObjectMap &mock_object_map) {
+    EXPECT_CALL(mock_object_map, open(_))
+                  .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_lock_object_map(MockImageCtx &mock_image_ctx,
+                              MockObjectMap &mock_object_map) {
+    EXPECT_CALL(mock_object_map, lock(_))
+                  .WillOnce(CompleteContext(0, mock_image_ctx.image_ctx->op_work_queue));
+  }
+
+  void expect_refresh_object_map(MockImageCtx &mock_image_ctx,
+                                 MockObjectMap &mock_object_map) {
+    if (mock_image_ctx.object_map != nullptr) {
+      expect_create_object_map(mock_image_ctx, &mock_object_map);
+      expect_open_object_map(mock_image_ctx, mock_object_map);
+    }
+  }
+
+  void expect_invalidate_cache(MockImageCtx &mock_image_ctx, int r) {
+    if (mock_image_ctx.object_cacher != nullptr) {
+      EXPECT_CALL(mock_image_ctx, invalidate_cache(_))
+                    .WillOnce(CompleteContext(r, NULL));
+    }
+  }
+
+  int when_snap_rollback(MockImageCtx &mock_image_ctx,
+                         const std::string &snap_name,
+                         uint64_t snap_id, uint64_t snap_size) {
+    C_SaferCond cond_ctx;
+    librbd::NoOpProgressContext prog_ctx;
+    MockSnapshotRollbackRequest *req = new MockSnapshotRollbackRequest(
+      mock_image_ctx, &cond_ctx, snap_name, snap_id, snap_size, prog_ctx);
+    {
+      RWLock::RLocker owner_locker(mock_image_ctx.owner_lock);
+      req->send();
+    }
+    return cond_ctx.wait();
+  }
+};
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, Success) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap *mock_object_map = new MockObjectMap();
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      *mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  MockResizeRequest mock_resize_request;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, 0);
+  expect_resize(mock_image_ctx, mock_resize_request, 0);
+  expect_rollback_object_map(mock_image_ctx, *mock_object_map);
+  expect_rollback(mock_image_ctx, 0);
+  expect_refresh_object_map(mock_image_ctx, *mock_object_map);
+  expect_invalidate_cache(mock_image_ctx, 0);
+  expect_commit_op_event(mock_image_ctx, 0);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(0, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, BlockWritesError) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap mock_object_map;
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, -EINVAL);
+  expect_commit_op_event(mock_image_ctx, -EINVAL);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, SkipResize) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap *mock_object_map = new MockObjectMap();
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      *mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, 0);
+  expect_get_image_size(mock_image_ctx, 345);
+  expect_rollback_object_map(mock_image_ctx, *mock_object_map);
+  expect_rollback(mock_image_ctx, 0);
+  expect_refresh_object_map(mock_image_ctx, *mock_object_map);
+  expect_invalidate_cache(mock_image_ctx, 0);
+  expect_commit_op_event(mock_image_ctx, 0);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(0, when_snap_rollback(mock_image_ctx, "snap", 123, 345));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, ResizeError) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap mock_object_map;
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  MockResizeRequest mock_resize_request;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, 0);
+  expect_resize(mock_image_ctx, mock_resize_request, -EINVAL);
+  expect_commit_op_event(mock_image_ctx, -EINVAL);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, RollbackObjectsError) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap mock_object_map;
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  MockResizeRequest mock_resize_request;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, 0);
+  expect_resize(mock_image_ctx, mock_resize_request, 0);
+  expect_rollback_object_map(mock_image_ctx, mock_object_map);
+  expect_rollback(mock_image_ctx, -EINVAL);
+  expect_commit_op_event(mock_image_ctx, -EINVAL);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+TEST_F(TestMockOperationSnapshotRollbackRequest, InvalidateCacheError) {
+  librbd::ImageCtx *ictx;
+  ASSERT_EQ(0, open_image(m_image_name, &ictx));
+
+  MockImageCtx mock_image_ctx(*ictx);
+  MockExclusiveLock mock_exclusive_lock;
+  MockJournal mock_journal;
+  MockObjectMap *mock_object_map = new MockObjectMap();
+  initialize_features(ictx, mock_image_ctx, mock_exclusive_lock, mock_journal,
+                      *mock_object_map);
+  expect_op_work_queue(mock_image_ctx);
+
+  InSequence seq;
+  MockResizeRequest mock_resize_request;
+  expect_append_op_event(mock_image_ctx, 0);
+  expect_block_writes(mock_image_ctx, 0);
+  expect_resize(mock_image_ctx, mock_resize_request, 0);
+  expect_rollback_object_map(mock_image_ctx, *mock_object_map);
+  expect_rollback(mock_image_ctx, 0);
+  expect_refresh_object_map(mock_image_ctx, *mock_object_map);
+  expect_invalidate_cache(mock_image_ctx, -EINVAL);
+  expect_commit_op_event(mock_image_ctx, -EINVAL);
+  expect_unblock_writes(mock_image_ctx);
+  ASSERT_EQ(-EINVAL, when_snap_rollback(mock_image_ctx, "snap", 123, 0));
+}
+
+} // namespace operation
+} // namespace librbd
index d181914f3717960142afcf2d6a8fb599c9aa05a1..f5a2394fc32c2fe126903cdb7dcd54dd19208427 100644 (file)
@@ -17,6 +17,8 @@ template class librbd::operation::Request<librbd::MockImageCtx>;
 
 using ::testing::_;
 using ::testing::DoDefault;
+using ::testing::Return;
+using ::testing::WithArg;
 
 TestMockFixture::TestRadosClientPtr TestMockFixture::s_test_rados_client;
 ::testing::NiceMock<librados::MockTestMemRadosClient> *
@@ -68,3 +70,52 @@ librados::MockTestMemIoCtxImpl &TestMockFixture::get_mock_io_ctx(
     reinterpret_cast<librados::MockTestMemIoCtxImpl **>(&ioctx);
   return **mock;
 }
+
+void TestMockFixture::initialize_features(librbd::ImageCtx *ictx,
+                                          librbd::MockImageCtx &mock_image_ctx,
+                                          librbd::MockExclusiveLock &mock_exclusive_lock,
+                                          librbd::MockJournal &mock_journal,
+                                          librbd::MockObjectMap &mock_object_map) {
+  if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
+    mock_image_ctx.exclusive_lock = &mock_exclusive_lock;
+  }
+  if (ictx->test_features(RBD_FEATURE_JOURNALING)) {
+    mock_image_ctx.journal = &mock_journal;
+  }
+  if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
+    mock_image_ctx.object_map = &mock_object_map;
+  }
+}
+
+void TestMockFixture::expect_is_journal_replaying(librbd::MockJournal &mock_journal) {
+  EXPECT_CALL(mock_journal, is_journal_replaying()).WillOnce(Return(false));
+}
+
+void TestMockFixture::expect_is_journal_ready(librbd::MockJournal &mock_journal) {
+  EXPECT_CALL(mock_journal, is_journal_ready()).WillOnce(Return(true));
+}
+
+void TestMockFixture::expect_allocate_op_tid(librbd::MockImageCtx &mock_image_ctx) {
+  if (mock_image_ctx.journal != nullptr) {
+    EXPECT_CALL(*mock_image_ctx.journal, allocate_op_tid())
+                  .WillOnce(Return(1U));
+  }
+}
+
+void TestMockFixture::expect_append_op_event(librbd::MockImageCtx &mock_image_ctx, int r) {
+  if (mock_image_ctx.journal != nullptr) {
+    expect_is_journal_replaying(*mock_image_ctx.journal);
+    expect_allocate_op_tid(mock_image_ctx);
+    EXPECT_CALL(*mock_image_ctx.journal, append_op_event_mock(_, _, _))
+                  .WillOnce(WithArg<2>(CompleteContext(r, mock_image_ctx.image_ctx->op_work_queue)));
+  }
+}
+
+void TestMockFixture::expect_commit_op_event(librbd::MockImageCtx &mock_image_ctx, int r) {
+  if (mock_image_ctx.journal != nullptr) {
+    expect_is_journal_replaying(*mock_image_ctx.journal);
+    expect_is_journal_ready(*mock_image_ctx.journal);
+    EXPECT_CALL(*mock_image_ctx.journal, commit_op_event(1U, r));
+  }
+}
+
index bf8a0af7b9d7e8820e0218e595354011b70ba8a6..ce3e6d5abb1761b9d5dd34692345678fe987b9d5 100644 (file)
@@ -74,6 +74,18 @@ public:
   void expect_op_work_queue(librbd::MockImageCtx &mock_image_ctx);
   void expect_unlock_exclusive_lock(librbd::ImageCtx &ictx);
 
+  void initialize_features(librbd::ImageCtx *ictx,
+                           librbd::MockImageCtx &mock_image_ctx,
+                           librbd::MockExclusiveLock &mock_exclusive_lock,
+                           librbd::MockJournal &mock_journal,
+                           librbd::MockObjectMap &mock_object_map);
+
+  void expect_is_journal_replaying(librbd::MockJournal &mock_journal);
+  void expect_is_journal_ready(librbd::MockJournal &mock_journal);
+  void expect_allocate_op_tid(librbd::MockImageCtx &mock_image_ctx);
+  void expect_append_op_event(librbd::MockImageCtx &mock_image_ctx, int r);
+  void expect_commit_op_event(librbd::MockImageCtx &mock_image_ctx, int r);
+
 private:
   static TestRadosClientPtr s_test_rados_client;
   static ::testing::NiceMock<librados::MockTestMemRadosClient> *s_mock_rados_client;