23 Commits

Author SHA1 Message Date
c960df8f70 refactor
Some checks failed
BlockStorage/repertory/pipeline/head There was a failure building this commit
2024-10-22 15:08:50 -05:00
9d0baf30b8 refactor 2024-10-22 15:07:15 -05:00
312e4bc0f1 fix 2024-10-22 15:05:54 -05:00
1d7f5b7ef1 fix 2024-10-22 15:04:38 -05:00
08e381a307 refactor 2024-10-22 15:01:04 -05:00
49e518ac19 refactor 2024-10-22 13:10:01 -05:00
8e2ebf05b8 refactor 2024-10-22 13:06:02 -05:00
924b79add2 refactor 2024-10-22 13:02:17 -05:00
857dcc5535 cleanup 2024-10-22 12:22:58 -05:00
7c0d583435 temp disable drive and provider unit tests 2024-10-22 11:53:24 -05:00
2b6a88f8cb continue refactor drive tests 2024-10-22 10:47:51 -05:00
fbf31c77ed fixes 2024-10-22 10:45:08 -05:00
9c2aa62f1f continue refactor drive tests 2024-10-22 10:36:39 -05:00
b6456abf0d continue refactor drive tests 2024-10-22 10:27:31 -05:00
5138b0d1ab refactor 2024-10-22 09:25:55 -05:00
0f60a5a467 continue refactor drive tests 2024-10-22 09:18:09 -05:00
982e5357a5 continue refactor drive tests 2024-10-22 09:14:33 -05:00
0ad0ff508b continue refactor drive tests 2024-10-22 08:18:53 -05:00
b4d61649cb refactor winfsp test 2024-10-22 08:00:16 -05:00
cdfbaa47b6 grammar 2024-10-21 11:57:38 -05:00
211805e460 updated CHANGELOG.md 2024-10-21 11:55:32 -05:00
841d57cf13 fuse permission fixes 2024-10-21 11:52:21 -05:00
bd25904371 fix 2024-10-21 08:30:26 -05:00
32 changed files with 896 additions and 848 deletions

View File

@ -170,6 +170,9 @@ renterd
richtext
rocksdb_library
rpcrt4
s_igid
s_isvtx
s_iuid
sddl_revision_1
secp256k1
secur32
@ -218,4 +221,4 @@ wsign-conversion
wunused
wuseless
wxwidgets_version
xattr
xattr

View File

