From 4627666788e9c99eed0499a229a5ac643f1a972f Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Thu, 31 Mar 2016 23:48:43 +0800 Subject: [PATCH] Generalize stat() tests in FsppFileTest for all node types --- src/fspp/fstest/FsTest.h | 17 ++- src/fspp/fstest/FsppFileTest.h | 52 +------- src/fspp/fstest/FsppNodeTest_Rename.h | 10 -- src/fspp/fstest/FsppNodeTest_Stat.h | 84 +++++++++++++ src/fspp/fstest/testutils/FileSystemTest.h | 15 +++ src/fspp/fstest/testutils/FileTest.h | 1 + src/fspp/fstest/testutils/FsppNodeTest.h | 136 +++++++++++++++------ 7 files changed, 217 insertions(+), 98 deletions(-) create mode 100644 src/fspp/fstest/FsppNodeTest_Stat.h diff --git a/src/fspp/fstest/FsTest.h b/src/fspp/fstest/FsTest.h index 815bfc87..7ea963e3 100644 --- a/src/fspp/fstest/FsTest.h +++ b/src/fspp/fstest/FsTest.h @@ -8,14 +8,19 @@ #include "FsppFileTest.h" #include "FsppSymlinkTest.h" #include "FsppNodeTest_Rename.h" +#include "FsppNodeTest_Stat.h" #include "FsppOpenFileTest.h" #define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ - INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDeviceTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppDirTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppFileTest, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppSymlinkTest, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Rename, FIXTURE); \ + INSTANTIATE_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Stat, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_FileOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_DirOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppNodeTest_Stat_SymlinkOnly, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, FsppOpenFileTest, FIXTURE); \ #endif diff --git a/src/fspp/fstest/FsppFileTest.h b/src/fspp/fstest/FsppFileTest.h index 7417cd49..18b1f9b0 100644 --- a/src/fspp/fstest/FsppFileTest.h +++ b/src/fspp/fstest/FsppFileTest.h @@ -55,22 +55,6 @@ public: this->EXPECT_SIZE(0, *file); } - void Test_Stat_CreatedFileIsEmpty(fspp::File *file) { - this->EXPECT_SIZE(0, *file); - } - - void Test_Stat_Nlink(fspp::File *file) { - this->IN_STAT(*file, [] (struct stat st) { - EXPECT_EQ(1u, st.st_nlink); - }); - } - - void Test_Stat_IsFile(fspp::File *file) { - this->IN_STAT(*file, [] (struct stat st) { - EXPECT_TRUE(S_ISREG(st.st_mode)); - }); - } - void Test_Chown_Uid(fspp::File *file) { file->chown(100, 200); this->IN_STAT(*file, [] (struct stat st){ @@ -177,30 +161,6 @@ TYPED_TEST_P(FsppFileTest, Truncate_ShrinkTo0_Nested) { this->Test_Truncate_ShrinkTo0(this->file_nested.get()); } -TYPED_TEST_P(FsppFileTest, Stat_IsFile) { - this->Test_Stat_IsFile(this->file_root.get()); -} - -TYPED_TEST_P(FsppFileTest, Stat_IsFile_Nested) { - this->Test_Stat_IsFile(this->file_nested.get()); -} - -TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty) { - this->Test_Stat_CreatedFileIsEmpty(this->file_root.get()); -} - -TYPED_TEST_P(FsppFileTest, Stat_CreatedFileIsEmpty_Nested) { - this->Test_Stat_CreatedFileIsEmpty(this->file_nested.get()); -} - -TYPED_TEST_P(FsppFileTest, Stat_Nlink) { - this->Test_Stat_Nlink(this->file_root.get()); -} - -TYPED_TEST_P(FsppFileTest, Stat_Nlink_Nested) { - this->Test_Stat_Nlink(this->file_nested.get()); -} - TYPED_TEST_P(FsppFileTest, Chown_Uid) { this->Test_Chown_Uid(this->file_root.get()); } @@ -252,12 +212,6 @@ REGISTER_TYPED_TEST_CASE_P(FsppFileTest, Truncate_Shrink_Nested, Truncate_ShrinkTo0, Truncate_ShrinkTo0_Nested, - Stat_CreatedFileIsEmpty, - Stat_CreatedFileIsEmpty_Nested, - Stat_Nlink, - Stat_Nlink_Nested, - Stat_IsFile, - Stat_IsFile_Nested, Chown_Uid, Chown_Uid_Nested, Chown_Gid, @@ -272,4 +226,10 @@ REGISTER_TYPED_TEST_CASE_P(FsppFileTest, //TODO unlink //TODO Test all operations do (or don't) affect file timestamps correctly (including rename, which shouldn't modify access/modify time, but inode change time) +//TODO Move applicable test cases to new instances of FsppNodeTest (like FsppNodeTest_Rename) (e.g. utimens, chmod, ...) +//TODO access +//TODO utimens +//TODO chmod +//TODO chown + #endif diff --git a/src/fspp/fstest/FsppNodeTest_Rename.h b/src/fspp/fstest/FsppNodeTest_Rename.h index d46472ee..ab5bb33e 100644 --- a/src/fspp/fstest/FsppNodeTest_Rename.h +++ b/src/fspp/fstest/FsppNodeTest_Rename.h @@ -158,9 +158,6 @@ public: } }; -/* - * Register each of the given test cases for all three classes: FsppNodeTest_File, FsppNodeTest_Dir and FsppNodeTest_Symlink - */ REGISTER_NODE_TEST_CASE(FsppNodeTest_Rename, TargetParentDirDoesntExist, TargetParentDirIsFile, @@ -186,11 +183,4 @@ REGISTER_NODE_TEST_CASE(FsppNodeTest_Rename, //TODO Test for rename (success AND error cases) that contents stay unchanged (i.e. file contents, directory children, symlink target) //TODO (here and in other fstest operations): Test error paths -//TODO Move other applicable test cases from FsppFileTest to here (e.g. utimens, chmod, ...) -//TODO stat -//TODO access -//TODO utimens -//TODO chmod -//TODO chown - //TODO Test all operations do (or don't) affect timestamps correctly \ No newline at end of file diff --git a/src/fspp/fstest/FsppNodeTest_Stat.h b/src/fspp/fstest/FsppNodeTest_Stat.h new file mode 100644 index 00000000..0412bf0a --- /dev/null +++ b/src/fspp/fstest/FsppNodeTest_Stat.h @@ -0,0 +1,84 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPNODETEST_STAT_H_ +#define MESSMER_FSPP_FSTEST_FSPPNODETEST_STAT_H_ + +#include "testutils/FsppNodeTest.h" +#include "../fuse/FuseErrnoException.h" + +template +class FsppNodeTest_Stat: public FsppNodeTest { +public: + void Test_Nlink() { + auto node = this->CreateNode("/mynode"); + this->IN_STAT(*node, [] (struct stat st) { + EXPECT_EQ(1u, st.st_nlink); + }); + } +}; + +// Test cases only run for file nodes +template +class FsppNodeTest_Stat_FileOnly: public FileSystemTest, public FsppNodeTest_File_Helper {}; + +TYPED_TEST_CASE_P(FsppNodeTest_Stat_FileOnly); + +TYPED_TEST_P(FsppNodeTest_Stat_FileOnly, CreatedFileIsEmpty) { + auto file = this->CreateFile("/myfile"); + this->EXPECT_SIZE(0, *file); +} + +TYPED_TEST_P(FsppNodeTest_Stat_FileOnly, FileIsFile) { + auto file = this->CreateFile("/myfile"); + this->IN_STAT(*file, [] (struct stat st) { + EXPECT_TRUE(S_ISREG(st.st_mode)); + }); +} + +// Test cases only run for dir nodes +template +class FsppNodeTest_Stat_DirOnly: public FileSystemTest, public FsppNodeTest_Dir_Helper {}; + +TYPED_TEST_CASE_P(FsppNodeTest_Stat_DirOnly); + +TYPED_TEST_P(FsppNodeTest_Stat_DirOnly, DirIsDir) { + auto file = this->CreateDir("/mydir"); + this->IN_STAT(*file, [] (struct stat st) { + EXPECT_TRUE(S_ISDIR(st.st_mode)); + }); +} + +// Test cases only run for symlink nodes +template +class FsppNodeTest_Stat_SymlinkOnly: public FileSystemTest, public FsppNodeTest_Symlink_Helper {}; + +TYPED_TEST_CASE_P(FsppNodeTest_Stat_SymlinkOnly); + +TYPED_TEST_P(FsppNodeTest_Stat_SymlinkOnly, SymlinkIsSymlink) { + auto file = this->CreateSymlink("/mysymlink"); + this->IN_STAT(*file, [] (struct stat st) { + EXPECT_TRUE(S_ISLNK(st.st_mode)); + }); +} + +REGISTER_NODE_TEST_CASE(FsppNodeTest_Stat, + Nlink +); + +REGISTER_TYPED_TEST_CASE_P(FsppNodeTest_Stat_FileOnly, + CreatedFileIsEmpty, + FileIsFile +); + +REGISTER_TYPED_TEST_CASE_P(FsppNodeTest_Stat_DirOnly, + DirIsDir +); + +REGISTER_TYPED_TEST_CASE_P(FsppNodeTest_Stat_SymlinkOnly, + SymlinkIsSymlink +); + +#endif + +//TODO More test cases + +//TODO Test all operations do (or don't) affect timestamps correctly diff --git a/src/fspp/fstest/testutils/FileSystemTest.h b/src/fspp/fstest/testutils/FileSystemTest.h index f8b03a98..78473ef2 100644 --- a/src/fspp/fstest/testutils/FileSystemTest.h +++ b/src/fspp/fstest/testutils/FileSystemTest.h @@ -58,6 +58,21 @@ public: EXPECT_NE(boost::none, symlink); return std::move(*symlink); } + + cpputils::unique_ref CreateDir(const boost::filesystem::path &path) { + this->LoadDir(path.parent_path())->createDir(path.filename().native(), this->MODE_PUBLIC, 0, 0); + return this->LoadDir(path); + } + + cpputils::unique_ref CreateFile(const boost::filesystem::path &path) { + this->LoadDir(path.parent_path())->createAndOpenFile(path.filename().native(), this->MODE_PUBLIC, 0, 0); + return this->LoadFile(path); + } + + cpputils::unique_ref CreateSymlink(const boost::filesystem::path &path) { + this->LoadDir(path.parent_path())->createSymlink(path.filename().native(), "/my/symlink/target", 0, 0); + return this->LoadSymlink(path); + } }; diff --git a/src/fspp/fstest/testutils/FileTest.h b/src/fspp/fstest/testutils/FileTest.h index c15c00e1..46833955 100644 --- a/src/fspp/fstest/testutils/FileTest.h +++ b/src/fspp/fstest/testutils/FileTest.h @@ -22,6 +22,7 @@ public: std::unique_ptr file_root; std::unique_ptr file_nested; + //TODO IN_STAT still needed after moving it to FsppNodeTest? void IN_STAT(const fspp::File &file, std::function callback) { struct stat st1, st2; file.stat(&st1); diff --git a/src/fspp/fstest/testutils/FsppNodeTest.h b/src/fspp/fstest/testutils/FsppNodeTest.h index 7268fdcd..ff731f10 100644 --- a/src/fspp/fstest/testutils/FsppNodeTest.h +++ b/src/fspp/fstest/testutils/FsppNodeTest.h @@ -7,70 +7,134 @@ #include #include -template -class FsppNodeTest: public FileSystemTest { +class FsppNodeTestBase { public: - virtual cpputils::unique_ref CreateNode(const boost::filesystem::path &path) = 0; - -protected: - cpputils::unique_ref CreateDir(const boost::filesystem::path &path) { - this->LoadDir(path.parent_path())->createDir(path.filename().native(), this->MODE_PUBLIC, 0, 0); - return this->LoadDir(path); - } - - cpputils::unique_ref CreateFile(const boost::filesystem::path &path) { - this->LoadDir(path.parent_path())->createAndOpenFile(path.filename().native(), this->MODE_PUBLIC, 0, 0); - return this->LoadFile(path); - } - - cpputils::unique_ref CreateSymlink(const boost::filesystem::path &path) { - this->LoadDir(path.parent_path())->createSymlink(path.filename().native(), "/my/symlink/target", 0, 0); - return this->LoadSymlink(path); - }; + virtual void IN_STAT(const fspp::Node &node, std::function callback) = 0; + virtual void EXPECT_SIZE(uint64_t expectedSize, const fspp::Node &node) = 0; }; -#define REGISTER_SINGLE_NODE_TEST_CASE(r, Class, Name) \ +/** + * Inherit your fixture from this class to write a test case that is run on nodes, i.e. files, directories and symlinks. + * You can use this->CreateNode() to create a node and then call fspp::Node functions on it. + * Add your test cases as void Test_xxx() functions to your fixture and register/instantiate them using + * REGISTER_NODE_TEST_CASE and INSTANTIATE_NODE_TEST_CASE. + * It will then automatically create a test case for each node type (file, directory, symlink). + * See FsppNodeTest_Rename for an example. + */ +template +class FsppNodeTest: public virtual FsppNodeTestBase, public FileSystemTest { +public: + virtual cpputils::unique_ref CreateNode(const boost::filesystem::path &path) = 0; +}; + +class FsppNodeTest_File_Helper: public virtual FsppNodeTestBase { +public: + void IN_STAT(const fspp::Node &file, std::function callback) override { + struct stat st1, st2; + file.stat(&st1); + callback(st1); + dynamic_cast(file).open(O_RDONLY)->stat(&st2); + callback(st2); + } + + void EXPECT_SIZE(uint64_t expectedSize, const fspp::Node &node) override { + IN_STAT(node, [expectedSize] (struct stat st) { + EXPECT_EQ(expectedSize, (uint64_t)st.st_size); + }); + + EXPECT_NUMBYTES_READABLE(expectedSize, dynamic_cast(node)); + } + + void EXPECT_NUMBYTES_READABLE(uint64_t expectedSize, const fspp::File &file) { + auto openFile = file.open(O_RDONLY); + cpputils::Data data(expectedSize); + //Try to read one byte more than the expected size + ssize_t readBytes = openFile->read(data.data(), expectedSize+1, 0); + //and check that it only read the expected size (but also not less) + EXPECT_EQ(expectedSize, (uint64_t)readBytes); + } +}; + +class FsppNodeTest_Dir_Helper: public virtual FsppNodeTestBase { +public: + void IN_STAT(const fspp::Node &file, std::function callback) override { + struct stat st; + file.stat(&st); + callback(st); + } + + void EXPECT_SIZE(uint64_t expectedSize, const fspp::Node &node) override { + IN_STAT(node, [expectedSize] (struct stat st) { + EXPECT_EQ(expectedSize, (uint64_t)st.st_size); + }); + } +}; + +class FsppNodeTest_Symlink_Helper: public virtual FsppNodeTestBase { +public: + void IN_STAT(const fspp::Node &file, std::function callback) override { + struct stat st; + file.stat(&st); + callback(st); + } + + void EXPECT_SIZE(uint64_t expectedSize, const fspp::Node &node) override { + IN_STAT(node, [expectedSize] (struct stat st) { + EXPECT_EQ(expectedSize, (uint64_t)st.st_size); + }); + } +}; + +#define _REGISTER_SINGLE_NODE_TEST_CASE(r, Class, Name) \ TYPED_TEST_P(Class, Name) { \ this->BOOST_PP_CAT(Test_,Name)(); \ } \ -#define REGISTER_NODE_TEST_CASES_FOR_CLASS(Class, ...) \ - TYPED_TEST_CASE_P(Class); \ - BOOST_PP_SEQ_FOR_EACH(REGISTER_SINGLE_NODE_TEST_CASE, Class, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)); \ +#define _REGISTER_NODE_TEST_CASES_FOR_CLASS(Class, ...) \ + BOOST_PP_SEQ_FOR_EACH(_REGISTER_SINGLE_NODE_TEST_CASE, Class, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)); \ REGISTER_TYPED_TEST_CASE_P(Class, __VA_ARGS__); \ -#define REGISTER_NODE_TEST_CASE(Class, ...) \ +#define _REGISTER_FILE_TEST_CASE(Class, ...) \ template \ - class Class##_File: public Class { \ + class Class##_FileNode: public Class, public FsppNodeTest_File_Helper { \ public: \ cpputils::unique_ref CreateNode(const boost::filesystem::path &path) override { \ return this->CreateFile(path); \ } \ }; \ - \ + TYPED_TEST_CASE_P(Class##_FileNode); \ + _REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_FileNode, __VA_ARGS__); \ + +#define _REGISTER_DIR_TEST_CASE(Class, ...) \ template \ - class Class##_Dir: public Class { \ + class Class##_DirNode: public Class, public FsppNodeTest_Dir_Helper { \ public: \ cpputils::unique_ref CreateNode(const boost::filesystem::path &path) override { \ return this->CreateDir(path); \ } \ }; \ - \ + TYPED_TEST_CASE_P(Class##_DirNode); \ + _REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_DirNode, __VA_ARGS__); \ + +#define _REGISTER_SYMLINK_TEST_CASE(Class, ...) \ template \ - class Class##_Symlink: public Class { \ + class Class##_SymlinkNode: public Class, public FsppNodeTest_Symlink_Helper { \ public: \ cpputils::unique_ref CreateNode(const boost::filesystem::path &path) override { \ return this->CreateSymlink(path); \ } \ }; \ - \ - REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_File, __VA_ARGS__); \ - REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_Dir, __VA_ARGS__); \ - REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_Symlink, __VA_ARGS__); \ + TYPED_TEST_CASE_P(Class##_SymlinkNode); \ + _REGISTER_NODE_TEST_CASES_FOR_CLASS(Class##_SymlinkNode, __VA_ARGS__); \ + +#define REGISTER_NODE_TEST_CASE(Class, ...) \ + _REGISTER_FILE_TEST_CASE(Class, __VA_ARGS__); \ + _REGISTER_DIR_TEST_CASE(Class, __VA_ARGS__); \ + _REGISTER_SYMLINK_TEST_CASE(Class, __VA_ARGS__); \ #define INSTANTIATE_NODE_TEST_CASE(FS_NAME, Class, FIXTURE) \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_File, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_Dir, FIXTURE); \ - INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_Symlink, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_FileNode, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_DirNode, FIXTURE); \ + INSTANTIATE_TYPED_TEST_CASE_P(FS_NAME, Class##_SymlinkNode, FIXTURE); \ #endif