Test cases for read()
This commit is contained in:
parent
96826349e7
commit
6c86b4ab2a
@ -399,8 +399,6 @@ int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_
|
|||||||
//printf("read(%s, _, %zu, %zu, _)\n", path.c_str(), size, offset);
|
//printf("read(%s, _, %zu, %zu, _)\n", path.c_str(), size, offset);
|
||||||
UNUSED(path);
|
UNUSED(path);
|
||||||
try {
|
try {
|
||||||
//printf("Reading from file %d\n", fileinfo->fh);
|
|
||||||
//fflush(stdout);
|
|
||||||
return _fs->read(fileinfo->fh, buf, size, offset);
|
return _fs->read(fileinfo->fh, buf, size, offset);
|
||||||
} catch (FuseErrnoException &e) {
|
} catch (FuseErrnoException &e) {
|
||||||
return -e.getErrno();
|
return -e.getErrno();
|
||||||
|
@ -20,8 +20,8 @@ public:
|
|||||||
virtual void fstat(int descriptor, struct ::stat *stbuf) = 0;
|
virtual void fstat(int descriptor, struct ::stat *stbuf) = 0;
|
||||||
virtual void truncate(const boost::filesystem::path &path, off_t size) = 0;
|
virtual void truncate(const boost::filesystem::path &path, off_t size) = 0;
|
||||||
virtual void ftruncate(int descriptor, off_t size) = 0;
|
virtual void ftruncate(int descriptor, off_t size) = 0;
|
||||||
//TODO Unit-Tests for all functions below
|
|
||||||
virtual int read(int descriptor, void *buf, size_t count, off_t offset) = 0;
|
virtual int read(int descriptor, void *buf, size_t count, off_t offset) = 0;
|
||||||
|
//TODO Unit-Tests for all functions below
|
||||||
virtual void write(int descriptor, const void *buf, size_t count, off_t offset) = 0;
|
virtual void write(int descriptor, const void *buf, size_t count, off_t offset) = 0;
|
||||||
virtual void fsync(int descriptor) = 0;
|
virtual void fsync(int descriptor) = 0;
|
||||||
virtual void fdatasync(int descriptor) = 0;
|
virtual void fdatasync(int descriptor) = 0;
|
||||||
|
64
src/test/fspp/fuse/read/FuseReadErrorTest.cpp
Normal file
64
src/test/fspp/fuse/read/FuseReadErrorTest.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "testutils/FuseReadTest.h"
|
||||||
|
|
||||||
|
#include "fspp/impl/FuseErrnoException.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::WithParamInterface;
|
||||||
|
using ::testing::Values;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Ne;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
using ::testing::Throw;
|
||||||
|
|
||||||
|
using namespace fspp;
|
||||||
|
|
||||||
|
class FuseReadErrorTest: public FuseReadTest, public WithParamInterface<int> {
|
||||||
|
public:
|
||||||
|
size_t FILESIZE = 1024*1024*1024;
|
||||||
|
size_t READCOUNT = 512*1024*1024;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
//Make the file size big enough that fuse should issue at least two reads
|
||||||
|
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
|
||||||
|
OnOpenReturnFileDescriptor(FILENAME, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
INSTANTIATE_TEST_CASE_P(FuseReadErrorTest, FuseReadErrorTest, Values(EAGAIN, EBADF, EFAULT, EINTR, EINVAL, EIO, EISDIR, EOVERFLOW, ESPIPE, ENXIO));
|
||||||
|
|
||||||
|
|
||||||
|
TEST_P(FuseReadErrorTest, ReturnErrorOnFirstReadCall) {
|
||||||
|
EXPECT_CALL(fsimpl, read(0, _, _, _))
|
||||||
|
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
|
||||||
|
|
||||||
|
char *buf = new char[READCOUNT];
|
||||||
|
errno = 0;
|
||||||
|
int retval = ReadFileAllowError(FILENAME, buf, READCOUNT, 0);
|
||||||
|
EXPECT_EQ(GetParam(), errno);
|
||||||
|
EXPECT_EQ(-1, retval);
|
||||||
|
delete[] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(FuseReadErrorTest, ReturnErrorOnSecondReadCall) {
|
||||||
|
// The first read request is from the beginning of the file and works, but the later ones fail.
|
||||||
|
// We store the number of bytes the first call could successfully read and check later that our
|
||||||
|
// read syscall returns exactly this number of bytes
|
||||||
|
size_t successfullyReadBytes = -1;
|
||||||
|
EXPECT_CALL(fsimpl, read(0, _, _, Eq(0)))
|
||||||
|
.Times(1)
|
||||||
|
.WillOnce(Invoke([&successfullyReadBytes](int, void*, size_t count, off_t) {
|
||||||
|
// Store the number of successfully read bytes
|
||||||
|
successfullyReadBytes = count;
|
||||||
|
return count;
|
||||||
|
}));
|
||||||
|
EXPECT_CALL(fsimpl, read(0, _, _, Ne(0)))
|
||||||
|
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
|
||||||
|
|
||||||
|
char *buf = new char[READCOUNT];
|
||||||
|
errno = 0;
|
||||||
|
size_t retval = ReadFileAllowError(FILENAME, buf, READCOUNT, 0);
|
||||||
|
EXPECT_EQ(0, errno);
|
||||||
|
EXPECT_EQ(successfullyReadBytes, retval); // Check that we're getting the number of successfully read bytes (the first read call) returned
|
||||||
|
delete[] buf;
|
||||||
|
}
|
29
src/test/fspp/fuse/read/FuseReadFileDescriptorTest.cpp
Normal file
29
src/test/fspp/fuse/read/FuseReadFileDescriptorTest.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include "testutils/FuseReadTest.h"
|
||||||
|
|
||||||
|
#include "fspp/impl/FuseErrnoException.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::WithParamInterface;
|
||||||
|
using ::testing::Values;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
using ::testing::Throw;
|
||||||
|
|
||||||
|
using namespace fspp;
|
||||||
|
|
||||||
|
class FuseReadFileDescriptorTest: public FuseReadTest, public WithParamInterface<int> {
|
||||||
|
};
|
||||||
|
INSTANTIATE_TEST_CASE_P(FuseReadFileDescriptorTest, FuseReadFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
|
||||||
|
|
||||||
|
|
||||||
|
TEST_P(FuseReadFileDescriptorTest, FileDescriptorIsCorrect) {
|
||||||
|
ReturnIsFileOnLstat(FILENAME);
|
||||||
|
OnOpenReturnFileDescriptor(FILENAME, GetParam());
|
||||||
|
EXPECT_CALL(fsimpl, read(Eq(GetParam()), _, _, _))
|
||||||
|
.Times(1).WillOnce(ReturnSuccessfulRead);
|
||||||
|
|
||||||
|
char buf[1];
|
||||||
|
ReadFile(FILENAME, buf, 0, 0);
|
||||||
|
}
|
40
src/test/fspp/fuse/read/FuseReadOverflowTest.cpp
Normal file
40
src/test/fspp/fuse/read/FuseReadOverflowTest.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include "testutils/FuseReadTest.h"
|
||||||
|
|
||||||
|
#include "fspp/impl/FuseErrnoException.h"
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
using ::testing::Action;
|
||||||
|
|
||||||
|
using std::min;
|
||||||
|
|
||||||
|
using namespace fspp;
|
||||||
|
|
||||||
|
class FuseReadOverflowTest: public FuseReadTest {
|
||||||
|
public:
|
||||||
|
const size_t FILESIZE = 1000;
|
||||||
|
const size_t READSIZE = 2000;
|
||||||
|
const size_t OFFSET = 500;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
|
||||||
|
OnOpenReturnFileDescriptor(FILENAME, 0);
|
||||||
|
EXPECT_CALL(fsimpl, read(0, _, _, _)).WillRepeatedly(ReturnSuccessfulReadRegardingSize(FILESIZE));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromBeginning) {
|
||||||
|
char buf[READSIZE];
|
||||||
|
size_t read_bytes = ReadFileAllowError(FILENAME, buf, READSIZE, 0);
|
||||||
|
EXPECT_EQ(FILESIZE, read_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FuseReadOverflowTest, ReadMoreThanFileSizeFromMiddle) {
|
||||||
|
char buf[READSIZE];
|
||||||
|
size_t read_bytes = ReadFileAllowError(FILENAME, buf, READSIZE, OFFSET);
|
||||||
|
EXPECT_EQ(FILESIZE-OFFSET, read_bytes);
|
||||||
|
}
|
95
src/test/fspp/fuse/read/FuseReadReturnedDataTest.cpp
Normal file
95
src/test/fspp/fuse/read/FuseReadReturnedDataTest.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include "testutils/FuseReadTest.h"
|
||||||
|
|
||||||
|
#include "fspp/impl/FuseErrnoException.h"
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
using ::testing::StrEq;
|
||||||
|
using ::testing::WithParamInterface;
|
||||||
|
using ::testing::Values;
|
||||||
|
using ::testing::Combine;
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Return;
|
||||||
|
using ::testing::Invoke;
|
||||||
|
using ::testing::Action;
|
||||||
|
|
||||||
|
using std::tuple;
|
||||||
|
using std::get;
|
||||||
|
using std::min;
|
||||||
|
|
||||||
|
using namespace fspp;
|
||||||
|
|
||||||
|
// We can't test the count or size parameter directly, because fuse doesn't pass them 1:1.
|
||||||
|
// It usually asks to read bigger blocks (probably does some caching).
|
||||||
|
// But we can test that the data returned from the ::read syscall is the correct data region.
|
||||||
|
|
||||||
|
struct TestData {
|
||||||
|
TestData(): count(0), offset(0), additional_bytes_at_end_of_file(0) {}
|
||||||
|
TestData(const tuple<size_t, off_t, size_t> &data): count(get<0>(data)), offset(get<1>(data)), additional_bytes_at_end_of_file(get<2>(data)) {}
|
||||||
|
size_t count;
|
||||||
|
off_t offset;
|
||||||
|
//How many more bytes does the file have after the read block?
|
||||||
|
size_t additional_bytes_at_end_of_file;
|
||||||
|
size_t fileSize() {
|
||||||
|
return count + offset + additional_bytes_at_end_of_file;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The testcase creates random data in memory, offers a mock read() implementation to read from this
|
||||||
|
// memory region and check methods to check for data equality of a region.
|
||||||
|
class FuseReadReturnedDataTest: public FuseReadTest, public WithParamInterface<tuple<size_t, off_t, size_t>> {
|
||||||
|
public:
|
||||||
|
char *fileData;
|
||||||
|
TestData testData;
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
testData = GetParam();
|
||||||
|
setupFileData();
|
||||||
|
|
||||||
|
ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize());
|
||||||
|
OnOpenReturnFileDescriptor(FILENAME, 0);
|
||||||
|
EXPECT_CALL(fsimpl, read(0, _, _, _))
|
||||||
|
.WillRepeatedly(ReadFromFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
delete[] fileData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true, iff the given data is equal to the data of the file at the given offset.
|
||||||
|
bool fileContentCorrect(char *content, size_t count, off_t offset) {
|
||||||
|
return 0 == memcmp(content, fileData + offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This read() mock implementation reads from the stored random data.
|
||||||
|
Action<int(int, void*, size_t, off_t)> ReadFromFile = Invoke([this](int, void *buf, size_t count, off_t offset) {
|
||||||
|
size_t realCount = min(count, testData.fileSize() - offset);
|
||||||
|
memcpy(buf, fileData+offset, realCount);
|
||||||
|
return realCount;
|
||||||
|
});
|
||||||
|
private:
|
||||||
|
void setupFileData() {
|
||||||
|
fileData = new char[testData.fileSize()];
|
||||||
|
fillFileWithRandomData();
|
||||||
|
}
|
||||||
|
void fillFileWithRandomData() {
|
||||||
|
long long int val = 1;
|
||||||
|
for(unsigned int i=0; i<testData.fileSize()/sizeof(long long int); ++i) {
|
||||||
|
//MMIX linear congruential generator
|
||||||
|
val *= 6364136223846793005L;
|
||||||
|
val += 1442695040888963407;
|
||||||
|
reinterpret_cast<long long int*>(fileData)[i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
INSTANTIATE_TEST_CASE_P(FuseReadReturnedDataTest, FuseReadReturnedDataTest, Combine(Values(0,1,10,1000,1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024), Values(0, 1, 10, 1024, 10*1024*1024)));
|
||||||
|
|
||||||
|
|
||||||
|
TEST_P(FuseReadReturnedDataTest, ReturnedDataRangeIsCorrect) {
|
||||||
|
char *buf = new char[testData.count];
|
||||||
|
ReadFile(FILENAME, buf, testData.count, testData.offset);
|
||||||
|
EXPECT_TRUE(fileContentCorrect(buf, testData.count, testData.offset));
|
||||||
|
delete[] buf;
|
||||||
|
}
|
20
src/test/fspp/fuse/read/testutils/FuseReadTest.cpp
Normal file
20
src/test/fspp/fuse/read/testutils/FuseReadTest.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "FuseReadTest.h"
|
||||||
|
|
||||||
|
void FuseReadTest::ReadFile(const char *filename, void *buf, size_t count, off_t offset) {
|
||||||
|
size_t retval = ReadFileAllowError(filename, buf, count, offset);
|
||||||
|
EXPECT_EQ(count, retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FuseReadTest::ReadFileAllowError(const char *filename, void *buf, size_t count, off_t offset) {
|
||||||
|
auto fs = TestFS();
|
||||||
|
|
||||||
|
int fd = OpenFile(fs.get(), filename);
|
||||||
|
return ::pread(fd, buf, count, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FuseReadTest::OpenFile(const TempTestFS *fs, const char *filename) {
|
||||||
|
auto realpath = fs->mountDir() / filename;
|
||||||
|
int fd = ::open(realpath.c_str(), O_RDONLY);
|
||||||
|
EXPECT_GE(fd, 0) << "Error opening file";
|
||||||
|
return fd;
|
||||||
|
}
|
31
src/test/fspp/fuse/read/testutils/FuseReadTest.h
Normal file
31
src/test/fspp/fuse/read/testutils/FuseReadTest.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef TEST_FSPP_FUSE_READ_TESTUTILS_FUSEREADTEST_H_
|
||||||
|
#define TEST_FSPP_FUSE_READ_TESTUTILS_FUSEREADTEST_H_
|
||||||
|
|
||||||
|
#include "test/testutils/FuseTest.h"
|
||||||
|
|
||||||
|
class FuseReadTest: public FuseTest {
|
||||||
|
public:
|
||||||
|
const char *FILENAME = "/myfile";
|
||||||
|
|
||||||
|
void ReadFile(const char *filename, void *buf, size_t count, off_t offset);
|
||||||
|
size_t ReadFileAllowError(const char *filename, void *buf, size_t count, off_t offset);
|
||||||
|
|
||||||
|
::testing::Action<int(int, void*, size_t, off_t)> ReturnSuccessfulRead =
|
||||||
|
::testing::Invoke([](int, void *, size_t count, off_t) {
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
|
||||||
|
// This read() mock implementation reads from the stored random data.
|
||||||
|
::testing::Action<int(int, void*, size_t, off_t)> ReturnSuccessfulReadRegardingSize(size_t filesize) {
|
||||||
|
return ::testing::Invoke([filesize](int, void *, size_t count, off_t offset) {
|
||||||
|
size_t ableToReadCount = std::min(count, filesize - offset);
|
||||||
|
return ableToReadCount;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int OpenFile(const TempTestFS *fs, const char *filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -15,6 +15,10 @@ void FuseTest::ReturnIsFileOnLstat(const bf::path &path) {
|
|||||||
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsFile);
|
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FuseTest::ReturnIsFileOnLstatWithSize(const bf::path &path, const size_t size) {
|
||||||
|
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsFileWithSize(size));
|
||||||
|
}
|
||||||
|
|
||||||
void FuseTest::ReturnIsDirOnLstat(const bf::path &path) {
|
void FuseTest::ReturnIsDirOnLstat(const bf::path &path) {
|
||||||
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsDir);
|
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsDir);
|
||||||
}
|
}
|
||||||
|
@ -125,11 +125,15 @@ public:
|
|||||||
MockFilesystem fsimpl;
|
MockFilesystem fsimpl;
|
||||||
|
|
||||||
//TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros
|
//TODO Combine ReturnIsFile and ReturnIsFileFstat. This should be possible in gmock by either (a) using ::testing::Undefined as parameter type or (b) using action macros
|
||||||
::testing::Action<void(const char*, struct ::stat*)> ReturnIsFile =
|
::testing::Action<void(const char*, struct ::stat*)> ReturnIsFile = ReturnIsFileWithSize(0);
|
||||||
::testing::Invoke([](const char*, struct ::stat* result) {
|
|
||||||
|
::testing::Action<void(const char*, struct ::stat*)> ReturnIsFileWithSize(size_t size) {
|
||||||
|
return ::testing::Invoke([size](const char*, struct ::stat* result) {
|
||||||
result->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
result->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
result->st_nlink = 1;
|
result->st_nlink = 1;
|
||||||
|
result->st_size = size;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
::testing::Action<void(int, struct ::stat*)> ReturnIsFileFstat =
|
::testing::Action<void(int, struct ::stat*)> ReturnIsFileFstat =
|
||||||
::testing::Invoke([](int, struct ::stat* result) {
|
::testing::Invoke([](int, struct ::stat* result) {
|
||||||
@ -146,6 +150,7 @@ public:
|
|||||||
::testing::Action<void(const char*, struct ::stat*)> ReturnDoesntExist = ::testing::Throw(fspp::FuseErrnoException(ENOENT));
|
::testing::Action<void(const char*, struct ::stat*)> ReturnDoesntExist = ::testing::Throw(fspp::FuseErrnoException(ENOENT));
|
||||||
|
|
||||||
void ReturnIsFileOnLstat(const bf::path &path);
|
void ReturnIsFileOnLstat(const bf::path &path);
|
||||||
|
void ReturnIsFileOnLstatWithSize(const bf::path &path, const size_t size);
|
||||||
void ReturnIsDirOnLstat(const bf::path &path);
|
void ReturnIsDirOnLstat(const bf::path &path);
|
||||||
void ReturnDoesntExistOnLstat(const bf::path &path);
|
void ReturnDoesntExistOnLstat(const bf::path &path);
|
||||||
void OnOpenReturnFileDescriptor(const char *filename, int descriptor);
|
void OnOpenReturnFileDescriptor(const char *filename, int descriptor);
|
||||||
|
Loading…
Reference in New Issue
Block a user