[block-cache] unit tests + debug io_engine and copier
This commit is contained in:
parent
34c039d7dc
commit
a94bfea798
@ -166,8 +166,8 @@ INSTALL_DATA = $(INSTALL) -p -m 644
|
|||||||
|
|
||||||
ifeq ("@TESTING@", "yes")
|
ifeq ("@TESTING@", "yes")
|
||||||
TEST_INCLUDES=\
|
TEST_INCLUDES=\
|
||||||
-Igmock-1.6.0/include \
|
-Igoogletest/googlemock/include \
|
||||||
-Igmock-1.6.0/gtest/include
|
-Igoogletest/googletest/include
|
||||||
else
|
else
|
||||||
TEST_INCLUDES=
|
TEST_INCLUDES=
|
||||||
endif
|
endif
|
||||||
|
@ -8,18 +8,25 @@ using namespace std;
|
|||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
copier::copier(string const &src, string const &dest,
|
copier::copier(io_engine &engine,
|
||||||
|
string const &src, string const &dest,
|
||||||
sector_t block_size, size_t mem)
|
sector_t block_size, size_t mem)
|
||||||
: pool_(block_size, mem),
|
: pool_(block_size * 512, mem),
|
||||||
block_size_(block_size),
|
block_size_(block_size),
|
||||||
nr_blocks_(mem / block_size),
|
nr_blocks_(mem / block_size),
|
||||||
engine_(nr_blocks_),
|
engine_(engine),
|
||||||
src_handle_(engine_.open_file(src, io_engine::READ_ONLY)),
|
src_handle_(engine_.open_file(src, io_engine::READ_ONLY)),
|
||||||
dest_handle_(engine_.open_file(dest, io_engine::READ_WRITE)),
|
dest_handle_(engine_.open_file(dest, io_engine::READ_WRITE)),
|
||||||
genkey_count_(0)
|
genkey_count_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copier::~copier()
|
||||||
|
{
|
||||||
|
engine_.close_file(src_handle_);
|
||||||
|
engine_.close_file(dest_handle_);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
copier::issue(copy_op const &op)
|
copier::issue(copy_op const &op)
|
||||||
{
|
{
|
||||||
@ -37,14 +44,17 @@ copier::issue(copy_op const &op)
|
|||||||
job.op.read_complete = job.op.write_complete = false;
|
job.op.read_complete = job.op.write_complete = false;
|
||||||
unsigned key = genkey(); // used as context for the io_engine
|
unsigned key = genkey(); // used as context for the io_engine
|
||||||
|
|
||||||
cerr << "data = " << data << "\n";
|
auto r = engine_.issue_io(src_handle_,
|
||||||
engine_.issue_io(src_handle_,
|
io_engine::READ,
|
||||||
io_engine::READ,
|
to_sector(op.src_b),
|
||||||
to_sector(op.src_b),
|
to_sector(op.src_e),
|
||||||
to_sector(op.src_e),
|
data,
|
||||||
data,
|
key);
|
||||||
key);
|
|
||||||
jobs_.insert(make_pair(key, job));
|
if (r)
|
||||||
|
jobs_.insert(make_pair(key, job));
|
||||||
|
else
|
||||||
|
complete(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned
|
unsigned
|
||||||
@ -56,7 +66,7 @@ copier::nr_pending() const
|
|||||||
boost::optional<copy_op>
|
boost::optional<copy_op>
|
||||||
copier::wait()
|
copier::wait()
|
||||||
{
|
{
|
||||||
while (complete_.empty() && !jobs_.empty())
|
while (!jobs_.empty() && complete_.empty())
|
||||||
wait_();
|
wait_();
|
||||||
|
|
||||||
if (complete_.empty())
|
if (complete_.empty())
|
||||||
@ -77,28 +87,31 @@ copier::wait_()
|
|||||||
if (it == jobs_.end())
|
if (it == jobs_.end())
|
||||||
throw runtime_error("Internal error. Lost track of copy job.");
|
throw runtime_error("Internal error. Lost track of copy job.");
|
||||||
|
|
||||||
copy_job j = it->second;
|
copy_job &j = it->second;
|
||||||
if (!p.first) {
|
if (!p.first) {
|
||||||
// IO was unsuccessful
|
// IO was unsuccessful
|
||||||
jobs_.erase(it);
|
|
||||||
complete(j);
|
complete(j);
|
||||||
|
jobs_.erase(it);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IO was successful
|
// IO was successful
|
||||||
if (!j.op.read_complete) {
|
if (!j.op.read_complete) {
|
||||||
j.op.read_complete = true;
|
j.op.read_complete = true;
|
||||||
engine_.issue_io(dest_handle_,
|
if (!engine_.issue_io(dest_handle_,
|
||||||
io_engine::WRITE,
|
io_engine::WRITE,
|
||||||
to_sector(j.op.dest_b),
|
to_sector(j.op.dest_b),
|
||||||
to_sector(j.op.dest_b + (j.op.src_e - j.op.src_b)),
|
to_sector(j.op.dest_b + (j.op.src_e - j.op.src_b)),
|
||||||
j.data,
|
j.data,
|
||||||
it->first);
|
it->first)) {
|
||||||
|
complete(j);
|
||||||
|
jobs_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
j.op.write_complete = true;
|
j.op.write_complete = true;
|
||||||
jobs_.erase(it);
|
|
||||||
complete(j);
|
complete(j);
|
||||||
|
jobs_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,11 +15,13 @@ namespace bcache {
|
|||||||
|
|
||||||
struct copy_op {
|
struct copy_op {
|
||||||
copy_op()
|
copy_op()
|
||||||
: read_complete(false),
|
: src_b(0),
|
||||||
|
src_e(0),
|
||||||
|
dest_b(0),
|
||||||
|
read_complete(false),
|
||||||
write_complete(false) {
|
write_complete(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
copy_op(block_address src_b_,
|
copy_op(block_address src_b_,
|
||||||
block_address src_e_,
|
block_address src_e_,
|
||||||
block_address dest_b_)
|
block_address dest_b_)
|
||||||
@ -30,6 +32,10 @@ namespace bcache {
|
|||||||
write_complete(false) {
|
write_complete(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success() const {
|
||||||
|
return read_complete && write_complete;
|
||||||
|
}
|
||||||
|
|
||||||
block_address src_b, src_e;
|
block_address src_b, src_e;
|
||||||
block_address dest_b;
|
block_address dest_b;
|
||||||
|
|
||||||
@ -49,8 +55,10 @@ namespace bcache {
|
|||||||
|
|
||||||
class copier {
|
class copier {
|
||||||
public:
|
public:
|
||||||
copier(std::string const &src, std::string const &dest,
|
copier(io_engine &engine,
|
||||||
|
std::string const &src, std::string const &dest,
|
||||||
sector_t block_size, size_t mem);
|
sector_t block_size, size_t mem);
|
||||||
|
~copier();
|
||||||
|
|
||||||
sector_t get_block_size() const {
|
sector_t get_block_size() const {
|
||||||
return block_size_;
|
return block_size_;
|
||||||
@ -72,7 +80,7 @@ namespace bcache {
|
|||||||
mempool pool_;
|
mempool pool_;
|
||||||
sector_t block_size_;
|
sector_t block_size_;
|
||||||
unsigned nr_blocks_;
|
unsigned nr_blocks_;
|
||||||
io_engine engine_;
|
io_engine &engine_;
|
||||||
io_engine::handle src_handle_;
|
io_engine::handle src_handle_;
|
||||||
io_engine::handle dest_handle_;
|
io_engine::handle dest_handle_;
|
||||||
unsigned genkey_count_;
|
unsigned genkey_count_;
|
||||||
|
@ -12,7 +12,12 @@ using namespace bcache;
|
|||||||
using namespace boost;
|
using namespace boost;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
#define SECTOR_SHIFT 9
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
unsigned const SECTOR_SHIFT = 9;
|
||||||
|
unsigned const PAGE_SIZE = 4096;
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
@ -55,23 +60,22 @@ control_block_set::context(iocb *cb) const
|
|||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
io_engine::io_engine(unsigned max_io)
|
aio_engine::aio_engine(unsigned max_io)
|
||||||
: aio_context_(0),
|
: aio_context_(0),
|
||||||
cbs_(max_io),
|
cbs_(max_io)
|
||||||
events_(max_io)
|
|
||||||
{
|
{
|
||||||
int r = io_setup(max_io, &aio_context_);
|
int r = io_setup(max_io, &aio_context_);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
throw runtime_error("io_setup failed");
|
throw runtime_error("io_setup failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
io_engine::~io_engine()
|
aio_engine::~aio_engine()
|
||||||
{
|
{
|
||||||
io_destroy(aio_context_);
|
io_destroy(aio_context_);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_engine::handle
|
aio_engine::handle
|
||||||
io_engine:: open_file(std::string const &path, mode m)
|
aio_engine::open_file(std::string const &path, mode m)
|
||||||
{
|
{
|
||||||
int flags = (m == READ_ONLY) ? O_RDONLY : O_RDWR;
|
int flags = (m == READ_ONLY) ? O_RDONLY : O_RDWR;
|
||||||
int fd = ::open(path.c_str(), O_DIRECT | flags);
|
int fd = ::open(path.c_str(), O_DIRECT | flags);
|
||||||
@ -87,7 +91,7 @@ io_engine:: open_file(std::string const &path, mode m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
io_engine::close_file(handle h)
|
aio_engine::close_file(handle h)
|
||||||
{
|
{
|
||||||
for (auto it = descriptors_.begin(); it != descriptors_.end(); ++it) {
|
for (auto it = descriptors_.begin(); it != descriptors_.end(); ++it) {
|
||||||
unsigned it_h = it->get();
|
unsigned it_h = it->get();
|
||||||
@ -103,71 +107,60 @@ io_engine::close_file(handle h)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
io_engine::issue_io(handle h, dir d, sector_t b, sector_t e, void *data, unsigned context)
|
aio_engine::issue_io(handle h, dir d, sector_t b, sector_t e, void *data, unsigned context)
|
||||||
{
|
{
|
||||||
auto cb = cbs_.alloc(context);
|
if (reinterpret_cast<uint64_t>(data) & (PAGE_SIZE - 1))
|
||||||
|
throw runtime_error("Data passed to issue_io must be page aligned\n");
|
||||||
|
|
||||||
|
iocb *cb;
|
||||||
|
|
||||||
|
cb = cbs_.alloc(context);
|
||||||
if (!cb)
|
if (!cb)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
memset(cb, 0, sizeof(*cb));
|
memset(cb, 0, sizeof(*cb));
|
||||||
|
|
||||||
cb->aio_fildes = static_cast<int>(h);
|
cb->aio_fildes = static_cast<int>(h);
|
||||||
cb->u.c.buf = data;
|
cb->u.c.buf = data;
|
||||||
cb->u.c.offset = b << SECTOR_SHIFT;
|
cb->u.c.offset = b << SECTOR_SHIFT;
|
||||||
cb->u.c.nbytes = (e - b) << SECTOR_SHIFT;
|
cb->u.c.nbytes = (e - b) << SECTOR_SHIFT;
|
||||||
|
|
||||||
cb->aio_lio_opcode = (d == READ) ? IO_CMD_PREAD : IO_CMD_PWRITE;
|
cb->aio_lio_opcode = (d == READ) ? IO_CMD_PREAD : IO_CMD_PWRITE;
|
||||||
|
|
||||||
int r = io_submit(aio_context_, 1, &cb);
|
int r = io_submit(aio_context_, 1, &cb);
|
||||||
if (r != 1) {
|
return r == 1;
|
||||||
std::ostringstream out;
|
|
||||||
out << "couldn't issue "
|
|
||||||
<< ((d == READ) ? "READ" : "WRITE")
|
|
||||||
<< " io: io_submit ";
|
|
||||||
if (r < 0)
|
|
||||||
out << "failed with " << r;
|
|
||||||
else
|
|
||||||
out << "succeeded, but queued no io";
|
|
||||||
|
|
||||||
throw std::runtime_error(out.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<bool, io_engine::handle>
|
std::pair<bool, io_engine::handle>
|
||||||
io_engine::wait()
|
aio_engine::wait()
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
unsigned i;
|
struct io_event event;
|
||||||
|
|
||||||
r = io_getevents(aio_context_, 1, events_.size(), &events_[0], NULL);
|
memset(&event, 0, sizeof(event));
|
||||||
|
|
||||||
|
r = io_getevents(aio_context_, 1, 1, &event, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
out << "io_getevents failed: " << r;
|
out << "io_getevents failed: " << r;
|
||||||
throw std::runtime_error(out.str());
|
throw std::runtime_error(out.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < static_cast<unsigned>(r); i++) {
|
iocb *cb = reinterpret_cast<iocb *>(event.obj);
|
||||||
io_event const &e = events_[i];
|
unsigned context = cbs_.context(cb);
|
||||||
iocb *cb = reinterpret_cast<iocb *>(e.obj);
|
|
||||||
unsigned context = cbs_.context(cb);
|
if (event.res == cb->u.c.nbytes) {
|
||||||
cbs_.free(cb);
|
cbs_.free(cb);
|
||||||
|
return make_pair(true, context);
|
||||||
|
|
||||||
if (e.res == cb->u.c.nbytes)
|
} else if (static_cast<int>(event.res) < 0) {
|
||||||
return make_pair(true, context);
|
cbs_.free(cb);
|
||||||
|
return make_pair(false, context);
|
||||||
|
|
||||||
else {
|
} else {
|
||||||
std::ostringstream out;
|
cbs_.free(cb);
|
||||||
out << "io failed"
|
return make_pair(false, context);
|
||||||
<< ", e.res = " << e.res
|
|
||||||
<< ", e.res2 = " << e.res2
|
|
||||||
<< ", offset = " << cb->u.c.offset
|
|
||||||
<< ", nbytes = " << cb->u.c.nbytes;
|
|
||||||
return make_pair(false, context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// shouldn't get here
|
// shouldn't get here
|
||||||
return make_pair(false, 0);
|
return make_pair(false, 0);
|
||||||
}
|
}
|
||||||
|
@ -5,16 +5,50 @@
|
|||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <libaio.h>
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <libaio.h>
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace bcache {
|
namespace bcache {
|
||||||
using sector_t = uint64_t;
|
using sector_t = uint64_t;
|
||||||
|
|
||||||
//----------------
|
// Virtual base class to aid unit testing
|
||||||
|
class io_engine {
|
||||||
|
public:
|
||||||
|
enum mode {
|
||||||
|
READ_ONLY,
|
||||||
|
READ_WRITE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum dir {
|
||||||
|
READ,
|
||||||
|
WRITE
|
||||||
|
};
|
||||||
|
|
||||||
|
io_engine() {}
|
||||||
|
virtual ~io_engine() {}
|
||||||
|
|
||||||
|
using handle = unsigned;
|
||||||
|
|
||||||
|
virtual handle open_file(std::string const &path, mode m) = 0;
|
||||||
|
virtual void close_file(handle h) = 0;
|
||||||
|
|
||||||
|
// returns false if there are insufficient resources to
|
||||||
|
// queue the IO
|
||||||
|
virtual bool issue_io(handle h, dir d, sector_t b, sector_t e, void *data, unsigned context) = 0;
|
||||||
|
|
||||||
|
// returns (success, context)
|
||||||
|
using wait_result = std::pair<bool, unsigned>;
|
||||||
|
virtual wait_result wait() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
io_engine(io_engine const &) = delete;
|
||||||
|
io_engine &operator =(io_engine const &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
|
||||||
class control_block_set {
|
class control_block_set {
|
||||||
public:
|
public:
|
||||||
@ -37,43 +71,32 @@ namespace bcache {
|
|||||||
|
|
||||||
//----------------
|
//----------------
|
||||||
|
|
||||||
class io_engine {
|
class aio_engine : public io_engine {
|
||||||
public:
|
public:
|
||||||
enum mode {
|
|
||||||
READ_ONLY,
|
|
||||||
READ_WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
enum dir {
|
|
||||||
READ,
|
|
||||||
WRITE
|
|
||||||
};
|
|
||||||
|
|
||||||
// max_io is the maximum nr of concurrent ios expected
|
// max_io is the maximum nr of concurrent ios expected
|
||||||
io_engine(unsigned max_io);
|
aio_engine(unsigned max_io);
|
||||||
~io_engine();
|
~aio_engine();
|
||||||
|
|
||||||
using handle = unsigned;
|
using handle = unsigned;
|
||||||
|
|
||||||
handle open_file(std::string const &path, mode m);
|
// FIXME: open exclusive?
|
||||||
void close_file(handle h);
|
virtual handle open_file(std::string const &path, mode m);
|
||||||
|
virtual void close_file(handle h);
|
||||||
|
|
||||||
// returns false if there are insufficient resources to
|
// Returns false if queueing the io failed
|
||||||
// queue the IO
|
virtual bool issue_io(handle h, dir d, sector_t b, sector_t e, void *data, unsigned context);
|
||||||
bool issue_io(handle h, dir d, sector_t b, sector_t e, void *data, unsigned context);
|
|
||||||
|
|
||||||
// returns (success, context)
|
// returns (success, context)
|
||||||
std::pair<bool, unsigned> wait();
|
virtual wait_result wait();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<base::unique_fd> descriptors_;
|
std::list<base::unique_fd> descriptors_;
|
||||||
|
|
||||||
io_context_t aio_context_;
|
io_context_t aio_context_;
|
||||||
control_block_set cbs_;
|
control_block_set cbs_;
|
||||||
std::vector<io_event> events_;
|
|
||||||
|
|
||||||
io_engine(io_engine const &) = delete;
|
aio_engine(io_engine const &) = delete;
|
||||||
io_engine &operator =(io_engine const &) = delete;
|
aio_engine &operator =(io_engine const &) = delete;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +120,8 @@ namespace {
|
|||||||
int writeback_(flags const &f) {
|
int writeback_(flags const &f) {
|
||||||
block_manager<>::ptr bm = open_bm(*f.metadata_dev, block_manager<>::READ_ONLY);
|
block_manager<>::ptr bm = open_bm(*f.metadata_dev, block_manager<>::READ_ONLY);
|
||||||
metadata md(bm, metadata::OPEN);
|
metadata md(bm, metadata::OPEN);
|
||||||
|
aio_engine engine(f.cache_size / md.sb_.data_block_size);
|
||||||
copier c(*f.fast_dev, *f.origin_dev,
|
copier c(engine, *f.fast_dev, *f.origin_dev,
|
||||||
md.sb_.data_block_size, f.cache_size);
|
md.sb_.data_block_size, f.cache_size);
|
||||||
copy_visitor cv(c, clean_shutdown(md));
|
copy_visitor cv(c, clean_shutdown(md));
|
||||||
ignore_damage_visitor dv;
|
ignore_damage_visitor dv;
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
# with thin-provisioning-tools. If not, see
|
# with thin-provisioning-tools. If not, see
|
||||||
# <http://www.gnu.org/licenses/>.
|
# <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
GMOCK_DIR=gmock-1.6.0/
|
GMOCK_DIR=googletest
|
||||||
GMOCK_INCLUDES=\
|
GMOCK_INCLUDES=\
|
||||||
-Igmock-1.6.0/include \
|
-I$(GMOCK_DIR)/googlemock/include \
|
||||||
-Igmock-1.6.0/gtest/include
|
-I$(GMOCK_DIR)/googletest/include
|
||||||
|
|
||||||
GMOCK_FLAGS=\
|
GMOCK_FLAGS=\
|
||||||
-Wno-unused-local-typedefs
|
-Wno-unused-local-typedefs
|
||||||
@ -28,16 +28,16 @@ GMOCK_LIBS=\
|
|||||||
-Llib -lpdata -lgmock -lpthread -laio
|
-Llib -lpdata -lgmock -lpthread -laio
|
||||||
|
|
||||||
GMOCK_DEPS=\
|
GMOCK_DEPS=\
|
||||||
$(wildcard $(GMOCK_DIR)/include/*.h) \
|
$(wildcard $(GMOCK_DIR)/googlemock/include/*.h) \
|
||||||
$(wildcard $(GMOCK_DIR)/src/*.cc) \
|
$(wildcard $(GMOCK_DIR)/googlemock/src/*.cc) \
|
||||||
$(wildcard $(GMOCK_DIR)/gtest/include/*.h) \
|
$(wildcard $(GMOCK_DIR)/googletest/include/*.h) \
|
||||||
$(wildcard $(GMOCK_DIR)/gtest/src/*.cc)
|
$(wildcard $(GMOCK_DIR)/googletest/src/*.cc)
|
||||||
|
|
||||||
lib/libgmock.a: $(GMOCK_DEPS)
|
lib/libgmock.a: $(GMOCK_DEPS)
|
||||||
@echo " [CXX] gtest"
|
@echo " [CXX] gtest"
|
||||||
$(V)g++ $(GMOCK_INCLUDES) -I$(GMOCK_DIR)/gtest -c $(GMOCK_DIR)/gtest/src/gtest-all.cc
|
$(V)g++ $(GMOCK_INCLUDES) -I$(GMOCK_DIR)/googletest -c $(GMOCK_DIR)/googletest/src/gtest-all.cc
|
||||||
@echo " [CXX] gmock"
|
@echo " [CXX] gmock"
|
||||||
$(V)g++ $(GMOCK_INCLUDES) -I$(GMOCK_DIR) -c $(GMOCK_DIR)/src/gmock-all.cc
|
$(V)g++ $(GMOCK_INCLUDES) -I$(GMOCK_DIR)/googlemock -c $(GMOCK_DIR)/googlemock/src/gmock-all.cc
|
||||||
@echo " [AR] $<"
|
@echo " [AR] $<"
|
||||||
$(V)ar -rv lib/libgmock.a gtest-all.o gmock-all.o > /dev/null 2>&1
|
$(V)ar -rv lib/libgmock.a gtest-all.o gmock-all.o > /dev/null 2>&1
|
||||||
|
|
||||||
@ -59,6 +59,7 @@ TEST_SOURCE=\
|
|||||||
unit-tests/damage_tracker_t.cc \
|
unit-tests/damage_tracker_t.cc \
|
||||||
unit-tests/endian_t.cc \
|
unit-tests/endian_t.cc \
|
||||||
unit-tests/error_state_t.cc \
|
unit-tests/error_state_t.cc \
|
||||||
|
unit-tests/io_engine_t.cc \
|
||||||
unit-tests/mem_pool_t.cc \
|
unit-tests/mem_pool_t.cc \
|
||||||
unit-tests/rmap_visitor_t.cc \
|
unit-tests/rmap_visitor_t.cc \
|
||||||
unit-tests/rolling_hash_t.cc \
|
unit-tests/rolling_hash_t.cc \
|
||||||
|
@ -31,72 +31,197 @@ using namespace testing;
|
|||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class temp_file {
|
class io_engine_mock : public io_engine {
|
||||||
public:
|
public:
|
||||||
temp_file(string const &name_base, unsigned meg_size)
|
MOCK_METHOD2(open_file, handle(string const &, mode));
|
||||||
: path_(gen_path(name_base)) {
|
MOCK_METHOD1(close_file, void(handle));
|
||||||
|
MOCK_METHOD6(issue_io, bool(handle, dir, sector_t, sector_t, void *, unsigned));
|
||||||
|
|
||||||
int fd = ::open(path_.c_str(), O_CREAT | O_RDWR, 0666);
|
MOCK_METHOD0(wait, wait_result());
|
||||||
if (fd < 0)
|
|
||||||
throw runtime_error("couldn't open file");
|
|
||||||
|
|
||||||
if (::fallocate(fd, 0, 0, 1024 * 1024 * meg_size))
|
|
||||||
throw runtime_error("couldn't fallocate");
|
|
||||||
|
|
||||||
::close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
~temp_file() {
|
|
||||||
::unlink(path_.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
string const &get_path() const {
|
|
||||||
return path_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static string gen_path(string const &base) {
|
|
||||||
return string("./") + base + string(".tmp");
|
|
||||||
}
|
|
||||||
|
|
||||||
string path_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsigned const BLOCK_SIZE = 64u;
|
||||||
|
using wait_result = io_engine::wait_result;
|
||||||
|
|
||||||
class CopierTests : public Test {
|
class CopierTests : public Test {
|
||||||
public:
|
public:
|
||||||
CopierTests()
|
CopierTests()
|
||||||
: src_file_("copy_src", 32),
|
: src_file_("copy_src"),
|
||||||
dest_file_("copy_dest", 32),
|
dest_file_("copy_dest") {
|
||||||
copier_(src_file_.get_path(),
|
|
||||||
dest_file_.get_path(),
|
|
||||||
64, 1 * 1024 * 1024) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copier &get_copier() {
|
unique_ptr<copier> make_copier() {
|
||||||
return copier_;
|
EXPECT_CALL(engine_, open_file(src_file_, io_engine::READ_ONLY)).
|
||||||
|
WillOnce(Return(SRC_HANDLE));
|
||||||
|
EXPECT_CALL(engine_, open_file(dest_file_, io_engine::READ_WRITE)).
|
||||||
|
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<copier>(new copier(engine_, src_file_,
|
||||||
|
dest_file_,
|
||||||
|
BLOCK_SIZE, 1 * 1024 * 1024));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
void issue_successful_op(copier &c, copy_op &op, unsigned context) {
|
||||||
temp_file src_file_;
|
InSequence dummy;
|
||||||
temp_file dest_file_;
|
|
||||||
|
|
||||||
copier copier_;
|
unsigned nr_pending = c.nr_pending();
|
||||||
|
EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::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(wait_result(true, context)));
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::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(wait_result(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<io_engine_mock> engine_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
TEST_F(CopierTests, empty_test)
|
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::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::READ, 0, BLOCK_SIZE, _, 0)).
|
||||||
|
WillOnce(Return(true));
|
||||||
|
c->issue(op1);
|
||||||
|
|
||||||
|
ASSERT_EQ(c->nr_pending(), 1u);
|
||||||
|
EXPECT_CALL(engine_, wait()).
|
||||||
|
WillOnce(Return(wait_result(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::READ, 0, BLOCK_SIZE, _, 0)).
|
||||||
|
WillOnce(Return(true));
|
||||||
|
c->issue(op1);
|
||||||
|
|
||||||
|
ASSERT_EQ(c->nr_pending(), 1u);
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, wait()).
|
||||||
|
WillOnce(Return(wait_result(true, 0u)));
|
||||||
|
ASSERT_EQ(c->nr_pending(), 1u);
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::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 first block
|
||||||
copy_op op1(0, 1, 0);
|
copy_op op1(0, 1, 0);
|
||||||
get_copier().issue(op1);
|
|
||||||
auto mop = get_copier().wait();
|
|
||||||
|
|
||||||
if (mop) {
|
auto c = make_copier();
|
||||||
cerr << "op completed\n";
|
|
||||||
} else {
|
InSequence dummy;
|
||||||
cerr << "no op completed\n";
|
EXPECT_CALL(engine_, issue_io(SRC_HANDLE, io_engine::READ, 0, BLOCK_SIZE, _, 0)).
|
||||||
|
WillOnce(Return(true));
|
||||||
|
c->issue(op1);
|
||||||
|
ASSERT_EQ(c->nr_pending(), 1u);
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, wait()).
|
||||||
|
WillOnce(Return(wait_result(true, 0u)));
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, issue_io(DEST_HANDLE, io_engine::WRITE, 0, BLOCK_SIZE, _, 0)).
|
||||||
|
WillOnce(Return(true));
|
||||||
|
|
||||||
|
EXPECT_CALL(engine_, wait()).
|
||||||
|
WillOnce(Return(wait_result(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
185
unit-tests/io_engine_t.cc
Normal file
185
unit-tests/io_engine_t.cc
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
// 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
|
||||||
|
// <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "block-cache/mem_pool.h"
|
||||||
|
#include "block-cache/io_engine.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
using namespace boost;
|
||||||
|
using namespace std;
|
||||||
|
using namespace test;
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
unsigned const MAX_IO = 64;
|
||||||
|
unsigned const PAGE_SIZE = 4096;
|
||||||
|
|
||||||
|
class IOEngineTests : public Test {
|
||||||
|
public:
|
||||||
|
IOEngineTests()
|
||||||
|
: pool_(64 * 512, 128 * 512, PAGE_SIZE),
|
||||||
|
src_file_("copy_src", 32),
|
||||||
|
dest_file_("copy_dest", 32),
|
||||||
|
engine_(new aio_engine(MAX_IO)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// in sectors
|
||||||
|
static uint64_t meg(unsigned n) {
|
||||||
|
return 2 * 1024 * n;
|
||||||
|
}
|
||||||
|
|
||||||
|
mempool pool_;
|
||||||
|
temp_file src_file_;
|
||||||
|
temp_file dest_file_;
|
||||||
|
unique_ptr<io_engine> engine_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, empty_test)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, open_and_close)
|
||||||
|
{
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
auto dest_handle = engine_->open_file(dest_file_.get_path(), io_engine::READ_WRITE);
|
||||||
|
ASSERT_TRUE(src_handle != dest_handle);
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
engine_->close_file(dest_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, you_can_read_a_read_only_handle)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(src_handle,
|
||||||
|
io_engine::READ,
|
||||||
|
0, nr_sectors,
|
||||||
|
data,
|
||||||
|
123);
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
auto wr = engine_->wait();
|
||||||
|
ASSERT_TRUE(wr.first);
|
||||||
|
ASSERT_TRUE(wr.second == 123);
|
||||||
|
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
pool_.free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, you_cannot_write_to_a_read_only_handle)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(src_handle,
|
||||||
|
io_engine::WRITE,
|
||||||
|
0, nr_sectors,
|
||||||
|
data,
|
||||||
|
0);
|
||||||
|
ASSERT_FALSE(r);
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
pool_.free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, you_can_write_to_a_read_write_handle)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(src_handle,
|
||||||
|
io_engine::READ,
|
||||||
|
0, nr_sectors,
|
||||||
|
data,
|
||||||
|
123);
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
auto wr = engine_->wait();
|
||||||
|
ASSERT_TRUE(wr.first);
|
||||||
|
ASSERT_TRUE(wr.second == 123);
|
||||||
|
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
pool_.free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, final_block_read_succeeds)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(src_handle,
|
||||||
|
io_engine::READ,
|
||||||
|
meg(32) - nr_sectors, meg(32),
|
||||||
|
data,
|
||||||
|
123);
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
auto wr = engine_->wait();
|
||||||
|
ASSERT_TRUE(wr.first);
|
||||||
|
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
pool_.free(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, out_of_bounds_read_fails)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto src_handle = engine_->open_file(src_file_.get_path(), io_engine::READ_ONLY);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(src_handle,
|
||||||
|
io_engine::READ,
|
||||||
|
meg(32), meg(32) + nr_sectors,
|
||||||
|
data,
|
||||||
|
123);
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
auto wr = engine_->wait();
|
||||||
|
ASSERT_FALSE(wr.first);
|
||||||
|
|
||||||
|
engine_->close_file(src_handle);
|
||||||
|
pool_.free(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(IOEngineTests, out_of_bounds_write_succeeds)
|
||||||
|
{
|
||||||
|
unsigned nr_sectors = 8;
|
||||||
|
auto handle = engine_->open_file(dest_file_.get_path(), io_engine::READ_WRITE);
|
||||||
|
void *data = pool_.alloc();
|
||||||
|
bool r = engine_->issue_io(handle,
|
||||||
|
io_engine::WRITE,
|
||||||
|
meg(32), meg(32) + nr_sectors,
|
||||||
|
data,
|
||||||
|
123);
|
||||||
|
ASSERT_TRUE(r);
|
||||||
|
auto wr = engine_->wait();
|
||||||
|
ASSERT_TRUE(wr.first);
|
||||||
|
|
||||||
|
engine_->close_file(handle);
|
||||||
|
pool_.free(data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
@ -93,4 +93,20 @@ TEST_F(MempoolTests, exhaust_pool)
|
|||||||
ASSERT_EQ(md, nullptr);
|
ASSERT_EQ(md, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use valgrind
|
||||||
|
TEST_F(MempoolTests, data_can_be_written)
|
||||||
|
{
|
||||||
|
mempool mp(512, 100 * 512, 512);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < 100; i++) {
|
||||||
|
auto md = mp.alloc();
|
||||||
|
ASSERT_NE(md, nullptr);
|
||||||
|
|
||||||
|
memset(md, 0, 512);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto md = mp.alloc();
|
||||||
|
ASSERT_EQ(md, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
#include "persistent-data/space-maps/core.h"
|
#include "persistent-data/space-maps/core.h"
|
||||||
|
|
||||||
using namespace persistent_data;
|
using namespace persistent_data;
|
||||||
|
using namespace std;
|
||||||
|
using namespace test;
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
@ -21,3 +23,35 @@ test::open_temporary_tm(block_manager<>::ptr bm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
temp_file::temp_file(string const &name_base, unsigned meg_size)
|
||||||
|
: path_(gen_path(name_base))
|
||||||
|
{
|
||||||
|
int fd = ::open(path_.c_str(), O_CREAT | O_RDWR, 0666);
|
||||||
|
if (fd < 0)
|
||||||
|
throw runtime_error("couldn't open file");
|
||||||
|
|
||||||
|
if (::fallocate(fd, 0, 0, 1024 * 1024 * meg_size))
|
||||||
|
throw runtime_error("couldn't fallocate");
|
||||||
|
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_file::~temp_file()
|
||||||
|
{
|
||||||
|
// ::unlink(path_.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
string const &
|
||||||
|
temp_file::get_path() const
|
||||||
|
{
|
||||||
|
return path_;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
temp_file::gen_path(string const &base)
|
||||||
|
{
|
||||||
|
return string("./") + base + string(".tmp");
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
@ -114,6 +114,18 @@ namespace test {
|
|||||||
std::unique_ptr<with_directory> dir_;
|
std::unique_ptr<with_directory> dir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class temp_file {
|
||||||
|
public:
|
||||||
|
temp_file(std::string const &name_base, unsigned meg_size);
|
||||||
|
~temp_file();
|
||||||
|
std::string const &get_path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static string gen_path(string const &base);
|
||||||
|
|
||||||
|
string path_;
|
||||||
|
};
|
||||||
|
|
||||||
//--------------------------------
|
//--------------------------------
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
Loading…
Reference in New Issue
Block a user