fuse(4): combine common code in the tests

Combine a bunch of mostly similar expect_* methods into utils.cc, and only
define FH in a single place.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
asomers 2019-03-14 00:12:59 +00:00
parent bed5b48ade
commit f496adcf86
23 changed files with 360 additions and 753 deletions

View File

@ -39,7 +39,13 @@ extern "C" {
using namespace testing;
class Access: public FuseTest {};
class Access: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
};
/* TODO: test methods for the default_permissions mount option */
@ -52,12 +58,7 @@ TEST_F(Access, DISABLED_eaccess)
uint64_t ino = 42;
mode_t access_mode = X_OK;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_ACCESS &&
@ -81,12 +82,7 @@ TEST_F(Access, DISABLED_ok)
uint64_t ino = 42;
mode_t access_mode = R_OK;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_ACCESS &&

View File

@ -295,14 +295,7 @@ TEST_F(Create, DISABLED_entry_cache_negative_purge)
ASSERT_LE(0, fd) << strerror(errno);
/* Finally, a subsequent lookup should query the daemon */
EXPECT_LOOKUP(1, RELPATH).Times(1)
.WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.error = 0;
out->body.entry.nodeid = ino;
out->body.entry.attr.mode = S_IFREG | mode;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELPATH, ino, S_IFREG | mode, 1);
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
/* Deliberately leak fd. close(2) will be tested in release.cc */

View File

@ -40,8 +40,6 @@ using namespace testing;
class Flush: public FuseTest {
const static uint64_t FH = 0xdeadbeef1a7ebabe;
public:
void expect_flush(uint64_t ino, int times, ProcessMockerT r)
{
@ -49,58 +47,16 @@ void expect_flush(uint64_t ino, int times, ProcessMockerT r)
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_FLUSH &&
in->header.nodeid == ino &&
in->body.flush.fh == Flush::FH);
in->body.flush.fh == FH);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke(r));
}
void expect_getattr(uint64_t ino)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino, int times)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke([](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = Flush::FH;
}));
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
/*
@ -130,8 +86,8 @@ TEST_F(Flush, DISABLED_dup)
int fd, fd2;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_flush(ino, 2, ReturnErrno(0));
expect_release();
@ -161,8 +117,8 @@ TEST_F(Flush, DISABLED_eio)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_flush(ino, 1, ReturnErrno(EIO));
expect_release();
@ -182,8 +138,8 @@ TEST_F(Flush, DISABLED_flush)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_flush(ino, 1, ReturnErrno(0));
expect_release();

View File

@ -50,7 +50,6 @@ using namespace testing;
class Fsync: public FuseTest {
public:
const static uint64_t FH = 0xdeadbeef1a7ebabe;
void expect_fsync(uint64_t ino, uint32_t flags, int error)
{
EXPECT_CALL(*m_mock, process(
@ -65,79 +64,14 @@ void expect_fsync(uint64_t ino, uint32_t flags, int error)
).WillOnce(Invoke(ReturnErrno(error)));
}
void expect_getattr(uint64_t ino)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
out->body.attr.attr_valid = UINT64_MAX;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
}
void expect_release(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_RELEASE &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke(ReturnErrno(0)));
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
void expect_write(uint64_t ino, uint64_t size, const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
const char *buf = (const char*)in->body.bytes +
sizeof(struct fuse_write_in);
return (in->header.opcode == FUSE_WRITE &&
in->header.nodeid == ino &&
0 == bcmp(buf, contents, size));
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, write);
out->body.write.size = size;
}));
FuseTest::expect_write(ino, 0, size, size, 0, contents);
}
};
@ -156,8 +90,8 @@ TEST_F(Fsync, DISABLED_aio_fsync)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, bufsize, CONTENTS);
expect_fsync(ino, 0, 0);
@ -190,8 +124,8 @@ TEST_F(Fsync, close)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, bufsize, CONTENTS);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
@ -209,7 +143,7 @@ TEST_F(Fsync, close)
}, Eq(true)),
_)
).Times(0);
expect_release(ino);
expect_release(ino, 1, 0);
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
@ -228,8 +162,8 @@ TEST_F(Fsync, DISABLED_eio)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, bufsize, CONTENTS);
expect_fsync(ino, FUSE_FSYNC_FDATASYNC, EIO);
@ -253,8 +187,8 @@ TEST_F(Fsync, DISABLED_fdatasync)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, bufsize, CONTENTS);
expect_fsync(ino, FUSE_FSYNC_FDATASYNC, 0);
@ -278,8 +212,8 @@ TEST_F(Fsync, DISABLED_fsync)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, bufsize, CONTENTS);
expect_fsync(ino, 0, 0);
@ -303,8 +237,8 @@ TEST_F(Fsync, DISABLED_fsync_metadata_only)
mode_t mode = 0755;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_SETATTR);
@ -335,8 +269,8 @@ TEST_F(Fsync, nop)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
fd = open(FULLPATH, O_WRONLY);
ASSERT_LE(0, fd) << strerror(errno);
@ -346,4 +280,4 @@ TEST_F(Fsync, nop)
/* Deliberately leak fd. close(2) will be tested in release.cc */
}
// TODO: ENOSYS test

