summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2019-07-21 13:54:41 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2019-07-21 13:54:41 +0100
commit67684b3a9a7205cef15f5978b5adc4df632f5af5 (patch)
tree630a28d313047bf6440b0c349047c6c8de0cb298
parentAdd a test symlink (diff)
downloadnetfs-gitfs-67684b3a9a7205cef15f5978b5adc4df632f5af5.tar.bz2
netfs-gitfs-67684b3a9a7205cef15f5978b5adc4df632f5af5.tar.xz
netfs-gitfs-67684b3a9a7205cef15f5978b5adc4df632f5af5.zip
Basically all the core functionality
-rw-r--r--src/Jamfile.jam2
-rw-r--r--src/blob.cpp35
-rw-r--r--src/blob.h7
-rw-r--r--src/dir.cpp55
-rw-r--r--src/dir.h9
-rw-r--r--src/git.cpp41
-rw-r--r--src/git.h9
-rw-r--r--src/repo.cpp61
-rw-r--r--src/repo.h2
-rw-r--r--src/repoList.cpp3
-rw-r--r--unittests/Jamfile.jam5
-rw-r--r--unittests/core.cpp340
-rw-r--r--unittests/mockDefs.cpp6
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 ]
:
<library>..//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 <Ice/ObjectAdapter.h>
+#include <sys/stat.h>
#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<const char *>(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 <Ice/ObjectAdapter.h>
+#include <sys/stat.h>
+#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 <execinfo.h>
#include <exceptions.h>
+#include <types.h>
+#include <sys/stat.h>
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<typename T>
using TPtr = std::shared_ptr<T>;
@@ -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<NetFS::DirectoryV2Prx>(ice.adapter->addWithUUID(
- std::make_shared<Directory>(tree, path)));
+ std::make_shared<Directory>(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<const ::std::string::value_type *>(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<NetFS::FilePrx>(ice.adapter->addWithUUID(
- std::make_shared<Blob>(repo, Git::TreeEntryByPath(tree, path))));
+ std::make_shared<Blob>(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
:
<library>dryice
- <library>../src//gitfs
+ <library>../src//gitfs++11
<library>..//adhocutil
<library>boost_utf
<define>ROOT=\"$(me)\"
: :
<library>dryice
- <library>../src//gitfs
+ <library>../src//gitfs++11
<library>boost_utf
<define>ROOT=\"$(me)\"
<define>BOOST_TEST_DYN_LINK
@@ -23,7 +23,6 @@ lib common :
run core.cpp : : :
- <library>../src//gitfs
<library>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 <boost/test/unit_test.hpp>
+#include <boost/test/data/test_case.hpp>
#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<std::string, int> &)
+ {
+ return strm;
+ }
+
+ template<typename T>
+ std::ostream & operator<<(std::ostream & strm, const std::vector<T> & 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<std::vector<std::string>>({
+ {".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<std::vector<std::tuple<std::string, int>>>({
+ {{".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<NetFS::DirectoryV2Prx>(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<NetFS::ServicePrx>("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();
}