Files
syncspirit/tests/071-fs_actor.cpp

769 lines
34 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2019-2026 Ivan Baidakou
#include "test-utils.h"
#include "fs/file_actor.h"
#include "fs/utils.h"
#include "fs/platform/context_base.h"
#include "net/names.h"
#include "test_supervisor.h"
#include "access.h"
#include "utils/error_code.h"
#include "syncspirit-config.h"
#include <filesystem>
#include <boost/nowide/convert.hpp>
#include <optional>
#include <utility>
#include <utils/platform.h>
using namespace syncspirit;
using namespace syncspirit::db;
using namespace syncspirit::test;
using namespace syncspirit::model;
using namespace syncspirit::net;
using namespace syncspirit::fs;
namespace bfs = std::filesystem;
using perms_t = std::filesystem::perms;
using boost::nowide::narrow;
namespace {
struct fixture_t;
using io_commands_t = fs::message::io_commands_t;
using io_commands_t_ptr_t = r::intrusive_ptr_t<io_commands_t>;
struct my_context_t final : platform::context_base_t {
using parent_t = platform::context_base_t;
my_context_t() : parent_t(pt::milliseconds{1}) {};
void poll_events() noexcept override {};
};
struct chain_builder_t {
template <typename Reply>
chain_builder_t(fixture_t *fixture_, io_commands_t_ptr_t msg, std::in_place_type_t<Reply>) : fixture{fixture_} {
message = msg;
if (msg) {
auto &commands = msg->payload.commands;
REQUIRE(commands.size() == 1);
auto reply = std::get_if<Reply>(&commands.front());
REQUIRE(reply);
auto &result = reply->result;
if (result) {
response = sys::error_code{};
} else {
response = result.assume_error();
}
message.reset();
}
}
fixture_t &check_success() noexcept {
REQUIRE(response);
CHECK(!*response);
return *fixture;
}
fixture_t &check_fail(const sys::error_code &ec = {}) noexcept {
REQUIRE(response);
CHECK(*response);
if (ec) {
CHECK(*response == ec);
} else {
CHECK(response->message() != "");
}
return *fixture;
}
std::optional<sys::error_code> response;
io_commands_t_ptr_t message;
fixture_t *fixture;
};
struct fixture_t {
fixture_t() noexcept : root_path{unique_path()}, path_guard{root_path} { bfs::create_directory(root_path); }
virtual configure_callback_t configure() noexcept {
return [&](r::plugin::plugin_base_t &plugin) {
plugin.template with_casted<r::plugin::registry_plugin_t>(
[&](auto &p) { p.register_name(net::names::db, sup->get_address()); });
plugin.template with_casted<r::plugin::starter_plugin_t>([&](auto &p) {
p.subscribe_actor(r::lambda<io_commands_t>([&](io_commands_t &msg) { reply = &msg; }));
});
};
}
virtual void create_file_actor() noexcept {
file_actor = sup->create_actor<fs::file_actor_t>()
.timeout(timeout)
.change_retension(retension)
.updates_mediator(updates_mediator)
.watched_folders(watched_folders)
.finish();
}
virtual void run() noexcept {
auto ctx = my_context_t();
sup = ctx.create_supervisor<supervisor_t>()
.auto_finish(false)
.auto_ack_io(false)
.timeout(timeout)
.create_registry()
.configure_callback(configure())
.finish();
sup->start();
sup->do_process();
CHECK(static_cast<r::actor_base_t *>(sup.get())->access<to::state>() == r::state_t::OPERATIONAL);
updates_mediator = new fs::updates_mediator_t(retension);
watched_folders.reset(new watched_folders_t());
auto folder_info = fs::folder_info_t(root_path, narrow(root_path.generic_wstring()));
watched_folders->emplace(std::make_pair(folder_id, std::move(folder_info)));
create_file_actor();
sup->do_process();
sequencer = sup->sequencer;
CHECK(static_cast<r::actor_base_t *>(file_actor.get())->access<to::state>() == r::state_t::OPERATIONAL);
file_addr = file_actor->get_address();
main();
sup->shutdown();
sup->do_process();
CHECK(static_cast<r::actor_base_t *>(sup.get())->access<to::state>() == r::state_t::SHUT_DOWN);
}
virtual void main() noexcept {}
chain_builder_t append_block(const bfs::path &path, utils::bytes_view_t data, std::uint64_t offset,
std::uint64_t file_size) noexcept {
auto bytes = utils::bytes_t(data.begin(), data.end());
auto context = fs::payload::extendended_context_prt_t{};
auto payload =
fs::payload::append_block_t(std::move(context), folder_id, path, std::move(bytes), offset, file_size);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
sup->do_process();
return chain_builder_t(this, reply, std::in_place_type_t<decltype(payload)>());
}
chain_builder_t clone_block(const bfs::path &target, std::uint64_t target_offset, std::uint64_t target_size,
const bfs::path &source, std::uint64_t source_offset,
std::uint64_t block_size) noexcept {
auto context = fs::payload::extendended_context_prt_t{};
auto payload = fs::payload::clone_block_t(std::move(context), folder_id, target, target_offset, target_size,
source, source_offset, block_size);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
sup->do_process();
return chain_builder_t(this, reply, std::in_place_type_t<decltype(payload)>());
}
chain_builder_t finish_file(const bfs::path &path, std::uint64_t file_size, std::int64_t modification_s,
std::uint32_t permissions, bool no_permissions,
const bfs::path &conflict_path = {}) noexcept {
auto context = fs::payload::extendended_context_prt_t{};
auto payload = fs::payload::finish_file_t(std::move(context), folder_id, path, conflict_path, file_size,
modification_s, permissions, no_permissions);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
sup->do_process();
return chain_builder_t(this, reply, std::in_place_type_t<decltype(payload)>());
}
chain_builder_t remote_copy(const bfs::path &path, const proto::FileInfo &meta,
const bfs::path &conflict_path = {}) noexcept {
auto context = fs::payload::extendended_context_prt_t{};
auto type = proto::get_type(meta);
auto size = proto::get_size(meta);
auto deleted = proto::get_deleted(meta);
auto perms = proto::get_permissions(meta);
auto modificaiton = proto::get_modified_s(meta);
auto target = std::string(proto::get_symlink_target(meta));
auto payload = fs::payload::remote_copy_t(std::move(context), folder_id, path, conflict_path, type, size, perms,
modificaiton, target, deleted, false);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
sup->do_process();
return chain_builder_t(this, reply, std::in_place_type_t<decltype(payload)>());
}
chain_builder_t update_meta(const bfs::path &path, std::int64_t modification_s_, std::uint32_t permissions_,
bool no_permissions_) noexcept {
auto context = fs::payload::extendended_context_prt_t{};
auto payload = fs::payload::update_meta_t(std::move(context), folder_id, path, modification_s_, permissions_,
no_permissions_);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
sup->do_process();
return chain_builder_t(this, reply, std::in_place_type_t<decltype(payload)>());
}
r::address_ptr_t file_addr;
r::pt::time_duration timeout = r::pt::millisec{10};
r::pt::time_duration retension = r::pt::microseconds{1};
model::sequencer_ptr_t sequencer;
r::intrusive_ptr_t<supervisor_t> sup;
r::intrusive_ptr_t<fs::file_actor_t> file_actor;
fs::updates_mediator_ptr_t updates_mediator;
fs::watched_folders_ptr_t watched_folders;
bfs::path root_path;
test::path_guard_t path_guard;
r::system_context_t ctx;
io_commands_t_ptr_t reply;
std::string folder_id = "1234-5678";
};
} // namespace
void test_remote_copy() {
struct F : fixture_t {
void main() noexcept override {
proto::FileInfo pr_fi;
std::int64_t modified = 1641828421;
proto::set_modified_s(pr_fi, modified);
proto::set_permissions(pr_fi, 0666);
SECTION("empty regular file") {
auto path = root_path / L"папка" / L"файл.txt";
auto path_str = narrow(path.generic_wstring());
remote_copy(path, pr_fi).check_success();
REQUIRE(bfs::exists(path));
REQUIRE(bfs::file_size(path) == 0);
REQUIRE(to_unix(bfs::last_write_time(path)) == 1641828421);
CHECK(updates_mediator->is_masked(path_str) >= 2);
#ifndef SYNCSPIRIT_WIN
auto status = bfs::status(path);
auto p = status.permissions();
CHECK((p & perms_t::owner_read) != perms_t::none);
CHECK((p & perms_t::owner_write) != perms_t::none);
CHECK((p & perms_t::group_read) != perms_t::none);
CHECK((p & perms_t::group_write) != perms_t::none);
CHECK((p & perms_t::others_read) != perms_t::none);
CHECK((p & perms_t::others_write) != perms_t::none);
#endif
}
SECTION("empty regular file in a subdir") {
auto path = root_path / L"а" / L"б" / L"в" / L"г" / L"д" / L"файл.txt";
auto path_str = narrow(path.generic_wstring());
remote_copy(path, pr_fi).check_success();
REQUIRE(bfs::exists(path));
REQUIRE(bfs::file_size(path) == 0);
REQUIRE(to_unix(bfs::last_write_time(path)) == 1641828421);
CHECK(updates_mediator->is_masked(path_str) >= 2);
#ifndef SYNCSPIRIT_WIN
auto status = bfs::status(path);
auto p = status.permissions();
CHECK((p & perms_t::owner_read) != perms_t::none);
CHECK((p & perms_t::owner_write) != perms_t::none);
CHECK((p & perms_t::group_read) != perms_t::none);
CHECK((p & perms_t::group_write) != perms_t::none);
CHECK((p & perms_t::others_read) != perms_t::none);
CHECK((p & perms_t::others_write) != perms_t::none);
#endif
}
SECTION("non-empty regular file") {
proto::set_size(pr_fi, 5);
auto path = root_path / L"папка" / L"файл.txt";
auto path_str = narrow(path.generic_wstring());
write_file(path, "12345");
remote_copy(path, pr_fi).check_success();
auto tmp_path = path.parent_path() / (path.filename().wstring() + L".syncspirit-tmp");
auto tmp_path_str = narrow(tmp_path.generic_wstring());
REQUIRE(!bfs::exists(tmp_path));
auto status = bfs::status(path);
CHECK(to_unix(bfs::last_write_time(path)) == 1641828421);
CHECK(updates_mediator->is_masked(path_str) >= 2);
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
#ifndef SYNCSPIRIT_WIN
auto p = status.permissions();
CHECK((p & perms_t::owner_read) != perms_t::none);
CHECK((p & perms_t::owner_write) != perms_t::none);
CHECK((p & perms_t::group_read) != perms_t::none);
CHECK((p & perms_t::group_write) != perms_t::none);
CHECK((p & perms_t::others_read) != perms_t::none);
CHECK((p & perms_t::others_write) != perms_t::none);
#endif
}
SECTION("directory") {
auto path = root_path / L"папка";
auto path_str = narrow(path.generic_wstring());
proto::set_type(pr_fi, proto::FileInfoType::DIRECTORY);
remote_copy(path, pr_fi).check_success();
REQUIRE(bfs::exists(path));
REQUIRE(bfs::is_directory(path));
CHECK(updates_mediator->is_masked(path_str) >= 1);
}
SECTION("symlink") {
SECTION("existing file") {
auto path = root_path / L"папка" / L"файл.txt";
bfs::path target = root_path / "content";
proto::set_type(pr_fi, proto::FileInfoType::SYMLINK);
proto::set_symlink_target(pr_fi, boost::nowide::narrow(target.wstring()));
write_file(target, "zzz");
remote_copy(path, pr_fi).check_success();
#ifndef SYNCSPIRIT_WIN
auto path_str = narrow(path.generic_wstring());
CHECK(updates_mediator->is_masked(path_str) == 1);
CHECK(bfs::exists(path));
CHECK(bfs::is_symlink(path));
CHECK(bfs::read_symlink(path) == target);
#endif
}
SECTION("non-existing file") {
auto path = root_path / L"папка" / L"файл.txt";
bfs::path target = root_path / "content";
proto::set_type(pr_fi, proto::FileInfoType::SYMLINK);
proto::set_symlink_target(pr_fi, boost::nowide::narrow(target.wstring()));
remote_copy(path, pr_fi).check_success();
CHECK(!bfs::exists(path));
#ifndef SYNCSPIRIT_WIN
auto path_str = narrow(path.generic_wstring());
CHECK(updates_mediator->is_masked(path_str) == 1);
CHECK(bfs::is_symlink(path));
CHECK(bfs::read_symlink(path) == target);
#endif
}
}
SECTION("deleted file") {
auto name = bfs::path(L"папка") / L"файл.bin";
pr_fi = {};
proto::set_name(pr_fi, boost::nowide::narrow(name.generic_wstring()));
proto::set_modified_s(pr_fi, modified);
proto::set_deleted(pr_fi, true);
bfs::path target = root_path / name;
auto path_str = narrow(target.generic_wstring());
bfs::create_directories(target.parent_path());
write_file(target, "zzz");
REQUIRE(bfs::exists(target));
remote_copy(target, pr_fi).check_success();
REQUIRE(!bfs::exists(target));
#ifndef SYNCSPIRIT_WATCHER_KQUEUE
CHECK(updates_mediator->is_masked(path_str) == 1);
#else
CHECK(updates_mediator->is_masked(target.parent_path().string()) == 1);
#endif
remote_copy(target, pr_fi).check_success();
CHECK(updates_mediator->is_masked(path_str) == 0);
REQUIRE(!bfs::exists(target));
}
SECTION("conflict") {
auto name = bfs::path(L"папка") / L"файл.bin";
proto::set_name(pr_fi, boost::nowide::narrow(name.generic_wstring()));
proto::set_modified_s(pr_fi, modified);
bfs::path target = root_path / name;
bfs::path conflict = target.parent_path() / L"конфликт.bin";
bfs::create_directories(target.parent_path());
write_file(target, "zzz");
REQUIRE(bfs::exists(target));
remote_copy(target, pr_fi, conflict).check_success();
CHECK(bfs::exists(target));
CHECK(bfs::exists(conflict));
CHECK(as_owned_bytes("zzz") == as_bytes(read_file(conflict)));
auto path_str = narrow(target.generic_wstring());
auto conflict_str = narrow(conflict.generic_wstring());
CHECK(updates_mediator->is_masked(path_str) >= 3);
CHECK(updates_mediator->is_masked(conflict_str) == 1);
}
}
};
F().run();
}
void test_append_block() {
struct F : fixture_t {
void main() noexcept override {
std::int64_t modified = 1641828421;
auto path_rel = bfs::path(L"путявка") / bfs::path(L"инфо.txt");
auto path_wstr = path_rel.generic_wstring();
auto path_str = boost::nowide::narrow(path_wstr);
auto data_1 = as_owned_bytes("12345");
auto perms = std::uint32_t(0444);
auto no_perms = !utils::platform_t::permissions_supported(path_rel);
SECTION("attempt finish non-existing") {
auto path = bfs::absolute(root_path / path_rel);
auto path_str = narrow(path.generic_wstring());
auto ec = utils::make_error_code(utils::error_code_t::flush_non_opened);
finish_file(path, 5, 1641828421, perms, no_perms).check_fail(ec);
CHECK(updates_mediator->is_masked(path_str) == 0);
}
SECTION("finish unflushed") {
auto dir_path = root_path / path_rel.parent_path();
bfs::create_directories(dir_path);
auto tmp_path = bfs::absolute(dir_path / L"инфо.txt.syncspirit-tmp");
write_file(tmp_path, "12345");
auto path = bfs::absolute(root_path / path_rel);
auto path_str = narrow(path.generic_wstring());
auto tmp_str = narrow(tmp_path.generic_wstring());
finish_file(path, 5, 1641828421, perms, no_perms).check_success();
REQUIRE(bfs::exists(path));
REQUIRE(bfs::file_size(path) == 5);
CHECK(updates_mediator->is_masked(path_str) >= 2);
CHECK(updates_mediator->is_masked(tmp_str) == 0);
CHECK(data_1 == as_bytes(read_file(path)));
CHECK(to_unix(bfs::last_write_time(path)) == 1641828421);
if (!no_perms) {
CHECK(static_cast<std::uint32_t>(bfs::status(path).permissions()) == perms);
}
}
SECTION("file with 1 block") {
auto path = bfs::absolute(root_path / path_rel);
auto path_str = narrow(path.generic_wstring());
auto tmp_path = narrow(make_temporal(path).generic_wstring());
append_block(path, data_1, 0, 5)
.check_success()
.finish_file(path, 5, 1641828421, perms, no_perms)
.check_success();
REQUIRE(bfs::exists(path));
REQUIRE(bfs::file_size(path) == 5);
CHECK(data_1 == as_bytes(read_file(path)));
CHECK(to_unix(bfs::last_write_time(path)) == 1641828421);
CHECK(updates_mediator->is_masked(path_str) >= 2);
CHECK(updates_mediator->is_masked(tmp_path) == 0);
#ifdef SYNCSPIRIT_WATCHER_KQUEUE
CHECK(updates_mediator->is_masked(path.parent_path().string()) == 2);
#endif
if (!no_perms) {
CHECK(static_cast<std::uint32_t>(bfs::status(path).permissions()) == perms);
}
}
SECTION("file with 1 block & conflict rename") {
auto path = bfs::absolute(root_path / path_rel);
auto path_str = narrow(path.generic_wstring());
auto tmp_path = narrow(make_temporal(path).generic_wstring());
write_file(path, "abcdef");
auto conflict_path = path.parent_path() / L"экс-инфо.txt";
append_block(path, data_1, 0, 5)
.check_success()
.finish_file(path, 5, 1641828421, perms, no_perms, conflict_path)
.check_success();
REQUIRE(bfs::exists(path));
CHECK(bfs::file_size(path) == 5);
CHECK(data_1 == as_bytes(read_file(path)));
CHECK(to_unix(bfs::last_write_time(path)) == 1641828421);
if (!no_perms) {
CHECK(static_cast<std::uint32_t>(bfs::status(path).permissions()) == perms);
}
REQUIRE(bfs::exists(conflict_path));
CHECK(bfs::file_size(conflict_path) == 6);
CHECK(as_bytes(read_file(conflict_path)) == as_owned_bytes("abcdef"));
CHECK(updates_mediator->is_masked(path_str) >= 2);
CHECK(updates_mediator->is_masked(tmp_path) == 0);
#ifdef SYNCSPIRIT_WATCHER_KQUEUE
CHECK(updates_mediator->is_masked(path.parent_path().string()) == 3);
#endif
}
SECTION("file with 2 different blocks") {
auto wfilename = boost::nowide::widen(path_str) + L".syncspirit-tmp";
auto filename = boost::nowide::narrow(wfilename);
auto tmp_path = root_path / filename;
auto path = root_path / path_wstr;
auto path_str = narrow(path.generic_wstring());
auto tmp_path_str = narrow(make_temporal(path).generic_wstring());
auto data = as_owned_bytes("12345");
append_block(path, data, 0, 10).check_success();
#ifndef SYNCSPIRIT_WIN
REQUIRE(bfs::exists(tmp_path));
REQUIRE(bfs::file_size(tmp_path) == 10);
CHECK(read_file(tmp_path).substr(0, 5) == "12345");
#endif
append_block(path, as_owned_bytes("67890"), 5, 10).check_success();
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
SECTION("add 2nd block") {
finish_file(path, 5, 1641828421, perms, no_perms).check_success();
REQUIRE(!bfs::exists(tmp_path));
REQUIRE(bfs::exists(path));
REQUIRE(bfs::file_size(path) == 10);
auto data = read_file(path);
CHECK(data == "1234567890");
CHECK(to_unix(bfs::last_write_time(path)) == 1641828421);
CHECK(updates_mediator->is_masked(path_str) >= 2);
if (!no_perms) {
CHECK(static_cast<std::uint32_t>(bfs::status(path).permissions()) == perms);
}
}
#ifndef SYNCSPIRIT_WIN
SECTION("remove folder (simulate err)") {
bfs::remove_all(root_path);
finish_file(path, 5, 1641828421, perms, no_perms).check_fail();
CHECK(updates_mediator->is_masked(path_str) == 0);
}
#endif
}
}
};
F().run();
}
void test_clone_block() {
struct F : fixture_t {
void main() noexcept override {
std::int64_t modified = 1641828421;
auto perms = std::uint32_t(0444);
#ifndef SYNCSPIRIT_WIN
auto no_perms = false;
#else
auto no_perms = true;
#endif
SECTION("source & target are different files") {
auto source_path = root_path / L"ать.txt";
auto target_path = root_path / L"ять.txt";
auto path_str = narrow(target_path.generic_wstring());
auto tmp_path_str = narrow(make_temporal(target_path).generic_wstring());
SECTION("single block target file") {
auto data = as_owned_bytes("12345");
append_block(source_path, data, 0, 5)
.check_success()
.finish_file(source_path, 5, modified, perms, no_perms)
.check_success()
.clone_block(target_path, 0, 5, source_path, 0, 5)
.check_success()
.finish_file(target_path, 5, modified, perms, no_perms)
.check_success();
REQUIRE(bfs::exists(target_path));
REQUIRE(bfs::file_size(target_path) == 5);
CHECK(read_file(target_path) == "12345");
CHECK(to_unix(bfs::last_write_time(target_path)) == modified);
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
#ifdef SYNCSPIRIT_WATCHER_KQUEUE
CHECK(updates_mediator->is_masked(target_path.parent_path().string()) == 4);
#endif
}
SECTION("multi block target file") {
auto data_1 = as_owned_bytes("12345");
auto data_2 = as_owned_bytes("67890");
append_block(source_path, data_1, 0, 10)
.check_success()
.append_block(source_path, data_2, 5, 10)
.check_success()
.finish_file(source_path, 10, modified, perms, no_perms)
.check_success()
.clone_block(target_path, 0, 10, source_path, 0, 5)
.check_success()
.clone_block(target_path, 5, 10, source_path, 5, 5)
.check_success()
.finish_file(target_path, 10, modified, perms, no_perms)
.check_success();
REQUIRE(bfs::exists(target_path));
REQUIRE(bfs::file_size(target_path) == 10);
CHECK(read_file(target_path) == "1234567890");
CHECK(to_unix(bfs::last_write_time(target_path)) == modified);
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
#ifdef SYNCSPIRIT_WATCHER_KQUEUE
CHECK(updates_mediator->is_masked(target_path.parent_path().string()) == 4);
#endif
}
SECTION("source/target different sizes") {
auto data_1 = as_owned_bytes("12345");
auto data_2 = as_owned_bytes("67890");
append_block(source_path, data_2, 0, 5)
.check_success()
.finish_file(source_path, 5, modified, perms, no_perms)
.check_success()
.append_block(target_path, data_1, 0, 10)
.check_success()
.clone_block(target_path, 5, 10, source_path, 0, 5)
.check_success()
.finish_file(target_path, 10, modified, perms, no_perms)
.check_success();
REQUIRE(bfs::exists(target_path));
REQUIRE(bfs::file_size(target_path) == 10);
CHECK(read_file(target_path) == "1234567890");
CHECK(to_unix(bfs::last_write_time(target_path)) == modified);
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
}
}
SECTION("source & target are is the same file") {
auto target_path = root_path / L"ы" / L"ять.txt";
auto data = as_owned_bytes("12345");
append_block(target_path, data, 0, 10)
.check_success()
.clone_block(target_path, 5, 10, target_path, 0, 5)
.check_success()
.finish_file(target_path, 10, modified, perms, no_perms)
.check_success();
REQUIRE(bfs::exists(target_path));
REQUIRE(bfs::file_size(target_path) == 10);
CHECK(read_file(target_path) == "1234512345");
CHECK(to_unix(bfs::last_write_time(target_path)) == modified);
auto tmp_path_str = narrow(make_temporal(target_path).generic_wstring());
CHECK(updates_mediator->is_masked(tmp_path_str) == 0);
}
}
};
F().run();
}
void test_update_meta() {
struct F : fixture_t {
void main() noexcept override {
std::int64_t modified = 1641828421;
auto perms = std::uint32_t(0444);
#ifndef SYNCSPIRIT_WIN
auto no_perms = false;
#else
auto no_perms = true;
#endif
auto path = root_path / L"файл.bin";
auto path_str = narrow(path.generic_wstring());
SECTION("file") {
write_file(path, "12345");
update_meta(path, modified, perms, no_perms).check_success();
CHECK(to_unix(bfs::last_write_time(path)) == modified);
#ifndef SYNCSPIRIT_WIN
CHECK(bfs::status(path).permissions() == static_cast<bfs::perms>(perms));
#endif
}
SECTION("file does not exists") { update_meta(path, modified, perms, no_perms).check_fail(); }
#ifndef SYNCSPIRIT_WIN
SECTION("dir") {
bfs::create_directories(path);
update_meta(path, modified, perms, no_perms).check_success();
CHECK(to_unix(bfs::last_write_time(path)) == modified);
CHECK(bfs::status(path).permissions() == static_cast<bfs::perms>(perms));
}
#endif
}
};
F().run();
}
void test_requesting_block() {
struct F : fixture_t {
void main() noexcept override {
bfs::path target = root_path / "a.txt";
std::int64_t modified = 1641828421;
auto fs_addr = file_actor->get_address();
auto back_addr = sup->get_address();
auto context = fs::payload::extendended_context_prt_t{};
auto payload = fs::payload::block_request_t(std::move(context), target, 0, 5);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto cmds = fs::payload::io_commands_t{nullptr};
cmds.commands.emplace_back(std::move(cmd));
sup->route<fs::payload::io_commands_t>(file_addr, sup->get_address(), std::move(cmds));
SECTION("error, no file") {
sup->do_process();
REQUIRE(reply);
auto &cmds = reply->payload.commands;
REQUIRE(cmds.size() == 1);
auto reply_payload = std::get_if<decltype(payload)>(&cmds.front());
REQUIRE(reply_payload);
REQUIRE(reply_payload->result.has_error());
}
SECTION("error, oversized request") {
write_file(target, "1234");
sup->do_process();
REQUIRE(reply);
auto &cmds = reply->payload.commands;
REQUIRE(cmds.size() == 1);
auto reply_payload = std::get_if<decltype(payload)>(&cmds.front());
REQUIRE(reply_payload);
REQUIRE(reply_payload->result.has_error());
}
SECTION("successful file reading") {
write_file(target, "1234567890");
sup->do_process();
REQUIRE(reply);
auto &cmds = reply->payload.commands;
REQUIRE(cmds.size() == 1);
auto reply_payload = std::get_if<decltype(payload)>(&cmds.front());
REQUIRE(reply_payload);
REQUIRE(reply_payload->result.has_value());
REQUIRE(reply_payload->result.value() == as_bytes("12345"));
reply.reset();
auto context = fs::payload::extendended_context_prt_t{};
auto payload = fs::payload::block_request_t(std::move(context), target, 5, 5);
auto cmd = fs::payload::io_command_t(std::move(payload));
auto command = fs::payload::io_commands_t{};
command.commands.emplace_back(std::move(cmd));
auto msg = r::make_routed_message<fs::payload::io_commands_t>(file_addr, sup->get_address(),
std::move(command));
sup->put(std::move(msg));
sup->do_process();
REQUIRE(reply);
REQUIRE(reply->payload.commands.size() == 1);
reply_payload = std::get_if<decltype(payload)>(&reply->payload.commands.front());
REQUIRE(reply_payload);
REQUIRE(reply_payload->result.has_value());
REQUIRE(reply_payload->result.value() == as_bytes("67890"));
}
}
};
F().run();
}
int _init() {
test::init_logging();
REGISTER_TEST_CASE(test_remote_copy, "test_remote_copy", "[fs]");
REGISTER_TEST_CASE(test_append_block, "test_append_block", "[fs]");
REGISTER_TEST_CASE(test_clone_block, "test_clone_block", "[fs]");
REGISTER_TEST_CASE(test_update_meta, "test_update_meta", "[fs]");
REGISTER_TEST_CASE(test_requesting_block, "test_requesting_block", "[fs]");
return 1;
}
static int v = _init();