View File

@ -50,7 +50,6 @@ using namespace testing;
class FsyncDir: public FuseTest {
public:
const static uint64_t FH = 0xdeadbeef1a7ebabe;
void expect_fsyncdir(uint64_t ino, uint32_t flags, int error)
{
EXPECT_CALL(*m_mock, process(
@ -67,29 +66,7 @@ void expect_fsyncdir(uint64_t ino, uint32_t flags, int error)
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_opendir(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPENDIR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 1);
}
};

View File

@ -45,7 +45,6 @@ TEST_F(Getattr, DISABLED_attr_cache)
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const uint64_t ino = 42;
const uint64_t generation = 13;
struct stat sb;
EXPECT_LOOKUP(1, RELPATH).WillRepeatedly(Invoke([=](auto in, auto out) {
@ -53,7 +52,6 @@ TEST_F(Getattr, DISABLED_attr_cache)
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.generation = generation;
}));
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
@ -84,7 +82,6 @@ TEST_F(Getattr, attr_cache_timeout)
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const uint64_t ino = 42;
const uint64_t generation = 13;
struct stat sb;
/*
* The timeout should be longer than the longest plausible time the
@ -92,14 +89,7 @@ TEST_F(Getattr, attr_cache_timeout)
*/
long timeout_ns = 250'000'000;
EXPECT_LOOKUP(1, RELPATH).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.entry_valid = UINT64_MAX;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.generation = generation;
}));
expect_lookup(RELPATH, ino, S_IFREG | 0644, 2);
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
@ -128,13 +118,7 @@ TEST_F(Getattr, enoent)
struct stat sb;
const uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = 0100644;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
@ -151,16 +135,9 @@ TEST_F(Getattr, ok)
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const uint64_t ino = 42;
const uint64_t generation = 13;
struct stat sb;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.generation = generation;
}));
expect_lookup(RELPATH, ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
@ -202,9 +179,6 @@ TEST_F(Getattr, ok)
EXPECT_EQ(ino, sb.st_ino);
EXPECT_EQ(S_IFREG | 0644, sb.st_mode);
// fuse(4) does not _yet_ support inode generations
//EXPECT_EQ(generation, sb.st_gen);
//st_birthtim and st_flags are not supported by protocol 7.8. They're
//only supported as OS-specific extensions to OSX.
//EXPECT_EQ(, sb.st_birthtim);

View File

@ -37,7 +37,13 @@ extern "C" {
using namespace testing;
class Link: public FuseTest {};
class Link: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
};
TEST_F(Link, emlink)
{
@ -48,12 +54,7 @@ TEST_F(Link, emlink)
uint64_t dst_ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = dst_ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELDST, dst_ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
@ -80,12 +81,7 @@ TEST_F(Link, ok)
const uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke(ReturnErrno(ENOENT)));
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = dst_ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELDST, dst_ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {

View File

@ -43,51 +43,10 @@ using namespace testing;
/* For testing filesystems without posix locking support */
class Fallback: public FuseTest {
public:
const static uint64_t FH = 0xdeadbeef1a7ebabe;
void expect_getattr(uint64_t ino)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
out->body.attr.attr_valid = UINT64_MAX;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
};
@ -120,8 +79,8 @@ TEST_F(GetlkFallback, local)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
@ -150,8 +109,8 @@ TEST_F(Getlk, DISABLED_no_locks)
pid_t pid = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETLK &&
@ -197,8 +156,8 @@ TEST_F(Getlk, DISABLED_lock_exists)
pid_t pid2 = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETLK &&
@ -251,8 +210,8 @@ TEST_F(SetlkFallback, local)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
@ -278,8 +237,8 @@ TEST_F(Setlk, DISABLED_set)
pid_t pid = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_SETLK &&
@ -323,8 +282,8 @@ TEST_F(Setlk, DISABLED_set_eof)
pid_t pid = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_SETLK &&
@ -368,8 +327,8 @@ TEST_F(Setlk, DISABLED_eagain)
pid_t pid = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_SETLK &&
@ -410,8 +369,8 @@ TEST_F(SetlkwFallback, local)
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
fd = open(FULLPATH, O_RDWR);
ASSERT_LE(0, fd) << strerror(errno);
@ -441,8 +400,8 @@ TEST_F(Setlkw, DISABLED_set)
pid_t pid = 1234;
expect_lookup(RELPATH, ino);
expect_open(ino);
expect_getattr(ino);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_SETLK &&

View File

@ -49,6 +49,7 @@ TEST_F(Lookup, DISABLED_attr_cache)
const char FULLPATH[] = "mountpoint/some_file.txt";
const char RELPATH[] = "some_file.txt";
const uint64_t ino = 42;
const uint64_t generation = 13;
struct stat sb;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
@ -70,6 +71,7 @@ TEST_F(Lookup, DISABLED_attr_cache)
out->body.entry.attr.uid = 10;
out->body.entry.attr.gid = 11;
out->body.entry.attr.rdev = 12;
out->body.entry.generation = generation;
}));
/* stat(2) issues a VOP_LOOKUP followed by a VOP_GETATTR */
ASSERT_EQ(0, stat(FULLPATH, &sb)) << strerror(errno);
@ -124,18 +126,7 @@ TEST_F(Lookup, attr_cache_timeout)
out->body.entry.attr.ino = ino; // Must match nodeid
out->body.entry.attr.mode = S_IFREG | 0644;
}));
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
}));
expect_getattr(ino, 0);
/* access(2) will issue a VOP_LOOKUP but not a VOP_GETATTR */
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
@ -246,6 +237,12 @@ TEST_F(Lookup, DISABLED_entry_cache_timeout)
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
}
// TODO: export_support
// After upgrading the protocol to 7.10, check that the kernel will only
// attempt to lookup "." and ".." if the filesystem sets FUSE_EXPORT_SUPPORT in
// the init flags. If not, then all lookups for those entries will return
// ESTALE.
TEST_F(Lookup, ok)
{
const char FULLPATH[] = "mountpoint/some_file.txt";

View File

@ -145,14 +145,7 @@ TEST_F(Mkdir, DISABLED_entry_cache_negative_purge)
ASSERT_EQ(0, mkdir(FULLPATH, mode)) << strerror(errno);
/* Finally, a subsequent lookup should query the daemon */
EXPECT_LOOKUP(1, RELPATH).Times(1)
.WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.error = 0;
out->body.entry.nodeid = ino;
out->body.entry.attr.mode = S_IFDIR | mode;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELPATH, ino, S_IFDIR | mode, 1);
ASSERT_EQ(0, access(FULLPATH, F_OK)) << strerror(errno);
}

