#define BOOST_TEST_MODULE GitFS_Core #include #include #include "mockDefs.h" #include "sys/stat.h" #include "sys/fcntl.h" 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 dir2 = Ice::checkedCast(dir); BOOST_REQUIRE(dir2); auto list = dir2->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(env, {}), 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(env); 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(env); 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();