From 67684b3a9a7205cef15f5978b5adc4df632f5af5 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 21 Jul 2019 13:54:41 +0100 Subject: Basically all the core functionality --- src/Jamfile.jam | 2 +- src/blob.cpp | 35 ++++- src/blob.h | 7 +- src/dir.cpp | 55 +++++++- src/dir.h | 9 +- src/git.cpp | 41 ++++++ src/git.h | 9 ++ src/repo.cpp | 61 +++++++-- src/repo.h | 2 + src/repoList.cpp | 3 +- unittests/Jamfile.jam | 5 +- unittests/core.cpp | 340 ++++++++++++++++++++++++++++++++++++++++++++----- unittests/mockDefs.cpp | 6 +- 13 files changed, 507 insertions(+), 68 deletions(-) diff --git a/src/Jamfile.jam b/src/Jamfile.jam index 4cf083d..d342134 100644 --- a/src/Jamfile.jam +++ b/src/Jamfile.jam @@ -1,7 +1,7 @@ # Testing path-constant gitdir : ../.git ; -lib gitfs : +lib gitfs++11 : [ glob *.cpp ] : ..//adhocutil diff --git a/src/blob.cpp b/src/blob.cpp index 8fb0509..4bd8b15 100644 --- a/src/blob.cpp +++ b/src/blob.cpp @@ -1,13 +1,39 @@ #include +#include #include "blob.h" +#include "repo.h" -GitFS::Blob::Blob(const GitFS::Git::RepositoryPtr & r, const GitFS::Git::TreeEntryPtr & te) : - blob(Git::BlobLookup(r, *git_tree_entry_id(te.get()))), +GitFS::Blob::Blob(const Repo * r, const std::string & path) : + repo(r), + entry(getTreeEntry(path)), + blob(getBlob()), blobSize(git_blob_rawsize(blob.get())), blobContent(static_cast(git_blob_rawcontent(blob.get()))) { } +GitFS::Git::TreeEntryPtr +GitFS::Blob::getTreeEntry(const std::string & path) const +{ + try { + return Git::TreeEntryByPath(repo->tree, path); + } + catch (const Git::Error & e) { + Git::ErrorToSystemError(e); + } +} + +GitFS::Git::BlobPtr +GitFS::Blob::getBlob() const +{ + const auto mode = git_tree_entry_filemode(entry.get()); + + if (S_ISDIR(mode)) throw NetFS::SystemError(EISDIR); + if (S_ISLNK(mode)) throw NetFS::SystemError(ELOOP); + + return Git::BlobLookup(repo->repo, *git_tree_entry_id(entry.get())); +} + void GitFS::Blob::close(const ::Ice::Current& current) { @@ -18,7 +44,10 @@ GitFS::Blob::close(const ::Ice::Current& current) NetFS::Attr GitFS::Blob::fgetattr(ReqEnv, const ::Ice::Current&) { - return {}; + NetFS::Attr a; + a << *blob << *entry << *repo->commit; + a.gid = a.uid = "root"; + return a; } diff --git a/src/blob.h b/src/blob.h index 0a2a77d..64f41ac 100644 --- a/src/blob.h +++ b/src/blob.h @@ -6,9 +6,10 @@ namespace GitFS { using namespace NetFS; + class Repo; class Blob : public File { public: - Blob(const GitFS::Git::RepositoryPtr & r, const Git::TreeEntryPtr &); + Blob(const Repo * r, const std::string &); void close(const ::Ice::Current& current) override; Attr fgetattr(ReqEnv env, const ::Ice::Current& current) override; @@ -17,6 +18,10 @@ namespace GitFS { void write(long long int offset, long long int size, Buffer data, const ::Ice::Current& current) override; private: + Git::TreeEntryPtr getTreeEntry(const std::string & path) const; + Git::BlobPtr getBlob() const; + const Repo * repo; + Git::TreeEntryPtr entry; Git::BlobPtr blob; const decltype(git_blob_rawsize({})) blobSize; const char * const blobContent; diff --git a/src/dir.cpp b/src/dir.cpp index 8a9fd86..cee26f0 100644 --- a/src/dir.cpp +++ b/src/dir.cpp @@ -1,10 +1,36 @@ #include +#include +#include "repo.h" #include "dir.h" -GitFS::Directory::Directory(const GitFS::Git::TreePtr & t, const std::string & p) : - tree(t), - path(p) +GitFS::Directory::Directory(const Repo * r, const std::string & p) : + repo(r), + path(p), + subTreeCacheRootId({}) { + getSubtree(); +} + +GitFS::Git::TreePtr +GitFS::Directory::getSubtree() const +{ + if (!git_oid_equal(&subTreeCacheRootId, git_tree_id(repo->tree.get()))) { + if (path == "/") { + subTreeCache = repo->tree; + } + else { + try { + auto e = Git::TreeEntryByPath(repo->tree, path); + if (!S_ISDIR(git_tree_entry_filemode(e.get()))) throw NetFS::SystemError(ENOTDIR); + subTreeCache = Git::TreeLookup(repo->repo, *git_tree_entry_id(e.get())); + } + catch (const Git::Error & e) { + Git::ErrorToSystemError(e); + } + } + subTreeCacheRootId = *git_tree_id(repo->tree.get()); + } + return subTreeCache; } void @@ -17,14 +43,33 @@ GitFS::Directory::close(const ::Ice::Current& current) NetFS::NameList GitFS::Directory::readdir(const ::Ice::Current&) { - return {}; + const auto subTree = getSubtree(); + NetFS::NameList list; + for (int idx = git_tree_entrycount(subTree.get()); idx--;) { + const auto entry = git_tree_entry_byindex(subTree.get(), idx); + list.push_back(git_tree_entry_name(entry)); + } + return list; } NetFS::DirectoryContents GitFS::Directory::listdir(const ::Ice::Current&) { - return {}; + const auto subTree = getSubtree(); + NetFS::DirectoryContents list; + for (int idx = git_tree_entrycount(subTree.get()); idx--;) { + const auto entry = git_tree_entry_byindex(subTree.get(), idx); + NetFS::Attr a {}; + a << *entry << *repo->commit; + if (S_ISREG(git_tree_entry_filemode(entry))) { + auto blob = Git::BlobLookup(repo->repo, *git_tree_entry_id(entry)); + a << *blob; + } + a.gid = a.uid = "root"; + list.emplace(git_tree_entry_name(entry), a); + } + return list; } diff --git a/src/dir.h b/src/dir.h index 74afde3..7174976 100644 --- a/src/dir.h +++ b/src/dir.h @@ -6,17 +6,22 @@ namespace GitFS { using namespace NetFS; + class Repo; class Directory : public DirectoryV2 { public: - Directory(const GitFS::Git::TreePtr & r, const std::string &); + Directory(const Repo * r, const std::string &); void close(const ::Ice::Current& current) override; NameList readdir(const ::Ice::Current& current) override; DirectoryContents listdir(const ::Ice::Current& current) override; private: - Git::TreePtr tree; + Git::TreePtr getSubtree() const; + const Repo * repo; const std::string path; + + mutable Git::TreePtr subTreeCache; + mutable git_oid subTreeCacheRootId; }; } diff --git a/src/git.cpp b/src/git.cpp index 084e6ca..850b8b3 100644 --- a/src/git.cpp +++ b/src/git.cpp @@ -1,6 +1,8 @@ #include "git.h" #include #include +#include +#include namespace GitFS { namespace Git { @@ -14,6 +16,45 @@ namespace GitFS { #endif throw Error { err, e->klass, e->message }; } + + [[noreturn]] void ErrorToSystemError(const Error & e) + { + if (e.err == GIT_ENOTFOUND) { + throw NetFS::SystemError(ENOENT); + } + throw NetFS::SystemError(EIO); + } + + } +} + +namespace NetFS { + Attr & operator<<(Attr & a, const git_tree_entry & e) + { + a.mode = git_tree_entry_filemode(&e); + if (S_ISDIR(a.mode)) { + a.mode |= S_IXUSR | S_IXGRP | S_IXOTH | S_IRUSR | S_IRGRP | S_IROTH; + } + else if (S_ISLNK(a.mode)) { + a.mode |= S_IRUSR | S_IRGRP | S_IROTH; + } + else { + a.mode ^= S_IWUSR; + } + return a; + } + + Attr & operator<<(Attr & a, const git_commit & c) + { + a.ctime = a.atime = a.mtime = git_commit_time(&c); + return a; + } + + Attr & operator<<(Attr & a, const git_blob & b) + { + a.blockSize = 1; + a.blocks = a.size = git_blob_rawsize(&b); + return a; } } diff --git a/src/git.h b/src/git.h index c0ae4e3..9107f8b 100644 --- a/src/git.h +++ b/src/git.h @@ -23,6 +23,8 @@ namespace GitFS { } } + [[noreturn]] void ErrorToSystemError(const Error & e); + template using TPtr = std::shared_ptr; @@ -83,6 +85,13 @@ namespace GitFS { } } +namespace NetFS { + struct Attr; + Attr & operator<<(Attr &, const git_tree_entry &); + Attr & operator<<(Attr &, const git_commit &); + Attr & operator<<(Attr &, const git_blob &); +} + namespace std { std::ostream & operator<<(std::ostream &, const git_oid &); } diff --git a/src/repo.cpp b/src/repo.cpp index 4c2a13f..0db5b23 100644 --- a/src/repo.cpp +++ b/src/repo.cpp @@ -9,7 +9,7 @@ GitFS::Repo::Repo() : repo(Git::RepositoryOpenBare(rootDir)), - commit(Git::CommitLookup(repo, Git::OidParse("9197cb3c6e58e6f24deb0af326f695aac87bc36d"))), + commit(Git::CommitLookup(repo, Git::OidParse("7a0ccb40084c3ab31d9856e7f689c0514c28c930"))), tree(Git::TreeLookup(repo, *git_commit_tree_id(commit.get()))) { } @@ -24,14 +24,18 @@ GitFS::Repo::disconnect(const ::Ice::Current& current) NetFS::DirectoryPrxPtr GitFS::Repo::opendir(ReqEnv, ::std::string path, const ::Ice::Current& ice) { + if (path.empty()) throw NetFS::SystemError(EINVAL); + return Ice::uncheckedCast(ice.adapter->addWithUUID( - std::make_shared(tree, path))); + std::make_shared(this, path))); } NetFS::VFS -GitFS::Repo::statfs(ReqEnv, ::std::string, const ::Ice::Current&) +GitFS::Repo::statfs(ReqEnv, ::std::string path, const ::Ice::Current&) { + if (path.empty()) throw NetFS::SystemError(EINVAL); + return {}; } @@ -48,6 +52,7 @@ GitFS::Repo::access(ReqEnv, ::std::string path, int mode, const ::Ice::Current&) const auto emode = git_tree_entry_filemode(e.get()); if (S_ISDIR(emode)) return 0; + if (S_ISLNK(emode)) return 0; if (mode & R_OK && !(S_IRUSR & emode)) return EACCES; if (mode & X_OK && !(S_IXUSR & emode)) return EACCES; @@ -57,7 +62,7 @@ GitFS::Repo::access(ReqEnv, ::std::string path, int mode, const ::Ice::Current&) if (e.err == GIT_ENOTFOUND) { return ENOENT; } - throw NetFS::SystemError(EIO); + Git::ErrorToSystemError(e); } } @@ -65,29 +70,57 @@ GitFS::Repo::access(ReqEnv, ::std::string path, int mode, const ::Ice::Current&) NetFS::Attr GitFS::Repo::getattr(ReqEnv, ::std::string path, const ::Ice::Current&) { - NetFS::Attr a {}; - (void)path; - a.ctime = a.atime = a.mtime = git_commit_time(commit.get()); - return a; + if (path.empty()) throw NetFS::SystemError(EINVAL); + + try { + NetFS::Attr a {}; + if (path == "/") { + a.mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + } + else { + auto entry = Git::TreeEntryByPath(tree, path); + a << *entry; + if (S_ISREG(git_tree_entry_filemode(entry.get()))) { + auto blob = Git::BlobLookup(repo, *git_tree_entry_id(entry.get())); + a << *blob; + } + } + a << *commit; + a.gid = a.uid = "root"; + return a; + } + catch (const Git::Error & e) { + Git::ErrorToSystemError(e); + } } ::std::string GitFS::Repo::readlink(ReqEnv, ::std::string path, const ::Ice::Current&) { - auto e = Git::TreeEntryByPath(tree, path); - if (!e) throw NetFS::SystemError(ENOENT); - const auto emode = git_tree_entry_filemode(e.get()); - if (!S_ISLNK(emode)) throw NetFS::SystemError(EINVAL); - return {}; + if (path.empty() || path == "/") throw NetFS::SystemError(EINVAL); + + try { + auto e = Git::TreeEntryByPath(tree, path); + if (!S_ISLNK(git_tree_entry_filemode(e.get()))) throw NetFS::SystemError(EINVAL); + auto blob = Git::BlobLookup(repo, *git_tree_entry_id(e.get())); + auto n = static_cast(git_blob_rawcontent(blob.get())); + return { n, n + git_blob_rawsize(blob.get()) }; + } + catch (const Git::Error & e) { + Git::ErrorToSystemError(e); + } } NetFS::FilePrxPtr GitFS::Repo::open(ReqEnv, ::std::string path, int, const ::Ice::Current& ice) { + if (path.empty()) throw NetFS::SystemError(EINVAL); + if (path == "/") throw NetFS::SystemError(EISDIR); + return Ice::uncheckedCast(ice.adapter->addWithUUID( - std::make_shared(repo, Git::TreeEntryByPath(tree, path)))); + std::make_shared(this, path))); } diff --git a/src/repo.h b/src/repo.h index e6f7d94..0b8cc56 100644 --- a/src/repo.h +++ b/src/repo.h @@ -31,6 +31,8 @@ namespace GitFS { void utimens(ReqEnv env, ::std::string path, long long int atime, long long int atimens, long long int mtime, long long int mtimens, const ::Ice::Current& current) override; private: + friend class Directory; + friend class Blob; Git::RepositoryPtr repo; Git::CommitPtr commit; Git::TreePtr tree; diff --git a/src/repoList.cpp b/src/repoList.cpp index 0ee92c5..51eb6b2 100644 --- a/src/repoList.cpp +++ b/src/repoList.cpp @@ -4,10 +4,9 @@ GitFS::RepoList::RepoList(const Ice::PropertiesPtr &) { - } -NetFS::VolumePrxPtr +NetFS::VolumePrxPtr GitFS::RepoList::connect(const ::std::string, const ::std::string, const ::Ice::Current & ice) { diff --git a/unittests/Jamfile.jam b/unittests/Jamfile.jam index 9de6ea6..a44275f 100644 --- a/unittests/Jamfile.jam +++ b/unittests/Jamfile.jam @@ -9,13 +9,13 @@ lib common : mockDefs.cpp : dryice - ../src//gitfs + ../src//gitfs++11 ..//adhocutil boost_utf ROOT=\"$(me)\" : : dryice - ../src//gitfs + ../src//gitfs++11 boost_utf ROOT=\"$(me)\" BOOST_TEST_DYN_LINK @@ -23,7 +23,6 @@ lib common : run core.cpp : : : - ../src//gitfs common ; diff --git a/unittests/core.cpp b/unittests/core.cpp index aad6b65..78be8d7 100644 --- a/unittests/core.cpp +++ b/unittests/core.cpp @@ -1,69 +1,341 @@ #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(client, Client) +BOOST_FIXTURE_TEST_SUITE(client, Client); // TODO BOOST_AUTO_TEST_SUITE_END(); -BOOST_FIXTURE_TEST_SUITE(volume, VolumeClient) +BOOST_FIXTURE_TEST_SUITE(volume, VolumeClient); -BOOST_AUTO_TEST_CASE( access ) +BOOST_AUTO_TEST_CASE( unsupported_rofs_ops ) { - // Noting is writable - // Directories are all readable and executable + 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_CHECK_EQUAL(EINVAL, v->access(env, "", R_OK)); +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, "/")); +} - BOOST_CHECK_EQUAL(0, v->access(env, "/", R_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/", W_OK)); - BOOST_CHECK_EQUAL(0, v->access(env, "/", X_OK)); +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_CHECK_EQUAL(0, v->access(env, "/src", R_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/src", W_OK)); - BOOST_CHECK_EQUAL(0, v->access(env, "/src", X_OK)); +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); +} - BOOST_CHECK_EQUAL(0, v->access(env, "/src/Jamfile.jam", R_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/src/Jamfile.jam", W_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/src/Jamfile.jam", X_OK)); - BOOST_CHECK_EQUAL(0, v->access(env, "/unittests/fixtures/executable", R_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/unittests/fixtures/executable", W_OK)); - BOOST_CHECK_EQUAL(0, v->access(env, "/unittests/fixtures/executable", X_OK)); +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_CHECK_EQUAL(ENOENT, v->access(env, "/missing", R_OK)); - BOOST_CHECK_EQUAL(EACCES, v->access(env, "/missing", W_OK)); - BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/missing", X_OK)); +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_CHECK_EQUAL(ENOENT, v->access(env, "/.", R_OK)); - BOOST_CHECK_EQUAL(ENOENT, v->access(env, "/../", R_OK)); - BOOST_CHECK_EQUAL(ENOENT, v->access(env, ".", R_OK)); - BOOST_CHECK_EQUAL(ENOENT, v->access(env, "..", R_OK)); - BOOST_CHECK_EQUAL(ENOENT, v->access(env, "../", R_OK)); + +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_AUTO_TEST_CASE( statRoot ) +BOOST_DATA_TEST_CASE( openInval, INVALIDPATHS, path ) { - //auto a = v->getattr(env, "/"); - //BOOST_CHECK_EQUAL(0170555, a.mode); - //BOOST_CHECK_EQUAL(1563566842, a.mtime); - //BOOST_CHECK_EQUAL(1563566842, a.ctime); - //BOOST_CHECK_EQUAL(1563566842, a.atime); + 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_AUTO_TEST_CASE(stat_root) +BOOST_DATA_TEST_CASE( openFileGetAttr, REGPATHS + EXECPATHS, path ) { - auto attr = v->getattr(env, ""); + 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(); diff --git a/unittests/mockDefs.cpp b/unittests/mockDefs.cpp index 78ad270..6d884d9 100644 --- a/unittests/mockDefs.cpp +++ b/unittests/mockDefs.cpp @@ -9,18 +9,18 @@ GitFS::Test::Client::Client() : s(getProxy("Service")) { BOOST_TEST_REQUIRE(s); - s->ice_ping(); + s->ice_ping(); } GitFS::Test::VolumeClient::VolumeClient() : v(s->connect("testrepo", "testauth")) { BOOST_TEST_REQUIRE(v); - v->ice_ping(); + v->ice_ping(); } GitFS::Test::VolumeClient::~VolumeClient() { - v->disconnect(); + v->disconnect(); } -- cgit v1.2.3