View File

@ -48,14 +48,7 @@ void test_ok(int os_flags, int fuse_flags) {
uint64_t ino = 42;
int fd;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
FuseTest::expect_lookup(RELPATH, ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
@ -101,13 +94,7 @@ TEST_F(Open, enoent)
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
@ -129,13 +116,7 @@ TEST_F(Open, eperm)
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&

View File

@ -42,13 +42,7 @@ class Opendir: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 1);
}
};

View File

@ -48,72 +48,9 @@ using namespace testing;
class Read: public FuseTest {
public:
const static uint64_t FH = 0xdeadbeef1a7ebabe;
void expect_getattr(uint64_t ino, uint64_t size)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
out->body.attr.attr.size = size;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino, uint32_t flags, int times)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = Read::FH;
out->body.open.open_flags = flags;
}));
}
void expect_read(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize,
const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == Read::FH &&
in->body.read.offset == offset &&
in->body.read.size == isize);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(struct fuse_out_header) + osize;
memmove(out->body.bytes, contents, osize);
})).RetiresOnSaturation();
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
};

View File

@ -40,43 +40,10 @@ using namespace testing;
using namespace std;
class Readdir: public FuseTest {
const static uint64_t FH = 0xdeadbeef1a7ebabe;
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_opendir(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_STATFS);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, statfs);
}));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPENDIR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 1);
}
void expect_readdir(uint64_t ino, uint64_t off, vector<struct dirent> &ents)