@ -6,7 +6,7 @@ pipeline {
environment {
DEVELOPER_PRIVATE_KEY = "/.ci/repertory/cert/developer.priv"
DEVELOPER_PUBLIC_KEY = "/.ci/repertory/cert/developer.pub"
PROJECT_TEST_DIR = "/.ci/repertory/test"
PROJECT_TEST_CONFIG_DIR = "/.ci/repertory/test_config"
}
options {

View File

@ -11,13 +11,14 @@
* A single 64-bit Linux Jenkins server is used to build all Linux and Windows versions
* All dependency sources are now included
* MSVC is no longer supported
* MSYS2 is required for building Windows binaries Windows
* MSYS2 is required for building Windows binaries on Windows
* OS X support is temporarily disabled
* \#19 \[bug\] Rename file is broken for files that are existing
### Changes from v2.0.1-rc
* Corrected file times on S3 and Sia providers
* Corrected handling of `chown()` and `chmod()`
## v2.0.1-rc

View File

@ -3,8 +3,8 @@
CURRENT_DIR=$(dirname "$0")
CURRENT_DIR=$(realpath ${CURRENT_DIR})
rsync -av --progress ${CURRENT_DIR}/${PROJECT_NAME}/${PROJECT_NAME}_test/test_config/ \
${PROJECT_BUILD_DIR}/build/test_config/
rsync -av --progress ${CURRENT_DIR}/${PROJECT_NAME}/${PROJECT_NAME}_test/test_input/ \
${PROJECT_BUILD_DIR}/build/test_input/
rsync -av --progress ${CURRENT_DIR}/${PROJECT_NAME}/${PROJECT_NAME}_test/test_config/ \
${PROJECT_DIST_DIR}/test_config/
rsync -av --progress ${CURRENT_DIR}/${PROJECT_NAME}/${PROJECT_NAME}_test/test_input/ \
${PROJECT_DIST_DIR}/test_input/

View File

@ -67,6 +67,11 @@ auto fuse_drive::chmod_impl(std::string api_path, mode_t mode,
auto fuse_drive::chmod_impl(std::string api_path, mode_t mode) -> api_error {
#endif // FUSE_USE_VERSION >= 30
return check_and_perform(api_path, X_OK, [&](api_meta_map &) -> api_error {
if ((mode & (S_IGID | S_IUID | S_ISVTX) != 0) &&
(get_effective_uid() != 0)) {
return api_error::permission_denied;
}
return provider_.set_item_meta(api_path, META_MODE, std::to_string(mode));
});
}
@ -79,23 +84,32 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
-> api_error {
#endif
return check_and_perform(api_path, X_OK,
[&](api_meta_map &meta) -> api_error {
meta.clear();
if (uid != static_cast<uid_t>(-1)) {
meta[META_UID] = std::to_string(uid);
}
return check_and_perform(
api_path, X_OK, [&](api_meta_map &meta) -> api_error {
meta.clear();
if (uid != static_cast<uid_t>(-1)) {
if (get_effective_uid() != 0) {
return api_error::permission_denied;
}
if (gid != static_cast<gid_t>(-1)) {
meta[META_GID] = std::to_string(gid);
}
meta[META_UID] = std::to_string(uid);
}
if (not meta.empty()) {
return provider_.set_item_meta(api_path, meta);
}
if (gid != static_cast<gid_t>(-1)) {
if (get_effective_uid() != 0 &&
not utils::is_uid_member_of_group(get_effective_uid(), gid)) {
return api_error::permission_denied;
}
return api_error::success;
});
meta[META_GID] = std::to_string(gid);
}
if (meta.empty()) {
return api_error::success;
}
return provider_.set_item_meta(api_path, meta);
});
}
auto fuse_drive::create_impl(std::string api_path, mode_t mode,

View File

@ -43,8 +43,8 @@
#include "utils/time.hpp"
namespace {
[[nodiscard]] auto
create_resume_entry(const repertory::i_open_file &file) -> json {
[[nodiscard]] auto create_resume_entry(const repertory::i_open_file &file)
-> json {
return {
{"chunk_size", file.get_chunk_size()},
{"path", file.get_api_path()},
@ -338,7 +338,7 @@ auto file_manager::get_stored_downloads() const -> std::vector<json> {
auto result = utils::db::sqlite::db_select{*db_, resume_table}.go();
while (result.has_row()) {
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not result.get_row(row)) {
continue;
}
@ -375,7 +375,7 @@ auto file_manager::handle_file_rename(const std::string &from_api_path,
.where("api_path")
.equals(from_api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
should_upload = result.get_row(row) && row.has_value();
if (should_upload && source_path.empty()) {
source_path = row->get_column("source_path").get_value<std::string>();
@ -438,10 +438,11 @@ auto file_manager::open(const std::string &api_path, bool directory,
return open(api_path, directory, ofd, handle, file, nullptr);
}
auto file_manager::open(
const std::string &api_path, bool directory, const open_file_data &ofd,
std::uint64_t &handle, std::shared_ptr<i_open_file> &file,
std::shared_ptr<i_closeable_open_file> closeable_file) -> api_error {
auto file_manager::open(const std::string &api_path, bool directory,
const open_file_data &ofd, std::uint64_t &handle,
std::shared_ptr<i_open_file> &file,
std::shared_ptr<i_closeable_open_file> closeable_file)
-> api_error {
const auto create_and_add_handle =
[&](std::shared_ptr<i_closeable_open_file> cur_file) {
handle = get_next_handle();
@ -700,8 +701,8 @@ auto file_manager::rename_directory(const std::string &from_api_path,
}
auto file_manager::rename_file(const std::string &from_api_path,
const std::string &to_api_path,
bool overwrite) -> api_error {
const std::string &to_api_path, bool overwrite)
-> api_error {
if (not provider_.is_rename_supported()) {
return api_error::not_implemented;
}
@ -800,7 +801,7 @@ void file_manager::start() {
auto result = utils::db::sqlite::db_select{*db_, upload_active_table}.go();
while (result.has_row()) {
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
active_items.emplace_back(active_item{
row->get_column("api_path").get_value<std::string>(),
@ -824,7 +825,7 @@ void file_manager::start() {
while (result.has_row()) {
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return;
}
@ -1039,7 +1040,7 @@ void file_manager::upload_handler() {
.limit(1)
.go();
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
auto api_path = row->get_column("api_path").get_value<std::string>();
auto source_path =

View File

@ -69,9 +69,10 @@ const std::map<std::string, std::string> sql_create_tables = {
namespace repertory {
encrypt_provider::encrypt_provider(app_config &config) : config_(config) {}
auto encrypt_provider::create_api_file(
const std::string &api_path, bool directory,
const std::string &source_path) -> api_file {
auto encrypt_provider::create_api_file(const std::string &api_path,
bool directory,
const std::string &source_path)
-> api_file {
auto times = utils::file::get_times(source_path);
if (not times.has_value()) {
throw std::runtime_error("failed to get file times");
@ -97,10 +98,10 @@ auto encrypt_provider::create_api_file(
void encrypt_provider::create_item_meta(api_meta_map &meta, bool directory,
const api_file &file) {
#if defined(_WIN32)
struct _stat64 buf {};
struct _stat64 buf{};
_stat64(file.source_path.c_str(), &buf);
#else // !defined(_WIN32)
struct stat buf {};
struct stat buf{};
stat(file.source_path.c_str(), &buf);
#endif // defined(_WIN32)
@ -187,8 +188,9 @@ auto encrypt_provider::do_fs_operation(
return callback(cfg, source_path);
}
auto encrypt_provider::get_api_path_from_source(
const std::string &source_path, std::string &api_path) const -> api_error {
auto encrypt_provider::get_api_path_from_source(const std::string &source_path,
std::string &api_path) const
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
try {
@ -197,7 +199,7 @@ auto encrypt_provider::get_api_path_from_source(
.where("source_path")
.equals(source_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
api_path = row->get_column("data")
.get_value_as_json()
@ -253,8 +255,9 @@ auto encrypt_provider::get_directory_item_count(
return count;
}
auto encrypt_provider::get_directory_items(
const std::string &api_path, directory_item_list &list) const -> api_error {
auto encrypt_provider::get_directory_items(const std::string &api_path,
directory_item_list &list) const
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
return do_fs_operation(
@ -273,7 +276,7 @@ auto encrypt_provider::get_directory_items(
.where("source_path")
.equals(dir_entry->get_path())
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
current_api_path =
row->get_column("api_path").get_value<std::string>();
@ -302,7 +305,7 @@ auto encrypt_provider::get_directory_items(
.where("source_path")
.equals(dir_entry->get_path())
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
api_path_data =
row->get_column("data").get_value<std::string>();
@ -385,7 +388,7 @@ auto encrypt_provider::get_file(const std::string &api_path,
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return api_error::item_not_found;
}
@ -427,8 +430,9 @@ auto encrypt_provider::get_file_list(api_file_list &list) const -> api_error {
return api_error::error;
}
auto encrypt_provider::get_file_size(
const std::string &api_path, std::uint64_t &file_size) const -> api_error {
auto encrypt_provider::get_file_size(const std::string &api_path,
std::uint64_t &file_size) const
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
try {
@ -437,7 +441,7 @@ auto encrypt_provider::get_file_size(
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return api_error::item_not_found;
}
@ -454,15 +458,16 @@ auto encrypt_provider::get_file_size(
return api_error::error;
}
auto encrypt_provider::get_filesystem_item(
const std::string &api_path, bool directory,
filesystem_item &fsi) const -> api_error {
auto encrypt_provider::get_filesystem_item(const std::string &api_path,
bool directory,
filesystem_item &fsi) const
-> api_error {
auto result = utils::db::sqlite::db_select{*db_, source_table}
.column("source_path")
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return api_error::item_not_found;
}
@ -532,9 +537,10 @@ auto encrypt_provider::get_filesystem_item_from_source_path(
return get_filesystem_item(api_path, false, fsi);
}
auto encrypt_provider::get_filesystem_item_and_file(
const std::string &api_path, api_file &file,
filesystem_item &fsi) const -> api_error {
auto encrypt_provider::get_filesystem_item_and_file(const std::string &api_path,
api_file &file,
filesystem_item &fsi) const
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
try {
@ -576,7 +582,7 @@ auto encrypt_provider::get_item_meta(const std::string &api_path,
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return api_error::item_not_found;
}
@ -624,7 +630,7 @@ auto encrypt_provider::get_total_item_count() const -> std::uint64_t {
.count("api_path", "count")
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
return static_cast<std::uint64_t>(
row->get_column("count").get_value<std::int64_t>());
@ -647,7 +653,7 @@ auto encrypt_provider::is_directory(const std::string &api_path,
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
exists = false;
return api_error::success;
@ -661,14 +667,14 @@ auto encrypt_provider::is_directory(const std::string &api_path,
return api_error::success;
}
auto encrypt_provider::is_file(const std::string &api_path,
bool &exists) const -> api_error {
auto encrypt_provider::is_file(const std::string &api_path, bool &exists) const
-> api_error {
auto result = utils::db::sqlite::db_select{*db_, source_table}
.column("source_path")
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
exists = false;
return api_error::success;
@ -730,7 +736,7 @@ auto encrypt_provider::process_directory_entry(
.where("source_path")
.equals(current_source_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
current_api_path = row->get_column("api_path").get_value<std::string>();
}
@ -780,7 +786,7 @@ auto encrypt_provider::process_directory_entry(
.where("source_path")
.equals(dir_entry.get_path())
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
api_path_data = row->get_column("data").get_value<std::string>();
}
@ -854,7 +860,7 @@ auto encrypt_provider::read_file_bytes(const std::string &api_path,
.where("api_path")
.equals(api_path)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (not(result.get_row(row) && row.has_value())) {
return api_error::item_not_found;
}
@ -958,11 +964,11 @@ void encrypt_provider::remove_deleted_files() {
};
std::vector<removed_item> removed_list{};
std::vector<utils::db::sqlite::db_select::row> row_list{};
std::vector<utils::db::sqlite::db_result::row> row_list{};
auto result = utils::db::sqlite::db_select{*db_, source_table}.go();
while (result.has_row()) {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
row_list.push_back(row.value());
}
@ -1037,7 +1043,7 @@ auto encrypt_provider::start(api_item_added_callback /*api_item_added*/,
.where("api_path")
.equals("/")
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
source_path = row->get_column("source_path").get_value<std::string>();
} else {

View File

@ -66,7 +66,7 @@ auto meta_db::get_api_path(const std::string &source_path,
.limit(1)
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
api_path = row->get_column("api_path").get_value<std::string>();
return api_error::success;
@ -81,7 +81,7 @@ auto meta_db::get_api_path_list() -> std::vector<std::string> {
auto result =
utils::db::sqlite::db_select{*db_, table_name}.column("api_path").go();
while (result.has_row()) {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
ret.push_back(row->get_column("api_path").get_value<std::string>());
}
@ -90,8 +90,8 @@ auto meta_db::get_api_path_list() -> std::vector<std::string> {
return ret;
}
auto meta_db::get_item_meta(const std::string &api_path,
api_meta_map &meta) -> api_error {
auto meta_db::get_item_meta(const std::string &api_path, api_meta_map &meta)
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
auto result = utils::db::sqlite::db_select{*db_, table_name}
@ -106,7 +106,7 @@ auto meta_db::get_item_meta(const std::string &api_path,
}
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
meta = json::parse(row->get_column("data").get_value<std::string>())
.get<api_meta_map>();
@ -145,7 +145,7 @@ auto meta_db::get_item_meta(const std::string &api_path, const std::string &key,
}
try {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
value =
key == META_SOURCE
@ -183,7 +183,7 @@ auto meta_db::get_pinned_files() const -> std::vector<std::string> {
.equals(1)
.go();
while (result.has_row()) {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
ret.emplace_back(row->get_column("api_path").get_value<std::string>());
}
@ -205,7 +205,7 @@ auto meta_db::get_total_item_count() const -> std::uint64_t {
.count("api_path", "count")
.go();
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
if (result.get_row(row) && row.has_value()) {
ret = static_cast<std::uint64_t>(
row->get_column("count").get_value<std::int64_t>());
@ -274,8 +274,8 @@ auto meta_db::set_item_meta(const std::string &api_path,
return update_item_meta(api_path, existing_meta);
}
auto meta_db::update_item_meta(const std::string &api_path,
api_meta_map meta) -> api_error {
auto meta_db::update_item_meta(const std::string &api_path, api_meta_map meta)
-> api_error {
REPERTORY_USES_FUNCTION_NAME();
auto directory = utils::string::to_bool(meta[META_DIRECTORY]);

View File

@ -32,16 +32,22 @@
#include "providers/encrypt/encrypt_provider.hpp"
#include "providers/s3/s3_provider.hpp"
#include "providers/sia/sia_provider.hpp"
#include "types/repertory.hpp"
#include "utils/event_capture.hpp"
#include "utils/file_utils.hpp"
#include "utils/path.hpp"
#include "utils/utils.hpp"
#if !defined(ACCESSPERMS)
#define ACCESSPERMS (S_IRWXU | S_IRWXG | S_IRWXO) /* 0777 */
#endif
namespace repertory {
inline constexpr const auto SLEEP_SECONDS{1.5s};
namespace {
std::atomic<std::size_t> idx{0U};
constexpr const auto SLEEP_SECONDS{1.5s};
} // namespace
namespace repertory {
template <typename provider_t> class fuse_test : public ::testing::Test {
public:
static std::string cfg_directory;
@ -49,7 +55,6 @@ public:
static std::unique_ptr<app_config> config;
static std::filesystem::path current_directory;
static std::unique_ptr<fuse_drive> drive;
static lock_data lock_data_;
static std::string mount_location;
static std::unique_ptr<i_provider> provider;
static std::string test_directory;
@ -81,7 +86,7 @@ protected:
{
app_config src_cfg{
provider_type::s3,
utils::path::combine(test::get_test_input_dir(), {"storj"}),
utils::path::combine(test::get_test_config_dir(), {"storj"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
@ -100,7 +105,7 @@ protected:
{
app_config src_cfg{
provider_type::sia,
utils::path::combine(test::get_test_input_dir(), {"sia"}),
utils::path::combine(test::get_test_config_dir(), {"sia"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::debug);
@ -146,8 +151,9 @@ protected:
}
public:
static auto create_file_and_test(std::string name) -> std::string {
auto file_path = utils::path::combine(mount_location, {name});
static auto create_file_and_test(std::string &file_name) -> std::string {
file_name += std::to_string(++idx);
auto file_path = utils::path::combine(mount_location, {file_name});
auto fd =
open(file_path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP);
@ -166,40 +172,62 @@ public:
return file_path;
}
static auto create_root_file(std::string &file_name) -> std::string {
auto file_path = create_file_and_test(file_name);
auto api_path = utils::path::create_api_path(file_name);
provider->set_item_meta(api_path, {
{META_UID, "0"},
{META_GID, "0"},
});
return file_path;
}
static void execute_mount(auto &&drive_args) {
auto mount_cmd = "./repertory -dd \"" + config->get_data_directory() +
"\"" + " " + utils::string::join(drive_args, ' ');
std::cout << "mount command: " << mount_cmd << std::endl;
ASSERT_EQ(0, system(mount_cmd.c_str()));
std::this_thread::sleep_for(5s);
EXPECT_EQ(0, system(("mount|grep \"" + mount_location + "\"").c_str()));
ASSERT_TRUE(utils::file::directory{mount_location}.exists());
}
static void execute_unmount() {
auto unmounted{false};
auto unmount_cmd =
"./repertory -dd \"" + config->get_data_directory() + "\" -unmount";
for (int i = 0; not unmounted && (i < 50); i++) {
unmounted = (fuse_base::unmount(mount_location) == 0);
std::cout << "unmount command: " << unmount_cmd << std::endl;
ASSERT_EQ(0, system(unmount_cmd.c_str()));
unmounted = not utils::file::directory{mount_location}.exists();
if (not unmounted) {
std::this_thread::sleep_for(100ms);
std::this_thread::sleep_for(5s);
}
}
EXPECT_TRUE(unmounted);
}
static void unlink_file_and_test(const std::string &file_path) {
int ret = 0;
for (auto i = 0; ((ret = unlink(file_path.c_str())) != 0) && (i < 20);
i++) {
std::this_thread::sleep_for(100ms);
}
EXPECT_EQ(0, ret);
std::this_thread::sleep_for(SLEEP_SECONDS);
static void unlink_file_and_test(std::string_view file_path) {
EXPECT_TRUE(utils::file::file(file_path).remove());
EXPECT_FALSE(utils::file::file(file_path).exists());
EXPECT_FALSE(utils::file::directory(file_path).exists());
EXPECT_FALSE(utils::file::file(file_path).exists());
}
static void unlink_root_file(const std::string &file_path) {
auto api_path =
utils::path::create_api_path(utils::path::strip_to_filename(file_path));
provider->set_item_meta(api_path, {
{META_UID, std::to_string(getuid())},
{META_GID, std::to_string(getgid())},
});
unlink_file_and_test(file_path);
}
};
template <typename provider_t>
@ -217,8 +245,6 @@ std::filesystem::path fuse_test<provider_t>::current_directory{};
template <typename provider_t>
std::unique_ptr<fuse_drive> fuse_test<provider_t>::drive{};
template <typename provider_t> lock_data fuse_test<provider_t>::lock_data_{};
template <typename provider_t>
std::string fuse_test<provider_t>::mount_location{};
@ -228,7 +254,7 @@ std::unique_ptr<i_provider> fuse_test<provider_t>::provider{};
template <typename provider_t>
std::string fuse_test<provider_t>::test_directory;
typedef ::testing::Types<s3_provider, sia_provider> fuse_provider_types;
using fuse_provider_types = ::testing::Types<s3_provider, sia_provider>;
} // namespace repertory
#endif // !defined(_WIN32)

View File

@ -21,6 +21,7 @@
*/
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP
#define REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP
#if 0
#if defined(_WIN32)
#include "test_common.hpp"
@ -29,135 +30,222 @@
#include "comm/curl/curl_comm.hpp"
#include "drives/winfsp/winfsp_drive.hpp"
#include "platform/platform.hpp"
#include "providers/i_provider.hpp"
#include "providers/s3/s3_provider.hpp"
#include "providers/sia/sia_provider.hpp"
#include "types/repertory.hpp"
#include "utils/file_utils.hpp"
#include "utils/path.hpp"
extern std::size_t PROVIDER_INDEX;
namespace {
std::atomic<std::size_t> idx{0U};
constexpr const auto SLEEP_SECONDS{1.5s};
} // namespace
namespace repertory {
class winfsp_test : public ::testing::Test {
template <typename provider_t> class winfsp_test : public ::testing::Test {
public:
lock_data lock_data_;
std::unique_ptr<app_config> config;
std::unique_ptr<curl_comm> comm;
std::unique_ptr<i_provider> provider;
std::unique_ptr<winfsp_drive> drive;
static std::string cfg_directory;
static std::unique_ptr<curl_comm> comm;
static std::unique_ptr<app_config> config;
static std::filesystem::path current_directory;
static std::unique_ptr<winfsp_drive> drive;
static std::string mount_location;
static std::unique_ptr<i_provider> provider;
static std::string test_directory;
protected:
void SetUp() override {
if (PROVIDER_INDEX != 0) {
if (PROVIDER_INDEX == 1) {
EXPECT_TRUE(utils::file::directory(
utils::path::combine(
test::get_test_output_dir(),
{"winfsp_test" + std::to_string(PROVIDER_INDEX)}))
.remove_recursively());
static void SetUpTestCase() {
current_directory = std::filesystem::current_path();
app_config src_cfg(
test_directory = utils::path::combine(
test::get_test_output_dir(),
{
"winfsp_test",
std::to_string(static_cast<std::uint8_t>(provider_t::type)),
});
mount_location = "U:";
cfg_directory = utils::path::combine(test_directory, {"cfg"});
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
config = std::make_unique<app_config>(provider_t::type, cfg_directory);
std::vector<std::string> drive_args{};
switch (provider_t::type) {
case provider_type::s3: {
{
app_config src_cfg{
provider_type::s3,
utils::path::combine(test::get_test_input_dir(), {"storj"}));
config = std::make_unique<app_config>(
provider_type::s3,
utils::path::combine(
test::get_test_output_dir(),
{"winfsp_test" + std::to_string(PROVIDER_INDEX)}));
EXPECT_FALSE(config
->set_value_by_name("S3Config.AccessKey",
src_cfg.get_s3_config().access_key)
.empty());
EXPECT_FALSE(config
->set_value_by_name("S3Config.SecretKey",
src_cfg.get_s3_config().secret_key)
.empty());
EXPECT_FALSE(config
->set_value_by_name("S3Config.Region",
src_cfg.get_s3_config().region)
.empty());
EXPECT_FALSE(
config
->set_value_by_name("S3Config.EncryptionToken",
src_cfg.get_s3_config().encryption_token)
.empty());
EXPECT_FALSE(
config
->set_value_by_name("S3Config.URL", src_cfg.get_s3_config().url)
.empty());
EXPECT_FALSE(
config->set_value_by_name("S3Config.Bucket", "repertory").empty());
utils::path::combine(test::get_test_config_dir(), {"storj"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::trace);
config->set_enable_drive_events(true);
event_system::instance().start();
comm = std::make_unique<curl_comm>(config->get_s3_config());
provider = std::make_unique<s3_provider>(*config, *comm);
drive = std::make_unique<winfsp_drive>(*config, lock_data_, *provider);
return;
config->set_s3_config(src_cfg.get_s3_config());
}
if (PROVIDER_INDEX == 2) {
EXPECT_TRUE(utils::file::directory(
utils::path::combine(
test::get_test_output_dir(),
{"winfsp_test" + std::to_string(PROVIDER_INDEX)}))
.remove_recursively());
comm = std::make_unique<curl_comm>(config->get_s3_config());
drive_args = std::vector<std::string>({
"-s3",
"-na",
"storj",
});
} break;
app_config src_cfg(
case provider_type::sia: {
{
app_config src_cfg{
provider_type::sia,
utils::path::combine(test::get_test_input_dir(), {"sia"}));
config = std::make_unique<app_config>(
provider_type::sia,
utils::path::combine(
test::get_test_output_dir(),
{"winfsp_test" + std::to_string(PROVIDER_INDEX)}));
[[maybe_unused]] auto val = config->set_value_by_name(
"HostConfig.AgentString", src_cfg.get_host_config().agent_string);
EXPECT_FALSE(
config
->set_value_by_name("HostConfig.ApiPassword",
src_cfg.get_host_config().api_password)
.empty());
EXPECT_FALSE(config
->set_value_by_name(
"HostConfig.ApiPort",
std::to_string(src_cfg.get_host_config().api_port))
.empty());
EXPECT_FALSE(
config
->set_value_by_name("HostConfig.HostNameOrIp",
src_cfg.get_host_config().host_name_or_ip)
.empty());
utils::path::combine(test::get_test_config_dir(), {"sia"}),
};
config->set_enable_drive_events(true);
config->set_event_level(event_level::debug);
config->set_enable_drive_events(true);
event_system::instance().start();
comm = std::make_unique<curl_comm>(config->get_host_config());
provider = std::make_unique<sia_provider>(*config, *comm);
drive = std::make_unique<winfsp_drive>(*config, lock_data_, *provider);
return;
config->set_host_config(src_cfg.get_host_config());
}
comm = std::make_unique<curl_comm>(config->get_host_config());
} break;
// case 0U: {
// config =
// std::make_unique<app_config>(provider_type::encrypt,
// cfg_directory);
// {
// app_config src_cfg(
// provider_type::s3,
// utils::path::combine(test::get_test_input_dir(), {"encrypt"}));
// config->set_enable_drive_events(true);
// config->set_event_level(event_level::trace);
// config->set_s3_config(src_cfg.get_s3_config());
// }
//
// comm = std::make_unique<curl_comm>(config->get_s3_config());
// provider = std::make_unique<s3_provider>(*config, *comm);
// drive_args = std::vector<std::string>({"-en"});
// } break;
default:
throw std::runtime_error("provider type is not implemented");
return;
}
provider = std::make_unique<provider_t>(*config, *comm);
drive_args.push_back(mount_location);
}
void TearDown() override {
if (PROVIDER_INDEX != 0) {
drive.reset();
provider.reset();
comm.reset();
config.reset();
static void TearDownTestCase() {
execute_unmount();
std::filesystem::current_path(current_directory);
[[maybe_unused]] auto ret =
utils::file::directory(test_directory).remove_recursively();
}
event_system::instance().stop();
EXPECT_TRUE(utils::file::directory(
utils::path::combine(
test::get_test_output_dir(),
{"winfsp_test" + std::to_string(PROVIDER_INDEX)}))
.remove_recursively());
public:
[[nodiscard]] static auto create_directory_and_test(std::string &dir_name)
-> std::string {
dir_name += std::to_string(++idx);
auto api_path = utils::path::create_api_path(dir_name);
auto dir_path = utils::path::combine(mount_location, {dir_name});
EXPECT_FALSE(::PathIsDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
EXPECT_TRUE(::PathIsDirectoryA(dir_path.c_str()));
return dir_path;
}
[[nodiscard]] static auto create_file_and_test(std::string &file_name)
-> std::string {
file_name += std::to_string(++idx);
auto api_path = utils::path::create_api_path(file_name);
auto file_path = utils::path::combine(mount_location, {file_name});
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
EXPECT_TRUE(utils::file::file(file_path).exists());
auto opt_size = utils::file::file(file_path).size();
EXPECT_TRUE(opt_size.has_value());
EXPECT_EQ(0, opt_size.value());
std::string attr;
EXPECT_EQ(api_error::success,
provider->get_item_meta(api_path, META_ATTRIBUTES, attr));
EXPECT_EQ(FILE_ATTRIBUTE_NORMAL, utils::string::to_uint32(attr));
return file_path;
}
static void delete_directory_and_test(const std::string &dir_path) {
EXPECT_TRUE(::PathIsDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
EXPECT_FALSE(::PathIsDirectoryA(dir_path.c_str()));
}
static void delete_file_and_test(std::string_view file_path) {
EXPECT_TRUE(utils::file::file(file_path).remove());
EXPECT_FALSE(utils::file::file(file_path).exists());
EXPECT_FALSE(utils::file::directory(file_path).exists());
EXPECT_FALSE(utils::file::file(file_path).exists());
}
static void execute_mount(auto &&drive_args) {
auto mount_cmd = ".\\repertory.exe -dd \"" + config->get_data_directory() +
"\"" + " " + utils::string::join(drive_args, ' ');
std::cout << "mount command: " << mount_cmd << std::endl;
ASSERT_EQ(0, system(mount_cmd.c_str()));
std::this_thread::sleep_for(5s);
ASSERT_TRUE(utils::file::directory{mount_location}.exists());
}
static void execute_unmount() {
auto unmounted{false};
auto unmount_cmd = ".\\repertory.exe -dd \"" +
config->get_data_directory() + "\" -unmount";
for (int i = 0; not unmounted && (i < 50); i++) {
std::cout << "unmount command: " << unmount_cmd << std::endl;
ASSERT_EQ(0, system(unmount_cmd.c_str()));
unmounted = not utils::file::directory{mount_location}.exists();
if (not unmounted) {
std::this_thread::sleep_for(5s);
}
}
EXPECT_TRUE(unmounted);
}
};
template <typename provider_t>
std::string winfsp_test<provider_t>::cfg_directory;
template <typename provider_t>
std::unique_ptr<app_config> winfsp_test<provider_t>::config;
template <typename provider_t>
std::filesystem::path winfsp_test<provider_t>::current_directory;
template <typename provider_t>
std::unique_ptr<curl_comm> winfsp_test<provider_t>::comm;
template <typename provider_t>
std::string winfsp_test<provider_t>::mount_location;
template <typename provider_t>
std::unique_ptr<i_provider> winfsp_test<provider_t>::provider;
template <typename provider_t>
std::string winfsp_test<provider_t>::test_directory;
template <typename provider_t>
std::unique_ptr<winfsp_drive> winfsp_test<provider_t>::drive;
using winfsp_provider_types = ::testing::Types<s3_provider, sia_provider>;
} // namespace repertory
#endif
#endif // defined(_WIN32)
#endif // 0
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP

View File

@ -39,11 +39,12 @@ public:
: mount_location_(std::move(mount_location)) {}
private:
const std::string mount_location_;
std::string mount_location_;
public:
[[nodiscard]] auto get_directory_item_count(
const std::string & /*api_path*/) const -> std::uint64_t override {
[[nodiscard]] auto
get_directory_item_count(const std::string & /*api_path*/) const
-> std::uint64_t override {
return 1;
}
@ -54,13 +55,12 @@ public:
directory_item di{};
di.api_path = ".";
di.directory = true;
di.size = 0u;
di.meta = {
{META_ATTRIBUTES, "16"},
{META_MODIFIED, std::to_string(utils::time::get_time_now())},
{META_WRITTEN, std::to_string(utils::time::get_time_now())},
{META_ACCESSED, std::to_string(utils::time::get_time_now())},
{META_CREATION, std::to_string(utils::time::get_time_now())}};
di.size = 0U;
di.meta = {{META_ATTRIBUTES, "16"},
{META_MODIFIED, std::to_string(utils::time::get_time_now())},
{META_WRITTEN, std::to_string(utils::time::get_time_now())},
{META_ACCESSED, std::to_string(utils::time::get_time_now())},
{META_CREATION, std::to_string(utils::time::get_time_now())}};
list.emplace_back(di);
di.api_path = "..";
@ -75,7 +75,7 @@ public:
}
auto get_item_meta(const std::string & /*api_path*/,
api_meta_map &meta) const -> api_error override {
api_meta_map & /* meta */) const -> api_error override {
return api_error::error;
}
@ -85,19 +85,19 @@ public:
return api_error::error;
}
auto
get_security_by_name(PWSTR /*file_name*/, PUINT32 attributes,
PSECURITY_DESCRIPTOR descriptor,
std::uint64_t *descriptor_size) -> NTSTATUS override {
auto get_security_by_name(PWSTR /*file_name*/, PUINT32 attributes,
PSECURITY_DESCRIPTOR descriptor,
std::uint64_t *descriptor_size)
-> NTSTATUS override {
auto ret = STATUS_SUCCESS;
if (attributes) {
if (attributes != nullptr) {
*attributes = FILE_ATTRIBUTE_NORMAL;
}
if (descriptor_size) {
ULONG sz = 0;
PSECURITY_DESCRIPTOR sd = nullptr;
ULONG sz{0U};
PSECURITY_DESCRIPTOR sd{nullptr};
if (::ConvertStringSecurityDescriptorToSecurityDescriptor(
"O:BAG:BAD:P(A;;FA;;;SY)(A;;FA;;;BA)(A;;FA;;;WD)",
SDDL_REVISION_1, &sd, &sz)) {
@ -117,36 +117,38 @@ public:
}
[[nodiscard]] auto get_total_drive_space() const -> std::uint64_t override {
return 100 * 1024 * 1024;
return 100ULL * 1024ULL * 1024ULL;
}
[[nodiscard]] auto get_total_item_count() const -> std::uint64_t override {
return 0;
return 0U;
}
[[nodiscard]] auto get_used_drive_space() const -> std::uint64_t override {
return 0;
return 0U;
}
void get_volume_info(UINT64 &total_size, UINT64 &free_size,
std::string &volume_label) const override {
free_size = 100;
total_size = 200;
free_size = 100U;
total_size = 200U;
volume_label = "TestVolumeLabel";
}
auto populate_file_info(const std::string &api_path,
remote::file_info &file_info) -> api_error override {
const auto file_path = utils::path::combine(mount_location_, {api_path});
const auto directory = utils::file::directory(file_path).exists();
const auto attributes =
auto file_path = utils::path::combine(mount_location_, {api_path});
auto directory = utils::file::directory(file_path).exists();
auto attributes =
FILE_FLAG_BACKUP_SEMANTICS |
(directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL);
const auto share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
auto handle = ::CreateFileA(&file_path[0], GENERIC_READ, share_mode,
nullptr, OPEN_EXISTING, attributes, nullptr);
FILE_BASIC_INFO fi{};
::GetFileInformationByHandleEx(handle, FileBasicInfo, &fi, sizeof(fi));
auto share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE;
auto handle = ::CreateFileA(
file_path.c_str(), GENERIC_READ, static_cast<DWORD>(share_mode),
nullptr, OPEN_EXISTING, static_cast<DWORD>(attributes), nullptr);
FILE_BASIC_INFO basic_info{};
::GetFileInformationByHandleEx(handle, FileBasicInfo, &basic_info,
sizeof(basic_info));
if (not directory) {
auto opt_size = utils::file::file{file_path}.size();
if (not opt_size.has_value()) {
@ -161,11 +163,14 @@ public:
: utils::divide_with_ceiling(file_info.FileSize,
WINFSP_ALLOCATION_UNIT) *
WINFSP_ALLOCATION_UNIT;
file_info.FileAttributes = fi.FileAttributes;
file_info.ChangeTime = fi.ChangeTime.QuadPart;
file_info.CreationTime = fi.CreationTime.QuadPart;
file_info.LastAccessTime = fi.LastAccessTime.QuadPart;
file_info.LastWriteTime = fi.LastWriteTime.QuadPart;
file_info.FileAttributes = basic_info.FileAttributes;
file_info.ChangeTime = static_cast<UINT64>(basic_info.ChangeTime.QuadPart);
file_info.CreationTime =
static_cast<UINT64>(basic_info.CreationTime.QuadPart);
file_info.LastAccessTime =
static_cast<UINT64>(basic_info.LastAccessTime.QuadPart);
file_info.LastWriteTime =
static_cast<UINT64>(basic_info.LastWriteTime.QuadPart);
::CloseHandle(handle);
return api_error::success;
}

View File

@ -25,16 +25,9 @@
#include "initialize.hpp"
#include "test_common.hpp"
#if defined(_WIN32)
#include "utils/cli_utils.hpp"
#endif // defined(_WIN32)
using namespace repertory;
#if defined(_WIN32)
std::size_t PROVIDER_INDEX{0U};
#endif // defined(_WIN32)
auto main(int argc, char **argv) -> int {
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
static backward::SignalHandling sh;
@ -45,21 +38,6 @@ auto main(int argc, char **argv) -> int {
return -1;
}
#if defined(_WIN32)
std::vector<const char *> args;
{
auto args_span = std::span(argv, static_cast<std::size_t>(argc));
std::copy(args_span.begin(), args_span.end(), std::back_inserter(args));
}
if (utils::cli::has_option(args, "--provider_index")) {
PROVIDER_INDEX = static_cast<std::size_t>(
utils::string::to_uint64(
utils::cli::parse_option(args, "--provider_index", 1U)[0U]) +
1U);
}
#endif // defined(_WIN32)
::testing::InitGoogleTest(&argc, argv);
auto ret = RUN_ALL_TESTS();

View File

@ -19,22 +19,10 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if 0
#if !defined(_WIN32)
#include "fixtures/fuse_fixture.hpp"
// #include "app_config.hpp"
// #include "comm/curl/curl_comm.hpp"
// #include "drives/fuse/fuse_drive.hpp"
// #include "platform/platform.hpp"
// #include "providers/s3/s3_provider.hpp"
// #include "providers/sia/sia_provider.hpp"
// #include "types/repertory.hpp"
// #include "utils/event_capture.hpp"
// #include "utils/file_utils.hpp"
// #include "utils/utils.hpp"
namespace repertory {
// static void rmdir_and_test(const std::string &directory_path) {
// std::cout << __FUNCTION__ << std::endl;
@ -529,36 +517,133 @@ namespace repertory {
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
TYPED_TEST(fuse_test, chmod) {
auto file_path = this->create_file_and_test("chmod_test");
TYPED_TEST(fuse_test, can_chmod_if_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(0, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st {};
struct stat64 unix_st{};
stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR),
ACCESSPERMS & unix_st.st_mode);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, chown) {
auto file_path = this->create_file_and_test("chown_test");
EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
TYPED_TEST(fuse_test, can_not_chmod_if_not_owner) {
std::string file_name{"chmod_test"};
auto file_path = this->create_root_file(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR));
EXPECT_EQ(EPERM, errno);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setgid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_IGID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_setuid_if_not_root) {
std::string file_name{"chmod_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_IUID));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chmod_set_sticky_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
EXPECT_EQ(-1, chmod(file_path.c_str(), S_IRUSR | S_IWUSR | S_ISVTX));
EXPECT_EQ(EPERM, errno);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_chown_group_if_owner_and_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(0, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
std::this_thread::sleep_for(SLEEP_SECONDS);
struct stat64 unix_st {};
stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(0U, unix_st.st_gid);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(getgid(), unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
EXPECT_EQ(0, chown(file_path.c_str(), 0, static_cast<gid_t>(-1)));
std::this_thread::sleep_for(SLEEP_SECONDS);
this->unlink_file_and_test(file_path);
}
stat64(file_path.c_str(), &unix_st);
EXPECT_EQ(0U, unix_st.st_gid);
TYPED_TEST(fuse_test,
can_not_chown_group_if_owner_but_not_a_member_of_the_group) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), 0));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_group_if_not_the_owner) {
std::string file_name{"chown_test"};
auto file_path = this->create_root_file(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), static_cast<uid_t>(-1), getgid()));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_root_file(file_path);
}
TYPED_TEST(fuse_test, can_not_chown_user_if_not_root) {
std::string file_name{"chown_test"};
auto file_path = this->create_file_and_test(file_name);
struct stat64 unix_st{};
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
EXPECT_EQ(-1, chown(file_path.c_str(), 0, static_cast<gid_t>(-1)));
EXPECT_EQ(EPERM, errno);
struct stat64 unix_st2{};
stat64(file_path.c_str(), &unix_st2);
EXPECT_EQ(unix_st.st_gid, unix_st2.st_gid);
EXPECT_EQ(unix_st.st_uid, unix_st2.st_uid);
this->unlink_file_and_test(file_path);
}
} // namespace repertory
#endif // !defined(_WIN32)
#endif // 0

View File

@ -683,7 +683,7 @@ TEST(providers, s3_provider) {
{
app_config src_cfg(
provider_type::s3,
utils::path::combine(test::get_test_input_dir(), {"storj"}));
utils::path::combine(test::get_test_config_dir(), {"storj"}));
cfg.set_s3_config(src_cfg.get_s3_config());
}
@ -722,7 +722,7 @@ TEST(providers, sia_provider) {
{
app_config src_cfg(
provider_type::sia,
utils::path::combine(test::get_test_input_dir(), {"sia"}));
utils::path::combine(test::get_test_config_dir(), {"sia"}));
cfg.set_host_config(src_cfg.get_host_config());
}

View File

@ -436,15 +436,17 @@ static void set_basic_info_test(remote_client &client) {
const auto change_time = last_write_time;
#endif
EXPECT_EQ(STATUS_SUCCESS,
client.winfsp_set_basic_info(file_desc, attributes, creation_time,
last_access_time, last_write_time,
change_time, &fi));
EXPECT_EQ(static_cast<std::uint32_t>(attributes), fi.FileAttributes);
EXPECT_EQ(creation_time, fi.CreationTime);
EXPECT_EQ(last_access_time, fi.LastAccessTime);
EXPECT_EQ(last_write_time, fi.LastWriteTime);
EXPECT_EQ(change_time, fi.ChangeTime);
EXPECT_EQ(STATUS_SUCCESS, client.winfsp_set_basic_info(
file_desc, static_cast<UINT32>(attributes),
static_cast<UINT64>(creation_time),
static_cast<UINT64>(last_access_time),
static_cast<UINT64>(last_write_time),
static_cast<UINT64>(change_time), &fi));
EXPECT_EQ(static_cast<UINT32>(attributes), fi.FileAttributes);
EXPECT_EQ(static_cast<UINT64>(creation_time), fi.CreationTime);
EXPECT_EQ(static_cast<UINT64>(last_access_time), fi.LastAccessTime);
EXPECT_EQ(static_cast<UINT64>(last_write_time), fi.LastWriteTime);
EXPECT_EQ(static_cast<UINT64>(change_time), fi.ChangeTime);
EXPECT_EQ(STATUS_SUCCESS, client.winfsp_close(file_desc));

View File

@ -0,0 +1,225 @@
/*
Copyright <2018-2024> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if defined(_WIN32)
#if 0
#include "fixtures/winfsp_fixture.hpp"
namespace repertory {
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
TYPED_TEST(winfsp_test, root_is_created) {
WIN32_FILE_ATTRIBUTE_DATA ad{};
ASSERT_TRUE(::GetFileAttributesEx(this->mount_location.c_str(),
GetFileExInfoStandard, &ad));
EXPECT_EQ(FILE_ATTRIBUTE_DIRECTORY, ad.dwFileAttributes);
EXPECT_EQ(0, ad.nFileSizeHigh);
EXPECT_EQ(0, ad.nFileSizeLow);
}
TYPED_TEST(winfsp_test, can_create_and_delete_directory) {
std::string dir_name{"test_create_and_delete_dir"};
auto dir_path = this->create_directory_and_test(dir_name);
this->delete_directory_and_test(dir_path);
}
TYPED_TEST(winfsp_test, can_create_and_delete_file) {
std::string file_name{"test_create_and_delete_file"};
auto file_path = this->create_file_and_test(file_name);
this->delete_file_and_test(file_path);
}
TYPED_TEST(winfsp_test, can_write_to_and_read_from_file) {
std::string file_name{"test_write_file"};
auto file_path = this->create_file_and_test(file_name);
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
if (handle == INVALID_HANDLE_VALUE) {
return;
}
std::string write_buffer{"0123456789"};
{
DWORD bytes_written{0};
EXPECT_TRUE(::WriteFile(handle, write_buffer.c_str(),
static_cast<DWORD>(write_buffer.size()),
&bytes_written, nullptr));
EXPECT_EQ(static_cast<DWORD>(write_buffer.size()), bytes_written);
auto opt_size = utils::file::file(file_path).size();
EXPECT_TRUE(opt_size.has_value());
EXPECT_EQ(write_buffer.size(), opt_size.value());
}
{
data_buffer read_buffer;
read_buffer.resize(write_buffer.size());
DWORD bytes_read{0};
EXPECT_EQ(0, ::SetFilePointer(handle, 0, nullptr, FILE_BEGIN));
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(),
static_cast<DWORD>(read_buffer.size()), &bytes_read,
nullptr));
EXPECT_EQ(static_cast<DWORD>(write_buffer.size()), bytes_read);
EXPECT_EQ(0,
std::memcmp(write_buffer.data(), read_buffer.data(),
std::min(read_buffer.size(), write_buffer.size())));
}
EXPECT_TRUE(::CloseHandle(handle));
this->delete_file_and_test(file_path);
}
TYPED_TEST(winfsp_test, can_rename_file) {
std::string file_name{"rename_file"};
auto file_path = this->create_file_and_test(file_name);
auto api_path = utils::path::create_api_path(file_name);
api_meta_map meta1{};
EXPECT_EQ(api_error::success, this->provider->get_item_meta(api_path, meta1));
auto file_path2 =
utils::path::combine(this->mount_location, {file_name + "_2"});
auto api_path2 = api_path + "_2";
EXPECT_TRUE(::MoveFile(file_path.c_str(), file_path2.c_str()));
EXPECT_TRUE(utils::file::file(file_path2).exists());
EXPECT_FALSE(utils::file::file(file_path).exists());
api_meta_map meta2{};
EXPECT_EQ(api_error::success,
this->provider->get_item_meta(api_path2, meta2));
EXPECT_STREQ(meta1[META_SOURCE].c_str(), meta2[META_SOURCE].c_str());
filesystem_item fsi{};
EXPECT_EQ(api_error::success,
this->provider->get_filesystem_item(api_path2, false, fsi));
EXPECT_STREQ(meta1[META_SOURCE].c_str(), fsi.source_path.c_str());
filesystem_item fsi2{};
EXPECT_EQ(api_error::success,
this->provider->get_filesystem_item_from_source_path(
fsi.source_path, fsi2));
EXPECT_STREQ(api_path2.c_str(), fsi2.api_path.c_str());
EXPECT_EQ(api_error::item_not_found,
this->provider->get_item_meta(api_path, meta2));
this->delete_file_and_test(file_path2);
}
TYPED_TEST(winfsp_test, can_rename_directory) {
std::string dir_name{"rename_dir"};
auto dir_path = this->create_directory_and_test(dir_name);
auto dir_path2{dir_path + "_2"};
EXPECT_TRUE(::MoveFileA(dir_path.c_str(), dir_path2.c_str()));
EXPECT_FALSE(::PathIsDirectoryA(dir_path.c_str()));
EXPECT_TRUE(::PathIsDirectoryA(dir_path2.c_str()));
this->delete_directory_and_test(dir_path2);
}
TYPED_TEST(winfsp_test, can_overwrite_file) {
std::string file_name{"overwrite_file"};
auto file_path = this->create_file_and_test(file_name);
auto file_path2{file_path + "_2"};
EXPECT_TRUE(::CopyFile(file_path.c_str(), file_path2.c_str(), TRUE));
EXPECT_TRUE(::CopyFile(file_path.c_str(), file_path2.c_str(), FALSE));
EXPECT_FALSE(::CopyFile(file_path.c_str(), file_path2.c_str(), TRUE));
this->delete_file_and_test(file_path);
this->delete_file_and_test(file_path2);
}
TYPED_TEST(winfsp_test, can_get_and_set_basic_info_test) {
std::string file_name{"overwrite_file"};
auto file_path = this->create_file_and_test(file_name);
auto handle =
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
if (handle == INVALID_HANDLE_VALUE) {
return;
}
SYSTEMTIME st{};
::GetSystemTime(&st);
st.wMinute = 0;
FILETIME test_ch_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_ch_time);
FILETIME test_cr_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_cr_time);
FILETIME test_la_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_la_time);
FILETIME test_lw_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_lw_time);
FILE_BASIC_INFO fbi{};
fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
fbi.ChangeTime.HighPart = static_cast<LONG>(test_ch_time.dwHighDateTime);
fbi.ChangeTime.LowPart = test_ch_time.dwLowDateTime;
fbi.CreationTime = *reinterpret_cast<LARGE_INTEGER *>(&test_cr_time);
fbi.LastAccessTime = *reinterpret_cast<LARGE_INTEGER *>(&test_la_time);
fbi.LastWriteTime = *reinterpret_cast<LARGE_INTEGER *>(&test_lw_time);
EXPECT_TRUE(::SetFileInformationByHandle(handle, FileBasicInfo, &fbi,
sizeof(FILE_BASIC_INFO)));
FILE_BASIC_INFO fbi2{};
EXPECT_TRUE(::GetFileInformationByHandleEx(handle, FileBasicInfo, &fbi2,
sizeof(FILE_BASIC_INFO)));
EXPECT_EQ(0, memcmp(&fbi, &fbi2, sizeof(FILE_BASIC_INFO)));
std::cout << fbi.FileAttributes << " " << fbi.ChangeTime.QuadPart << " "
<< fbi.CreationTime.QuadPart << " " << fbi.LastAccessTime.QuadPart
<< " " << fbi.LastWriteTime.QuadPart << std::endl;
std::cout << fbi2.FileAttributes << " " << fbi2.ChangeTime.QuadPart << " "
<< fbi2.CreationTime.QuadPart << " " << fbi2.LastAccessTime.QuadPart
<< " " << fbi2.LastWriteTime.QuadPart << std::endl;
EXPECT_TRUE(::CloseHandle(handle));
this->delete_file_and_test(file_path);
}
} // namespace repertory
#endif // 0
#endif // defined(_WIN32)

View File

@ -1,385 +0,0 @@
/*
Copyright <2018-2024> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#if 0
#if defined(_WIN32)
#include "test_common.hpp"
#include "fixtures/winfsp_fixture.hpp"
#include "types/repertory.hpp"
#include "utils/event_capture.hpp"
namespace repertory {
void launch_app(std::string cmd) {
PROCESS_INFORMATION pi{};
STARTUPINFO si{};
si.cb = sizeof(si);
if (!::CreateProcessA(nullptr, (LPSTR)cmd.c_str(), nullptr, nullptr, FALSE,
CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, nullptr,
nullptr, &si, &pi)) {
throw std::runtime_error("CreateProcess failed (" +
std::to_string(::GetLastError()) + ")");
}
::WaitForSingleObject(pi.hProcess, INFINITE);
DWORD code{};
::GetExitCodeProcess(pi.hProcess, &code);
::CloseHandle(pi.hProcess);
::CloseHandle(pi.hThread);
EXPECT_EQ(0, code);
}
E_SIMPLE1(test_begin, info, false, std::string, test_name, TN, E_FROM_STRING);
#define TEST_HEADER(func) \
event_system::instance().raise<test_begin>( \
std::string(func) + \
"\r\n***********************\r\n***********************")
static auto mount_setup(std::string &mount_point) {
mount_point = "U:";
return std::vector<std::string>({"unittests", "-f", mount_point});
}
static void execute_mount(winfsp_test *test,
const std::vector<std::string> &drive_args,
std::thread &th) {
ASSERT_EQ(0, test->drive->mount(drive_args));
th.join();
}
static void unmount(winfsp_test *test, const std::string &mount_point) {
test->drive->shutdown();
auto mounted = utils::file::directory(mount_point).exists();
for (auto i = 0; mounted && (i < 50); i++) {
std::this_thread::sleep_for(100ms);
mounted = utils::file::directory(mount_point).exists();
}
EXPECT_FALSE(utils::file::directory(mount_point).exists());
}
static void root_creation_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
WIN32_FILE_ATTRIBUTE_DATA ad{};
EXPECT_TRUE(
::GetFileAttributesEx(mount_point.c_str(), GetFileExInfoStandard, &ad));
EXPECT_EQ(FILE_ATTRIBUTE_DIRECTORY, ad.dwFileAttributes);
EXPECT_EQ(0, ad.nFileSizeHigh);
EXPECT_EQ(0, ad.nFileSizeLow);
}
static auto create_test(winfsp_test *test, const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
auto file = utils::path::combine(mount_point, {{"test_create.txt"}});
auto handle = ::CreateFileA(&file[0], GENERIC_READ, FILE_SHARE_READ, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
EXPECT_TRUE(utils::file::file(file).exists());
auto opt_size = utils::file::file(file).size();
EXPECT_TRUE(opt_size.has_value());
EXPECT_EQ(0, opt_size.value());
std::string attr;
EXPECT_EQ(api_error::success, test->provider->get_item_meta(
"/test_create.txt", META_ATTRIBUTES, attr));
EXPECT_EQ(FILE_ATTRIBUTE_NORMAL, utils::string::to_uint32(attr));
return file;
}
static void delete_file_test(const std::string &file) {
TEST_HEADER(__FUNCTION__);
event_capture ec({"file_removed"});
EXPECT_TRUE(utils::file::file(file).remove());
EXPECT_FALSE(utils::file::file(file).exists());
}
static void create_directory_test(const std::string &directory) {
TEST_HEADER(__FUNCTION__);
EXPECT_FALSE(::PathIsDirectory(&directory[0]));
EXPECT_TRUE(::CreateDirectoryA(&directory[0], nullptr));
EXPECT_TRUE(::PathIsDirectory(&directory[0]));
}
static void remove_directory_test(const std::string &directory) {
TEST_HEADER(__FUNCTION__);
event_capture ec({"directory_removed"});
EXPECT_TRUE(::PathIsDirectory(&directory[0]));
EXPECT_TRUE(::RemoveDirectoryA(&directory[0]));
EXPECT_FALSE(::PathIsDirectory(&directory[0]));
}
static void write_file_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
const auto file = utils::path::combine(mount_point, {"test_write.txt"});
auto handle =
::CreateFileA(&file[0], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
const std::string data = "0123456789";
DWORD bytes_written = 0;
EXPECT_TRUE(::WriteFile(handle, &data[0], static_cast<DWORD>(data.size()),
&bytes_written, nullptr));
EXPECT_EQ(10, bytes_written);
EXPECT_TRUE(::CloseHandle(handle));
EXPECT_TRUE(utils::file::file(file).exists());
auto opt_size = utils::file::file(file).size();
EXPECT_TRUE(opt_size.has_value());
EXPECT_EQ(10U, opt_size.value());
}
static void read_file_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
const auto file = utils::path::combine(mount_point, {"test_read.txt"});
auto handle =
::CreateFileA(&file[0], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
const std::string data = "0123456789";
DWORD bytes_written = 0;
EXPECT_TRUE(::WriteFile(handle, &data[0], static_cast<DWORD>(data.size()),
&bytes_written, nullptr));
EXPECT_EQ(10, bytes_written);
data_buffer data2;
data2.resize(10);
DWORD bytes_read = 0;
EXPECT_EQ(0, ::SetFilePointer(handle, 0, nullptr, FILE_BEGIN));
EXPECT_TRUE(::ReadFile(handle, &data2[0], static_cast<DWORD>(data2.size()),
&bytes_read, nullptr));
EXPECT_EQ(10, bytes_read);
for (auto i = 0; i < data.size(); i++) {
EXPECT_EQ(data[i], data2[i]);
}
EXPECT_TRUE(::CloseHandle(handle));
}
static void rename_file_test(winfsp_test *test,
const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
const auto file = utils::path::combine(mount_point, {"rename_file.txt"});
auto handle = ::CreateFileA(&file[0], GENERIC_READ, FILE_SHARE_READ, nullptr,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
EXPECT_TRUE(::CloseHandle(handle));
api_meta_map meta1{};
EXPECT_EQ(api_error::success,
test->provider->get_item_meta("/rename_file.txt", meta1));
const auto file2 = utils::path::combine(mount_point, {"rename_file2.txt"});
EXPECT_TRUE(::MoveFile(&file[0], &file2[0]));
EXPECT_TRUE(utils::file::file(file2).exists());
EXPECT_FALSE(utils::file::file(file).exists());
api_meta_map meta2{};
EXPECT_EQ(api_error::success,
test->provider->get_item_meta("/rename_file2.txt", meta2));
EXPECT_STREQ(meta1[META_SOURCE].c_str(), meta2[META_SOURCE].c_str());
filesystem_item fsi{};
EXPECT_EQ(api_error::success, test->provider->get_filesystem_item(
"/rename_file2.txt", false, fsi));
EXPECT_STREQ(meta1[META_SOURCE].c_str(), fsi.source_path.c_str());
filesystem_item fsi2{};
EXPECT_EQ(api_error::success,
test->provider->get_filesystem_item_from_source_path(
fsi.source_path, fsi2));
EXPECT_STREQ("/rename_file2.txt", fsi2.api_path.c_str());
EXPECT_EQ(api_error::item_not_found,
test->provider->get_item_meta("/rename_file.txt", meta2));
}
static void rename_directory_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
std::string directory = "rename_dir";
const auto full_directory = utils::path::combine(mount_point, {directory});
std::string directory2 = "rename_dir2";
const auto full_directory2 = utils::path::combine(mount_point, {directory2});
EXPECT_FALSE(::PathIsDirectory(&full_directory[0]));
EXPECT_TRUE(::CreateDirectoryA(&full_directory[0], nullptr));
EXPECT_TRUE(::PathIsDirectory(&full_directory[0]));
EXPECT_TRUE(::MoveFile(&full_directory[0], &full_directory2[0]));
EXPECT_FALSE(::PathIsDirectory(&full_directory[0]));
EXPECT_TRUE(::PathIsDirectory(&full_directory2[0]));
}
static void get_set_basic_info_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
const auto file =
utils::path::combine(mount_point, {"setbasicinfo_file.txt"});
auto handle =
::CreateFileA(&file[0], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
SYSTEMTIME st{};
::GetSystemTime(&st);
st.wMinute = 0;
FILETIME test_ch_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_ch_time);
FILETIME test_cr_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_cr_time);
FILETIME test_la_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_la_time);
FILETIME test_lw_time{};
st.wMinute++;
::SystemTimeToFileTime(&st, &test_lw_time);
FILE_BASIC_INFO fbi{};
fbi.FileAttributes = FILE_ATTRIBUTE_HIDDEN;
fbi.ChangeTime.HighPart = test_ch_time.dwHighDateTime;
fbi.ChangeTime.LowPart = test_ch_time.dwLowDateTime;
fbi.CreationTime = *(LARGE_INTEGER *)&test_cr_time;
fbi.LastAccessTime = *(LARGE_INTEGER *)&test_la_time;
fbi.LastWriteTime = *(LARGE_INTEGER *)&test_lw_time;
EXPECT_TRUE(::SetFileInformationByHandle(handle, FileBasicInfo, &fbi,
sizeof(FILE_BASIC_INFO)));
FILE_BASIC_INFO fbi2{};
EXPECT_TRUE(::GetFileInformationByHandleEx(handle, FileBasicInfo, &fbi2,
sizeof(FILE_BASIC_INFO)));
EXPECT_EQ(0, memcmp(&fbi, &fbi2, sizeof(FILE_BASIC_INFO)));
std::cout << fbi.FileAttributes << " " << fbi.ChangeTime.QuadPart << " "
<< fbi.CreationTime.QuadPart << " " << fbi.LastAccessTime.QuadPart
<< " " << fbi.LastWriteTime.QuadPart << std::endl;
std::cout << fbi2.FileAttributes << " " << fbi2.ChangeTime.QuadPart << " "
<< fbi2.CreationTime.QuadPart << " " << fbi2.LastAccessTime.QuadPart
<< " " << fbi2.LastWriteTime.QuadPart << std::endl;
EXPECT_TRUE(::CloseHandle(handle));
}
static void overwrite_file_test(const std::string &mount_point) {
TEST_HEADER(__FUNCTION__);
const auto file = utils::path::combine("./", {"test_overwrite.txt"});
auto handle =
::CreateFileA(&file[0], GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
if (handle != INVALID_HANDLE_VALUE) {
const std::string data = "0123456789";
DWORD bytes_written = 0;
EXPECT_TRUE(::WriteFile(handle, &data[0], static_cast<DWORD>(data.size()),
&bytes_written, nullptr));
EXPECT_EQ(10, bytes_written);
EXPECT_TRUE(::CloseHandle(handle));
if (bytes_written == 10) {
const auto file2 =
utils::path::combine(mount_point, {"test_overwrite2.txt"});
EXPECT_TRUE(::CopyFile(&file[0], &file2[0], TRUE));
EXPECT_FALSE(::CopyFile(&file[0], &file2[0], TRUE));
}
}
}
TEST_F(winfsp_test, all_tests) {
if (PROVIDER_INDEX == 0) {
for (std::size_t idx = 0U; idx < 2U; idx++) {
launch_app(
("cmd.exe /c unittests.exe --gtest_filter=winfsp_test.all_tests "
"--provider_index " +
std::to_string(idx) + " > unittests" + std::to_string(idx) +
".log 2>&1"));
}
return;
}
if (PROVIDER_INDEX == 1U) {
return;
}
std::string mount_point;
const auto drive_args = mount_setup(mount_point);
event_capture ec({
"drive_mounted",
"drive_unmounted",
"drive_unmount_pending",
"drive_mount_result",
});
std::thread th([&] {
const auto mounted = ec.wait_for_event("drive_mounted");
EXPECT_TRUE(mounted);
if (mounted) {
root_creation_test(mount_point);
{
const auto file = create_test(this, mount_point);
delete_file_test(file);
}
{
const auto dir = utils::path::combine(mount_point, {"TestDir"});
create_directory_test(dir);
remove_directory_test(dir);
}
write_file_test(mount_point);
read_file_test(mount_point);
// TODO enable after rename support is available
// rename_file_test(this, mount_point);
// rename_directory_test(mount_point);
overwrite_file_test(mount_point);
get_set_basic_info_test(mount_point);
}
if (mounted) {
unmount(this, mount_point);
ec.wait_for_empty();
}
});
execute_mount(this, drive_args, th);
}
} // namespace repertory
#endif // defined(_WIN32)
#endif // 0

View File

@ -66,8 +66,6 @@ struct db_context_t {
sqlite3 *db3{};
std::string table_name;
db3_stmt_t stmt;
};
class db_column final {
@ -178,13 +176,14 @@ public:
}
};
template <typename ctx_t> struct db_result final {
db_result(std::shared_ptr<ctx_t> ctx, std::int32_t res)
: ctx_(std::move(ctx)), res_(res) {
if (res == SQLITE_OK) {
set_res(sqlite3_step(ctx_->stmt.get()));
}
}
struct db_result final {
struct context final {
db3_stmt_t stmt;
};
using row = db_row<context>;
db_result(sqlite3_stmt *stmt, std::int32_t res);
db_result() = default;
db_result(const db_result &) = default;
@ -193,52 +192,25 @@ template <typename ctx_t> struct db_result final {
auto operator=(const db_result &) -> db_result & = default;
auto operator=(db_result &&) -> db_result & = default;
~db_result() {
if (ctx_) {
ctx_->clear();
}
}
private:
std::shared_ptr<ctx_t> ctx_;
std::shared_ptr<context> ctx_;
mutable std::int32_t res_{};
private:
void set_res(std::int32_t res) const { res_ = res; }
public:
[[nodiscard]] auto ok() const -> bool {
return res_ == SQLITE_DONE || res_ == SQLITE_ROW;
}
[[nodiscard]] auto get_error() const -> std::int32_t { return res_; }
[[nodiscard]] auto get_error_str() const -> std::string {
auto &&err_msg = sqlite3_errstr(res_);
return err_msg == nullptr ? std::to_string(res_) : err_msg;
}
[[nodiscard]] auto get_error_str() const -> std::string;
[[nodiscard]] auto get_row(std::optional<db_row<ctx_t>> &row) const -> bool {
row.reset();
[[nodiscard]] auto get_row(std::optional<row> &opt_row) const -> bool;
if (not has_row()) {
return false;
}
[[nodiscard]] auto has_row() const -> bool;
row = db_row{ctx_};
set_res(sqlite3_step(ctx_->stmt.get()));
return true;
}
void next_row() const;
[[nodiscard]] auto has_row() const -> bool { return res_ == SQLITE_ROW; }
void next_row() const {
if (not has_row()) {
return;
}
set_res(sqlite3_step(ctx_->stmt.get()));
}
[[nodiscard]] auto ok() const -> bool;
};
} // namespace repertory::utils::db::sqlite

View File

@ -36,7 +36,7 @@ public:
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
};
context(sqlite3 *db3_, std::string table_name_)
@ -46,8 +46,6 @@ public:
using wd_t = where_data_t<w_t>;
std::unique_ptr<wd_t> where_data;
void clear();
};
using row = db_row<context>;
@ -64,10 +62,10 @@ private:
public:
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto
group(context::w_t::group_func_t func) -> context::w_t::wn_t;
[[nodiscard]] auto group(context::w_t::group_func_t func)
-> context::w_t::wn_t;
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
};

View File

@ -34,8 +34,6 @@ public:
bool or_replace{false};
std::map<std::string, db_types_t> values;
void clear();
};
using row = db_row<context>;
@ -55,12 +53,14 @@ public:
return *this;
}
[[nodiscard]] auto column_value(std::string column_name,
db_types_t value) -> db_insert &;
[[nodiscard]] auto column_value(std::string column_name, db_types_t value)
-> db_insert &;
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
};
} // namespace repertory::utils::db::sqlite

View File

@ -36,7 +36,7 @@ public:
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto group_by(std::string column_name) -> db_select_op_t;
@ -44,8 +44,10 @@ public:
[[nodiscard]] auto offset(std::int32_t value) -> db_select_op_t;
[[nodiscard]] auto order_by(std::string column_name,
bool ascending) -> db_select_op_t;
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_select_op_t;
};
context(sqlite3 *db3_, std::string table_name_)
@ -63,12 +65,8 @@ public:
std::optional<std::pair<std::string, bool>> order_by;
std::unique_ptr<wd_t> where_data;
void clear();
};
using row = db_row<context>;
public:
db_select(sqlite3 &db3, std::string table_name)
: ctx_(std::make_shared<context>(&db3, table_name)) {}
@ -81,24 +79,30 @@ private:
public:
[[nodiscard]] auto column(std::string column_name) -> db_select &;
[[nodiscard]] auto count(std::string column_name,
std::string as_column_name) -> db_select &;
[[nodiscard]] auto count(std::string column_name, std::string as_column_name)
-> db_select &;
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto group_by(std::string column_name) -> db_select &;
[[nodiscard]] auto
group(context::w_t::group_func_t func) -> context::w_t::wn_t;
[[nodiscard]] auto group(context::w_t::group_func_t func)
-> context::w_t::wn_t;
[[nodiscard]] auto limit(std::int32_t value) -> db_select &;
[[nodiscard]] auto offset(std::int32_t value) -> db_select &;
[[nodiscard]] auto order_by(std::string column_name,
bool ascending) -> db_select &;
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_select &;
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
};

View File

@ -39,12 +39,14 @@ public:
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto limit(std::int32_t value) -> db_update_op_t;
[[nodiscard]] auto order_by(std::string column_name,
bool ascending) -> db_update_op_t;
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_update_op_t;
};
using w_t = db_where_t<context, db_update_op_t>;
@ -55,8 +57,7 @@ public:
std::optional<std::pair<std::string, bool>> order_by;
std::unique_ptr<wd_t> where_data;
void clear();
clear();
};
using row = db_row<context>;
@ -71,20 +72,26 @@ private:
std::shared_ptr<context> ctx_;
public:
[[nodiscard]] auto column_value(std::string column_name,
db_types_t value) -> db_update &;
[[nodiscard]] auto column_value(std::string column_name, db_types_t value)
-> db_update &;
[[nodiscard]] auto dump() const -> std::string;
[[nodiscard]] auto go() const -> db_result<context>;
[[nodiscard]] auto go() const -> db_result;
[[nodiscard]] auto
group(context::w_t::group_func_t func) -> context::w_t::wn_t;
[[nodiscard]] auto group(context::w_t::group_func_t func)
-> context::w_t::wn_t;
[[nodiscard]] auto limit(std::int32_t value) -> db_update &;
[[nodiscard]] auto order_by(std::string column_name,
bool ascending) -> db_update &;
[[nodiscard]] auto order_by(std::string column_name, bool ascending)
-> db_update &;
[[nodiscard]] auto where(std::string column_name) const -> context::w_t::cn_t;
};

View File

@ -76,6 +76,49 @@ auto db_column::get_value_as_json() const -> nlohmann::json {
}
#endif // defined(PROJECT_ENABLE_JSON)
db_result::db_result(sqlite3_stmt *stmt, std::int32_t res)
: ctx_(std::make_shared<context>()), res_(res) {
ctx_->stmt = db3_stmt_t{
stmt,
sqlite3_statement_deleter(),
};
if (res == SQLITE_OK) {
set_res(sqlite3_step(ctx_->stmt.get()));
}
}
auto db_result::get_error_str() const -> std::string {
auto &&err_msg = sqlite3_errstr(res_);
return err_msg == nullptr ? std::to_string(res_) : err_msg;
}
auto db_result::get_row(std::optional<row> &opt_row) const -> bool {
opt_row.reset();
if (not has_row()) {
return false;
}
opt_row = db_row<context>{ctx_};
set_res(sqlite3_step(ctx_->stmt.get()));
return true;
}
auto db_result::has_row() const -> bool { return res_ == SQLITE_ROW; }
void db_result::next_row() const {
if (not has_row()) {
return;
}
set_res(sqlite3_step(ctx_->stmt.get()));
}
auto db_result::ok() const -> bool {
return res_ == SQLITE_DONE || res_ == SQLITE_ROW;
}
auto create_db(std::string db_path,
const std::map<std::string, std::string> &sql_create_tables)
-> db3_t {
@ -115,8 +158,8 @@ auto create_db(std::string db_path,
return db3;
}
auto execute_sql(sqlite3 &db3, const std::string &sql,
std::string &err) -> bool {
auto execute_sql(sqlite3 &db3, const std::string &sql, std::string &err)
-> bool {
REPERTORY_USES_FUNCTION_NAME();
char *err_msg{nullptr};

View File

@ -24,13 +24,11 @@
#include "utils/db/sqlite/db_delete.hpp"
namespace repertory::utils::db::sqlite {
void db_delete::context::clear() { where_data.reset(); }
auto db_delete::context::db_delete_op_t::dump() const -> std::string {
return db_delete{ctx}.dump();
}
auto db_delete::context::db_delete_op_t::go() const -> db_result<context> {
auto db_delete::context::db_delete_op_t::go() const -> db_result {
return db_delete{ctx}.go();
}
@ -48,22 +46,18 @@ auto db_delete::dump() const -> std::string {
return query.str();
}
auto db_delete::go() const -> db_result<context> {
auto db_delete::go() const -> db_result {
sqlite3_stmt *stmt_ptr{nullptr};
auto query_str = dump();
auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
ctx_->stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
if (not ctx_->where_data) {
return {ctx_, res};
return {stmt_ptr, res};
}
for (std::int32_t idx = 0;
@ -71,21 +65,21 @@ auto db_delete::go() const -> db_result<context> {
idx++) {
res = std::visit(
overloaded{
[this, &idx](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(ctx_->stmt.get(), idx + 1, data);
[&stmt_ptr, &idx](std::int64_t data) -> std::int32_t {
return sqlite3_bind_int64(stmt_ptr, idx + 1, data);
},
[this, &idx](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(ctx_->stmt.get(), idx + 1, data.c_str(),
-1, nullptr);
[&stmt_ptr, &idx](const std::string &data) -> std::int32_t {
return sqlite3_bind_text(stmt_ptr, idx + 1, data.c_str(), -1,
nullptr);
},
},
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
}
return {ctx_, res};
return {stmt_ptr, res};
}
auto db_delete::group(context::w_t::group_func_t func) -> context::w_t::wn_t {

View File

@ -24,10 +24,8 @@
#include "utils/db/sqlite/db_insert.hpp"
namespace repertory::utils::db::sqlite {
void db_insert::context::clear() { values.clear(); }
auto db_insert::column_value(std::string column_name,
db_types_t value) -> db_insert & {
auto db_insert::column_value(std::string column_name, db_types_t value)
-> db_insert & {
ctx_->values[column_name] = value;
return *this;
}
@ -61,18 +59,14 @@ auto db_insert::dump() const -> std::string {
return query.str();
}
auto db_insert::go() const -> db_result<context> {
auto db_insert::go() const -> db_result {
sqlite3_stmt *stmt_ptr{nullptr};
auto query_str = dump();
auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
ctx_->stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
for (std::int32_t idx = 0;
@ -89,11 +83,11 @@ auto db_insert::go() const -> db_result<context> {
},
std::next(ctx_->values.begin(), idx)->second);
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
}
return {ctx_, res};
return {stmt_ptr, res};
}
} // namespace repertory::utils::db::sqlite

View File

@ -24,21 +24,11 @@
#include "utils/db/sqlite/db_select.hpp"
namespace repertory::utils::db::sqlite {
void db_select::context::clear() {
columns.clear();
count_columns.clear();
group_by.clear();
limit.reset();
offset.reset();
order_by.reset();
where_data.reset();
}
auto db_select::context::db_select_op_t::dump() const -> std::string {
return db_select{ctx}.dump();
}
auto db_select::context::db_select_op_t::go() const -> db_result<context> {
auto db_select::context::db_select_op_t::go() const -> db_result {
return db_select{ctx}.go();
}
@ -72,8 +62,8 @@ auto db_select::column(std::string column_name) -> db_select & {
return *this;
}
auto db_select::count(std::string column_name,
std::string as_column_name) -> db_select & {
auto db_select::count(std::string column_name, std::string as_column_name)
-> db_select & {
ctx_->count_columns[column_name] = as_column_name;
return *this;
}
@ -142,22 +132,18 @@ auto db_select::dump() const -> std::string {
return query.str();
}
auto db_select::go() const -> db_result<context> {
auto db_select::go() const -> db_result {
sqlite3_stmt *stmt_ptr{nullptr};
auto query_str = dump();
auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
ctx_->stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
if (not ctx_->where_data) {
return {ctx_, res};
return {stmt_ptr, res};
}
for (std::int32_t idx = 0;
@ -175,11 +161,11 @@ auto db_select::go() const -> db_result<context> {
},
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
}
return {ctx_, res};
return {stmt_ptr, res};
}
auto db_select::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
@ -209,8 +195,8 @@ auto db_select::offset(std::int32_t value) -> db_select & {
return *this;
}
auto db_select::order_by(std::string column_name,
bool ascending) -> db_select & {
auto db_select::order_by(std::string column_name, bool ascending)
-> db_select & {
ctx_->order_by = {column_name, ascending};
return *this;
}

View File

@ -24,18 +24,11 @@
#include "utils/db/sqlite/db_update.hpp"
namespace repertory::utils::db::sqlite {
void db_update::context::clear() {
column_values.clear();
limit.reset();
order_by.reset();
where_data.reset();
}
auto db_update::context::db_update_op_t::dump() const -> std::string {
return db_update{ctx}.dump();
}
auto db_update::context::db_update_op_t::go() const -> db_result<context> {
auto db_update::context::db_update_op_t::go() const -> db_result {
return db_update{ctx}.go();
}
@ -52,8 +45,8 @@ auto db_update::context::db_update_op_t::order_by(std::string column_name,
return *this;
}
auto db_update::column_value(std::string column_name,
db_types_t value) -> db_update & {
auto db_update::column_value(std::string column_name, db_types_t value)
-> db_update & {
ctx_->column_values[column_name] = value;
return *this;
}
@ -91,18 +84,15 @@ auto db_update::dump() const -> std::string {
return query.str();
}
auto db_update::go() const -> db_result<context> {
auto db_update::go() const -> db_result {
sqlite3_stmt *stmt_ptr{nullptr};
auto query_str = dump();
auto res =
sqlite3_prepare_v2(ctx_->db3, query_str.c_str(), -1, &stmt_ptr, nullptr);
ctx_->stmt = db3_stmt_t{
stmt_ptr,
sqlite3_statement_deleter(),
};
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
for (std::int32_t idx = 0;
@ -119,12 +109,12 @@ auto db_update::go() const -> db_result<context> {
},
std::next(ctx_->column_values.begin(), idx)->second);
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
}
if (not ctx_->where_data) {
return {ctx_, res};
return {stmt_ptr, res};
}
for (std::int32_t idx = 0;
@ -149,11 +139,11 @@ auto db_update::go() const -> db_result<context> {
},
ctx_->where_data->values.at(static_cast<std::size_t>(idx)));
if (res != SQLITE_OK) {
return {ctx_, res};
return {stmt_ptr, res};
}
}
return {ctx_, res};
return {stmt_ptr, res};
}
auto db_update::group(context::w_t::group_func_t func) -> context::w_t::wn_t {
@ -173,8 +163,8 @@ auto db_update::limit(std::int32_t value) -> db_update & {
return *this;
}
auto db_update::order_by(std::string column_name,
bool ascending) -> db_update & {
auto db_update::order_by(std::string column_name, bool ascending)
-> db_update & {
ctx_->order_by = {column_name, ascending};
return *this;
}

View File

@ -37,8 +37,8 @@ using namespace ::testing;
#include "utils/all.hpp"
namespace repertory::test {
[[nodiscard]] auto
create_random_file(std::size_t size) -> utils::file::i_file &;
[[nodiscard]] auto create_random_file(std::size_t size)
-> utils::file::i_file &;
[[nodiscard]] auto
generate_test_file_name(std::string_view file_name_no_extension) -> std::string;
@ -53,6 +53,8 @@ static void decrypt_and_verify(const buffer_t &buffer, std::string_view token,
auto generate_test_directory() -> utils::file::i_directory &;
[[nodiscard]] auto get_test_config_dir() -> std::string;
[[nodiscard]] auto get_test_input_dir() -> std::string;
[[nodiscard]] auto get_test_output_dir() -> std::string;

View File

@ -110,10 +110,19 @@ auto generate_test_file_name(std::string_view file_name_no_extension)
return generated_files.back()->get_path();
}
auto get_test_config_dir() -> std::string {
static auto test_path = ([]() -> std::string {
auto dir = utils::get_environment_variable("PROJECT_TEST_CONFIG_DIR");
return utils::path::combine(dir.empty() ? "." : dir, {"test_config"});
})();
return test_path;
}
auto get_test_input_dir() -> std::string {
static auto test_path = ([]() -> std::string {
auto dir = utils::get_environment_variable("PROJECT_TEST_DIR");
return utils::path::combine(dir.empty() ? "." : dir, {"test_config"});
auto dir = utils::get_environment_variable("PROJECT_TEST_INPUT_DIR");
return utils::path::combine(dir.empty() ? "." : dir, {"test_input"});
})();
return test_path;

View File

@ -89,7 +89,7 @@ static void common_select(sqlite3 &db3, std::string value1, std::string value2,
std::size_t row_count{};
while (res.has_row()) {
std::optional<utils::db::sqlite::db_select::row> row;
std::optional<utils::db::sqlite::db_result::row> row;
EXPECT_TRUE(res.get_row(row));
EXPECT_TRUE(row.has_value());
if (row.has_value()) {