#define BOOST_TEST_MODULE GitFS_Core #include // IWYU pragma: keep #include #include "mockDefs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GitFS; using namespace GitFS::Test; namespace std { std::ostream & operator<<(std::ostream & strm, const std::tuple &) { return strm; } template std::ostream & operator<<(std::ostream & strm, const std::vector & v) { strm << "[ "; for (const auto & e : v) { if (&e != &v.front()) { strm << ", "; } strm << e; } strm << " ]"; return strm; } } #define BOOST_CHECK_THROW_SYSTEMERROR(CODE, ERRNO) \ try { \ BOOST_TEST_CHECKPOINT(""); \ [&]() { \ CODE; \ }(); \ BOOST_ERROR("No exception thrown, NetFS::SystemError expected"); \ } \ catch (const NetFS::SystemError & se) { \ BOOST_CHECK_EQUAL(se.syserrno, ERRNO); \ } \ catch (...) { \ BOOST_ERROR("NetFS::SystemError not thrown"); \ } BOOST_TEST_GLOBAL_FIXTURE(Service); BOOST_FIXTURE_TEST_SUITE(volume, VolumeClient); BOOST_AUTO_TEST_CASE(unsupported_rofs_ops) { BOOST_CHECK_THROW_SYSTEMERROR(v->create(env, {}, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->truncate(env, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->unlink(env, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->mkdir(env, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->rmdir(env, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->mknod(env, {}, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->symlink(env, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->link(env, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->rename(env, {}, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->chmod(env, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->chown(env, {}, {}, {}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(v->utimens(env, {}, {}, {}, {}, {}), EROFS); } BOOST_AUTO_TEST_CASE(statfs) { // Don't know what this should return, but it shouldn't error given a valid path BOOST_CHECK_THROW_SYSTEMERROR(v->statfs(env, ""), EINVAL); BOOST_CHECK_NO_THROW(v->statfs(env, "/")); } namespace btdata = boost::unit_test::data; const auto INVALIDPATHS = btdata::make({""}); const auto BADPATHS = btdata::make({ "/.", "/../", ".", "..", "../", }); const auto DIRPATHS = btdata::make({ "/", "/src", "/unittests", "/unittests/fixtures", }); const auto REGPATHS = btdata::make({ "/.gitignore", "/Jamroot.jam", "/src/Jamfile.jam", "/unittests/Jamfile.jam", }); const auto EXECPATHS = btdata::make({ "/unittests/fixtures/executable", }); const auto LINKPATHS = btdata::make({ "/unittests/fixtures/symlink", }); const auto MISSINGPATHS = btdata::make({ "/.missing", "/missing", "/src/missing", "/unittests/fixtures/missing", }); BOOST_DATA_TEST_CASE( accessWrite, INVALIDPATHS + DIRPATHS + REGPATHS + EXECPATHS + LINKPATHS + MISSINGPATHS + BADPATHS, path) { BOOST_CHECK_EQUAL(EACCES, v->access(env, path, W_OK)); } BOOST_DATA_TEST_CASE(accessDirs, DIRPATHS, path) { BOOST_CHECK_EQUAL(0, v->access(env, path, R_OK)); BOOST_CHECK_EQUAL(0, v->access(env, path, X_OK)); } BOOST_DATA_TEST_CASE(accessRead, REGPATHS, path) { BOOST_CHECK_EQUAL(0, v->access(env, path, R_OK)); BOOST_CHECK_EQUAL(EACCES, v->access(env, path, X_OK)); } BOOST_DATA_TEST_CASE(accessLink, LINKPATHS, path) { BOOST_CHECK_EQUAL(0, v->access(env, path, R_OK)); BOOST_CHECK_EQUAL(0, v->access(env, path, X_OK)); } BOOST_DATA_TEST_CASE(accessExec, EXECPATHS, path) { BOOST_CHECK_EQUAL(0, v->access(env, path, R_OK)); BOOST_CHECK_EQUAL(0, v->access(env, path, X_OK)); } BOOST_DATA_TEST_CASE(accessInval, INVALIDPATHS * btdata::make({R_OK, X_OK}), path, mode) { BOOST_CHECK_EQUAL(EINVAL, v->access(env, path, mode)); } BOOST_DATA_TEST_CASE(accessBad, BADPATHS * btdata::make({R_OK, X_OK}), path, mode) { BOOST_CHECK_EQUAL(ENOENT, v->access(env, path, mode)); } BOOST_DATA_TEST_CASE(statInval, INVALIDPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->getattr(env, path), EINVAL); } BOOST_DATA_TEST_CASE(statBad, BADPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->getattr(env, path), ENOENT); } const auto DIRMODE = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; const auto FILEMODE = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; const auto EXECMODE = FILEMODE | S_IXUSR | S_IXGRP | S_IXOTH; const auto LINKMODE = S_IFLNK | S_IRUSR | S_IRGRP | S_IROTH; const time_t COMMIT_TIME = 1563621030; const std::string USER = "root"; const std::string GROUP = "root"; BOOST_DATA_TEST_CASE(statDirs, DIRPATHS, path) { const auto attr = v->getattr(env, path); BOOST_CHECK_EQUAL(DIRMODE, attr.mode); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.mtime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.ctime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.atime); BOOST_CHECK_EQUAL(0, attr.size); BOOST_CHECK_EQUAL(USER, attr.uid); BOOST_CHECK_EQUAL(GROUP, attr.gid); } BOOST_DATA_TEST_CASE(statFiles, REGPATHS, path) { const auto attr = v->getattr(env, path); BOOST_CHECK_EQUAL(FILEMODE, attr.mode); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.mtime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.ctime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.atime); BOOST_CHECK_LE(4, attr.size); BOOST_CHECK_EQUAL(USER, attr.uid); BOOST_CHECK_EQUAL(GROUP, attr.gid); } BOOST_DATA_TEST_CASE(statExecs, EXECPATHS, path) { const auto attr = v->getattr(env, path); BOOST_CHECK_EQUAL(EXECMODE, attr.mode); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.mtime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.ctime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.atime); BOOST_CHECK_LE(4, attr.size); BOOST_CHECK_EQUAL(USER, attr.uid); BOOST_CHECK_EQUAL(GROUP, attr.gid); } BOOST_DATA_TEST_CASE(statSymlink, LINKPATHS, path) { const auto attr = v->getattr(env, path); BOOST_CHECK_EQUAL(LINKMODE, attr.mode); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.mtime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.ctime); BOOST_CHECK_EQUAL(COMMIT_TIME, attr.atime); BOOST_CHECK_EQUAL(0, attr.size); BOOST_CHECK_EQUAL(USER, attr.uid); BOOST_CHECK_EQUAL(GROUP, attr.gid); } BOOST_DATA_TEST_CASE(readlinkInval, INVALIDPATHS + DIRPATHS + REGPATHS + EXECPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->readlink(env, path), EINVAL); } BOOST_DATA_TEST_CASE(readlinkBad, BADPATHS + MISSINGPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->readlink(env, path), ENOENT); } BOOST_DATA_TEST_CASE(readlink, LINKPATHS ^ btdata::make({"executable"}), path, target) { BOOST_CHECK_EQUAL(target, v->readlink(env, path)); } BOOST_DATA_TEST_CASE(openDirInval, INVALIDPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->opendir(env, path), EINVAL); } BOOST_DATA_TEST_CASE(openDirBad, BADPATHS + MISSINGPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->opendir(env, path), ENOENT); } BOOST_DATA_TEST_CASE(openDirNotDir, REGPATHS + LINKPATHS + EXECPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->opendir(env, path), ENOTDIR); } const auto DIRCONTENTS = btdata::make>({ {".gitignore", "Jamroot.jam", "src", "unittests"}, {"Jamfile.jam", "blob.cpp", "blob.h", "dir.cpp", "dir.h", "git.cpp", "git.h", "main.cpp", "repo.cpp", "repo.h", "repoList.cpp", "repoList.h"}, {"Jamfile.jam", "core.cpp", "fixtures", "mockDefs.cpp", "mockDefs.h"}, {"executable", "symlink"}, }); BOOST_DATA_TEST_CASE(openDirRead, DIRPATHS ^ DIRCONTENTS, path, contents) { auto dir = v->opendir(env, path); BOOST_REQUIRE(dir); auto names = dir->readdir(); std::sort(names.begin(), names.end()); BOOST_CHECK_EQUAL_COLLECTIONS(names.begin(), names.end(), contents.begin(), contents.end()); dir->close(); } const auto DIRCONTENTMODES = btdata::make>>({ {{".gitignore", FILEMODE}, {"Jamroot.jam", FILEMODE}, {"src", DIRMODE}, {"unittests", DIRMODE}}, {{"Jamfile.jam", FILEMODE}, {"blob.cpp", FILEMODE}, {"blob.h", FILEMODE}, {"dir.cpp", FILEMODE}, {"dir.h", FILEMODE}, {"git.cpp", FILEMODE}, {"git.h", FILEMODE}, {"main.cpp", FILEMODE}, {"repo.cpp", FILEMODE}, {"repo.h", FILEMODE}, {"repoList.cpp", FILEMODE}, {"repoList.h", FILEMODE}}, {{"Jamfile.jam", FILEMODE}, {"core.cpp", FILEMODE}, {"fixtures", DIRMODE}, {"mockDefs.cpp", FILEMODE}, {"mockDefs.h", FILEMODE}}, {{"executable", EXECMODE}, {"symlink", LINKMODE}}, }); BOOST_DATA_TEST_CASE(openDirList, DIRPATHS ^ DIRCONTENTMODES, path, contents) { auto dir = v->opendir(env, path); BOOST_REQUIRE(dir); auto list = dir->listdir(); BOOST_REQUIRE_EQUAL(contents.size(), list.size()); for (const auto & c : contents) { auto li = list.find(std::get<0>(c)); BOOST_REQUIRE(li != list.end()); BOOST_CHECK_EQUAL(li->second.mode, std::get<1>(c)); BOOST_CHECK_EQUAL(COMMIT_TIME, li->second.mtime); BOOST_CHECK_EQUAL(COMMIT_TIME, li->second.ctime); BOOST_CHECK_EQUAL(COMMIT_TIME, li->second.atime); BOOST_CHECK_EQUAL(USER, li->second.uid); BOOST_CHECK_EQUAL(GROUP, li->second.gid); if (S_ISREG(li->second.mode)) { BOOST_CHECK_LE(4, li->second.size); } } dir->close(); } BOOST_DATA_TEST_CASE(openInval, INVALIDPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->open(env, path, O_RDONLY), EINVAL); } BOOST_DATA_TEST_CASE(openBad, BADPATHS + MISSINGPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->open(env, path, O_RDONLY), ENOENT); } BOOST_DATA_TEST_CASE(openNotFileDir, DIRPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->open(env, path, O_RDONLY), EISDIR); } BOOST_DATA_TEST_CASE(openNotFileLink, LINKPATHS, path) { BOOST_CHECK_THROW_SYSTEMERROR(v->open(env, path, O_RDONLY), ELOOP); } BOOST_DATA_TEST_CASE(openFileROFSOps, REGPATHS + EXECPATHS, path) { auto f = v->open(env, path, O_RDONLY); BOOST_REQUIRE(f); BOOST_CHECK_THROW_SYSTEMERROR(f->ftruncate({}), EROFS); BOOST_CHECK_THROW_SYSTEMERROR(f->write({}, {}, {}), EROFS); f->close(); } BOOST_DATA_TEST_CASE(openFileGetAttr, REGPATHS + EXECPATHS, path) { auto f = v->open(env, path, O_RDONLY); BOOST_REQUIRE(f); const auto attr = f->fgetattr(); BOOST_CHECK(S_ISREG(attr.mode)); BOOST_CHECK_EQUAL(attr.size, attr.blocks); BOOST_CHECK_EQUAL(1, attr.blockSize); BOOST_CHECK_LE(4, attr.size); BOOST_CHECK_EQUAL(USER, attr.uid); BOOST_CHECK_EQUAL(GROUP, attr.gid); f->close(); } BOOST_DATA_TEST_CASE(openFileRead, REGPATHS + EXECPATHS, path) { auto f = v->open(env, path, O_RDONLY); BOOST_REQUIRE(f); const auto attr = f->fgetattr(); const auto readAll = f->read(0, attr.size); BOOST_CHECK_EQUAL(attr.size, readAll.size()); const auto readTrunc = f->read(0, BUFSIZ); BOOST_CHECK_EQUAL(attr.size, readTrunc.size()); const auto readBeyond = f->read(BUFSIZ, BUFSIZ); BOOST_CHECK(readBeyond.empty()); f->close(); } BOOST_AUTO_TEST_SUITE_END();