Added write() test cases
This commit is contained in:
parent
b429432d83
commit
141c7d09cd
82
src/test/fspp/fuse/write/FuseWriteDataTest.cpp
Normal file
82
src/test/fspp/fuse/write/FuseWriteDataTest.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
#include "testutils/FuseWriteTest.h"
|
||||
|
||||
#include "test/testutils/VirtualTestFile.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 std::unique_ptr;
|
||||
using std::make_unique;
|
||||
|
||||
using namespace fspp;
|
||||
|
||||
// We can't test the count or size parameter directly, because fuse doesn't pass them 1:1.
|
||||
// But we can test that the data passed to the ::write syscall is correctly written.
|
||||
|
||||
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 write() implementation to write to this
|
||||
// memory region and check methods to check for data equality of a region.
|
||||
class FuseWriteDataTest: public FuseWriteTest, public WithParamInterface<tuple<size_t, off_t, size_t>> {
|
||||
public:
|
||||
unique_ptr<VirtualTestFileWriteable> testFile;
|
||||
TestData testData;
|
||||
|
||||
FuseWriteDataTest() {
|
||||
testData = GetParam();
|
||||
testFile = make_unique<VirtualTestFileWriteable>(testData.fileSize(), 1);
|
||||
|
||||
ReturnIsFileOnLstatWithSize(FILENAME, testData.fileSize());
|
||||
OnOpenReturnFileDescriptor(FILENAME, 0);
|
||||
EXPECT_CALL(fsimpl, write(0, _, _, _))
|
||||
.WillRepeatedly(WriteToFile);
|
||||
}
|
||||
|
||||
// This write() mock implementation writes to the stored virtual file.
|
||||
Action<void(int, const void*, size_t, off_t)> WriteToFile = Invoke([this](int, const void *buf, size_t count, off_t offset) {
|
||||
testFile->write(buf, count, offset);
|
||||
});
|
||||
};
|
||||
INSTANTIATE_TEST_CASE_P(FuseWriteDataTest, FuseWriteDataTest, 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(FuseWriteDataTest, DataWasCorrectlyWritten) {
|
||||
VirtualTestFile randomWriteData(testData.count, 2);
|
||||
WriteFile(FILENAME, randomWriteData.data(), testData.count, testData.offset);
|
||||
|
||||
EXPECT_TRUE(testFile->fileContentEqual(randomWriteData.data(), testData.count, testData.offset));
|
||||
}
|
||||
|
||||
TEST_P(FuseWriteDataTest, RestOfFileIsUnchanged) {
|
||||
VirtualTestFile randomWriteData(testData.count, 2);
|
||||
WriteFile(FILENAME, randomWriteData.data(), testData.count, testData.offset);
|
||||
|
||||
EXPECT_TRUE(testFile->sizeUnchanged());
|
||||
EXPECT_TRUE(testFile->regionUnchanged(0, testData.offset));
|
||||
EXPECT_TRUE(testFile->regionUnchanged(testData.offset + testData.count, testData.additional_bytes_at_end_of_file));
|
||||
}
|
64
src/test/fspp/fuse/write/FuseWriteErrorTest.cpp
Normal file
64
src/test/fspp/fuse/write/FuseWriteErrorTest.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "testutils/FuseWriteTest.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 FuseWriteErrorTest: public FuseWriteTest, public WithParamInterface<int> {
|
||||
public:
|
||||
size_t FILESIZE = 1024*1024*1024;
|
||||
size_t WRITECOUNT = 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(FuseWriteErrorTest, FuseWriteErrorTest, Values(EAGAIN, EBADF, EDESTADDRREQ, EDQUOT, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENOSPC, EPIPE, EOVERFLOW, ESPIPE, ENXIO));
|
||||
|
||||
|
||||
TEST_P(FuseWriteErrorTest, ReturnErrorOnFirstWriteCall) {
|
||||
EXPECT_CALL(fsimpl, write(0, _, _, _))
|
||||
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
|
||||
|
||||
char *buf = new char[WRITECOUNT];
|
||||
errno = 0;
|
||||
int retval = WriteFileAllowError(FILENAME, buf, WRITECOUNT, 0);
|
||||
EXPECT_EQ(GetParam(), errno);
|
||||
EXPECT_EQ(-1, retval);
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
TEST_P(FuseWriteErrorTest, ReturnErrorOnSecondWriteCall) {
|
||||
// The first write 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 write and check later that our
|
||||
// write syscall returns exactly this number of bytes
|
||||
size_t successfullyWrittenBytes = -1;
|
||||
EXPECT_CALL(fsimpl, write(0, _, _, Eq(0)))
|
||||
.Times(1)
|
||||
.WillOnce(Invoke([&successfullyWrittenBytes](int, const void*, size_t count, off_t) {
|
||||
// Store the number of successfully written bytes
|
||||
successfullyWrittenBytes = count;
|
||||
}));
|
||||
EXPECT_CALL(fsimpl, write(0, _, _, Ne(0)))
|
||||
.WillRepeatedly(Throw(FuseErrnoException(GetParam())));
|
||||
|
||||
char *buf = new char[WRITECOUNT];
|
||||
errno = 0;
|
||||
size_t retval = WriteFileAllowError(FILENAME, buf, WRITECOUNT, 0);
|
||||
EXPECT_EQ(0, errno);
|
||||
EXPECT_EQ(successfullyWrittenBytes, retval); // Check that we're getting the number of successfully written bytes (the first write call) returned
|
||||
delete[] buf;
|
||||
}
|
||||
|
29
src/test/fspp/fuse/write/FuseWriteFileDescriptorTest.cpp
Normal file
29
src/test/fspp/fuse/write/FuseWriteFileDescriptorTest.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "testutils/FuseWriteTest.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 FuseWriteFileDescriptorTest: public FuseWriteTest, public WithParamInterface<int> {
|
||||
};
|
||||
INSTANTIATE_TEST_CASE_P(FuseWriteFileDescriptorTest, FuseWriteFileDescriptorTest, Values(0,1,10,1000,1024*1024*1024));
|
||||
|
||||
|
||||
TEST_P(FuseWriteFileDescriptorTest, FileDescriptorIsCorrect) {
|
||||
ReturnIsFileOnLstat(FILENAME);
|
||||
OnOpenReturnFileDescriptor(FILENAME, GetParam());
|
||||
EXPECT_CALL(fsimpl, write(Eq(GetParam()), _, _, _))
|
||||
.Times(1).WillOnce(Return());
|
||||
|
||||
char buf[1];
|
||||
WriteFile(FILENAME, buf, 1, 0);
|
||||
}
|
86
src/test/fspp/fuse/write/FuseWriteOverflowTest.cpp
Normal file
86
src/test/fspp/fuse/write/FuseWriteOverflowTest.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "testutils/FuseWriteTest.h"
|
||||
|
||||
#include "test/testutils/VirtualTestFile.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 FuseWriteOverflowTest: public FuseWriteTest {
|
||||
public:
|
||||
size_t FILESIZE;
|
||||
size_t WRITESIZE;
|
||||
size_t OFFSET;
|
||||
|
||||
VirtualTestFileWriteable testFile;
|
||||
VirtualTestFile writeData;
|
||||
|
||||
FuseWriteOverflowTest(size_t filesize, size_t writesize, size_t offset)
|
||||
: FILESIZE(filesize), WRITESIZE(writesize), OFFSET(offset), testFile(FILESIZE), writeData(WRITESIZE) {
|
||||
ReturnIsFileOnLstatWithSize(FILENAME, FILESIZE);
|
||||
OnOpenReturnFileDescriptor(FILENAME, 0);
|
||||
EXPECT_CALL(fsimpl, write(0, _, _, _)).WillRepeatedly(WriteToFile);
|
||||
}
|
||||
|
||||
// This write() mock implementation writes to the stored virtual file.
|
||||
Action<void(int, const void*, size_t, off_t)> WriteToFile =
|
||||
Invoke([this](int, const void *buf, size_t count, off_t offset) {
|
||||
testFile.write(buf, count, offset);
|
||||
});
|
||||
};
|
||||
|
||||
class FuseWriteOverflowTestWithNonemptyFile: public FuseWriteOverflowTest {
|
||||
public:
|
||||
FuseWriteOverflowTestWithNonemptyFile(): FuseWriteOverflowTest(1000, 2000, 500) {}
|
||||
};
|
||||
|
||||
TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteMoreThanFileSizeFromBeginning) {
|
||||
WriteFile(FILENAME, writeData.data(), WRITESIZE, 0);
|
||||
|
||||
EXPECT_EQ(WRITESIZE, testFile.size());
|
||||
EXPECT_TRUE(testFile.fileContentEqual(writeData.data(), WRITESIZE, 0));
|
||||
}
|
||||
|
||||
TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteMoreThanFileSizeFromMiddle) {
|
||||
WriteFile(FILENAME, writeData.data(), WRITESIZE, OFFSET);
|
||||
|
||||
EXPECT_EQ(OFFSET + WRITESIZE, testFile.size());
|
||||
EXPECT_TRUE(testFile.regionUnchanged(0, OFFSET));
|
||||
EXPECT_TRUE(testFile.fileContentEqual(writeData.data(), WRITESIZE, OFFSET));
|
||||
}
|
||||
|
||||
TEST_F(FuseWriteOverflowTestWithNonemptyFile, WriteAfterFileEnd) {
|
||||
WriteFile(FILENAME, writeData.data(), WRITESIZE, FILESIZE + OFFSET);
|
||||
|
||||
EXPECT_EQ(FILESIZE + OFFSET + WRITESIZE, testFile.size());
|
||||
EXPECT_TRUE(testFile.regionUnchanged(0, FILESIZE));
|
||||
EXPECT_TRUE(testFile.fileContentEqual(writeData.data(), WRITESIZE, FILESIZE + OFFSET));
|
||||
}
|
||||
|
||||
class FuseWriteOverflowTestWithEmptyFile: public FuseWriteOverflowTest {
|
||||
public:
|
||||
FuseWriteOverflowTestWithEmptyFile(): FuseWriteOverflowTest(0, 2000, 500) {}
|
||||
};
|
||||
|
||||
TEST_F(FuseWriteOverflowTestWithEmptyFile, WriteToBeginOfEmptyFile) {
|
||||
WriteFile(FILENAME, writeData.data(), WRITESIZE, 0);
|
||||
|
||||
EXPECT_EQ(WRITESIZE, testFile.size());
|
||||
EXPECT_TRUE(testFile.fileContentEqual(writeData.data(), WRITESIZE, 0));
|
||||
}
|
||||
|
||||
TEST_F(FuseWriteOverflowTestWithEmptyFile, WriteAfterFileEnd) {
|
||||
WriteFile(FILENAME, writeData.data(), WRITESIZE, OFFSET);
|
||||
|
||||
EXPECT_EQ(OFFSET + WRITESIZE, testFile.size());
|
||||
EXPECT_TRUE(testFile.fileContentEqual(writeData.data(), WRITESIZE, OFFSET));
|
||||
}
|
20
src/test/fspp/fuse/write/testutils/FuseWriteTest.cpp
Normal file
20
src/test/fspp/fuse/write/testutils/FuseWriteTest.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "FuseWriteTest.h"
|
||||
|
||||
void FuseWriteTest::WriteFile(const char *filename, const void *buf, size_t count, off_t offset) {
|
||||
size_t retval = WriteFileAllowError(filename, buf, count, offset);
|
||||
EXPECT_EQ(count, retval);
|
||||
}
|
||||
|
||||
size_t FuseWriteTest::WriteFileAllowError(const char *filename, const void *buf, size_t count, off_t offset) {
|
||||
auto fs = TestFS();
|
||||
|
||||
int fd = OpenFile(fs.get(), filename);
|
||||
return ::pwrite(fd, buf, count, offset);
|
||||
}
|
||||
|
||||
int FuseWriteTest::OpenFile(const TempTestFS *fs, const char *filename) {
|
||||
auto realpath = fs->mountDir() / filename;
|
||||
int fd = ::open(realpath.c_str(), O_WRONLY);
|
||||
EXPECT_GE(fd, 0) << "Error opening file";
|
||||
return fd;
|
||||
}
|
18
src/test/fspp/fuse/write/testutils/FuseWriteTest.h
Normal file
18
src/test/fspp/fuse/write/testutils/FuseWriteTest.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#ifndef TEST_FSPP_FUSE_WRITE_TESTUTILS_FUSEWRITETEST_H_
|
||||
#define TEST_FSPP_FUSE_WRITE_TESTUTILS_FUSEWRITETEST_H_
|
||||
|
||||
#include "test/testutils/FuseTest.h"
|
||||
|
||||
class FuseWriteTest: public FuseTest {
|
||||
public:
|
||||
const char *FILENAME = "/myfile";
|
||||
|
||||
void WriteFile(const char *filename, const void *buf, size_t count, off_t offset);
|
||||
size_t WriteFileAllowError(const char *filename, const void *buf, size_t count, off_t offset);
|
||||
|
||||
private:
|
||||
int OpenFile(const TempTestFS *fs, const char *filename);
|
||||
};
|
||||
|
||||
#endif
|
@ -5,16 +5,16 @@
|
||||
|
||||
using std::min;
|
||||
|
||||
VirtualTestFile::VirtualTestFile(size_t size): _fileData(new char[size]), _size(size) {
|
||||
fillFileWithRandomData();
|
||||
VirtualTestFile::VirtualTestFile(size_t size, long long int IV): _fileData(new char[size]), _size(size) {
|
||||
fillFileWithRandomData(IV);
|
||||
}
|
||||
|
||||
VirtualTestFile::~VirtualTestFile() {
|
||||
delete[] _fileData;
|
||||
}
|
||||
|
||||
void VirtualTestFile::fillFileWithRandomData() {
|
||||
long long int val = 1;
|
||||
void VirtualTestFile::fillFileWithRandomData(long long int IV) {
|
||||
long long int val = IV;
|
||||
for(size_t i=0; i<_size/sizeof(long long int); ++i) {
|
||||
//MMIX linear congruential generator
|
||||
val *= 6364136223846793005L;
|
||||
@ -23,22 +23,58 @@ void VirtualTestFile::fillFileWithRandomData() {
|
||||
}
|
||||
}
|
||||
|
||||
const char *VirtualTestFile::data() {
|
||||
return _fileData;
|
||||
}
|
||||
|
||||
int VirtualTestFile::read(void *buf, size_t count, off_t offset) {
|
||||
size_t realCount = min(count, _size - offset);
|
||||
memcpy(buf, _fileData+offset, realCount);
|
||||
return realCount;
|
||||
}
|
||||
|
||||
bool VirtualTestFile::fileContentEqual(char *content, size_t count, off_t offset) {
|
||||
size_t VirtualTestFile::size() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
bool VirtualTestFile::fileContentEqual(const char *content, size_t count, off_t offset) {
|
||||
return 0 == memcmp(content, _fileData + offset, count);
|
||||
}
|
||||
|
||||
VirtualTestFileWriteable::VirtualTestFileWriteable(size_t size)
|
||||
:VirtualTestFile(size) {
|
||||
originalFileData = new char[size];
|
||||
memcpy(originalFileData, _fileData, size);
|
||||
VirtualTestFileWriteable::VirtualTestFileWriteable(size_t size, long long int IV)
|
||||
:VirtualTestFile(size, IV), _originalSize(size) {
|
||||
_originalFileData = new char[size];
|
||||
memcpy(_originalFileData, _fileData, size);
|
||||
}
|
||||
|
||||
VirtualTestFileWriteable::~VirtualTestFileWriteable() {
|
||||
delete[] originalFileData;
|
||||
delete[] _originalFileData;
|
||||
}
|
||||
|
||||
void VirtualTestFileWriteable::write(const void *buf, size_t count, off_t offset) {
|
||||
extendFileSizeIfNecessary(count + offset);
|
||||
|
||||
memcpy(_fileData+offset, buf, count);
|
||||
}
|
||||
|
||||
void VirtualTestFileWriteable::extendFileSizeIfNecessary(size_t size) {
|
||||
if (size > _size) {
|
||||
extendFileSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualTestFileWriteable::extendFileSize(size_t size) {
|
||||
char *newfile = new char[size];
|
||||
memcpy(newfile, _fileData, _size);
|
||||
delete[] _fileData;
|
||||
_fileData = newfile;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
bool VirtualTestFileWriteable::sizeUnchanged() {
|
||||
return _size == _originalSize;
|
||||
}
|
||||
|
||||
bool VirtualTestFileWriteable::regionUnchanged(off_t offset, size_t count) {
|
||||
return 0 == memcmp(_fileData+offset, _originalFileData+offset, count);
|
||||
}
|
||||
|
@ -6,28 +6,42 @@
|
||||
|
||||
class VirtualTestFile {
|
||||
public:
|
||||
VirtualTestFile(size_t size);
|
||||
VirtualTestFile(size_t size, long long int IV = 1);
|
||||
virtual ~VirtualTestFile();
|
||||
|
||||
int read(void *buf, size_t count, off_t offset);
|
||||
|
||||
// Return true, iff the given data is equal to the data of the file at the given offset.
|
||||
bool fileContentEqual(char *content, size_t count, off_t offset);
|
||||
bool fileContentEqual(const char *content, size_t count, off_t offset);
|
||||
|
||||
const char *data();
|
||||
|
||||
size_t size();
|
||||
|
||||
protected:
|
||||
char *_fileData;
|
||||
size_t _size;
|
||||
|
||||
private:
|
||||
void fillFileWithRandomData();
|
||||
void fillFileWithRandomData(long long int IV);
|
||||
};
|
||||
|
||||
class VirtualTestFileWriteable: public VirtualTestFile {
|
||||
public:
|
||||
VirtualTestFileWriteable(size_t size);
|
||||
VirtualTestFileWriteable(size_t size, long long int IV = 1);
|
||||
virtual ~VirtualTestFileWriteable();
|
||||
|
||||
void write(const void *buf, size_t count, off_t offset);
|
||||
|
||||
bool sizeUnchanged();
|
||||
bool regionUnchanged(off_t offset, size_t count);
|
||||
|
||||
private:
|
||||
char *originalFileData;
|
||||
void extendFileSizeIfNecessary(size_t size);
|
||||
void extendFileSize(size_t size);
|
||||
|
||||
char *_originalFileData;
|
||||
size_t _originalSize;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user