View File

@ -37,7 +37,13 @@ extern "C" {
using namespace testing;
class Readlink: public FuseTest {};
class Readlink: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
FuseTest::expect_lookup(relpath, ino, S_IFLNK | 0777, 1);
}
};
TEST_F(Readlink, eloop)
{
@ -46,12 +52,7 @@ TEST_F(Readlink, eloop)
const uint64_t ino = 42;
char buf[80];
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFLNK | 0777;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
@ -73,12 +74,7 @@ TEST_F(Readlink, ok)
const uint64_t ino = 42;
char buf[80];
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFLNK | 0777;
out->body.entry.nodeid = ino;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {

View File

@ -40,67 +40,10 @@ using namespace testing;
class Release: public FuseTest {
const static uint64_t FH = 0xdeadbeef1a7ebabe;
public:
void expect_getattr(uint64_t ino)
void expect_lookup(const char *relpath, uint64_t ino, int times)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino, int times)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke([](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = Release::FH;
}));
}
void expect_release(uint64_t ino, int times, ProcessMockerT r)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_RELEASE &&
in->header.nodeid == ino &&
in->body.release.fh == Release::FH);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke(r));
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, times);
}
};
@ -114,10 +57,10 @@ TEST_F(Release, dup)
uint64_t ino = 42;
int fd, fd2;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_release(ino, 1, ReturnErrno(0));
expect_lookup(RELPATH, ino, 1);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_release(ino, 1, 0);
fd = open(FULLPATH, O_RDONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -143,10 +86,10 @@ TEST_F(Release, eio)
uint64_t ino = 42;
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_release(ino, 1, ReturnErrno(EIO));
expect_lookup(RELPATH, ino, 1);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_release(ino, 1, EIO);
fd = open(FULLPATH, O_WRONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -166,10 +109,10 @@ TEST_F(Release, multiple_opens)
uint64_t ino = 42;
int fd, fd2;
expect_lookup(RELPATH, ino);
expect_open(ino, 2);
expect_getattr(ino);
expect_release(ino, 2, ReturnErrno(0));
expect_lookup(RELPATH, ino, 2);
expect_open(ino, 0, 2);
expect_getattr(ino, 0);
expect_release(ino, 2, 0);
fd = open(FULLPATH, O_RDONLY);
EXPECT_LE(0, fd) << strerror(errno);
@ -188,10 +131,10 @@ TEST_F(Release, ok)
uint64_t ino = 42;
int fd;
expect_lookup(RELPATH, ino);
expect_open(ino, 1);
expect_getattr(ino);
expect_release(ino, 1, ReturnErrno(0));
expect_lookup(RELPATH, ino, 1);
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_release(ino, 1, 0);
fd = open(FULLPATH, O_RDONLY);
EXPECT_LE(0, fd) << strerror(errno);

View File

@ -39,44 +39,10 @@ using namespace testing;
class ReleaseDir: public FuseTest {
const static uint64_t FH = 0xdeadbeef1a7ebabe;
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_opendir(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_STATFS);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, statfs);
}));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPENDIR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
FuseTest::expect_lookup(relpath, ino, S_IFDIR | 0755, 1);
}
void expect_releasedir(uint64_t ino, ProcessMockerT r)

