From 514de8794e2a363e023dfa4debde4b4aabb348a2 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Fri, 27 May 2016 17:26:53 -0700 Subject: [PATCH] Add test cases that fspp::Node operations correctly modify the timestamps --- src/fspp/fstest/FsTest.h | 3 + src/fspp/fstest/FsppNodeTest_Stat.h | 2 - src/fspp/fstest/FsppNodeTest_Timestamps.h | 100 ++++++++++++++++ .../fstest/testutils/TimestampTestUtils.h | 107 ++++++++++++++++++ 4 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 src/fspp/fstest/FsppNodeTest_Timestamps.h create mode 100644 src/fspp/fstest/testutils/TimestampTestUtils.h diff --git a/src/fspp/fstest/FsTest.h b/src/fspp/fstest/FsTest.h index 7ea963e3..e360f169 100644 --- a/src/fspp/fstest/FsTest.h +++ b/src/fspp/fstest/FsTest.h @@ -9,6 +9,7 @@ #include "FsppSymlinkTest.h" #include "FsppNodeTest_Rename.h" #include "FsppNodeTest_Stat.h" +#include "FsppNodeTest_Timestamps.h" #include "FsppOpenFileTest.h" #define FSPP_ADD_FILESYTEM_TESTS(FS_NAME, FIXTURE) \ @@ -18,9 +19,11 @@ 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_NODE_TEST_CASE( FS_NAME, FsppNodeTest_Timestamps, 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/FsppNodeTest_Stat.h b/src/fspp/fstest/FsppNodeTest_Stat.h index 0412bf0a..e31996ef 100644 --- a/src/fspp/fstest/FsppNodeTest_Stat.h +++ b/src/fspp/fstest/FsppNodeTest_Stat.h @@ -80,5 +80,3 @@ REGISTER_TYPED_TEST_CASE_P(FsppNodeTest_Stat_SymlinkOnly, #endif //TODO More test cases - -//TODO Test all operations do (or don't) affect timestamps correctly diff --git a/src/fspp/fstest/FsppNodeTest_Timestamps.h b/src/fspp/fstest/FsppNodeTest_Timestamps.h new file mode 100644 index 00000000..87b92c9c --- /dev/null +++ b/src/fspp/fstest/FsppNodeTest_Timestamps.h @@ -0,0 +1,100 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_FSPPNODETEST_TIMESTAMPS_H_ +#define MESSMER_FSPP_FSTEST_FSPPNODETEST_TIMESTAMPS_H_ + +#include "testutils/FsppNodeTest.h" +#include "../fuse/FuseErrnoException.h" +#include "testutils/TimestampTestUtils.h" + +using namespace cpputils::time; +using std::function; + +template +class FsppNodeTest_Timestamps: public FsppNodeTest, public TimestampTestUtils { +public: + + void Test_Create() { + timespec lowerBound = now(); + auto node = this->CreateNode("/mynode"); + timespec upperBound = now(); + EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node); + EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node); + EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node); + } + + void Test_Stat() { + auto node = this->CreateNode("/mynode"); + auto operation = [&node] () { + struct stat st; + node->stat(&st); + }; + EXPECT_OPERATION_DOESNT_UPDATE_TIMESTAMPS(*node, operation); + } + + void Test_Chmod() { + auto node = this->CreateNode("/mynode"); + mode_t mode = stat(*node).st_mode; + auto operation = [&node, mode] () { + node->chmod(mode); + }; + EXPECT_OPERATION_DOESNT_UPDATE_ACCESS_TIMESTAMP(*node, operation); + EXPECT_OPERATION_DOESNT_UPDATE_MODIFICATION_TIMESTAMP(*node, operation); + EXPECT_OPERATION_UPDATES_METADATACHANGE_TIMESTAMP(*node, operation); + } + + void Test_Chown() { + auto node = this->CreateNode("/mynode"); + uid_t uid = stat(*node).st_uid; + gid_t gid = stat(*node).st_gid; + auto operation = [&node, uid, gid] () { + node->chown(uid, gid); + }; + EXPECT_OPERATION_DOESNT_UPDATE_ACCESS_TIMESTAMP(*node, operation); + EXPECT_OPERATION_DOESNT_UPDATE_MODIFICATION_TIMESTAMP(*node, operation); + EXPECT_OPERATION_UPDATES_METADATACHANGE_TIMESTAMP(*node, operation); + } + + void Test_Access() { + auto node = this->CreateNode("/mynode"); + auto operation = [&node] () { + node->access(0); + }; + EXPECT_OPERATION_DOESNT_UPDATE_TIMESTAMPS(*node, operation); + } + + void Test_Rename() { + auto node = this->CreateNode("/mynode"); + auto operation = [&node] () { + node->rename("newnodename"); + }; + EXPECT_OPERATION_DOESNT_UPDATE_ACCESS_TIMESTAMP(*node, operation); + EXPECT_OPERATION_DOESNT_UPDATE_MODIFICATION_TIMESTAMP(*node, operation); + EXPECT_OPERATION_UPDATES_METADATACHANGE_TIMESTAMP(*node, operation); + } + + // TODO Other rename cases (e.g. failed renames/error paths, moving to different dir, ...) from FsppNodeTest_Rename + + void Test_Utimens() { + auto node = this->CreateNode("/mynode"); + timespec atime = xSecondsAgo(100); + timespec mtime = xSecondsAgo(200); + auto operation = [&node, atime, mtime] () { + node->utimens(atime, mtime); + }; + EXPECT_OPERATION_UPDATES_METADATACHANGE_TIMESTAMP(*node, operation); + EXPECT_EQ(atime, stat(*node).st_atim); + EXPECT_EQ(mtime, stat(*node).st_mtim); + } +}; + +REGISTER_NODE_TEST_CASE(FsppNodeTest_Timestamps, + Create, + Stat, + Chmod, + Chown, + Access, + Rename, + Utimens +); + +#endif diff --git a/src/fspp/fstest/testutils/TimestampTestUtils.h b/src/fspp/fstest/testutils/TimestampTestUtils.h new file mode 100644 index 00000000..49c97065 --- /dev/null +++ b/src/fspp/fstest/testutils/TimestampTestUtils.h @@ -0,0 +1,107 @@ +#pragma once +#ifndef MESSMER_FSPP_FSTEST_TESTUTILS_TIMESTAMPTESTUTILS_H_ +#define MESSMER_FSPP_FSTEST_TESTUTILS_TIMESTAMPTESTUTILS_H_ + +#include + +class TimestampTestUtils { +public: + void EXPECT_ACCESS_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { + EXPECT_LE(lowerBound, stat(node).st_atim); + EXPECT_GE(upperBound, stat(node).st_atim); + } + + void EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { + EXPECT_LE(lowerBound, stat(node).st_mtim); + EXPECT_GE(upperBound, stat(node).st_mtim); + } + + void EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(timespec lowerBound, timespec upperBound, const fspp::Node &node) { + EXPECT_LE(lowerBound, stat(node).st_ctim); + EXPECT_GE(upperBound, stat(node).st_ctim); + } + + void EXPECT_OPERATION_DOESNT_UPDATE_ACCESS_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec oldTime = stat(node).st_atim; + operation(); + timespec newTime = stat(node).st_atim; + EXPECT_EQ(oldTime, newTime); + } + + void EXPECT_OPERATION_DOESNT_UPDATE_MODIFICATION_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec oldTime = stat(node).st_mtim; + operation(); + timespec newTime = stat(node).st_mtim; + EXPECT_EQ(oldTime, newTime); + } + + void EXPECT_OPERATION_DOESNT_UPDATE_METADATACHANGE_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec oldTime = stat(node).st_ctim; + operation(); + timespec newTime = stat(node).st_ctim; + EXPECT_EQ(oldTime, newTime); + } + + void EXPECT_OPERATION_UPDATES_ACCESS_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec lowerBound = cpputils::time::now(); + operation(); + timespec upperBound = cpputils::time::now(); + EXPECT_ACCESS_TIMESTAMP_BETWEEN(lowerBound, upperBound, node); + } + + void EXPECT_OPERATION_UPDATES_MODIFICATION_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec lowerBound = cpputils::time::now(); + operation(); + timespec upperBound = cpputils::time::now(); + EXPECT_MODIFICATION_TIMESTAMP_BETWEEN(lowerBound, upperBound, node); + } + + void EXPECT_OPERATION_UPDATES_METADATACHANGE_TIMESTAMP(const fspp::Node &node, std::function operation) { + ensureNodeTimestampsAreOld(node); + timespec lowerBound = cpputils::time::now(); + operation(); + timespec upperBound = cpputils::time::now(); + EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, node); + } + + void EXPECT_OPERATION_DOESNT_UPDATE_TIMESTAMPS(const fspp::Node &node, std::function operation) { + EXPECT_OPERATION_DOESNT_UPDATE_ACCESS_TIMESTAMP(node, operation); + EXPECT_OPERATION_DOESNT_UPDATE_MODIFICATION_TIMESTAMP(node, operation); + EXPECT_OPERATION_DOESNT_UPDATE_METADATACHANGE_TIMESTAMP(node, operation); + } + + struct stat stat(const fspp::Node &node) { + struct stat st; + node.stat(&st); + return st; + } + + timespec xSecondsAgo(int sec) { + timespec result = cpputils::time::now(); + result.tv_sec -= sec; + return result; + } + +private: + + void ensureNodeTimestampsAreOld(const fspp::Node &node) { + waitUntilClockProgresses(); + EXPECT_LT(stat(node).st_atim, cpputils::time::now()); + EXPECT_LT(stat(node).st_mtim, cpputils::time::now()); + EXPECT_LT(stat(node).st_ctim, cpputils::time::now()); + } + + void waitUntilClockProgresses() { + auto start = cpputils::time::now(); + while (start == cpputils::time::now()) { + // busy waiting is the fastest, we only have to wait for a nanosecond increment. + } + } +}; + +#endif