// -*- 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 "gmock/gmock.h" #include "gtest/gtest.h" namespace librbd { namespace { struct MockTestImageCtx : public MockImageCtx { MockTestImageCtx(ImageCtx &image_ctx) : MockImageCtx(image_ctx) { } }; } // anonymous namespace } // namespace librbd // template definitions #include "librbd/image/ValidatePoolRequest.cc" namespace librbd { namespace image { using ::testing::_; using ::testing::DoDefault; using ::testing::InSequence; using ::testing::Invoke; using ::testing::Return; using ::testing::StrEq; using ::testing::WithArg; class TestMockImageValidatePoolRequest : public TestMockFixture { public: typedef ValidatePoolRequest MockValidatePoolRequest; void SetUp() override { TestMockFixture::SetUp(); m_ioctx.remove(RBD_INFO); ASSERT_EQ(0, open_image(m_image_name, &image_ctx)); } void expect_clone(librados::MockTestMemIoCtxImpl &mock_io_ctx) { EXPECT_CALL(mock_io_ctx, clone()) .WillOnce(Invoke([&mock_io_ctx]() { mock_io_ctx.get(); return &mock_io_ctx; })); } void expect_read_rbd_info(librados::MockTestMemIoCtxImpl &mock_io_ctx, const std::string& data, int r) { auto& expect = EXPECT_CALL( mock_io_ctx, read(StrEq(RBD_INFO), 0, 0, _, _, _)); if (r < 0) { expect.WillOnce(Return(r)); } else { expect.WillOnce(WithArg<3>(Invoke([data](bufferlist* bl) { bl->append(data); return 0; }))); } } void expect_write_rbd_info(librados::MockTestMemIoCtxImpl &mock_io_ctx, const std::string& data, int r) { bufferlist bl; bl.append(data); EXPECT_CALL(mock_io_ctx, write(StrEq(RBD_INFO), ContentsEqual(bl), data.length(), 0, _)) .WillOnce(Return(r)); } void expect_allocate_snap_id(librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) { auto &expect = EXPECT_CALL(mock_io_ctx, selfmanaged_snap_create(_)); if (r < 0) { expect.WillOnce(Return(r)); } else { expect.WillOnce(DoDefault()); } } void expect_release_snap_id(librados::MockTestMemIoCtxImpl &mock_io_ctx, int r) { auto &expect = EXPECT_CALL(mock_io_ctx, selfmanaged_snap_remove(_)); if (r < 0) { expect.WillOnce(Return(r)); } else { expect.WillOnce(DoDefault()); } } librbd::ImageCtx *image_ctx; }; TEST_F(TestMockImageValidatePoolRequest, Success) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", -ENOENT); expect_allocate_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "validate", 0); expect_release_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, AlreadyValidated) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "overwrite validated", 0); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, SnapshotsValidated) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "validate", 0); expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, ReadError) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", -EPERM); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(-EPERM, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, CreateSnapshotError) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", 0); expect_allocate_snap_id(mock_io_ctx, -EPERM); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(-EPERM, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, WriteError) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", -ENOENT); expect_allocate_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "validate", -EPERM); expect_release_snap_id(mock_io_ctx, -EINVAL); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(-EPERM, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, RemoveSnapshotError) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", -ENOENT); expect_allocate_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "validate", 0); expect_release_snap_id(mock_io_ctx, -EPERM); expect_write_rbd_info(mock_io_ctx, "overwrite validated", 0); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(0, ctx.wait()); } TEST_F(TestMockImageValidatePoolRequest, OverwriteError) { librados::MockTestMemIoCtxImpl &mock_io_ctx(get_mock_io_ctx(m_ioctx)); InSequence seq; expect_clone(mock_io_ctx); expect_read_rbd_info(mock_io_ctx, "", -ENOENT); expect_allocate_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "validate", 0); expect_release_snap_id(mock_io_ctx, 0); expect_write_rbd_info(mock_io_ctx, "overwrite validated", -EOPNOTSUPP); C_SaferCond ctx; auto req = new MockValidatePoolRequest(m_ioctx, &ctx); req->send(); ASSERT_EQ(-EINVAL, ctx.wait()); } } // namespace image } // namespace librbd