View File

@ -62,12 +62,7 @@ TEST_F(Rename, einval)
const char RELSRC[] = "src";
uint64_t src_ino = 42;
EXPECT_LOOKUP(1, RELSRC).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = src_ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, src_ino, S_IFDIR | 0755, 2);
EXPECT_LOOKUP(src_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
ASSERT_NE(0, rename(FULLSRC, FULLDST));
@ -80,7 +75,7 @@ TEST_F(Rename, enoent)
const char FULLDST[] = "mountpoint/dst";
const char FULLSRC[] = "mountpoint/src";
const char RELSRC[] = "src";
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke(ReturnErrno(ENOENT)));
@ -98,7 +93,7 @@ TEST_F(Rename, DISABLED_entry_cache_negative)
const char RELDST[] = "dst";
const char FULLSRC[] = "mountpoint/src";
const char RELSRC[] = "src";
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
uint64_t dst_dir_ino = 1;
uint64_t ino = 42;
/*
@ -108,13 +103,7 @@ TEST_F(Rename, DISABLED_entry_cache_negative)
*/
struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, ino, S_IFREG | 0644, 1);
/* LOOKUP returns a negative cache entry for dst */
EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid));
@ -144,7 +133,7 @@ TEST_F(Rename, DISABLED_entry_cache_negative_purge)
const char RELDST[] = "dst";
const char FULLSRC[] = "mountpoint/src";
const char RELSRC[] = "src";
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
uint64_t dst_dir_ino = 1;
uint64_t ino = 42;
/*
@ -154,13 +143,7 @@ TEST_F(Rename, DISABLED_entry_cache_negative_purge)
*/
struct timespec entry_valid = {.tv_sec = 0, .tv_nsec = 0};
EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, ino, S_IFREG | 0644, 1);
/* LOOKUP returns a negative cache entry for dst */
EXPECT_LOOKUP(1, RELDST).WillOnce(ReturnNegativeCache(&entry_valid))
.RetiresOnSaturation();
@ -181,14 +164,7 @@ TEST_F(Rename, DISABLED_entry_cache_negative_purge)
ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
/* Finally, a subsequent lookup should query the daemon */
EXPECT_LOOKUP(1, RELDST).Times(1)
.WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.error = 0;
out->body.entry.nodeid = ino;
out->body.entry.attr.mode = S_IFREG | 0644;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, ino, S_IFREG | 0644, 1);
ASSERT_EQ(0, access(FULLDST, F_OK)) << strerror(errno);
}
@ -197,18 +173,13 @@ TEST_F(Rename, exdev)
{
const char FULLB[] = "mountpoint/src";
const char RELB[] = "src";
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
uint64_t b_ino = 42;
tmpfd = mkstemp(tmpfile);
ASSERT_LE(0, tmpfd) << strerror(errno);
EXPECT_LOOKUP(1, RELB).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = b_ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELB, b_ino, S_IFREG | 0644, 2);
ASSERT_NE(0, rename(tmpfile, FULLB));
ASSERT_EQ(EXDEV, errno);
@ -223,16 +194,11 @@ TEST_F(Rename, ok)
const char RELDST[] = "dst";
const char FULLSRC[] = "mountpoint/src";
const char RELSRC[] = "src";
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
uint64_t dst_dir_ino = 1;
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, ino, S_IFREG | 0644, 1);
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
EXPECT_CALL(*m_mock, process(
@ -260,23 +226,12 @@ TEST_F(Rename, overwrite)
const char RELSRC[] = "src";
// The inode of the already-existing destination file
uint64_t dst_ino = 2;
// FUSE hardcodes the mountpoint to inocde 1
// FUSE hardcodes the mountpoint to inode 1
uint64_t dst_dir_ino = 1;
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELSRC).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
SET_OUT_HEADER_LEN(out, entry);
}));
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = dst_ino;
SET_OUT_HEADER_LEN(out, entry);
}));
expect_lookup(RELSRC, ino, S_IFREG | 0644, 1);
expect_lookup(RELDST, dst_ino, S_IFREG | 0644, 1);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
const char *src = (const char*)in->body.bytes +

