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);
|
||||
UNUSED(path);
|
||||
try {
|
||||
//printf("Reading from file %d\n", fileinfo->fh);
|
||||
//fflush(stdout);
|
||||
return _fs->read(fileinfo->fh, buf, size, offset);
|
||||
} catch (FuseErrnoException &e) {
|
||||
return -e.getErrno();
|
||||
|
@ -20,8 +20,8 @@ public:
|
||||
virtual void fstat(int descriptor, struct ::stat *stbuf) = 0;
|
||||
virtual void truncate(const boost::filesystem::path &path, 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;
|
||||
//TODO Unit-Tests for all functions below
|
||||
virtual void write(int descriptor, const void *buf, size_t count, off_t offset) = 0;
|
||||
virtual void fsync(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);
|
||||
}
|
||||
|
||||
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) {
|
||||
EXPECT_CALL(fsimpl, lstat(::testing::StrEq(path.c_str()), ::testing::_)).WillRepeatedly(ReturnIsDir);
|
||||
}
|
||||
|
@ -125,11 +125,15 @@ public:
|
||||
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
|
||||
::testing::Action<void(const char*, struct ::stat*)> ReturnIsFile =
|
||||
::testing::Invoke([](const char*, struct ::stat* result) {
|
||||
::testing::Action<void(const char*, struct ::stat*)> ReturnIsFile = ReturnIsFileWithSize(0);
|
||||
|
||||
::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_nlink = 1;
|
||||
result->st_size = size;
|
||||
});
|
||||
}
|
||||
|
||||
::testing::Action<void(int, struct ::stat*)> ReturnIsFileFstat =
|
||||
::testing::Invoke([](int, struct ::stat* result) {
|
||||
@ -146,6 +150,7 @@ public:
|
||||
::testing::Action<void(const char*, struct ::stat*)> ReturnDoesntExist = ::testing::Throw(fspp::FuseErrnoException(ENOENT));
|
||||
|
||||
void ReturnIsFileOnLstat(const bf::path &path);
|
||||
void ReturnIsFileOnLstatWithSize(const bf::path &path, const size_t size);
|
||||
void ReturnIsDirOnLstat(const bf::path &path);
|
||||
void ReturnDoesntExistOnLstat(const bf::path &path);
|
||||
void OnOpenReturnFileDescriptor(const char *filename, int descriptor);
|
||||
|
Loading…
Reference in New Issue
Block a user