diff --git a/src/fspp/fuse/Filesystem.h b/src/fspp/fuse/Filesystem.h index 9849ead3..b7cb9143 100644 --- a/src/fspp/fuse/Filesystem.h +++ b/src/fspp/fuse/Filesystem.h @@ -31,9 +31,9 @@ public: virtual void unlink(const boost::filesystem::path &path) = 0; virtual void rename(const boost::filesystem::path &from, const boost::filesystem::path &to) = 0; virtual void utimens(const boost::filesystem::path &path, const timespec times[2]) = 0; + virtual void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) = 0; //TODO Unit-Tests for all functions below virtual std::unique_ptr> readDir(const boost::filesystem::path &path) = 0; - virtual void statfs(const boost::filesystem::path &path, struct statvfs *fsstat) = 0; }; //TODO Test error cases handled by libfuse (e.g. mkdir: Already exists, rmdir: Doesn't exist, ...) diff --git a/src/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp new file mode 100644 index 00000000..9bae9797 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsErrorTest.cpp @@ -0,0 +1,34 @@ +#include "testutils/FuseStatfsTest.h" + +#include "fspp/fuse/FuseErrnoException.h" + +using ::testing::StrEq; +using ::testing::_; +using ::testing::Throw; +using ::testing::Return; +using ::testing::WithParamInterface; +using ::testing::Values; + +using fspp::fuse::FuseErrnoException; + +class FuseStatfsErrorTest: public FuseStatfsTest, public WithParamInterface { +public: +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsErrorTest, FuseStatfsErrorTest, Values(EACCES, EBADF, EFAULT, EINTR, EIO, ELOOP, ENAMETOOLONG, ENOENT, ENOMEM, ENOSYS, ENOTDIR, EOVERFLOW)); + +TEST_F(FuseStatfsErrorTest, ReturnNoError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).Times(1).WillOnce(Return()); + errno = 0; + int retval = StatfsAllowErrors(FILENAME); + EXPECT_EQ(errno, 0); + EXPECT_EQ(retval, 0); +} + +TEST_P(FuseStatfsErrorTest, ReturnError) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).Times(1).WillOnce(Throw(FuseErrnoException(GetParam()))); + int retval = StatfsAllowErrors(FILENAME); + EXPECT_EQ(retval, -1); + EXPECT_EQ(GetParam(), errno); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp new file mode 100644 index 00000000..ecc42704 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsPathParameterTest.cpp @@ -0,0 +1,41 @@ +#include "testutils/FuseStatfsTest.h" + +using ::testing::_; +using ::testing::StrEq; +using ::testing::Return; + +class FuseStatfsPathParameterTest: public FuseStatfsTest { +}; + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectRoot) { + EXPECT_CALL(fsimpl, statfs(StrEq("/"), _)).Times(1).WillOnce(Return()); + Statfs("/"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectSimpleFile) { + ReturnIsFileOnLstat("/myfile"); + EXPECT_CALL(fsimpl, statfs(StrEq("/myfile"), _)).Times(1).WillOnce(Return()); + Statfs("/myfile"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectSimpleDir) { + ReturnIsDirOnLstat("/mydir"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectNestedFile) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsFileOnLstat("/mydir/mydir2/myfile"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir/mydir2/myfile"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir/mydir2/myfile"); +} + +TEST_F(FuseStatfsPathParameterTest, PathParameterIsCorrectNestedDir) { + ReturnIsDirOnLstat("/mydir"); + ReturnIsDirOnLstat("/mydir/mydir2"); + ReturnIsDirOnLstat("/mydir/mydir2/mydir3"); + EXPECT_CALL(fsimpl, statfs(StrEq("/mydir/mydir2/mydir3"), _)).Times(1).WillOnce(Return()); + Statfs("/mydir/mydir2/mydir3"); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp new file mode 100644 index 00000000..2c2f1f8e --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnBavailTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBavailTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_bavail = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBavailTest, FuseStatfsReturnBavailTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBavailTest, ReturnedBavailIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bavail); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp new file mode 100644 index 00000000..8529a27c --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnBfreeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBfreeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_bfree = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBfreeTest, FuseStatfsReturnBfreeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBfreeTest, ReturnedBfreeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bfree); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp new file mode 100644 index 00000000..b9f59912 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnBlocksTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBlocksTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsblkcnt_t value) override { + stat->f_blocks = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBlocksTest, FuseStatfsReturnBlocksTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBlocksTest, ReturnedBlocksIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_blocks); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp new file mode 100644 index 00000000..ccdb1278 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnBsizeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnBsizeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, unsigned long value) override { + stat->f_bsize = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnBsizeTest, FuseStatfsReturnBsizeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnBsizeTest, ReturnedBsizeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_bsize); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp new file mode 100644 index 00000000..e490810f --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnFfreeTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnFfreeTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsfilcnt_t value) override { + stat->f_ffree = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnFfreeTest, FuseStatfsReturnFfreeTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnFfreeTest, ReturnedFfreeIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_ffree); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp new file mode 100644 index 00000000..1a11ab54 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnFilesTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnFilesTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, fsfilcnt_t value) override { + stat->f_files = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnFilesTest, FuseStatfsReturnFilesTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnFilesTest, ReturnedFilesIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_files); +} diff --git a/src/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp b/src/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp new file mode 100644 index 00000000..c663e586 --- /dev/null +++ b/src/test/fspp/fuse/statfs/FuseStatfsReturnNamemaxTest.cpp @@ -0,0 +1,23 @@ +#include "testutils/FuseStatfsReturnTest.h" + +using ::testing::WithParamInterface; +using ::testing::Values; + +class FuseStatfsReturnNamemaxTest: public FuseStatfsReturnTest, public WithParamInterface { +private: + void set(struct ::statvfs *stat, unsigned long value) override { + stat->f_namemax = value; + } +}; +INSTANTIATE_TEST_CASE_P(FuseStatfsReturnNamemaxTest, FuseStatfsReturnNamemaxTest, Values( + 0, + 10, + 256, + 1024, + 4096 +)); + +TEST_P(FuseStatfsReturnNamemaxTest, ReturnedNamemaxIsCorrect) { + struct ::statvfs result = CallStatfsWithValue(GetParam()); + EXPECT_EQ(GetParam(), result.f_namemax); +} diff --git a/src/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h b/src/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h new file mode 100644 index 00000000..99e46707 --- /dev/null +++ b/src/test/fspp/fuse/statfs/testutils/FuseStatfsReturnTest.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef TEST_FSPP_FUSE_STATFS_FUSESTATFSRETURNTEST_H_ +#define TEST_FSPP_FUSE_STATFS_FUSESTATFSRETURNTEST_H_ + +#include "FuseStatfsTest.h" + +// This class offers test helpers for testing (struct statfs) entries. We return them from +// our mock filesystem, set up a temporary filesystem, call statfs syscall on it, and +// then check the return value. +template +class FuseStatfsReturnTest: public FuseStatfsTest { +public: + // Set the specified (struct statfs) entry to the given value, and test whether it is correctly returned from the syscall. + struct ::statvfs CallStatfsWithValue(Property value); + +private: + std::function SetPropertyImpl(Property value); + + // Override this function to specify, how to set the specified (struct statfs) entry on the passed (struct statfs *) object. + virtual void set(struct ::statvfs *statfs, Property value) = 0; +}; + +template +struct ::statvfs FuseStatfsReturnTest::CallStatfsWithValue(Property value) { + return CallStatfsWithImpl(SetPropertyImpl(value)); +} + +template +std::function FuseStatfsReturnTest::SetPropertyImpl(Property value) { + return [this, value] (struct ::statvfs *stat) { + set(stat, value); + }; +} + + +#endif diff --git a/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp b/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp new file mode 100644 index 00000000..13e6c599 --- /dev/null +++ b/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.cpp @@ -0,0 +1,41 @@ +#include "FuseStatfsTest.h" + +using std::function; +using ::testing::StrEq; +using ::testing::_; +using ::testing::Invoke; + +void FuseStatfsTest::Statfs(const std::string &path) { + struct ::statvfs dummy; + Statfs(path, &dummy); +} + +int FuseStatfsTest::StatfsAllowErrors(const std::string &path) { + struct ::statvfs dummy; + return StatfsAllowErrors(path, &dummy); +} + +void FuseStatfsTest::Statfs(const std::string &path, struct ::statvfs *result) { + int retval = StatfsAllowErrors(path, result); + EXPECT_EQ(0, retval) << "lstat syscall failed. errno: " << errno; +} + +int FuseStatfsTest::StatfsAllowErrors(const std::string &path, struct ::statvfs *result) { + auto fs = TestFS(); + + auto realpath = fs->mountDir() / path; + return ::statvfs(realpath.c_str(), result); +} + +struct ::statvfs FuseStatfsTest::CallStatfsWithImpl(function implementation) { + ReturnIsFileOnLstat(FILENAME); + EXPECT_CALL(fsimpl, statfs(StrEq(FILENAME), _)).WillRepeatedly(Invoke([implementation](const char*, struct ::statvfs *stat) { + implementation(stat); + })); + + struct ::statvfs result; + Statfs(FILENAME, &result); + + return result; +} + diff --git a/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h b/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h new file mode 100644 index 00000000..4f1a18a5 --- /dev/null +++ b/src/test/fspp/fuse/statfs/testutils/FuseStatfsTest.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef TEST_FSPP_FUSE_STATFS_FUSESTATFSTEST_H_ +#define TEST_FSPP_FUSE_STATFS_FUSESTATFSTEST_H_ + +#include "gmock/gmock.h" + +#include +#include +#include + +#include "test/testutils/FuseTest.h" + +// This class offers some utility functions for testing statfs(). +class FuseStatfsTest: public FuseTest { +protected: + const char *FILENAME = "/myfile"; + + // Set up a temporary filesystem (using the fsimpl mock in FuseTest as filesystem implementation) + // and call the statfs syscall on the given (filesystem-relative) path. + void Statfs(const std::string &path); + // Same as Statfs above, but also return the result of the statfs syscall. + void Statfs(const std::string &path, struct ::statvfs *result); + + // These two functions are the same as Statfs above, but they don't fail the test when the statfs syscall + // crashes. Instead, they return the result value of the statfs syscall. + int StatfsAllowErrors(const std::string &path); + int StatfsAllowErrors(const std::string &path, struct ::statvfs *result); + + // You can specify an implementation, which can modify the (struct statfs *) result, + // our fuse mock filesystem implementation will then return this to fuse on an statfs call. + // This functions then set up a temporary filesystem with this mock, calls statfs on a filesystem node + // and returns the (struct statfs) returned from an statfs syscall to this filesystem. + struct ::statvfs CallStatfsWithImpl(std::function implementation); +}; + +#endif