View File

@ -37,7 +37,19 @@ extern "C" {
using namespace testing;
class Rmdir: public FuseTest {};
class Rmdir: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 2;
}));
}
};
TEST_F(Rmdir, enotempty)
{
@ -45,13 +57,7 @@ TEST_F(Rmdir, enotempty)
const char RELPATH[] = "some_dir";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 2;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_RMDIR &&
@ -71,13 +77,7 @@ TEST_F(Rmdir, ok)
const char RELPATH[] = "some_dir";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFDIR | 0755;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 2;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_RMDIR &&

View File

@ -36,7 +36,13 @@ extern "C" {
using namespace testing;
class Unlink: public FuseTest {};
class Unlink: public FuseTest {
public:
void expect_lookup(const char *relpath, uint64_t ino)
{
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
};
TEST_F(Unlink, eperm)
{
@ -44,13 +50,7 @@ TEST_F(Unlink, eperm)
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 1;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_UNLINK &&
@ -70,13 +70,7 @@ TEST_F(Unlink, ok)
const char RELPATH[] = "some_file.txt";
uint64_t ino = 42;
EXPECT_LOOKUP(1, RELPATH).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 1;
}));
expect_lookup(RELPATH, ino);
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_UNLINK &&

View File

@ -38,7 +38,9 @@
#include "mockfs.hh"
#include "utils.hh"
class FuseEnv: public ::testing::Environment {
using namespace testing;
class FuseEnv: public Environment {
virtual void SetUp() {
const char *mod_name = "fuse";
const char *devnode = "/dev/fuse";
@ -82,6 +84,146 @@ void FuseTest::SetUp() {
}
}
void FuseTest::expect_getattr(uint64_t ino, uint64_t size)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
out->body.attr.attr.size = size;
out->body.attr.attr_valid = UINT64_MAX;
}));
}
void FuseTest::expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
int times)
{
EXPECT_LOOKUP(1, relpath)
.Times(times)
.WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = mode;
out->body.entry.nodeid = ino;
out->body.entry.attr.nlink = 1;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void FuseTest::expect_open(uint64_t ino, uint32_t flags, int times)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
out->body.open.open_flags = flags;
}));
}
void FuseTest::expect_opendir(uint64_t ino)
{
EXPECT_CALL(*m_mock, process(
ResultOf([](auto in) {
return (in->header.opcode == FUSE_STATFS);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, statfs);
}));
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPENDIR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = FH;
}));
}
void FuseTest::expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == FH &&
in->body.read.offset == offset &&
in->body.read.size == isize);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(struct fuse_out_header) + osize;
memmove(out->body.bytes, contents, osize);
})).RetiresOnSaturation();
}
void FuseTest::expect_release(uint64_t ino, int times, int error)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_RELEASE &&
in->header.nodeid == ino &&
in->body.release.fh == FH);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke(ReturnErrno(error)));
}
void FuseTest::expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
const char *buf = (const char*)in->body.bytes +
sizeof(struct fuse_write_in);
bool pid_ok;
if (in->body.write.write_flags & FUSE_WRITE_CACHE)
pid_ok = true;
else
pid_ok = (pid_t)in->header.pid == getpid();
return (in->header.opcode == FUSE_WRITE &&
in->header.nodeid == ino &&
in->body.write.fh == FH &&
in->body.write.offset == offset &&
in->body.write.size == isize &&
pid_ok &&
in->body.write.write_flags == flags &&
0 == bcmp(buf, contents, isize));
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, write);
out->body.write.size = osize;
}));
}
static void usage(char* progname) {
fprintf(stderr, "Usage: %s [-v]\n\t-v increase verbosity\n", progname);
exit(2);
@ -91,8 +233,8 @@ int main(int argc, char **argv) {
int ch;
FuseEnv *fuse_env = new FuseEnv;
::testing::InitGoogleTest(&argc, argv);
::testing::AddGlobalTestEnvironment(fuse_env);
InitGoogleTest(&argc, argv);
AddGlobalTestEnvironment(fuse_env);
while ((ch = getopt(argc, argv, "v")) != -1) {
switch (ch) {

View File

@ -28,11 +28,21 @@
* SUCH DAMAGE.
*/
/*
* TODO: remove FUSE_WRITE_CACHE definition when upgrading to protocol 7.9.
* This bit was actually part of kernel protocol version 7.2, but never
* documented until 7.9
*/
#ifndef FUSE_WRITE_CACHE
#define FUSE_WRITE_CACHE 1
#endif
class FuseTest : public ::testing::Test {
protected:
uint32_t m_maxreadahead;
uint32_t m_init_flags;
MockFS *m_mock = NULL;
const static uint64_t FH = 0xdeadbeef1a7ebabe;
public:
int m_maxbcachebuf;
@ -52,4 +62,54 @@ class FuseTest : public ::testing::Test {
if (m_mock)
delete m_mock;
}
/*
* Create an expectation that FUSE_GETATTR will be called for the given
* inode any number of times. It will respond with a few basic
* attributes, like the given size and the mode S_IFREG | 0644
*/
void expect_getattr(uint64_t ino, uint64_t size);
/*
* Create an expectation that FUSE_LOOKUP will be called for the given
* path exactly times times. It will respond with inode ino, mode
* mode, and cache validity forever.
*/
void expect_lookup(const char *relpath, uint64_t ino, mode_t mode,
int times);
/*
* Create an expectation that FUSE_GETATTR will be called for the given
* inode exactly times times. It will return with open_flags flags and
* file handle FH.
*/
void expect_open(uint64_t ino, uint32_t flags, int times);
/*
* Create an expectation that FUSE_OPENDIR will be called exactly once
* for inode ino.
*/
void expect_opendir(uint64_t ino);
/*
* Create an expectation that FUSE_READ will be called exactly once for
* the given inode, at offset offset and with size isize. It will
* return the first osize bytes from contents
*/
void expect_read(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, const void *contents);
/*
* Create an expectation that FUSE_RELEASE will be called times times
* for the given inode, returning error error
*/
void expect_release(uint64_t ino, int times, int error);
/*
* Create an expectation that FUSE_WRITE will be called exactly once
* for the given inode, at offset offset, with write_flags flags,
* size isize and buffer contents. It will return osize
*/
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize,
uint64_t osize, uint32_t flags, const void *contents);
};

View File

@ -43,87 +43,15 @@ extern "C" {
#include "mockfs.hh"
#include "utils.hh"
/*
* TODO: remove FUSE_WRITE_CACHE definition when upgrading to protocol 7.9.
* This bit was actually part of kernel protocol version 7.2, but never
* documented until 7.9
*/
#ifndef FUSE_WRITE_CACHE
#define FUSE_WRITE_CACHE 1
#endif
using namespace testing;
class Write: public FuseTest {
public:
const static uint64_t FH = 0xdeadbeef1a7ebabe;
void expect_getattr(uint64_t ino, uint64_t size)
{
/* Until the attr cache is working, we may send an additional GETATTR */
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_GETATTR &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, attr);
out->body.attr.attr.ino = ino; // Must match nodeid
out->body.attr.attr.mode = S_IFREG | 0644;
out->body.attr.attr.size = size;
out->body.attr.attr_valid = UINT64_MAX;
}));
}
void expect_lookup(const char *relpath, uint64_t ino)
{
EXPECT_LOOKUP(1, relpath).WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, entry);
out->body.entry.attr.mode = S_IFREG | 0644;
out->body.entry.nodeid = ino;
out->body.entry.attr_valid = UINT64_MAX;
}));
}
void expect_open(uint64_t ino, uint32_t flags, int times)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_OPEN &&
in->header.nodeid == ino);
}, Eq(true)),
_)
).Times(times)
.WillRepeatedly(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(out->header);
SET_OUT_HEADER_LEN(out, open);
out->body.open.fh = Write::FH;
out->body.open.open_flags = flags;
}));
}
void expect_read(uint64_t ino, uint64_t offset, uint64_t size,
const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
return (in->header.opcode == FUSE_READ &&
in->header.nodeid == ino &&
in->body.read.fh == Write::FH &&
in->body.read.offset == offset &&
in->body.read.size == size);
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
out->header.len = sizeof(struct fuse_out_header) + size;
memmove(out->body.bytes, contents, size);
})).RetiresOnSaturation();
FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, 1);
}
void expect_release(uint64_t ino, ProcessMockerT r)
@ -137,37 +65,6 @@ void expect_release(uint64_t ino, ProcessMockerT r)
).WillRepeatedly(Invoke(r));
}
void expect_write(uint64_t ino, uint64_t offset, uint64_t isize, uint64_t osize,
uint32_t flags, const void *contents)
{
EXPECT_CALL(*m_mock, process(
ResultOf([=](auto in) {
const char *buf = (const char*)in->body.bytes +
sizeof(struct fuse_write_in);
bool pid_ok;
if (in->body.write.write_flags & FUSE_WRITE_CACHE)
pid_ok = true;
else
pid_ok = (pid_t)in->header.pid == getpid();
return (in->header.opcode == FUSE_WRITE &&
in->header.nodeid == ino &&
in->body.write.fh == Write::FH &&
in->body.write.offset == offset &&
in->body.write.size == isize &&
pid_ok &&
in->body.write.write_flags == flags &&
0 == bcmp(buf, contents, isize));
}, Eq(true)),
_)
).WillOnce(Invoke([=](auto in, auto out) {
out->header.unique = in->header.unique;
SET_OUT_HEADER_LEN(out, write);
out->body.write.size = osize;
}));
}
};
class AioWrite: public Write {
@ -337,7 +234,7 @@ TEST_F(Write, DISABLED_direct_io_evicts_cache)
expect_lookup(RELPATH, ino);
expect_open(ino, 0, 1);
expect_getattr(ino, bufsize);
expect_read(ino, 0, bufsize, CONTENTS0);
expect_read(ino, 0, bufsize, bufsize, CONTENTS0);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS1);
fd = open(FULLPATH, O_RDWR);
@ -352,7 +249,7 @@ TEST_F(Write, DISABLED_direct_io_evicts_cache)
ASSERT_EQ(bufsize, write(fd, CONTENTS1, bufsize)) << strerror(errno);
// Read again. Cache should be bypassed
expect_read(ino, 0, bufsize, CONTENTS1);
expect_read(ino, 0, bufsize, bufsize, CONTENTS1);
ASSERT_EQ(0, fcntl(fd, F_SETFL, 0)) << strerror(errno);
ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
ASSERT_EQ(bufsize, read(fd, readbuf, bufsize)) << strerror(errno);
@ -459,7 +356,7 @@ TEST_F(Write, DISABLED_mmap)
expect_lookup(RELPATH, ino);
expect_open(ino, 0, 1);
expect_getattr(ino, len);
expect_read(ino, 0, len, zeros);
expect_read(ino, 0, len, len, zeros);
/*
* Writes from the pager may or may not be associated with the correct
* pid, so they must set FUSE_WRITE_CACHE
@ -663,7 +560,7 @@ TEST_F(WriteBack, o_direct)
expect_open(ino, 0, 1);
expect_getattr(ino, 0);
expect_write(ino, 0, bufsize, bufsize, 0, CONTENTS);
expect_read(ino, 0, bufsize, CONTENTS);
expect_read(ino, 0, bufsize, bufsize, CONTENTS);
fd = open(FULLPATH, O_RDWR | O_DIRECT);
EXPECT_LE(0, fd) << strerror(errno);