1
0
mirror of https://github.com/falk-werner/webfuse synced 2026-03-02 03:40:24 +00:00

sepated unit and integration tests

This commit is contained in:
Falk Werner
2023-01-01 13:01:35 +01:00
parent dcb590bd50
commit 0e0bb74872
19 changed files with 34 additions and 19 deletions

View File

@@ -0,0 +1,51 @@
#include "webfuse/webfuse.hpp"
#include "webfuse/test/fixture.hpp"
#include "webfuse/test/filesystem_mock.hpp"
#include "webfuse/test/daemon.hpp"
#include <gtest/gtest.h>
#include <unistd.h>
using testing::_;
using testing::Return;
using testing::Invoke;
using testing::AnyNumber;
namespace
{
int fs_getattr (std::string const & path, struct stat * attr)
{
memset(reinterpret_cast<void*>(attr),0, sizeof(struct stat));
if (path == "/")
{
attr->st_nlink = 0;
attr->st_mode = S_IFDIR | 0755;
return 0;
}
else if (path == "/foo")
{
attr->st_nlink = 0;
attr->st_mode = S_IFREG | 0755;
return 0;
}
else
{
return -ENOENT;
}
}
}
TEST(access, ok_NO_MEMCHECK)
{
webfuse::filesystem_mock fs;
EXPECT_CALL(fs, access("/",_)).Times(AnyNumber()).WillRepeatedly(Return(0));
EXPECT_CALL(fs, access("/foo",_)).WillOnce(Return(0));
EXPECT_CALL(fs, getattr(_,_)).WillRepeatedly(Invoke(fs_getattr));
webfuse::fixture fixture(fs);
auto const path = fixture.get_path() + "/foo";
int const rc = ::access(path.c_str(), F_OK);
ASSERT_EQ(0, rc);
}

View File

@@ -0,0 +1,37 @@
#include "webfuse/test/daemon.hpp"
#include <limits.h>
#include <unistd.h>
#include <libgen.h>
#include <csignal>
namespace
{
std::string get_executable_path()
{
char directory[PATH_MAX] = { '\0' };
readlink("/proc/self/exe", directory, PATH_MAX);
dirname(directory);
return std::string(directory) + "/webfuse";
}
}
namespace webfuse
{
daemon::daemon(std::string const & mountpoint)
: p({get_executable_path(), "-f", mountpoint})
{
}
daemon::~daemon()
{
p.kill(SIGINT);
}
}

View File

@@ -0,0 +1,25 @@
#ifndef WEBFUSE_DAEMOM_HPP
#define WEBFUSE_DAEMOM_HPP
#include "webfuse/test/process.hpp"
#include <string>
namespace webfuse
{
class daemon
{
daemon(daemon const &) = delete;
daemon& operator=(daemon const &) = delete;
daemon(daemon &&) = delete;
daemon& operator=(daemon &&) = delete;
public:
explicit daemon(std::string const & mountpoint);
~daemon();
private:
process p;
};
}
#endif

View File

@@ -0,0 +1,47 @@
#ifndef WEBFUSE_FILESYSTEM_MOCK_HPP
#define WEBFUSE_FILESYSTEM_MOCK_HPP
#include "webfuse/filesystem/filesystem_i.hpp"
#include <gmock/gmock.h>
namespace webfuse
{
class filesystem_mock: public filesystem_i
{
public:
~filesystem_mock() override = default;
MOCK_METHOD(int, access, (std::string const & path, int mode));
MOCK_METHOD(int, getattr, (std::string const & path, struct stat * attr));
MOCK_METHOD(int, readlink, (std::string const & path, std::string & out));
MOCK_METHOD(int, symlink, (std::string const & target, std::string const & linkpath));
MOCK_METHOD(int, link, (std::string const & old_path, std::string const & new_path));
MOCK_METHOD(int, rename, (std::string const & old_path, std::string const & new_path, int flags));
MOCK_METHOD(int, chmod, (std::string const & path, mode_t mode));
MOCK_METHOD(int, chown, (std::string const & path, uid_t uid, gid_t gid));
MOCK_METHOD(int, truncate, (std::string const & path, uint64_t size, uint64_t handle));
MOCK_METHOD(int, fsync, (std::string const & path, bool is_datasync, uint64_t handle));
MOCK_METHOD(int, utimens, (std::string const &path, struct timespec tv[2], uint64_t handle));
MOCK_METHOD(int, open, (std::string const & path, int flags, uint64_t & handle));
MOCK_METHOD(int, mknod, (std::string const & path, mode_t mode, dev_t rdev));
MOCK_METHOD(int, create, (std::string const & path, mode_t mode, uint64_t & handle));
MOCK_METHOD(int, release, (std::string const & path, uint64_t handle));
MOCK_METHOD(int, unlink, (std::string const & path));
MOCK_METHOD(int, read, (std::string const & path, char * buffer, size_t buffer_size, uint64_t offset, uint64_t handle));
MOCK_METHOD(int, write, (std::string const & path, char const * buffer, size_t buffer_size, uint64_t offset, uint64_t handle));
MOCK_METHOD(int, mkdir, (std::string const & path, mode_t mode));
MOCK_METHOD(int, readdir, (std::string const & path, std::vector<std::string> & entries, uint64_t handle));
MOCK_METHOD(int, rmdir, (std::string const & path));
MOCK_METHOD(int, statfs, (std::string const & path, struct statvfs * statistics));
};
}
#endif

