// Copyright (C) 2016 Red Hat, Inc. All rights reserved. // // This file is part of the thin-provisioning-tools source. // // thin-provisioning-tools is free software: you can redistribute it // and/or modify it under the terms of the GNU General Public License // as published by the Free Software Foundation, either version 3 of // the License, or (at your option) any later version. // // thin-provisioning-tools is distributed in the hope that it will be // useful, but WITHOUT ANY WARRANTY; without even the implied warranty // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with thin-provisioning-tools. If not, see // . #include "gmock/gmock.h" #include "block-cache/copier.h" #include "test_utils.h" #include using namespace boost; using namespace std; using namespace test; using namespace testing; //---------------------------------------------------------------- namespace { unsigned const BLOCK_SIZE = 64u; using wait_result = io_engine::wait_result; ostream &operator <<(ostream &out, optional const &mwr) { if (mwr) { out << "Just[wait_result[" << mwr->first << ", " << mwr->second << "]]"; } else out << "Nothing"; return out; } class io_engine_mock : public io_engine { public: MOCK_METHOD3(open_file, handle(string const &, mode, sharing)); MOCK_METHOD1(close_file, void(handle)); MOCK_METHOD6(issue_io, bool(handle, dir, sector_t, sector_t, void *, unsigned)); MOCK_METHOD0(wait, optional()); MOCK_METHOD1(wait, optional(unsigned &)); }; class CopierTests : public Test { public: CopierTests() : src_file_("copy_src"), dest_file_("copy_dest") { } unique_ptr make_copier() { EXPECT_CALL(engine_, open_file(src_file_, io_engine::M_READ_ONLY, io_engine::EXCLUSIVE)). WillOnce(Return(SRC_HANDLE)); EXPECT_CALL(engine_, open_file(dest_file_, io_engine::M_READ_WRITE, io_engine::EXCLUSIVE)). WillOnce(Return(DEST_HANDLE)); EXPECT_CALL(engine_, close_file(SRC_HANDLE)).Times(1); EXPECT_CALL(engine_, close_file(DEST_HANDLE)).Times(1); return unique_ptr(new copier(engine_, src_file_, dest_file_, BLOCK_SIZE, 1 * 1024 * 1024)); } static optional make_wr(bool success, unsigned context) { return optional(wait_result(success, context)); } void issue_successful_op(copier &c, copy_op &op, unsigned context) { InSequence dummy; unsigned nr_pending = c.nr_pending(); EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, op.src_b * BLOCK_SIZE, op.src_e * BLOCK_SIZE, _, context)). WillOnce(Return(true)); c.issue(op); ASSERT_TRUE(c.nr_pending() == nr_pending + 1); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(true, context))); EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE, op.dest_b * BLOCK_SIZE, (op.dest_b + (op.src_e - op.src_b)) * BLOCK_SIZE, _, context)). WillOnce(Return(true)); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(true, context))); auto mop = c.wait(); ASSERT_EQ(c.nr_pending(), nr_pending); ASSERT_TRUE(mop->success()); } unsigned const SRC_HANDLE = 10; unsigned const DEST_HANDLE = 11; string src_file_; string dest_file_; StrictMock engine_; }; } //---------------------------------------------------------------- TEST_F(CopierTests, empty_test) { auto c = make_copier(); } TEST_F(CopierTests, successful_copy) { // Copy first block copy_op op1(0, 1, 0); auto c = make_copier(); issue_successful_op(*c, op1, 0); } TEST_F(CopierTests, unsuccessful_issue_read) { copy_op op1(0, 1, 0); auto c = make_copier(); InSequence dummy; EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(false)); c->issue(op1); ASSERT_EQ(c->nr_pending(), 1u); auto mop = c->wait(); ASSERT_EQ(c->nr_pending(), 0u); ASSERT_FALSE(mop->success()); } TEST_F(CopierTests, unsuccessful_read) { copy_op op1(0, 1, 0); auto c = make_copier(); InSequence dummy; EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); c->issue(op1); ASSERT_EQ(c->nr_pending(), 1u); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(false, 0u))); ASSERT_EQ(c->nr_pending(), 1u); auto mop = c->wait(); ASSERT_EQ(c->nr_pending(), 0u); ASSERT_FALSE(mop->success()); } TEST_F(CopierTests, unsuccessful_issue_write) { copy_op op1(0, 1, 0); auto c = make_copier(); InSequence dummy; EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); c->issue(op1); ASSERT_EQ(c->nr_pending(), 1u); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(true, 0u))); ASSERT_EQ(c->nr_pending(), 1u); EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(false)); auto mop = c->wait(); ASSERT_EQ(c->nr_pending(), 0u); ASSERT_FALSE(mop->success()); } TEST_F(CopierTests, unsuccessful_write) { // Copy first block copy_op op1(0, 1, 0); auto c = make_copier(); InSequence dummy; EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); c->issue(op1); ASSERT_EQ(c->nr_pending(), 1u); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(true, 0u))); EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); EXPECT_CALL(engine_, wait()). WillOnce(Return(make_wr(false, 0u))); auto mop = c->wait(); ASSERT_EQ(c->nr_pending(), 0u); ASSERT_FALSE(mop->success()); } TEST_F(CopierTests, copy_the_same_block_many_times) { auto c = make_copier(); copy_op op1(0, 1, 0); for (unsigned i = 0; i < 50000; i++) issue_successful_op(*c, op1, i); } TEST_F(CopierTests, copy_different_blocks) { auto c = make_copier(); for (unsigned i = 0; i < 5000; i++) { copy_op op(i, i + 1, i); issue_successful_op(*c, op, i); } } TEST_F(CopierTests, wait_can_timeout) { copy_op op1(0, 1, 0); auto c = make_copier(); InSequence dummy; EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::D_READ, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); c->issue(op1); ASSERT_EQ(c->nr_pending(), 1u); unsigned micro = 10000; EXPECT_CALL(engine_, wait(micro)). WillOnce(Return(make_wr(true, 0u))); ASSERT_EQ(c->nr_pending(), 1u); EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::D_WRITE, 0, BLOCK_SIZE, _, 0)). WillOnce(Return(true)); EXPECT_CALL(engine_, wait(micro)). WillOnce(DoAll(SetArgReferee<0>(0u), Return(optional()))); auto mop = c->wait(micro); ASSERT_FALSE(mop); ASSERT_EQ(c->nr_pending(), 1u); } //----------------------------------------------------------------