View File

@@ -0,0 +1,63 @@
#include "webfuse/test/fixture.hpp"
#include <csignal>
#include <cstring>
#include <chrono>
#include <iostream>
namespace webfuse
{
fixture::fixture(filesystem_i & fs)
: shutdown_requested(false)
, provider_running(false)
, fs_provider(fs)
, app(working_dir.name())
{
fs_provider.set_connection_listener([this](bool is_connected) {
if (is_connected)
{
this->provider_running = true;
}
if ((!is_connected) && (!this->provider_running))
{
this->reconnect();
}
});
provider_thread = std::thread(std::bind(&fixture::provider_run, this));
while (!provider_running)
{
std::this_thread::yield();
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
fixture::~fixture()
{
shutdown_requested = true;
fs_provider.interrupt();
provider_thread.join();
}
void fixture::provider_run()
{
fs_provider.connect("ws://localhost:8081/");
while (!shutdown_requested)
{
fs_provider.service();
}
}
void fixture::reconnect()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
fs_provider.connect("ws://localhost:8081/");
}
std::string const & fixture::get_path() const
{
return working_dir.name();
}
}

View File

@@ -0,0 +1,46 @@
#ifndef WEBFUSE_FIXTURE_HPP
#define WEBFUSE_FIXTURE_HPP
#include "webfuse/filesystem/filesystem_i.hpp"
#include "webfuse/webfuse.hpp"
#include "webfuse/provider.hpp"
#include "webfuse/test/tempdir.hpp"
#include "webfuse/test/daemon.hpp"
#include <string>
#include <memory>
#include <thread>
#include <atomic>
namespace webfuse
{
class fixture
{
fixture(fixture const &) = delete;
fixture& operator=(fixture const &) = delete;
fixture(fixture const &&) = delete;
fixture& operator=(fixture &&) = delete;
public:
explicit fixture(filesystem_i & fs);
~fixture();
std::string const & get_path() const;
void reconnect();
private:
void provider_run();
std::atomic<bool> shutdown_requested;
std::atomic<bool> provider_running;
provider fs_provider;
tempdir working_dir;
daemon app;
std::thread provider_thread;
};
}
#endif

View File

@@ -0,0 +1,93 @@
#include "webfuse/test/process.hpp"
#include <sys/wait.h>
#include <fcntl.h>
#include <csignal>
#include <cstring>
#include <cstdlib>
#include <stdexcept>
namespace webfuse
{
process::process(std::vector<std::string> const & commandline)
{
if (commandline.empty())
{
throw std::runtime_error("missing command");
}
pid = fork();
if (pid == 0)
{
size_t const count = commandline.size() + 1;
char ** args = reinterpret_cast<char**>(malloc(sizeof(char*) * count));
args[count - 1] = nullptr;
for(size_t i = 0; i < commandline.size(); i++)
{
args[i] = strdup(commandline[i].c_str());
}
closefrom(0);
open("/dev/null", O_RDONLY);
open("/dev/null", O_WRONLY);
dup2(STDOUT_FILENO, STDERR_FILENO);
execv(args[0], args);
// this should not be reached
for(size_t i = 0; i < count; i++)
{
free(args[i]);
}
free(args);
exit(EXIT_FAILURE);
}
else if (pid > 0)
{
// parent: do nothing
}
else
{
throw std::runtime_error("failed to fork");
}
}
process::~process()
{
if (pid > 0)
{
wait();
}
}
void process::kill(int signal_number)
{
if (pid > 0)
{
::kill(pid, signal_number);
}
}
int process::wait()
{
int exit_code = -1;
if (pid > 0)
{
int status = 0;
int rc = waitpid(pid, &status, 0);
if (rc == 0)
{
exit_code = WEXITSTATUS(status);
pid = 0;
}
}
return exit_code;
}
}

View File

@@ -0,0 +1,29 @@
#ifndef WEBFUSE_PROCESS_HPP
#define WEBFUSE_PROCESS_HPP
#include <unistd.h>
#include <string>
#include <vector>
namespace webfuse
{
class process
{
process(process const &) = delete;
process operator=(process const &) = delete;
process(process &&) = delete;
process operator=(process &&) = delete;
public:
process(std::vector<std::string> const & commandline);
~process();
void kill(int signal_number);
int wait();
private:
pid_t pid;
};
}
#endif

View File

@@ -0,0 +1,23 @@
#include "webfuse/test/tempdir.hpp"
#include <unistd.h>
namespace webfuse
{
tempdir::tempdir()
{
char path_template[] = "/tmp/webfuse_test_XXXXXX";
path = mkdtemp(path_template);
}
tempdir::~tempdir()
{
unlink(path.c_str());
}
std::string const & tempdir::name() const
{
return path;
}
}

View File

@@ -0,0 +1,22 @@
#ifndef WEBFUSE_TEMPDIR_HPP
#define WEBFUSE_TEMPDIR_HPP
#include <string>
namespace webfuse
{
class tempdir
{
public:
tempdir();
~tempdir();
std::string const & name() const;
private:
std::string path;
};
}
#endif