v2.0.2-rc (#27)
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
Some checks reported errors
BlockStorage/repertory/pipeline/head Something is wrong with the build of this commit
## v2.0.2-rc ### BREAKING CHANGES * Refactored `config.json` - will need to verify configuration settings prior to mounting ### Issues * \#12 \[Unit Test\] Complete all providers unit tests * \#14 \[Unit Test\] SQLite mini-ORM unit tests and cleanup * \#16 Add support for bucket name in Sia provider * \#17 Update to common c++ build system * 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 on Windows * OS X support is temporarily disabled * \#19 \[bug\] Rename file is broken for files that are existing * \#23 \[bug\] Incorrect file size displayed while upload is pending * \#24 RocksDB implementations should be transactional * \#25 Writes should block when maximum cache size is reached * \#26 Complete ring buffer and direct download support ### Changes from v2.0.1-rc * Ability to choose between RocksDB and SQLite databases * Added direct reads and implemented download fallback * Corrected file times on S3 and Sia providers * Corrected handling of `chown()` and `chmod()` * Fixed erroneous download of chunks after resize Reviewed-on: #27
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_FILE_DB_FIXTURE_HPP
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_FILE_DB_FIXTURE_HPP
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "db/impl/rdb_file_db.hpp"
|
||||
#include "db/impl/sqlite_file_db.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
|
||||
namespace repertory {
|
||||
template <typename db_t> class file_db_test : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<app_config> config;
|
||||
static console_consumer console_;
|
||||
static std::unique_ptr<db_t> file_db;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
static std::uint64_t idx{};
|
||||
|
||||
event_system::instance().start();
|
||||
auto cfg_directory = utils::path::combine(test::get_test_output_dir(),
|
||||
{
|
||||
"file_db_test",
|
||||
std::to_string(++idx),
|
||||
});
|
||||
config = std::make_unique<app_config>(provider_type::s3, cfg_directory);
|
||||
file_db = std::make_unique<db_t>(*config);
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
file_db.reset();
|
||||
config.reset();
|
||||
event_system::instance().stop();
|
||||
}
|
||||
};
|
||||
|
||||
using file_db_types = ::testing::Types<rdb_file_db, sqlite_file_db>;
|
||||
|
||||
template <typename db_t> std::unique_ptr<app_config> file_db_test<db_t>::config;
|
||||
|
||||
template <typename db_t> console_consumer file_db_test<db_t>::console_;
|
||||
|
||||
template <typename db_t> std::unique_ptr<db_t> file_db_test<db_t>::file_db;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FILE_DB_FIXTURE_HPP
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_FILE_MGR_DB_FIXTURE_HPP
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_FILE_MGR_DB_FIXTURE_HPP
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "db/impl/rdb_file_mgr_db.hpp"
|
||||
#include "db/impl/sqlite_file_mgr_db.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
|
||||
namespace repertory {
|
||||
template <typename db_t> class file_mgr_db_test : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<app_config> config;
|
||||
static console_consumer console_;
|
||||
static std::unique_ptr<db_t> file_mgr_db;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
static std::uint64_t idx{};
|
||||
|
||||
event_system::instance().start();
|
||||
auto cfg_directory = utils::path::combine(test::get_test_output_dir(),
|
||||
{
|
||||
"file_mgr_db_test",
|
||||
std::to_string(++idx),
|
||||
});
|
||||
config = std::make_unique<app_config>(provider_type::s3, cfg_directory);
|
||||
file_mgr_db = std::make_unique<db_t>(*config);
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
file_mgr_db.reset();
|
||||
config.reset();
|
||||
event_system::instance().stop();
|
||||
}
|
||||
};
|
||||
|
||||
using file_mgr_db_types = ::testing::Types<rdb_file_mgr_db, sqlite_file_mgr_db>;
|
||||
|
||||
template <typename db_t>
|
||||
std::unique_ptr<app_config> file_mgr_db_test<db_t>::config;
|
||||
|
||||
template <typename db_t> console_consumer file_mgr_db_test<db_t>::console_;
|
||||
|
||||
template <typename db_t>
|
||||
std::unique_ptr<db_t> file_mgr_db_test<db_t>::file_mgr_db;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FILE_MGR_DB_FIXTURE_HPP
|
415
repertory/repertory_test/include/fixtures/fuse_fixture.hpp
Normal file
415
repertory/repertory_test/include/fixtures/fuse_fixture.hpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "comm/curl/curl_comm.hpp"
|
||||
#include "db/i_meta_db.hpp"
|
||||
#include "db/meta_db.hpp"
|
||||
#include "drives/fuse/fuse_drive.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#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 {
|
||||
std::atomic<std::size_t> idx{0U};
|
||||
constexpr const auto SLEEP_SECONDS{1.5s};
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
struct local_s3 final {
|
||||
static constexpr const provider_type type{provider_type::s3};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
};
|
||||
|
||||
struct local_sia final {
|
||||
static constexpr const provider_type type{provider_type::sia};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
};
|
||||
|
||||
struct remote_s3 final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
};
|
||||
|
||||
struct remote_sia final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
};
|
||||
|
||||
template <typename provider_t> class fuse_test : public ::testing::Test {
|
||||
public:
|
||||
static std::unique_ptr<app_config> config;
|
||||
static std::filesystem::path current_directory;
|
||||
static provider_type current_provider;
|
||||
static std::vector<std::string> drive_args;
|
||||
static std::vector<std::string> drive_args2;
|
||||
static std::unique_ptr<i_meta_db> meta;
|
||||
static std::string mount_location;
|
||||
static std::string mount_location2;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
current_directory = std::filesystem::current_path();
|
||||
|
||||
const auto mount_s3 = [&]() {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"fuse_test",
|
||||
app_config::get_provider_name(current_provider),
|
||||
});
|
||||
|
||||
mount_location = utils::path::combine(test_directory, {"mount"});
|
||||
ASSERT_TRUE(utils::file::directory(mount_location).create_directory());
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
config = std::make_unique<app_config>(current_provider, cfg_directory);
|
||||
{
|
||||
app_config src_cfg{
|
||||
provider_type::s3,
|
||||
utils::path::combine(test::get_test_config_dir(), {"s3"}),
|
||||
};
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
config->set_s3_config(src_cfg.get_s3_config());
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
drive_args = std::vector<std::string>({
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-s3",
|
||||
"-na",
|
||||
"s3",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
|
||||
meta = create_meta_db(*config);
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_sia = [&]() {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"fuse_test",
|
||||
app_config::get_provider_name(current_provider),
|
||||
});
|
||||
|
||||
mount_location = utils::path::combine(test_directory, {"mount"});
|
||||
ASSERT_TRUE(utils::file::directory(mount_location).create_directory());
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
config = std::make_unique<app_config>(current_provider, cfg_directory);
|
||||
{
|
||||
app_config src_cfg{
|
||||
provider_type::sia,
|
||||
utils::path::combine(test::get_test_config_dir(), {"sia"}),
|
||||
};
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
config->set_host_config(src_cfg.get_host_config());
|
||||
config->set_sia_config(src_cfg.get_sia_config());
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
drive_args = std::vector<std::string>({
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-na",
|
||||
"sia",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
|
||||
meta = create_meta_db(*config);
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_remote = [&]() {
|
||||
{
|
||||
mount_location2 = mount_location;
|
||||
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"fuse_test",
|
||||
app_config::get_provider_name(provider_t::type) + '_' +
|
||||
app_config::get_provider_name(provider_t::type2),
|
||||
});
|
||||
|
||||
mount_location = utils::path::combine(test_directory, {"mount"});
|
||||
ASSERT_TRUE(utils::file::directory(mount_location).create_directory());
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
auto config2 =
|
||||
std::make_unique<app_config>(provider_type::remote, cfg_directory);
|
||||
config2->set_enable_drive_events(true);
|
||||
config2->set_event_level(event_level::trace);
|
||||
|
||||
drive_args2 = std::vector<std::string>({
|
||||
"-dd",
|
||||
config2->get_data_directory(),
|
||||
"-rm",
|
||||
"localhost:30000",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
|
||||
execute_mount(drive_args2, mount_location);
|
||||
};
|
||||
|
||||
switch (provider_t::type) {
|
||||
case provider_type::s3: {
|
||||
mount_s3();
|
||||
} break;
|
||||
|
||||
case provider_type::sia: {
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
case provider_type::remote: {
|
||||
switch (provider_t::type2) {
|
||||
case provider_type::s3: {
|
||||
mount_s3();
|
||||
} break;
|
||||
|
||||
case provider_type::sia: {
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("remote provider type is not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
mount_remote();
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("provider type is not implemented");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
if (provider_t::type == provider_type::remote) {
|
||||
execute_unmount(mount_location);
|
||||
execute_unmount(mount_location2);
|
||||
} else {
|
||||
execute_unmount(mount_location);
|
||||
}
|
||||
|
||||
meta.reset();
|
||||
config.reset();
|
||||
|
||||
std::filesystem::current_path(current_directory);
|
||||
}
|
||||
|
||||
public:
|
||||
static auto create_file_path(std::string &file_name) {
|
||||
file_name += std::to_string(++idx);
|
||||
auto file_path = utils::path::combine(mount_location, {file_name});
|
||||
return file_path;
|
||||
}
|
||||
|
||||
static auto create_file_and_test(std::string &file_name, mode_t perms)
|
||||
-> std::string {
|
||||
file_name += std::to_string(++idx);
|
||||
auto file_path = utils::path::combine(mount_location, {file_name});
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_EXCL | O_RDWR, perms);
|
||||
EXPECT_LE(1, handle);
|
||||
|
||||
auto opt_size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(opt_size.has_value());
|
||||
if (opt_size.has_value()) {
|
||||
EXPECT_EQ(0U, opt_size.value());
|
||||
}
|
||||
|
||||
EXPECT_EQ(0, close(handle));
|
||||
|
||||
EXPECT_TRUE(utils::file::file(file_path).exists());
|
||||
EXPECT_FALSE(utils::file::directory(file_path).exists());
|
||||
|
||||
struct stat64 unix_st{};
|
||||
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
|
||||
EXPECT_EQ(getgid(), unix_st.st_gid);
|
||||
EXPECT_EQ(getuid(), unix_st.st_uid);
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
static auto create_file_and_test(std::string &file_name) -> std::string {
|
||||
return create_file_and_test(file_name, ACCESSPERMS);
|
||||
}
|
||||
|
||||
static auto create_directory_and_test(std::string &dir_name, mode_t perms)
|
||||
-> std::string {
|
||||
dir_name += std::to_string(++idx);
|
||||
|
||||
auto dir_path = utils::path::combine(mount_location, {dir_name});
|
||||
mkdir(dir_path.c_str(), perms);
|
||||
|
||||
EXPECT_TRUE(utils::file::directory(dir_path).exists());
|
||||
EXPECT_FALSE(utils::file::file(dir_path).exists());
|
||||
|
||||
struct stat64 unix_st{};
|
||||
EXPECT_EQ(0, stat64(dir_path.c_str(), &unix_st));
|
||||
EXPECT_EQ(getgid(), unix_st.st_gid);
|
||||
EXPECT_EQ(getuid(), unix_st.st_uid);
|
||||
|
||||
return dir_path;
|
||||
}
|
||||
|
||||
static auto create_directory_and_test(std::string &dir_name) -> std::string {
|
||||
return create_directory_and_test(dir_name, ACCESSPERMS);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
[[maybe_unused]] auto res =
|
||||
meta->set_item_meta(api_path, {
|
||||
{META_UID, "0"},
|
||||
{META_GID, "0"},
|
||||
});
|
||||
std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
|
||||
return file_path;
|
||||
}
|
||||
|
||||
static void execute_mount(auto args, auto location) {
|
||||
auto mount_cmd = "./repertory " + utils::string::join(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{location}.exists());
|
||||
}
|
||||
|
||||
static void execute_unmount(auto location) {
|
||||
auto unmounted{false};
|
||||
for (int i = 0; not unmounted && (i < 50); i++) {
|
||||
auto res = fuse_base::unmount(location);
|
||||
unmounted = res == 0;
|
||||
ASSERT_EQ(0, res);
|
||||
if (not unmounted) {
|
||||
std::this_thread::sleep_for(5s);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(unmounted);
|
||||
}
|
||||
|
||||
static void rmdir_and_test(std::string_view dir_path) {
|
||||
EXPECT_TRUE(utils::file::directory(dir_path).remove());
|
||||
EXPECT_FALSE(utils::file::directory(dir_path).exists());
|
||||
|
||||
EXPECT_FALSE(utils::file::file(dir_path).exists());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
static void unlink_root_file(const std::string &file_path) {
|
||||
auto api_path = utils::path::create_api_path(
|
||||
utils::path::strip_to_file_name(file_path));
|
||||
|
||||
[[maybe_unused]] auto res =
|
||||
meta->set_item_meta(api_path, {
|
||||
{META_UID, std::to_string(getuid())},
|
||||
{META_GID, std::to_string(getgid())},
|
||||
});
|
||||
std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
|
||||
unlink_file_and_test(file_path);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<app_config> fuse_test<provider_t>::config;
|
||||
|
||||
template <typename provider_t>
|
||||
std::filesystem::path fuse_test<provider_t>::current_directory;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type fuse_test<provider_t>::current_provider{provider_t::type2};
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> fuse_test<provider_t>::drive_args;
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> fuse_test<provider_t>::drive_args2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::unique_ptr<i_meta_db> fuse_test<provider_t>::meta{};
|
||||
|
||||
template <typename provider_t>
|
||||
std::string fuse_test<provider_t>::mount_location;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string fuse_test<provider_t>::mount_location2;
|
||||
|
||||
using fuse_provider_types = ::testing::Types<local_s3, remote_s3>;
|
||||
// using fuse_provider_types =
|
||||
// ::testing::Types<local_s3, remote_s3, local_sia, remote_sia>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FUSE_FIXTURE_HPP
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_META_DB_FIXTURE_HPP
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_META_DB_FIXTURE_HPP
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "db/impl/rdb_meta_db.hpp"
|
||||
#include "db/impl/sqlite_meta_db.hpp"
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
|
||||
namespace repertory {
|
||||
template <typename db_t> class meta_db_test : public ::testing::Test {
|
||||
protected:
|
||||
static std::unique_ptr<app_config> config;
|
||||
static console_consumer console_;
|
||||
static std::unique_ptr<db_t> meta_db;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
static std::uint64_t idx{};
|
||||
|
||||
event_system::instance().start();
|
||||
auto cfg_directory = utils::path::combine(test::get_test_output_dir(),
|
||||
{
|
||||
"meta_db_test",
|
||||
std::to_string(++idx),
|
||||
});
|
||||
config = std::make_unique<app_config>(provider_type::s3, cfg_directory);
|
||||
meta_db = std::make_unique<db_t>(*config);
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
meta_db.reset();
|
||||
config.reset();
|
||||
event_system::instance().stop();
|
||||
}
|
||||
};
|
||||
|
||||
using meta_db_types = ::testing::Types<rdb_meta_db, sqlite_meta_db>;
|
||||
|
||||
template <typename db_t> std::unique_ptr<app_config> meta_db_test<db_t>::config;
|
||||
|
||||
template <typename db_t> console_consumer meta_db_test<db_t>::console_;
|
||||
|
||||
template <typename db_t> std::unique_ptr<db_t> meta_db_test<db_t>::meta_db;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_META_DB_FIXTURE_HPP
|
289
repertory/repertory_test/include/fixtures/winfsp_fixture.hpp
Normal file
289
repertory/repertory_test/include/fixtures/winfsp_fixture.hpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.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"
|
||||
|
||||
namespace {
|
||||
std::atomic<std::size_t> idx{0U};
|
||||
constexpr const auto SLEEP_SECONDS{1.5s};
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
struct local_s3 final {
|
||||
static constexpr const provider_type type{provider_type::s3};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
};
|
||||
|
||||
struct local_sia final {
|
||||
static constexpr const provider_type type{provider_type::sia};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
};
|
||||
|
||||
struct remote_s3 final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::s3};
|
||||
};
|
||||
|
||||
struct remote_sia final {
|
||||
static constexpr const provider_type type{provider_type::remote};
|
||||
static constexpr const provider_type type2{provider_type::sia};
|
||||
};
|
||||
|
||||
template <typename provider_t> class winfsp_test : public ::testing::Test {
|
||||
public:
|
||||
static std::filesystem::path current_directory;
|
||||
static provider_type current_provider;
|
||||
static std::vector<std::string> drive_args;
|
||||
static std::vector<std::string> drive_args2;
|
||||
static std::string mount_location;
|
||||
static std::string mount_location2;
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase() {
|
||||
current_directory = std::filesystem::current_path();
|
||||
|
||||
mount_location = utils::string::to_lower(std::string{"U:"});
|
||||
|
||||
const auto mount_s3 = [&]() {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"winfsp_test",
|
||||
app_config::get_provider_name(provider_type::s3),
|
||||
});
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
auto config =
|
||||
std::make_unique<app_config>(provider_type::s3, cfg_directory);
|
||||
{
|
||||
app_config src_cfg{
|
||||
provider_type::s3,
|
||||
utils::path::combine(test::get_test_config_dir(), {"s3"}),
|
||||
};
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
config->set_s3_config(src_cfg.get_s3_config());
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
drive_args = std::vector<std::string>({
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-s3",
|
||||
"-na",
|
||||
"s3",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_sia = [&]() {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"winfsp_test",
|
||||
app_config::get_provider_name(provider_type::sia),
|
||||
});
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
auto config =
|
||||
std::make_unique<app_config>(provider_type::s3, cfg_directory);
|
||||
{
|
||||
app_config src_cfg{
|
||||
provider_type::sia,
|
||||
utils::path::combine(test::get_test_config_dir(), {"sia"}),
|
||||
};
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
config->set_host_config(src_cfg.get_host_config());
|
||||
config->set_sia_config(src_cfg.get_sia_config());
|
||||
|
||||
auto r_cfg = config->get_remote_mount();
|
||||
r_cfg.enable = true;
|
||||
r_cfg.api_port = 30000U;
|
||||
config->set_remote_mount(r_cfg);
|
||||
}
|
||||
|
||||
drive_args = std::vector<std::string>({
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-na",
|
||||
"sia",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
|
||||
execute_mount(drive_args, mount_location);
|
||||
};
|
||||
|
||||
const auto mount_remote = [&]() {
|
||||
{
|
||||
auto test_directory = utils::path::combine(
|
||||
test::get_test_output_dir(),
|
||||
{
|
||||
"winfsp_test",
|
||||
app_config::get_provider_name(provider_type::remote),
|
||||
});
|
||||
|
||||
mount_location2 = mount_location;
|
||||
mount_location = utils::string::to_lower(std::string{"V:"});
|
||||
|
||||
auto cfg_directory = utils::path::combine(test_directory, {"cfg2"});
|
||||
ASSERT_TRUE(utils::file::directory(cfg_directory).create_directory());
|
||||
|
||||
auto config =
|
||||
std::make_unique<app_config>(provider_type::remote, cfg_directory);
|
||||
config->set_enable_drive_events(true);
|
||||
config->set_event_level(event_level::trace);
|
||||
|
||||
drive_args2 = std::vector<std::string>({
|
||||
"-dd",
|
||||
config->get_data_directory(),
|
||||
"-rm",
|
||||
"localhost:30000",
|
||||
mount_location,
|
||||
});
|
||||
}
|
||||
|
||||
execute_mount(drive_args2, mount_location);
|
||||
};
|
||||
|
||||
switch (provider_t::type) {
|
||||
case provider_type::s3: {
|
||||
mount_s3();
|
||||
} break;
|
||||
|
||||
case provider_type::sia: {
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
case provider_type::remote: {
|
||||
switch (provider_t::type2) {
|
||||
case provider_type::s3: {
|
||||
mount_s3();
|
||||
} break;
|
||||
|
||||
case provider_type::sia: {
|
||||
mount_sia();
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("remote provider type is not implemented");
|
||||
return;
|
||||
}
|
||||
|
||||
mount_remote();
|
||||
} break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("provider type is not implemented");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void TearDownTestCase() {
|
||||
if (provider_t::type == provider_type::remote) {
|
||||
execute_unmount(drive_args2, mount_location);
|
||||
execute_unmount(drive_args, mount_location2);
|
||||
} else {
|
||||
execute_unmount(drive_args, mount_location);
|
||||
}
|
||||
std::filesystem::current_path(current_directory);
|
||||
}
|
||||
|
||||
static void execute_mount(auto args, auto location) {
|
||||
auto mount_cmd =
|
||||
"start .\\repertory.exe -f " + utils::string::join(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{location}.exists());
|
||||
}
|
||||
|
||||
static void execute_unmount(auto args, auto location) {
|
||||
auto unmounted{false};
|
||||
|
||||
auto unmount_cmd =
|
||||
".\\repertory.exe " + utils::string::join(args, ' ') + " -unmount";
|
||||
for (int i = 0; not unmounted && (i < 6); i++) {
|
||||
std::cout << "unmount command: " << unmount_cmd << std::endl;
|
||||
system(unmount_cmd.c_str());
|
||||
unmounted = not utils::file::directory{location}.exists();
|
||||
if (not unmounted) {
|
||||
std::this_thread::sleep_for(5s);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(unmounted);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename provider_t>
|
||||
std::filesystem::path winfsp_test<provider_t>::current_directory;
|
||||
|
||||
template <typename provider_t>
|
||||
provider_type winfsp_test<provider_t>::current_provider{provider_t::type2};
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> winfsp_test<provider_t>::drive_args;
|
||||
|
||||
template <typename provider_t>
|
||||
std::vector<std::string> winfsp_test<provider_t>::drive_args2;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string winfsp_test<provider_t>::mount_location;
|
||||
|
||||
template <typename provider_t>
|
||||
std::string winfsp_test<provider_t>::mount_location2;
|
||||
|
||||
// using winfsp_provider_types = ::testing::Types<local_s3, remote_s3,
|
||||
// local_sia, remote_sia>;
|
||||
using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_WINFSP_FIXTURE_HPP
|
159
repertory/repertory_test/include/mocks/mock_fuse_drive.hpp
Normal file
159
repertory/repertory_test/include/mocks/mock_fuse_drive.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_FIXTURES_MOCKS_MOCK_FUSE_DRIVE_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_FIXTURES_MOCKS_MOCK_FUSE_DRIVE_HPP_
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "drives/fuse/i_fuse_drive.hpp"
|
||||
#include "types/remote.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class mock_fuse_drive final : public virtual i_fuse_drive {
|
||||
public:
|
||||
explicit mock_fuse_drive(std::string mount_location)
|
||||
: mount_location_(std::move(mount_location)) {}
|
||||
|
||||
private:
|
||||
std::string mount_location_;
|
||||
std::unordered_map<std::string, api_meta_map> meta_;
|
||||
|
||||
public:
|
||||
auto check_owner(const std::string &) const -> api_error override {
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto check_parent_access(const std::string &,
|
||||
int) const -> api_error override {
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto get_directory_item_count(const std::string &) const
|
||||
-> std::uint64_t override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto get_directory_items(const std::string &) const
|
||||
-> directory_item_list override {
|
||||
directory_item_list list{};
|
||||
|
||||
directory_item dir_item{};
|
||||
dir_item.api_path = ".";
|
||||
dir_item.directory = true;
|
||||
dir_item.size = 0;
|
||||
dir_item.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(dir_item);
|
||||
|
||||
dir_item.api_path = "..";
|
||||
list.emplace_back(dir_item);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
auto get_file_size(const std::string &) const -> std::uint64_t override {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
auto get_item_meta(const std::string &api_path,
|
||||
api_meta_map &meta) const -> api_error override {
|
||||
meta = const_cast<mock_fuse_drive *>(this)->meta_[api_path];
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto get_item_meta(const std::string &api_path, const std::string &name,
|
||||
std::string &value) const -> api_error override {
|
||||
value = const_cast<mock_fuse_drive *>(this)->meta_[api_path][name];
|
||||
if (value.empty()) {
|
||||
value = "0";
|
||||
}
|
||||
return api_error::success;
|
||||
}
|
||||
|
||||
auto get_total_drive_space() const -> std::uint64_t override {
|
||||
return 100ULL * 1024ULL * 1024ULL;
|
||||
}
|
||||
|
||||
auto get_total_item_count() const -> std::uint64_t override { return 0U; }
|
||||
|
||||
auto get_used_drive_space() const -> std::uint64_t override { return 0U; }
|
||||
|
||||
void get_volume_info(UINT64 &total_size, UINT64 &free_size,
|
||||
std::string &volume_label) const override {
|
||||
free_size = 100u;
|
||||
total_size = 200u;
|
||||
volume_label = "TestVolumeLabel";
|
||||
}
|
||||
|
||||
auto rename_directory(const std::string &from_api_path,
|
||||
const std::string &to_api_path) -> int override {
|
||||
const auto from_file_path =
|
||||
utils::path::combine(mount_location_, {from_api_path});
|
||||
const auto to_file_path =
|
||||
utils::path::combine(mount_location_, {to_api_path});
|
||||
return rename(from_file_path.c_str(), to_file_path.c_str());
|
||||
}
|
||||
|
||||
auto rename_file(const std::string &from_api_path,
|
||||
const std::string &to_api_path,
|
||||
bool overwrite) -> int override {
|
||||
const auto from_file_path =
|
||||
utils::path::combine(mount_location_, {from_api_path});
|
||||
const auto to_file_path =
|
||||
utils::path::combine(mount_location_, {to_api_path});
|
||||
|
||||
if (overwrite) {
|
||||
if (not utils::file::file(to_file_path).remove()) {
|
||||
return -1;
|
||||
}
|
||||
} else if (utils::file::directory(to_file_path).exists() ||
|
||||
utils::file::file(to_file_path).exists()) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return rename(from_file_path.c_str(), to_file_path.c_str());
|
||||
}
|
||||
|
||||
auto is_processing(const std::string &) const -> bool override {
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_item_meta(const std::string &api_path, const std::string &key,
|
||||
const std::string &value) override {
|
||||
meta_[api_path][key] = value;
|
||||
}
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
||||
#endif // REPERTORY_TEST_INCLUDE_FIXTURES_MOCKS_MOCK_FUSE_DRIVE_HPP_
|
108
repertory/repertory_test/include/mocks/mock_open_file.hpp
Normal file
108
repertory/repertory_test/include/mocks/mock_open_file.hpp
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_MOCKS_MOCK_OPEN_FILE_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_MOCKS_MOCK_OPEN_FILE_HPP_
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "file_manager/i_open_file.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class mock_open_file : public virtual i_closeable_open_file {
|
||||
public:
|
||||
MOCK_METHOD(void, add, (std::uint64_t handle, open_file_data ofd),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(bool, can_close, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, close, (), (override));
|
||||
|
||||
MOCK_METHOD(std::string, get_api_path, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::size_t, get_chunk_size, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::uint64_t, get_file_size, (), (const, override));
|
||||
|
||||
MOCK_METHOD(filesystem_item, get_filesystem_item, (), (const, override));
|
||||
|
||||
MOCK_METHOD(open_file_data &, get_open_data, (std::uint64_t handle),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(const open_file_data &, get_open_data, (std::uint64_t handle),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(std::size_t, get_open_file_count, (), (const, override));
|
||||
|
||||
MOCK_METHOD(boost::dynamic_bitset<>, get_read_state, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, get_allocated, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::vector<std::uint64_t>, get_handles, (), (const, override));
|
||||
|
||||
MOCK_METHOD((std::map<std::uint64_t, open_file_data> &), get_open_data, (),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD((const std::map<std::uint64_t, open_file_data> &), get_open_data,
|
||||
(), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, get_read_state, (std::size_t chunk), (const, override));
|
||||
|
||||
MOCK_METHOD(std::string, get_source_path, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, has_handle, (std::uint64_t handle), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, is_complete, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, is_directory, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, is_modified, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, is_write_supported, (), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, native_operation, (native_operation_callback callback),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, native_operation,
|
||||
(std::uint64_t new_file_size, native_operation_callback callback),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, read,
|
||||
(std::size_t read_size, std::uint64_t read_offset,
|
||||
data_buffer &data),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, remove, (std::uint64_t handle), (override));
|
||||
|
||||
MOCK_METHOD(void, remove_all, (), (override));
|
||||
|
||||
MOCK_METHOD(api_error, resize, (std::uint64_t new_file_size), (override));
|
||||
|
||||
MOCK_METHOD(void, set_api_path, (const std::string &api_path), (override));
|
||||
|
||||
MOCK_METHOD(api_error, write,
|
||||
(std::uint64_t write_offset, const data_buffer &data,
|
||||
std::size_t &bytes_written),
|
||||
(override));
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_MOCKS_MOCK_OPEN_FILE_HPP_
|
162
repertory/repertory_test/include/mocks/mock_provider.hpp
Normal file
162
repertory/repertory_test/include/mocks/mock_provider.hpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_MOCKS_MOCK_PROVIDER_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_MOCKS_MOCK_PROVIDER_HPP_
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class mock_provider : public virtual i_provider {
|
||||
public:
|
||||
mock_provider(bool allow_rename = true) : allow_rename_(allow_rename) {}
|
||||
|
||||
private:
|
||||
const bool allow_rename_;
|
||||
|
||||
public:
|
||||
MOCK_METHOD(api_error, create_directory,
|
||||
(const std::string &api_path, api_meta_map &meta), (override));
|
||||
|
||||
MOCK_METHOD(api_error, create_directory_clone_source_meta,
|
||||
(const std::string &source_api_path, const std::string &api_path),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, create_file,
|
||||
(const std::string &api_path, api_meta_map &meta), (override));
|
||||
|
||||
MOCK_METHOD(api_error, get_api_path_from_source,
|
||||
(const std::string &source_path, std::string &api_path),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(std::uint64_t, get_directory_item_count,
|
||||
(const std::string &api_path), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_directory_items,
|
||||
(const std::string &api_path, directory_item_list &list),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_file,
|
||||
(const std::string &api_path, api_file &file), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_file_list,
|
||||
(api_file_list & list, std::string &marker), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_file_size,
|
||||
(const std::string &api_path, std::uint64_t &file_size),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_filesystem_item,
|
||||
(const std::string &api_path, bool directory,
|
||||
filesystem_item &fsi),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_filesystem_item_and_file,
|
||||
(const std::string &api_path, api_file &file,
|
||||
filesystem_item &fsi),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_filesystem_item_from_source_path,
|
||||
(const std::string &source_path, filesystem_item &fsi),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_item_meta,
|
||||
(const std::string &api_path, api_meta_map &meta),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(api_error, get_item_meta,
|
||||
(const std::string &api_path, const std::string &key,
|
||||
std::string &value),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD((std::vector<std::string>), get_pinned_files, (),
|
||||
(const, override));
|
||||
|
||||
MOCK_METHOD(provider_type, get_provider_type, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::uint64_t, get_total_drive_space, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::uint64_t, get_total_item_count, (), (const, override));
|
||||
|
||||
MOCK_METHOD(std::uint64_t, get_used_drive_space, (), (const, override));
|
||||
|
||||
MOCK_METHOD(bool, is_read_only, (), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, is_directory,
|
||||
(const std::string &api_path, bool &exists), (const, override));
|
||||
|
||||
MOCK_METHOD(api_error, is_file, (const std::string &api_path, bool &exists),
|
||||
(const, override));
|
||||
|
||||
bool is_file_writeable(const std::string & /* api_path */) const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOCK_METHOD(bool, is_online, (), (const, override));
|
||||
|
||||
bool is_rename_supported() const override { return allow_rename_; }
|
||||
|
||||
MOCK_METHOD(api_error, read_file_bytes,
|
||||
(const std::string &path, std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data, stop_type &stop_requested),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, remove_directory, (const std::string &api_path),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, remove_file, (const std::string &api_path),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, remove_item_meta,
|
||||
(const std::string &api_path, const std::string &key),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, rename_file,
|
||||
(const std::string &from_api_path,
|
||||
const std::string &to_api_path),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, set_item_meta,
|
||||
(const std::string &api_path, const std::string &key,
|
||||
const std::string &value),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(api_error, set_item_meta,
|
||||
(const std::string &api_path, const api_meta_map &meta),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(bool, start,
|
||||
(api_item_added_callback api_item_added, i_file_manager *fm),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, stop, (), (override));
|
||||
|
||||
MOCK_METHOD(api_error, upload_file,
|
||||
(const std::string &api_path, const std::string &source_path,
|
||||
stop_type &stop_requested),
|
||||
(override));
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_MOCKS_MOCK_PROVIDER_HPP_
|
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_MOCKS_MOCK_UPLOAD_MANAGER_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_MOCKS_MOCK_UPLOAD_MANAGER_HPP_
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "file_manager/i_upload_manager.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class mock_upload_manager : public i_upload_manager {
|
||||
public:
|
||||
MOCK_METHOD(void, queue_upload, (const i_open_file &o), (override));
|
||||
|
||||
MOCK_METHOD(void, remove_resume,
|
||||
(const std::string &api_path, const std::string &source_path),
|
||||
(override));
|
||||
|
||||
MOCK_METHOD(void, remove_upload, (const std::string &api_path), (override));
|
||||
|
||||
MOCK_METHOD(void, store_resume, (const i_open_file &o), (override));
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_MOCKS_MOCK_UPLOAD_MANAGER_HPP_
|
182
repertory/repertory_test/include/mocks/mock_winfsp_drive.hpp
Normal file
182
repertory/repertory_test/include/mocks/mock_winfsp_drive.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_MOCKS_MOCK_WINFSP_DRIVE_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_MOCKS_MOCK_WINFSP_DRIVE_HPP_
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "drives/winfsp/i_winfsp_drive.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class mock_winfsp_drive final : public virtual i_winfsp_drive {
|
||||
public:
|
||||
explicit mock_winfsp_drive(std::string mount_location)
|
||||
: mount_location_(std::move(mount_location)) {}
|
||||
|
||||
private:
|
||||
std::string mount_location_;
|
||||
|
||||
public:
|
||||
[[nodiscard]] auto
|
||||
get_directory_item_count(const std::string & /*api_path*/) const
|
||||
-> std::uint64_t override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_directory_items(const std::string & /*api_path*/) const
|
||||
-> directory_item_list override {
|
||||
directory_item_list list{};
|
||||
|
||||
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())}};
|
||||
list.emplace_back(di);
|
||||
|
||||
di.api_path = "..";
|
||||
list.emplace_back(di);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_file_size(const std::string & /*api_path*/) const
|
||||
-> std::uint64_t override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto get_item_meta(const std::string & /*api_path*/,
|
||||
api_meta_map & /* meta */) const -> api_error override {
|
||||
return api_error::error;
|
||||
}
|
||||
|
||||
auto get_item_meta(const std::string & /*api_path*/,
|
||||
const std::string & /*name*/,
|
||||
std::string & /*value*/) const -> api_error override {
|
||||
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 ret = STATUS_SUCCESS;
|
||||
|
||||
if (attributes != nullptr) {
|
||||
*attributes = FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
|
||||
if (descriptor_size) {
|
||||
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)) {
|
||||
if (sz > *descriptor_size) {
|
||||
ret = STATUS_BUFFER_TOO_SMALL;
|
||||
} else {
|
||||
::CopyMemory(descriptor, sd, sz);
|
||||
}
|
||||
*descriptor_size = sz;
|
||||
::LocalFree(sd);
|
||||
} else {
|
||||
ret = FspNtStatusFromWin32(::GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_total_drive_space() const -> std::uint64_t override {
|
||||
return 100ULL * 1024ULL * 1024ULL;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_total_item_count() const -> std::uint64_t override {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto get_used_drive_space() const -> std::uint64_t override {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
void get_volume_info(UINT64 &total_size, UINT64 &free_size,
|
||||
std::string &volume_label) const override {
|
||||
free_size = 100U;
|
||||
total_size = 200U;
|
||||
volume_label = "TestVolumeLabel";
|
||||
}
|
||||
|
||||
auto populate_file_info(const std::string &api_path,
|
||||
remote::file_info &file_info) const
|
||||
-> api_error override {
|
||||
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_ARCHIVE);
|
||||
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()) {
|
||||
return api_error::os_error;
|
||||
}
|
||||
|
||||
file_info.FileSize = opt_size.value();
|
||||
}
|
||||
|
||||
file_info.AllocationSize =
|
||||
directory ? 0
|
||||
: utils::divide_with_ceiling(file_info.FileSize,
|
||||
WINFSP_ALLOCATION_UNIT) *
|
||||
WINFSP_ALLOCATION_UNIT;
|
||||
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;
|
||||
}
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // REPERTORY_TEST_INCLUDE_MOCKS_MOCK_WINFSP_DRIVE_HPP_
|
33
repertory/repertory_test/include/test_common.hpp
Normal file
33
repertory/repertory_test/include/test_common.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_TEST_COMMON_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_TEST_COMMON_HPP_
|
||||
|
||||
REPERTORY_IGNORE_WARNINGS_ENABLE()
|
||||
#include "test.hpp"
|
||||
REPERTORY_IGNORE_WARNINGS_DISABLE()
|
||||
|
||||
#include "events/consumers/console_consumer.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "events/events.hpp"
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_TEST_COMMON_HPP_
|
109
repertory/repertory_test/include/utils/event_capture.hpp
Normal file
109
repertory/repertory_test/include/utils/event_capture.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#ifndef REPERTORY_TEST_INCLUDE_UTILS_EVENT_CAPTURE_HPP_
|
||||
#define REPERTORY_TEST_INCLUDE_UTILS_EVENT_CAPTURE_HPP_
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class event_capture final {
|
||||
E_CONSUMER();
|
||||
|
||||
public:
|
||||
explicit event_capture(std::vector<std::string> event_names,
|
||||
std::vector<std::string> non_fired_event_names = {})
|
||||
: event_names_(std::move(event_names)),
|
||||
non_fired_event_names_(std::move(non_fired_event_names)) {
|
||||
E_SUBSCRIBE_ALL(process_event);
|
||||
}
|
||||
|
||||
~event_capture() {
|
||||
wait_for_empty();
|
||||
|
||||
E_CONSUMER_RELEASE();
|
||||
|
||||
EXPECT_TRUE(event_names_.empty());
|
||||
for (const auto &event_name : event_names_) {
|
||||
std::cerr << '\t' << event_name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> event_names_;
|
||||
std::vector<std::string> fired_event_names_;
|
||||
std::vector<std::string> non_fired_event_names_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable notify_;
|
||||
|
||||
public:
|
||||
void process_event(const event &event) {
|
||||
unique_mutex_lock l(mutex_);
|
||||
utils::collection::remove_element(event_names_, event.get_name());
|
||||
fired_event_names_.emplace_back(event.get_name());
|
||||
notify_.notify_all();
|
||||
l.unlock();
|
||||
|
||||
for (size_t i = 0; i < non_fired_event_names_.size(); i++) {
|
||||
const auto it = std::find(non_fired_event_names_.begin(),
|
||||
non_fired_event_names_.end(), event.get_name());
|
||||
EXPECT_EQ(non_fired_event_names_.end(), it);
|
||||
if (it != non_fired_event_names_.end()) {
|
||||
std::cerr << '\t' << *it << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wait_for_empty() {
|
||||
const auto start_time = std::chrono::system_clock::now();
|
||||
unique_mutex_lock l(mutex_);
|
||||
while (not event_names_.empty() &&
|
||||
(std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now() - start_time)
|
||||
.count() < 10)) {
|
||||
notify_.wait_for(l, 1s);
|
||||
}
|
||||
l.unlock();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto wait_for_event(const std::string &event_name) -> bool {
|
||||
auto missing = true;
|
||||
const auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
unique_mutex_lock l(mutex_);
|
||||
while ((std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now() - start_time)
|
||||
.count() < 10) &&
|
||||
(missing =
|
||||
(std::find(fired_event_names_.begin(), fired_event_names_.end(),
|
||||
event_name) == fired_event_names_.end()))) {
|
||||
notify_.wait_for(l, 1s);
|
||||
}
|
||||
l.unlock();
|
||||
return not missing;
|
||||
}
|
||||
};
|
||||
} // namespace repertory
|
||||
|
||||
#endif // REPERTORY_TEST_INCLUDE_UTILS_EVENT_CAPTURE_HPP_
|
49
repertory/repertory_test/main.cpp
Normal file
49
repertory/repertory_test/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
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(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
#include "backward.hpp"
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
#include "initialize.hpp"
|
||||
#include "test_common.hpp"
|
||||
|
||||
using namespace repertory;
|
||||
|
||||
int PROJECT_TEST_RESULT{0};
|
||||
|
||||
auto main(int argc, char **argv) -> int {
|
||||
#if defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
static backward::SignalHandling sh;
|
||||
#endif // defined(PROJECT_ENABLE_BACKWARD_CPP)
|
||||
|
||||
if (not repertory::project_initialize()) {
|
||||
std::cerr << "fatal: failed to initialize repertory" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
PROJECT_TEST_RESULT = RUN_ALL_TESTS();
|
||||
|
||||
repertory::project_cleanup();
|
||||
|
||||
return PROJECT_TEST_RESULT;
|
||||
}
|
68
repertory/repertory_test/src/atomic_test.cpp
Normal file
68
repertory/repertory_test/src/atomic_test.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(atomic, atomic_primitive) {
|
||||
atomic<std::uint16_t> value;
|
||||
value = 5U;
|
||||
EXPECT_EQ(5U, static_cast<std::uint16_t>(value));
|
||||
EXPECT_EQ(5U, value.load());
|
||||
|
||||
value.store(6U);
|
||||
EXPECT_EQ(6U, static_cast<std::uint16_t>(value));
|
||||
EXPECT_EQ(6U, value.load());
|
||||
}
|
||||
|
||||
TEST(atomic, atomic_primitive_equality) {
|
||||
atomic<std::uint16_t> value1{5U};
|
||||
atomic<std::uint16_t> value2{5U};
|
||||
EXPECT_EQ(value1, value1);
|
||||
EXPECT_EQ(value2, value2);
|
||||
EXPECT_EQ(value1, value2);
|
||||
EXPECT_EQ(static_cast<std::uint16_t>(value1), 5U);
|
||||
EXPECT_EQ(static_cast<std::uint16_t>(value2), 5U);
|
||||
}
|
||||
|
||||
TEST(atomic, atomic_primitive_inequality) {
|
||||
atomic<std::uint16_t> value1{5U};
|
||||
atomic<std::uint16_t> value2{6U};
|
||||
EXPECT_NE(value1, value2);
|
||||
EXPECT_NE(static_cast<std::uint16_t>(value1), 6U);
|
||||
EXPECT_NE(static_cast<std::uint16_t>(value2), 5U);
|
||||
}
|
||||
|
||||
TEST(atomic, atomic_struct) {
|
||||
atomic<encrypt_config> value{
|
||||
encrypt_config{
|
||||
.encryption_token = "token",
|
||||
.path = "path",
|
||||
},
|
||||
};
|
||||
|
||||
auto data = static_cast<encrypt_config>(value);
|
||||
EXPECT_STREQ("token", data.encryption_token.c_str());
|
||||
EXPECT_STREQ("path", data.path.c_str());
|
||||
}
|
||||
} // namespace repertory
|
694
repertory/repertory_test/src/config_test.cpp
Normal file
694
repertory/repertory_test/src/config_test.cpp
Normal file
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "utils/common.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace repertory {
|
||||
class config_test : public ::testing::Test {
|
||||
public:
|
||||
console_consumer cs;
|
||||
|
||||
static std::atomic<std::uint64_t> idx;
|
||||
|
||||
std::string s3_directory;
|
||||
std::string sia_directory;
|
||||
|
||||
void SetUp() override {
|
||||
s3_directory = utils::path::combine(test::get_test_output_dir(),
|
||||
{
|
||||
"config_test",
|
||||
"s3",
|
||||
std::to_string(++idx),
|
||||
});
|
||||
|
||||
sia_directory = utils::path::combine(test::get_test_output_dir(),
|
||||
{
|
||||
"config_test",
|
||||
"sia",
|
||||
std::to_string(++idx),
|
||||
});
|
||||
event_system::instance().start();
|
||||
}
|
||||
|
||||
void TearDown() override { event_system::instance().stop(); }
|
||||
};
|
||||
|
||||
std::atomic<std::uint64_t> config_test::idx{0U};
|
||||
|
||||
TEST_F(config_test, api_path) {
|
||||
std::string original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_api_auth();
|
||||
EXPECT_EQ(48U, original_value.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, api_auth) {
|
||||
std::string original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_api_auth();
|
||||
config.set_api_auth(original_value.substr(0, 20));
|
||||
EXPECT_EQ(original_value.substr(0, 20), config.get_api_auth());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value.substr(0, 20), config.get_api_auth());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, api_port) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_api_port();
|
||||
config.set_api_port(original_value + 5);
|
||||
EXPECT_EQ(original_value + 5, config.get_api_port());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5, config.get_api_port());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, api_user) {
|
||||
std::string original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_api_user();
|
||||
config.set_api_user(original_value.substr(0, 2));
|
||||
EXPECT_EQ(original_value.substr(0, 2), config.get_api_user());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value.substr(0, 2), config.get_api_user());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, download_timeout_secs) {
|
||||
std::uint8_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_download_timeout_secs();
|
||||
config.set_download_timeout_secs(original_value + 5);
|
||||
EXPECT_EQ(original_value + 5, config.get_download_timeout_secs());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5, config.get_download_timeout_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, enable_download_timeout) {
|
||||
bool original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_enable_download_timeout();
|
||||
config.set_enable_download_timeout(not original_value);
|
||||
EXPECT_EQ(not original_value, config.get_enable_download_timeout());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(not original_value, config.get_enable_download_timeout());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, enable_drive_events) {
|
||||
bool original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_enable_drive_events();
|
||||
config.set_enable_drive_events(not original_value);
|
||||
EXPECT_EQ(not original_value, config.get_enable_drive_events());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(not original_value, config.get_enable_drive_events());
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
TEST_F(config_test, enable_mount_manager) {
|
||||
bool original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_enable_mount_manager();
|
||||
config.set_enable_mount_manager(not original_value);
|
||||
EXPECT_EQ(not original_value, config.get_enable_mount_manager());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(not original_value, config.get_enable_mount_manager());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
TEST_F(config_test, event_level) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_event_level(event_level::debug);
|
||||
EXPECT_EQ(event_level::debug, config.get_event_level());
|
||||
config.set_event_level(event_level::warn);
|
||||
EXPECT_EQ(event_level::warn, config.get_event_level());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(event_level::warn, config.get_event_level());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, eviction_delay_mins) {
|
||||
std::uint32_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_eviction_delay_mins();
|
||||
config.set_eviction_delay_mins(original_value + 5);
|
||||
EXPECT_EQ(original_value + 5, config.get_eviction_delay_mins());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5, config.get_eviction_delay_mins());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, eviction_uses_accessed_time) {
|
||||
bool original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_eviction_uses_accessed_time();
|
||||
config.set_eviction_uses_accessed_time(not original_value);
|
||||
EXPECT_EQ(not original_value, config.get_eviction_uses_accessed_time());
|
||||
}
|
||||
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(not original_value, config.get_eviction_uses_accessed_time());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, high_frequency_interval_secs) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_high_frequency_interval_secs();
|
||||
config.set_high_frequency_interval_secs(original_value + 5U);
|
||||
EXPECT_EQ(original_value + 5U, config.get_high_frequency_interval_secs());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5U, config.get_high_frequency_interval_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, low_frequency_interval_secs) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_low_frequency_interval_secs();
|
||||
config.set_low_frequency_interval_secs(original_value + 5U);
|
||||
EXPECT_EQ(original_value + 5U, config.get_low_frequency_interval_secs());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5U, config.get_low_frequency_interval_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, med_frequency_interval_secs) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_med_frequency_interval_secs();
|
||||
config.set_med_frequency_interval_secs(original_value + 5U);
|
||||
EXPECT_EQ(original_value + 5U, config.get_med_frequency_interval_secs());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5U, config.get_med_frequency_interval_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, max_cache_size_bytes) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_max_cache_size_bytes(100 * 1024 * 1024);
|
||||
EXPECT_EQ(100U * 1024 * 1024, config.get_max_cache_size_bytes());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(100U * 1024 * 1024, config.get_max_cache_size_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, max_upload_count) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_max_upload_count(8U);
|
||||
EXPECT_EQ(std::uint8_t(8U), config.get_max_upload_count());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(std::uint8_t(8U), config.get_max_upload_count());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_max_upload_count(0U);
|
||||
EXPECT_EQ(std::uint8_t(1U), config.get_max_upload_count());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, online_check_retry_secs) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_online_check_retry_secs();
|
||||
config.set_online_check_retry_secs(original_value + 1);
|
||||
EXPECT_EQ(original_value + 1, config.get_online_check_retry_secs());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 1, config.get_online_check_retry_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, online_check_retry_secs_minimum_value) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_online_check_retry_secs(14);
|
||||
EXPECT_EQ(15, config.get_online_check_retry_secs());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, orphaned_file_retention_days) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_orphaned_file_retention_days();
|
||||
config.set_orphaned_file_retention_days(original_value + 1);
|
||||
EXPECT_EQ(original_value + 1, config.get_orphaned_file_retention_days());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 1, config.get_orphaned_file_retention_days());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, orphaned_file_retention_days_minimum_value) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_orphaned_file_retention_days(0);
|
||||
EXPECT_EQ(1, config.get_orphaned_file_retention_days());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, orphaned_file_retention_days_maximum_value) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_orphaned_file_retention_days(32);
|
||||
EXPECT_EQ(31, config.get_orphaned_file_retention_days());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_cache_directory) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_STREQ(utils::path::combine(sia_directory, {"cache"}).c_str(),
|
||||
config.get_cache_directory().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_config_file_path) {
|
||||
{
|
||||
const auto config_file = utils::path::absolute(
|
||||
utils::path::combine(sia_directory, {"config.json"}));
|
||||
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_STREQ(config_file.c_str(), config.get_config_file_path().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_data_directory) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_STREQ(sia_directory.c_str(), config.get_data_directory().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_log_directory) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_STREQ(utils::path::combine(sia_directory, {"logs"}).c_str(),
|
||||
config.get_log_directory().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, ring_buffer_file_size) {
|
||||
std::uint16_t original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_ring_buffer_file_size();
|
||||
config.set_ring_buffer_file_size(original_value + 5u);
|
||||
EXPECT_EQ(original_value + 5u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 5u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, ring_buffer_file_size_minimum_size) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_ring_buffer_file_size(63u);
|
||||
EXPECT_EQ(64u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(64u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, ring_buffer_file_size_maximum_size) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_ring_buffer_file_size(1025u);
|
||||
EXPECT_EQ(1024u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(1024u, config.get_ring_buffer_file_size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, preferred_download_type) {
|
||||
download_type original_value;
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_preferred_download_type();
|
||||
config.set_preferred_download_type(download_type::ring_buffer);
|
||||
EXPECT_NE(original_value, config.get_preferred_download_type());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_NE(original_value, config.get_preferred_download_type());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, default_agent_name) {
|
||||
EXPECT_STREQ("Sia-Agent",
|
||||
app_config::default_agent_name(provider_type::sia).c_str());
|
||||
}
|
||||
|
||||
TEST_F(config_test, default_api_port) {
|
||||
EXPECT_EQ(9980U, app_config::default_api_port(provider_type::sia));
|
||||
}
|
||||
|
||||
TEST_F(config_test, default_data_directory) {
|
||||
const std::array<std::string, 1U> data_directory = {
|
||||
app_config::default_data_directory(provider_type::sia),
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
const auto local_app_data = utils::get_environment_variable("localappdata");
|
||||
#endif
|
||||
#if defined(__linux__)
|
||||
const auto local_app_data =
|
||||
utils::path::combine(utils::get_environment_variable("HOME"), {".local"});
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
const auto local_app_data = utils::path::combine(
|
||||
utils::get_environment_variable("HOME"), {"Library/Application Support"});
|
||||
#endif
|
||||
auto expected_directory =
|
||||
utils::path::combine(local_app_data, {"/repertory2/sia"});
|
||||
EXPECT_STREQ(expected_directory.c_str(), data_directory[0].c_str());
|
||||
}
|
||||
|
||||
TEST_F(config_test, default_rpc_port) {
|
||||
EXPECT_EQ(10000U, app_config::default_rpc_port(provider_type::sia));
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_provider_display_name) {
|
||||
EXPECT_STREQ(
|
||||
"Sia", app_config::get_provider_display_name(provider_type::sia).c_str());
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_provider_name) {
|
||||
EXPECT_STREQ("sia",
|
||||
app_config::get_provider_name(provider_type::sia).c_str());
|
||||
}
|
||||
|
||||
TEST_F(config_test, get_version) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(REPERTORY_CONFIG_VERSION, config.get_version());
|
||||
}
|
||||
}
|
||||
|
||||
// TEST_F(config_test, enable_remote_mount) {
|
||||
// bool original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_enable_remote_mount();
|
||||
// config.set_enable_remote_mount(not original_value);
|
||||
// EXPECT_EQ(not original_value, config.get_enable_remote_mount());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(not original_value, config.get_enable_remote_mount());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, is_remote_mount) {
|
||||
// bool original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_is_remote_mount();
|
||||
// config.set_is_remote_mount(not original_value);
|
||||
// EXPECT_EQ(not original_value, config.get_is_remote_mount());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(not original_value, config.get_is_remote_mount());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, enable_remote_mount_fails_if_remote_mount_is_true) {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_is_remote_mount(true);
|
||||
// config.set_enable_remote_mount(true);
|
||||
// EXPECT_FALSE(config.get_enable_remote_mount());
|
||||
// EXPECT_TRUE(config.get_is_remote_mount());
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, set_is_remote_mount_fails_if_enable_remote_mount_is_true)
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_enable_remote_mount(true);
|
||||
// config.set_is_remote_mount(true);
|
||||
// EXPECT_FALSE(config.get_is_remote_mount());
|
||||
// EXPECT_TRUE(config.get_enable_remote_mount());
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_host_name_or_ip) {
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_remote_host_name_or_ip("my.host.name");
|
||||
// EXPECT_STREQ("my.host.name",
|
||||
// config.get_remote_host_name_or_ip().c_str());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_STREQ("my.host.name",
|
||||
// config.get_remote_host_name_or_ip().c_str());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_api_port) {
|
||||
// std::uint16_t original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_remote_api_port();
|
||||
// config.set_remote_api_port(original_value + 5);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_api_port());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_api_port());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_receive_timeout_secs) {
|
||||
// std::uint16_t original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_remote_receive_timeout_secs();
|
||||
// config.set_remote_receive_timeout_secs(original_value + 5);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_receive_timeout_secs());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_receive_timeout_secs());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_send_timeout_secs) {
|
||||
// std::uint16_t original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_remote_send_timeout_secs();
|
||||
// config.set_remote_send_timeout_secs(original_value + 5);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_send_timeout_secs());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_send_timeout_secs());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_encryption_token) {
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_remote_encryption_token("myToken");
|
||||
// EXPECT_STREQ("myToken", config.get_remote_encryption_token().c_str());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_STREQ("myToken", config.get_remote_encryption_token().c_str());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TEST_F(config_test, remote_client_pool_size) {
|
||||
// std::uint8_t original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_remote_client_pool_size();
|
||||
// config.set_remote_client_pool_size(original_value + 5);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_client_pool_size());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_client_pool_size());
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TEST_F(config_test, remote_client_pool_size_minimum_value) {
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_remote_client_pool_size(0);
|
||||
// EXPECT_EQ(5, config.get_remote_client_pool_size());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(5, config.get_remote_client_pool_size());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_max_connections) {
|
||||
// std::uint8_t original_value{};
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// original_value = config.get_remote_max_connections();
|
||||
// config.set_remote_max_connections(original_value + 5);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_max_connections());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(original_value + 5, config.get_remote_max_connections());
|
||||
// }
|
||||
// }
|
||||
|
||||
// TEST_F(config_test, remote_max_connections_minimum_value) {
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// config.set_remote_max_connections(0);
|
||||
// EXPECT_EQ(1, config.get_remote_max_connections());
|
||||
// }
|
||||
// {
|
||||
// app_config config(provider_type::sia, sia_directory);
|
||||
// EXPECT_EQ(1, config.get_remote_max_connections());
|
||||
// }
|
||||
// }
|
||||
|
||||
TEST_F(config_test, retry_read_count) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_retry_read_count();
|
||||
config.set_retry_read_count(original_value + 1);
|
||||
EXPECT_EQ(original_value + 1, config.get_retry_read_count());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 1, config.get_retry_read_count());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, retry_read_count_minimum_value) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_retry_read_count(1);
|
||||
EXPECT_EQ(2, config.get_retry_read_count());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, task_wait_ms) {
|
||||
std::uint16_t original_value{};
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
original_value = config.get_task_wait_ms();
|
||||
config.set_task_wait_ms(original_value + 1U);
|
||||
EXPECT_EQ(original_value + 1U, config.get_task_wait_ms());
|
||||
}
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
EXPECT_EQ(original_value + 1U, config.get_task_wait_ms());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, task_wait_ms_minimum_value) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_task_wait_ms(1U);
|
||||
EXPECT_EQ(50U, config.get_task_wait_ms());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(config_test, can_set_database_type) {
|
||||
{
|
||||
app_config config(provider_type::sia, sia_directory);
|
||||
config.set_database_type(database_type::rocksdb);
|
||||
EXPECT_EQ(database_type::rocksdb, config.get_database_type());
|
||||
|
||||
config.set_database_type(database_type::sqlite);
|
||||
EXPECT_EQ(database_type::sqlite, config.get_database_type());
|
||||
|
||||
config.set_database_type(database_type::rocksdb);
|
||||
EXPECT_EQ(database_type::rocksdb, config.get_database_type());
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
77
repertory/repertory_test/src/curl_comm_test.cpp
Normal file
77
repertory/repertory_test/src/curl_comm_test.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "comm/curl/curl_comm.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(curl_comm, can_create_s3_host_config) {
|
||||
s3_config config{};
|
||||
config.bucket = "repertory";
|
||||
config.url = "https://s3.test.com";
|
||||
config.region = "any";
|
||||
|
||||
auto hc = curl_comm::create_host_config(config, false);
|
||||
EXPECT_STREQ("https", hc.protocol.c_str());
|
||||
EXPECT_STREQ("repertory.s3.test.com", hc.host_name_or_ip.c_str());
|
||||
EXPECT_TRUE(hc.path.empty());
|
||||
}
|
||||
|
||||
TEST(curl_comm, can_create_s3_host_config_with_path_style) {
|
||||
s3_config config{};
|
||||
config.bucket = "repertory";
|
||||
config.url = "https://s3.test.com";
|
||||
config.region = "any";
|
||||
|
||||
auto hc = curl_comm::create_host_config(config, true);
|
||||
EXPECT_STREQ("https", hc.protocol.c_str());
|
||||
EXPECT_STREQ("s3.test.com", hc.host_name_or_ip.c_str());
|
||||
EXPECT_STREQ("/repertory", hc.path.c_str());
|
||||
}
|
||||
|
||||
TEST(curl_comm, can_create_s3_host_config_with_region) {
|
||||
s3_config config{};
|
||||
config.bucket = "repertory";
|
||||
config.url = "https://s3.test.com";
|
||||
config.region = "any";
|
||||
config.use_region_in_url = true;
|
||||
|
||||
auto hc = curl_comm::create_host_config(config, false);
|
||||
EXPECT_STREQ("https", hc.protocol.c_str());
|
||||
EXPECT_STREQ("repertory.s3.any.test.com", hc.host_name_or_ip.c_str());
|
||||
EXPECT_TRUE(hc.path.empty());
|
||||
}
|
||||
|
||||
TEST(curl_comm, can_create_s3_host_config_with_region_and_path_style) {
|
||||
s3_config config{};
|
||||
config.bucket = "repertory";
|
||||
config.url = "https://s3.test.com";
|
||||
config.region = "any";
|
||||
config.use_region_in_url = true;
|
||||
|
||||
auto hc = curl_comm::create_host_config(config, true);
|
||||
EXPECT_STREQ("https", hc.protocol.c_str());
|
||||
EXPECT_STREQ("s3.any.test.com", hc.host_name_or_ip.c_str());
|
||||
EXPECT_STREQ("/repertory", hc.path.c_str());
|
||||
}
|
||||
} // namespace repertory
|
292
repertory/repertory_test/src/direct_open_file_test.cpp
Normal file
292
repertory/repertory_test/src/direct_open_file_test.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "file_manager/direct_open_file.hpp"
|
||||
#include "mocks/mock_provider.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr const std::size_t test_chunk_size{1024U};
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
class direct_open_file_test : public ::testing::Test {
|
||||
public:
|
||||
console_consumer con_consumer;
|
||||
mock_provider provider;
|
||||
|
||||
protected:
|
||||
void SetUp() override { event_system::instance().start(); }
|
||||
|
||||
void TearDown() override { event_system::instance().stop(); }
|
||||
};
|
||||
|
||||
TEST_F(direct_open_file_test, read_full_file) {
|
||||
auto &source_file = test::create_random_file(test_chunk_size * 32U);
|
||||
|
||||
auto dest_path = test::generate_test_file_name("direct_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.directory = false;
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(provider, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &source_file](
|
||||
const std::string & /* api_path */, std::size_t size,
|
||||
std::uint64_t offset, data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = source_file.read(data, offset, &bytes_read)
|
||||
? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
direct_open_file file(test_chunk_size, 30U, fsi, provider);
|
||||
|
||||
auto dest_file = utils::file::file::open_or_create_file(dest_path);
|
||||
EXPECT_TRUE(dest_file);
|
||||
|
||||
auto to_read{fsi.size};
|
||||
std::size_t chunk{0U};
|
||||
while (to_read > 0U) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
file.read(test_chunk_size, chunk * test_chunk_size, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(
|
||||
dest_file->write(data, chunk * test_chunk_size, &bytes_written));
|
||||
++chunk;
|
||||
to_read -= data.size();
|
||||
}
|
||||
dest_file->close();
|
||||
source_file.close();
|
||||
|
||||
auto hash1 = utils::file::file(source_file.get_path()).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(direct_open_file_test, read_full_file_in_reverse) {
|
||||
auto &source_file = test::create_random_file(test_chunk_size * 32U);
|
||||
|
||||
auto dest_path = test::generate_test_file_name("direct_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.directory = false;
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(provider, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &source_file](
|
||||
const std::string & /* api_path */, std::size_t size,
|
||||
std::uint64_t offset, data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = source_file.read(data, offset, &bytes_read)
|
||||
? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
direct_open_file file(test_chunk_size, 30U, fsi, provider);
|
||||
|
||||
auto dest_file = utils::file::file::open_or_create_file(dest_path);
|
||||
EXPECT_TRUE(dest_file);
|
||||
|
||||
auto to_read{fsi.size};
|
||||
std::size_t chunk{file.get_total_chunks() - 1U};
|
||||
while (to_read > 0U) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
file.read(test_chunk_size, chunk * test_chunk_size, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(
|
||||
dest_file->write(data, chunk * test_chunk_size, &bytes_written));
|
||||
--chunk;
|
||||
to_read -= data.size();
|
||||
}
|
||||
dest_file->close();
|
||||
source_file.close();
|
||||
|
||||
auto hash1 = utils::file::file(source_file.get_path()).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(direct_open_file_test, read_full_file_in_partial_chunks) {
|
||||
auto &source_file = test::create_random_file(test_chunk_size * 32U);
|
||||
|
||||
auto dest_path = test::generate_test_file_name("test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(provider, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &source_file](
|
||||
const std::string & /* api_path */, std::size_t size,
|
||||
std::uint64_t offset, data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = source_file.read(data, offset, &bytes_read)
|
||||
? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
direct_open_file file(test_chunk_size, 30U, fsi, provider);
|
||||
|
||||
auto dest_file = utils::file::file::open_or_create_file(dest_path);
|
||||
EXPECT_TRUE(dest_file);
|
||||
|
||||
auto total_read{std::uint64_t(0U)};
|
||||
while (total_read < fsi.size) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success, file.read(3U, total_read, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(dest_file->write(data.data(), data.size(), total_read,
|
||||
&bytes_written));
|
||||
total_read += data.size();
|
||||
}
|
||||
dest_file->close();
|
||||
source_file.close();
|
||||
|
||||
auto hash1 = utils::file::file(source_file.get_path()).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(direct_open_file_test, read_full_file_in_partial_chunks_in_reverse) {
|
||||
auto &source_file = test::create_random_file(test_chunk_size * 32U);
|
||||
|
||||
auto dest_path = test::generate_test_file_name("direct_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(provider, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &source_file](
|
||||
const std::string & /* api_path */, std::size_t size,
|
||||
std::uint64_t offset, data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = source_file.read(data, offset, &bytes_read)
|
||||
? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
direct_open_file file(test_chunk_size, 30U, fsi, provider);
|
||||
|
||||
auto dest_file = utils::file::file::open_or_create_file(dest_path);
|
||||
EXPECT_TRUE(dest_file);
|
||||
|
||||
std::uint64_t total_read{0U};
|
||||
auto read_size{3U};
|
||||
|
||||
while (total_read < fsi.size) {
|
||||
auto offset = fsi.size - total_read - read_size;
|
||||
auto remain = fsi.size - total_read;
|
||||
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
file.read(static_cast<std::size_t>(
|
||||
std::min(remain, std::uint64_t(read_size))),
|
||||
(remain >= read_size) ? offset : 0U, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(dest_file->write(data, (remain >= read_size) ? offset : 0U,
|
||||
&bytes_written));
|
||||
total_read += data.size();
|
||||
}
|
||||
dest_file->close();
|
||||
source_file.close();
|
||||
|
||||
auto hash1 = utils::file::file(source_file.get_path()).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
332
repertory/repertory_test/src/file_db_test.cpp
Normal file
332
repertory/repertory_test/src/file_db_test.cpp
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "fixtures/file_db_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(file_db_test, file_db_types);
|
||||
|
||||
TYPED_TEST(file_db_test, can_add_and_remove_directory) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_directory("/", "c:\\test"));
|
||||
|
||||
auto list = this->file_db->get_item_list();
|
||||
EXPECT_EQ(1U, list.size());
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->remove_item("/"));
|
||||
|
||||
list = this->file_db->get_item_list();
|
||||
EXPECT_EQ(0U, list.size());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_add_and_remove_file) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
0U,
|
||||
{},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
auto list = this->file_db->get_item_list();
|
||||
EXPECT_EQ(1U, list.size());
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->remove_item("/file"));
|
||||
|
||||
list = this->file_db->get_item_list();
|
||||
EXPECT_EQ(0U, list.size());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_api_path_for_directory) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_directory("/", "c:\\test"));
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_api_path("c:\\test", api_path));
|
||||
EXPECT_STREQ("/", api_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_api_path_for_file) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
0U,
|
||||
{},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_api_path("c:\\test\\file.txt", api_path));
|
||||
EXPECT_STREQ("/file", api_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test,
|
||||
item_not_found_is_returned_for_non_existing_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->file_db->get_api_path("c:\\test", api_path));
|
||||
EXPECT_TRUE(api_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_directory_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_directory("/", "c:\\test"));
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_directory_api_path("c:\\test", api_path));
|
||||
EXPECT_STREQ("/", api_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(
|
||||
file_db_test,
|
||||
directory_not_found_is_returned_for_non_existing_directory_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::directory_not_found,
|
||||
this->file_db->get_directory_api_path("c:\\test", api_path));
|
||||
EXPECT_TRUE(api_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_file_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
0U,
|
||||
{},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_file_api_path("c:\\test\\file.txt", api_path));
|
||||
EXPECT_STREQ("/file", api_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test,
|
||||
item_not_found_is_returned_for_non_existing_file_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->file_db->get_file_api_path("c:\\test", api_path));
|
||||
EXPECT_TRUE(api_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_directory_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_directory("/", "c:\\test"));
|
||||
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_directory_source_path("/", source_path));
|
||||
EXPECT_STREQ("c:\\test", source_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(
|
||||
file_db_test,
|
||||
directory_not_found_is_returned_for_non_existing_directory_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::directory_not_found,
|
||||
this->file_db->get_directory_source_path("/", source_path));
|
||||
EXPECT_TRUE(source_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_file_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
0U,
|
||||
{},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_file_source_path("/file", source_path));
|
||||
EXPECT_STREQ("c:\\test\\file.txt", source_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test,
|
||||
item_not_found_is_returned_for_non_existing_file_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->file_db->get_file_source_path("/file.txt", source_path));
|
||||
EXPECT_TRUE(source_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_file_data) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
i_file_db::file_data data{};
|
||||
EXPECT_EQ(api_error::success, this->file_db->get_file_data("/file", data));
|
||||
EXPECT_STREQ("/file", data.api_path.c_str());
|
||||
EXPECT_EQ(1U, data.file_size);
|
||||
EXPECT_EQ(2U, data.iv_list.size());
|
||||
EXPECT_STREQ("c:\\test\\file.txt", data.source_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test,
|
||||
item_not_found_is_returned_for_non_existing_file_data_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
i_file_db::file_data data{};
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->file_db->get_file_data("/file", data));
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_update_existing_file_iv) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
i_file_db::file_data data{};
|
||||
EXPECT_EQ(api_error::success, this->file_db->get_file_data("/file", data));
|
||||
EXPECT_STREQ("/file", data.api_path.c_str());
|
||||
EXPECT_EQ(1U, data.file_size);
|
||||
EXPECT_EQ(3U, data.iv_list.size());
|
||||
EXPECT_STREQ("c:\\test\\file.txt", data.source_path.c_str());
|
||||
|
||||
EXPECT_EQ(1U, this->file_db->count());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_update_existing_file_size) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
2U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
i_file_db::file_data data{};
|
||||
EXPECT_EQ(api_error::success, this->file_db->get_file_data("/file", data));
|
||||
EXPECT_STREQ("/file", data.api_path.c_str());
|
||||
EXPECT_EQ(2U, data.file_size);
|
||||
EXPECT_EQ(2U, data.iv_list.size());
|
||||
EXPECT_STREQ("c:\\test\\file.txt", data.source_path.c_str());
|
||||
|
||||
EXPECT_EQ(1U, this->file_db->count());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_update_existing_file_source_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
1U,
|
||||
{{}, {}},
|
||||
"c:\\test\\file2.txt",
|
||||
}));
|
||||
|
||||
i_file_db::file_data data{};
|
||||
EXPECT_EQ(api_error::success, this->file_db->get_file_data("/file", data));
|
||||
EXPECT_STREQ("/file", data.api_path.c_str());
|
||||
EXPECT_EQ(1U, data.file_size);
|
||||
EXPECT_EQ(2U, data.iv_list.size());
|
||||
EXPECT_STREQ("c:\\test\\file2.txt", data.source_path.c_str());
|
||||
|
||||
EXPECT_EQ(1U, this->file_db->count());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_source_path_for_directory) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_directory("/", "c:\\test"));
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_source_path("/", source_path));
|
||||
EXPECT_STREQ("c:\\test", source_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, can_get_source_path_for_file) {
|
||||
this->file_db->clear();
|
||||
|
||||
EXPECT_EQ(api_error::success, this->file_db->add_or_update_file({
|
||||
"/file",
|
||||
0U,
|
||||
{},
|
||||
"c:\\test\\file.txt",
|
||||
}));
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->file_db->get_source_path("/file", source_path));
|
||||
EXPECT_STREQ("c:\\test\\file.txt", source_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_db_test, item_not_found_is_returned_for_non_existing_api_path) {
|
||||
this->file_db->clear();
|
||||
|
||||
std::string source_path;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->file_db->get_source_path("/file", source_path));
|
||||
EXPECT_TRUE(source_path.empty());
|
||||
}
|
||||
} // namespace repertory
|
1639
repertory/repertory_test/src/file_manager_test.cpp
Normal file
1639
repertory/repertory_test/src/file_manager_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
160
repertory/repertory_test/src/file_mgr_db_test.cpp
Normal file
160
repertory/repertory_test/src/file_mgr_db_test.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "fixtures/file_mgr_db_fixture.hpp"
|
||||
#include <utils/time.hpp>
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(file_mgr_db_test, file_mgr_db_types);
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, can_add_and_remove_resume) {
|
||||
this->file_mgr_db->clear();
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->add_resume({
|
||||
"/test0",
|
||||
2ULL,
|
||||
{},
|
||||
"/src/test0",
|
||||
}));
|
||||
auto list = this->file_mgr_db->get_resume_list();
|
||||
EXPECT_EQ(1U, list.size());
|
||||
EXPECT_EQ(1U, list.size());
|
||||
EXPECT_STREQ("/test0", list.at(0U).api_path.c_str());
|
||||
EXPECT_EQ(2ULL, list.at(0U).chunk_size);
|
||||
EXPECT_STREQ("/src/test0", list.at(0U).source_path.c_str());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_resume("/test0"));
|
||||
list = this->file_mgr_db->get_resume_list();
|
||||
EXPECT_TRUE(list.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, can_get_resume_list) {
|
||||
this->file_mgr_db->clear();
|
||||
|
||||
for (auto idx = 0U; idx < 5U; ++idx) {
|
||||
EXPECT_TRUE(this->file_mgr_db->add_resume({
|
||||
"/test1_" + std::to_string(idx),
|
||||
2ULL + idx,
|
||||
{},
|
||||
"/src/test1_" + std::to_string(idx),
|
||||
}));
|
||||
}
|
||||
|
||||
auto list = this->file_mgr_db->get_resume_list();
|
||||
EXPECT_EQ(5U, list.size());
|
||||
for (auto idx = 0U; idx < list.size(); ++idx) {
|
||||
EXPECT_STREQ(("/test1_" + std::to_string(idx)).c_str(),
|
||||
list.at(idx).api_path.c_str());
|
||||
EXPECT_EQ(2ULL + idx, list.at(idx).chunk_size);
|
||||
EXPECT_STREQ(("/src/test1_" + std::to_string(idx)).c_str(),
|
||||
list.at(idx).source_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, can_replace_resume) {
|
||||
this->file_mgr_db->clear();
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->add_resume({
|
||||
"/test0",
|
||||
2ULL,
|
||||
{},
|
||||
"/src/test0",
|
||||
}));
|
||||
EXPECT_TRUE(this->file_mgr_db->add_resume({
|
||||
"/test0",
|
||||
3ULL,
|
||||
{},
|
||||
"/src/test1",
|
||||
}));
|
||||
|
||||
auto list = this->file_mgr_db->get_resume_list();
|
||||
EXPECT_EQ(1U, list.size());
|
||||
EXPECT_STREQ("/test0", list.at(0U).api_path.c_str());
|
||||
EXPECT_EQ(3ULL, list.at(0U).chunk_size);
|
||||
EXPECT_STREQ("/src/test1", list.at(0U).source_path.c_str());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_resume("/test0"));
|
||||
}
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, can_rename_resume) {
|
||||
this->file_mgr_db->clear();
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->add_resume({
|
||||
"/test0",
|
||||
2ULL,
|
||||
{},
|
||||
"/src/test0",
|
||||
}));
|
||||
EXPECT_TRUE(this->file_mgr_db->rename_resume("/test0", "/test1"));
|
||||
|
||||
auto list = this->file_mgr_db->get_resume_list();
|
||||
EXPECT_EQ(1U, list.size());
|
||||
EXPECT_STREQ("/test1", list.at(0U).api_path.c_str());
|
||||
EXPECT_EQ(2ULL, list.at(0U).chunk_size);
|
||||
EXPECT_STREQ("/src/test0", list.at(0U).source_path.c_str());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_resume("/test1"));
|
||||
}
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, can_add_get_and_remove_upload) {
|
||||
this->file_mgr_db->clear();
|
||||
EXPECT_TRUE(this->file_mgr_db->add_upload({
|
||||
"/test0",
|
||||
"/src/test0",
|
||||
}));
|
||||
|
||||
auto upload = this->file_mgr_db->get_upload("/test0");
|
||||
EXPECT_TRUE(upload.has_value());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_upload("/test0"));
|
||||
|
||||
upload = this->file_mgr_db->get_next_upload();
|
||||
EXPECT_FALSE(upload.has_value());
|
||||
}
|
||||
|
||||
TYPED_TEST(file_mgr_db_test, uploads_are_correctly_ordered) {
|
||||
this->file_mgr_db->clear();
|
||||
EXPECT_TRUE(this->file_mgr_db->add_upload({
|
||||
"/test08",
|
||||
"/src/test0",
|
||||
}));
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->add_upload({
|
||||
"/test07",
|
||||
"/src/test1",
|
||||
}));
|
||||
|
||||
auto upload = this->file_mgr_db->get_next_upload();
|
||||
EXPECT_TRUE(upload.has_value());
|
||||
EXPECT_STREQ("/test08", upload->api_path.c_str());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_upload("/test08"));
|
||||
upload = this->file_mgr_db->get_next_upload();
|
||||
EXPECT_TRUE(upload.has_value());
|
||||
EXPECT_STREQ("/test07", upload->api_path.c_str());
|
||||
|
||||
EXPECT_TRUE(this->file_mgr_db->remove_upload("/test07"));
|
||||
|
||||
upload = this->file_mgr_db->get_next_upload();
|
||||
EXPECT_FALSE(upload.has_value());
|
||||
}
|
||||
} // namespace repertory
|
125
repertory/repertory_test/src/fuse_drive_access_test.cpp
Normal file
125
repertory/repertory_test/src/fuse_drive_access_test.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#include "fixtures/fuse_fixture.hpp"
|
||||
|
||||
namespace {
|
||||
const auto access_permutations = {
|
||||
// clang-format off
|
||||
std::make_tuple(0000, R_OK, -1, EACCES), // No permissions, R_OK
|
||||
std::make_tuple(0000, W_OK, -1, EACCES), // No permissions, W_OK
|
||||
std::make_tuple(0000, X_OK, -1, EACCES), // No permissions, X_OK
|
||||
std::make_tuple(0444, R_OK, 0, 0), // Read-only, R_OK
|
||||
std::make_tuple(0444, W_OK, -1, EACCES), // Read-only, W_OK
|
||||
std::make_tuple(0444, X_OK, -1, EACCES), // Read-only, X_OK
|
||||
std::make_tuple(0222, R_OK, -1, EACCES), // Write-only, R_OK
|
||||
std::make_tuple(0222, W_OK, 0, 0), // Write-only, W_OK
|
||||
std::make_tuple(0222, X_OK, -1, EACCES), // Write-only, X_OK
|
||||
std::make_tuple(0111, R_OK, -1, EACCES), // Execute-only, R_OK
|
||||
std::make_tuple(0111, W_OK, -1, EACCES), // Execute-only, W_OK
|
||||
std::make_tuple(0111, X_OK, 0, 0), // Execute-only, X_OK
|
||||
std::make_tuple(0666, R_OK | W_OK, 0, 0), // Read and write, R_OK | W_OK
|
||||
std::make_tuple(0666, R_OK | X_OK, -1, EACCES), // Read and write, R_OK | X_OK
|
||||
std::make_tuple(0777, R_OK | W_OK | X_OK, 0, 0), // All permissions
|
||||
std::make_tuple(0555, R_OK | X_OK, 0, 0), // Read and execute, R_OK | X_OK
|
||||
std::make_tuple(0555, W_OK, -1, EACCES) // Read and execute, W_OK
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
void perform_access_test(auto &&permutation, auto &&item_path) {
|
||||
mode_t permissions{};
|
||||
int access_mode{};
|
||||
int expected_result{};
|
||||
int expected_errno{};
|
||||
std::tie(permissions, access_mode, expected_result, expected_errno) =
|
||||
permutation;
|
||||
|
||||
EXPECT_EQ(0, chmod(item_path.c_str(), permissions));
|
||||
|
||||
auto result = access(item_path.c_str(), access_mode);
|
||||
|
||||
EXPECT_EQ(result, expected_result)
|
||||
<< "Expected access result to be " << expected_result
|
||||
<< " for permissions " << std::oct << permissions << " and access mode "
|
||||
<< access_mode;
|
||||
if (result == -1) {
|
||||
EXPECT_EQ(errno, expected_errno)
|
||||
<< "Expected errno to be " << expected_errno << " for permissions "
|
||||
<< std::oct << permissions;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
|
||||
|
||||
TYPED_TEST(fuse_test, access_can_check_if_item_does_not_exist) {
|
||||
EXPECT_EQ(
|
||||
-1,
|
||||
access(utils::path::combine(this->mount_location, {"test_dir"}).c_str(),
|
||||
F_OK));
|
||||
EXPECT_EQ(ENOENT, utils::get_last_error_code());
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, access_can_check_if_directory_exists) {
|
||||
std::string dir_name{"access_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
|
||||
EXPECT_EQ(0, access(dir_path.c_str(), F_OK));
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, access_can_check_if_file_exists) {
|
||||
std::string file_name{"access_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
EXPECT_EQ(0, access(file_path.c_str(), F_OK));
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, access_directory_permutations_test) {
|
||||
std::string dir_name{"access_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
|
||||
for (auto &&permutation : access_permutations) {
|
||||
perform_access_test(permutation, dir_path);
|
||||
}
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, access_file_permutations_test) {
|
||||
std::string file_name{"access_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
for (auto &&permutation : access_permutations) {
|
||||
perform_access_test(permutation, file_path);
|
||||
}
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
85
repertory/repertory_test/src/fuse_drive_chmod_test.cpp
Normal file
85
repertory/repertory_test/src/fuse_drive_chmod_test.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#include "fixtures/fuse_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
|
||||
|
||||
TYPED_TEST(fuse_test, chmod_can_not_chmod_set_sticky_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_ISVTX));
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, chmod_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 {};
|
||||
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, chmod_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, chmod_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_ISGID));
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, chmod_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_ISUID));
|
||||
EXPECT_EQ(EPERM, errno);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
124
repertory/repertory_test/src/fuse_drive_chown_test.cpp
Normal file
124
repertory/repertory_test/src/fuse_drive_chown_test.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#include "fixtures/fuse_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
|
||||
|
||||
TYPED_TEST(fuse_test,
|
||||
chown_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_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);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(
|
||||
fuse_test,
|
||||
chown_can_chown_group_when_specifying_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(), getuid(), getgid()));
|
||||
std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
|
||||
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);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test,
|
||||
chown_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, chown_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, chown_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)
|
521
repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp
Normal file
521
repertory/repertory_test/src/fuse_drive_create_and_open_test.cpp
Normal file
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#include "fixtures/fuse_fixture.hpp"
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
|
||||
|
||||
TYPED_TEST(fuse_test, create_can_create_and_remove_directory) {
|
||||
std::string dir_name{"create_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_can_create_and_remove_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_can_create_directory_with_specific_perms) {
|
||||
std::string dir_name{"create_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name, S_IRUSR);
|
||||
|
||||
struct stat64 unix_st {};
|
||||
EXPECT_EQ(0, stat64(dir_path.c_str(), &unix_st));
|
||||
EXPECT_EQ(S_IRUSR, unix_st.st_mode & ACCESSPERMS);
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_can_create_file_with_specific_perms) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name, S_IRUSR);
|
||||
|
||||
struct stat64 unix_st {};
|
||||
EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
|
||||
EXPECT_EQ(S_IRUSR, unix_st.st_mode & ACCESSPERMS);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 1. Create File - O_CREAT
|
||||
TYPED_TEST(fuse_test, create_can_create_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 2. Create File - O_CREAT | O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_create_file_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 3. Create File - O_CREAT | O_RDWR
|
||||
TYPED_TEST(fuse_test, create_can_create_file_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 4. Create File - O_CREAT | O_TRUNC
|
||||
TYPED_TEST(fuse_test, create_can_create_with_truncate_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_TRUNC, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 5. Create File - O_CREAT | O_TRUNC | O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_create_with_truncate_file_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle =
|
||||
open(file_path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 6. Create File - O_CREAT | O_TRUNC | O_RDWR
|
||||
TYPED_TEST(fuse_test, create_can_create_with_truncate_file_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle =
|
||||
open(file_path.c_str(), O_CREAT | O_TRUNC | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 7. Create File - O_CREAT | O_APPEND
|
||||
TYPED_TEST(fuse_test, create_can_create_file_for_append) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_APPEND, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 8. Create File - O_CREAT | O_APPEND | O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_create_file_for_append_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle =
|
||||
open(file_path.c_str(), O_CREAT | O_APPEND | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 9. Create File - O_CREAT | O_EXCL | O_WRONLY (file does not exist)
|
||||
TYPED_TEST(fuse_test, create_can_create_file_excl_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle =
|
||||
open(file_path.c_str(), O_CREAT | O_EXCL | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 10. Create File - O_CREAT | O_EXCL | O_RDWR (file does not exist)
|
||||
TYPED_TEST(fuse_test, create_can_create_file_excl_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_EXCL | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 11. Create File - O_CREAT | O_EXCL (file does not exist)
|
||||
TYPED_TEST(fuse_test, create_can_create_file_excl) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_CREAT | O_EXCL, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 1. Open Existing File - O_RDONLY
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_ro) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 2. Open Existing File - O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 3. Open Existing File - O_RDWR
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 4. Open Existing File - O_APPEND
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_for_append) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_APPEND, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 5. Open Existing File - O_APPEND | O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_for_append_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_APPEND | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 6. Open Existing File - O_APPEND | O_RDWR
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_for_append_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_APPEND | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 7. Open Existing File - O_TRUNC | O_WRONLY
|
||||
TYPED_TEST(fuse_test, create_can_open_and_truncate_existing_file_wo) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
EXPECT_EQ(0, truncate(file_path.c_str(), 24U));
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(24U, *size);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_TRUNC | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 8. Open Existing File - O_TRUNC | O_RDWR
|
||||
TYPED_TEST(fuse_test, create_can_open_and_truncate_existing_file_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
EXPECT_EQ(0, truncate(file_path.c_str(), 24U));
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(24U, *size);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_TRUNC | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 9. Open Existing File - O_TRUNC
|
||||
TYPED_TEST(fuse_test, create_can_open_and_truncate_existing_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
EXPECT_EQ(0, truncate(file_path.c_str(), 24U));
|
||||
|
||||
auto size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(24U, *size);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_TRUNC, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(size.has_value());
|
||||
EXPECT_EQ(0U, *size);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 10. Open Existing File - O_EXCL | O_WRONLY (file does not exist)
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_with_excl_wr) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_EXCL | O_WRONLY, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
// 11. Open Existing File - O_EXCL | O_RDWR (file does not exist)
|
||||
TYPED_TEST(fuse_test, create_can_open_existing_file_with_excl_rw) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_EXCL | O_RDWR, ACCESSPERMS);
|
||||
EXPECT_LE(1, handle);
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_fails_with_excl_if_path_is_directory) {
|
||||
std::array<int, 3U> ops{
|
||||
O_CREAT | O_EXCL,
|
||||
O_CREAT | O_EXCL | O_RDWR,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
};
|
||||
|
||||
std::string dir_name{"create_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(dir_path.c_str(), flags, ACCESSPERMS);
|
||||
EXPECT_EQ(-1, handle);
|
||||
|
||||
EXPECT_EQ(EEXIST, errno);
|
||||
}
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_fails_with_excl_if_file_exists) {
|
||||
std::array<int, 3U> ops{
|
||||
O_CREAT | O_EXCL,
|
||||
O_CREAT | O_EXCL | O_RDWR,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
};
|
||||
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
|
||||
EXPECT_EQ(-1, handle);
|
||||
|
||||
EXPECT_EQ(EEXIST, errno);
|
||||
}
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_fails_if_path_is_directory) {
|
||||
std::array<int, 7U> ops{
|
||||
O_CREAT | O_APPEND,
|
||||
O_CREAT | O_RDWR,
|
||||
O_CREAT | O_TRUNC | O_RDWR,
|
||||
O_CREAT | O_TRUNC | O_WRONLY,
|
||||
O_CREAT | O_TRUNC,
|
||||
O_CREAT | O_WRONLY,
|
||||
O_CREAT,
|
||||
};
|
||||
|
||||
std::string dir_name{"create_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(dir_path.c_str(), flags, ACCESSPERMS);
|
||||
EXPECT_EQ(-1, handle);
|
||||
|
||||
EXPECT_EQ(EISDIR, errno);
|
||||
}
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_fails_if_parent_path_does_not_exist) {
|
||||
std::array<int, 10U> ops{
|
||||
O_CREAT | O_APPEND,
|
||||
O_CREAT | O_EXCL,
|
||||
O_CREAT | O_EXCL | O_RDWR,
|
||||
O_CREAT | O_EXCL | O_WRONLY,
|
||||
O_CREAT | O_RDWR,
|
||||
O_CREAT | O_TRUNC | O_RDWR,
|
||||
O_CREAT | O_TRUNC | O_WRONLY,
|
||||
O_CREAT | O_TRUNC,
|
||||
O_CREAT | O_WRONLY,
|
||||
O_CREAT,
|
||||
};
|
||||
|
||||
std::string file_name{"no_dir/create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
|
||||
EXPECT_EQ(-1, handle);
|
||||
|
||||
EXPECT_EQ(ENOENT, errno);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_fails_if_invalid) {
|
||||
std::array<int, 1U> ops{
|
||||
O_CREAT | O_TRUNC | O_APPEND,
|
||||
};
|
||||
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(file_path.c_str(), flags, ACCESSPERMS);
|
||||
EXPECT_EQ(-1, handle);
|
||||
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_open_fails_if_path_is_directory) {
|
||||
std::array<int, 9U> ops{
|
||||
O_APPEND, O_EXCL | O_WRONLY, O_RDWR | O_APPEND,
|
||||
O_RDWR | O_EXCL, O_RDWR | O_TRUNC, O_RDWR,
|
||||
O_TRUNC, O_WRONLY | O_APPEND, O_WRONLY,
|
||||
};
|
||||
|
||||
std::string dir_name{"create_test"};
|
||||
auto dir_path = this->create_directory_and_test(dir_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(dir_path.c_str(), flags);
|
||||
EXPECT_EQ(-1, handle);
|
||||
if (handle != -1) {
|
||||
std::cout << std::oct << flags << std::endl;
|
||||
}
|
||||
EXPECT_EQ(EISDIR, errno);
|
||||
}
|
||||
|
||||
this->rmdir_and_test(dir_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, create_open_fails_if_path_does_not_exist) {
|
||||
std::array<int, 10U> ops{
|
||||
O_APPEND,
|
||||
O_EXCL | O_WRONLY,
|
||||
O_EXCL,
|
||||
O_RDWR | O_APPEND,
|
||||
O_RDWR | O_EXCL,
|
||||
O_RDWR | O_TRUNC,
|
||||
O_RDWR,
|
||||
O_TRUNC,
|
||||
O_WRONLY | O_APPEND,
|
||||
O_WRONLY,
|
||||
};
|
||||
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_path(file_name);
|
||||
|
||||
for (auto &&flags : ops) {
|
||||
auto handle = open(file_path.c_str(), flags);
|
||||
EXPECT_EQ(-1, handle);
|
||||
EXPECT_EQ(ENOENT, errno);
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
154
repertory/repertory_test/src/fuse_drive_rdrw_test.cpp
Normal file
154
repertory/repertory_test/src/fuse_drive_rdrw_test.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
#include "fixtures/fuse_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(fuse_test, fuse_provider_types);
|
||||
|
||||
TYPED_TEST(fuse_test, rdrw_can_read_and_write_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDWR);
|
||||
ASSERT_GT(handle, -1);
|
||||
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(8096U);
|
||||
auto bytes_written =
|
||||
pwrite64(handle, write_buffer.data(), write_buffer.size(), 0U);
|
||||
EXPECT_EQ(write_buffer.size(), bytes_written);
|
||||
|
||||
data_buffer read_buffer(write_buffer.size());
|
||||
auto bytes_read = pread64(handle, read_buffer.data(), read_buffer.size(), 0U);
|
||||
EXPECT_EQ(bytes_written, bytes_read);
|
||||
|
||||
EXPECT_EQ(0, std::memcmp(write_buffer.data(), read_buffer.data(),
|
||||
write_buffer.size()));
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, rdrw_can_read_from_offset) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDWR);
|
||||
ASSERT_GT(handle, -1);
|
||||
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(8096U);
|
||||
auto bytes_written =
|
||||
pwrite64(handle, write_buffer.data(), write_buffer.size(), 0U);
|
||||
EXPECT_EQ(write_buffer.size(), bytes_written);
|
||||
|
||||
data_buffer read_buffer(1U);
|
||||
for (std::size_t idx = 0U; idx < write_buffer.size(); ++idx) {
|
||||
auto bytes_read =
|
||||
pread64(handle, read_buffer.data(), read_buffer.size(), idx);
|
||||
EXPECT_EQ(1U, bytes_read);
|
||||
|
||||
EXPECT_EQ(write_buffer.at(idx), read_buffer.at(0U));
|
||||
}
|
||||
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, rdrw_can_read_from_offset_after_eof) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDWR);
|
||||
ASSERT_GT(handle, -1);
|
||||
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(8096U);
|
||||
auto bytes_written =
|
||||
pwrite64(handle, write_buffer.data(), write_buffer.size(), 0U);
|
||||
EXPECT_EQ(write_buffer.size(), bytes_written);
|
||||
|
||||
data_buffer read_buffer(1U);
|
||||
for (std::size_t idx = 0U; idx < write_buffer.size() + 1U; ++idx) {
|
||||
auto bytes_read =
|
||||
pread64(handle, read_buffer.data(), read_buffer.size(), idx);
|
||||
if (idx == write_buffer.size()) {
|
||||
EXPECT_EQ(0U, bytes_read);
|
||||
} else {
|
||||
EXPECT_EQ(1U, bytes_read);
|
||||
|
||||
EXPECT_EQ(write_buffer.at(idx), read_buffer.at(0U));
|
||||
}
|
||||
}
|
||||
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, rdrw_can_not_write_to_ro_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_RDONLY);
|
||||
ASSERT_GT(handle, -1);
|
||||
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(8096U);
|
||||
auto bytes_written =
|
||||
pwrite64(handle, write_buffer.data(), write_buffer.size(), 0U);
|
||||
EXPECT_EQ(-1, bytes_written);
|
||||
EXPECT_EQ(EBADF, errno);
|
||||
|
||||
auto file_size = utils::file::file{file_path}.size();
|
||||
EXPECT_TRUE(file_size.has_value());
|
||||
EXPECT_EQ(0U, *file_size);
|
||||
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
|
||||
TYPED_TEST(fuse_test, rdrw_can_not_read_from_wo_file) {
|
||||
std::string file_name{"create_test"};
|
||||
auto file_path = this->create_file_and_test(file_name);
|
||||
|
||||
auto handle = open(file_path.c_str(), O_WRONLY);
|
||||
ASSERT_GT(handle, -1);
|
||||
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(8096U);
|
||||
auto bytes_written =
|
||||
pwrite64(handle, write_buffer.data(), write_buffer.size(), 0U);
|
||||
EXPECT_EQ(write_buffer.size(), bytes_written);
|
||||
|
||||
data_buffer read_buffer(1U);
|
||||
auto bytes_read =
|
||||
pread64(handle, read_buffer.data(), read_buffer.size(), idx);
|
||||
EXPECT_EQ(-1, bytes_read);
|
||||
EXPECT_EQ(EBADF, errno);
|
||||
|
||||
close(handle);
|
||||
|
||||
this->unlink_file_and_test(file_path);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // !defined(_WIN32)
|
411
repertory/repertory_test/src/fuse_drive_test_legacy.cpp
Normal file
411
repertory/repertory_test/src/fuse_drive_test_legacy.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
// static void test_rename_file(const std::string &from_file_path,
|
||||
// const std::string &to_file_path,
|
||||
// bool is_rename_supported) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// auto fd = open(from_file_path.c_str(), O_RDWR, S_IRUSR | S_IWUSR |
|
||||
// S_IRGRP); EXPECT_LE(1, fd); close(fd);
|
||||
//
|
||||
// std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
//
|
||||
// if (is_rename_supported) {
|
||||
// EXPECT_EQ(0, rename(from_file_path.c_str(), to_file_path.c_str()));
|
||||
// EXPECT_FALSE(utils::file::is_file(from_file_path));
|
||||
// EXPECT_TRUE(utils::file::is_file(to_file_path));
|
||||
// } else {
|
||||
// EXPECT_EQ(-1, rename(from_file_path.c_str(), to_file_path.c_str()));
|
||||
// EXPECT_TRUE(utils::file::is_file(from_file_path));
|
||||
// EXPECT_FALSE(utils::file::is_file(to_file_path));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void test_rename_directory(const std::string &from_dir_path,
|
||||
// const std::string &to_dir_path,
|
||||
// bool is_rename_supported) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// EXPECT_EQ(0, mkdir(from_dir_path.c_str(),
|
||||
// S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP));
|
||||
// std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
//
|
||||
// EXPECT_TRUE(utils::file::is_directory(from_dir_path));
|
||||
// if (is_rename_supported) {
|
||||
// EXPECT_EQ(0, rename(from_dir_path.c_str(), to_dir_path.c_str()));
|
||||
// EXPECT_FALSE(utils::file::is_directory(from_dir_path));
|
||||
// EXPECT_TRUE(utils::file::is_directory(to_dir_path));
|
||||
// } else {
|
||||
// EXPECT_EQ(-1, rename(from_dir_path.c_str(), to_dir_path.c_str()));
|
||||
// EXPECT_TRUE(utils::file::is_directory(from_dir_path));
|
||||
// EXPECT_FALSE(utils::file::is_directory(to_dir_path));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// static void test_truncate(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// EXPECT_EQ(0, truncate(file_path.c_str(), 10u));
|
||||
//
|
||||
// std::uint64_t file_size{};
|
||||
// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size));
|
||||
//
|
||||
// EXPECT_EQ(std::uint64_t(10u), file_size);
|
||||
// }
|
||||
//
|
||||
// static void test_ftruncate(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// auto fd = open(file_path.c_str(), O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
// EXPECT_LE(1, fd);
|
||||
//
|
||||
// EXPECT_EQ(0, ftruncate(fd, 10u));
|
||||
//
|
||||
// std::uint64_t file_size{};
|
||||
// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size));
|
||||
//
|
||||
// EXPECT_EQ(std::uint64_t(10u), file_size);
|
||||
//
|
||||
// close(fd);
|
||||
// }
|
||||
//
|
||||
// #if !defined(__APPLE__)
|
||||
// static void test_fallocate(const std::string & /* api_path */,
|
||||
// const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// auto file =
|
||||
// open(file_path.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP);
|
||||
// EXPECT_LE(1, file);
|
||||
// EXPECT_EQ(0, fallocate(file, 0, 0, 16));
|
||||
//
|
||||
// std::uint64_t file_size{};
|
||||
// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size));
|
||||
// EXPECT_EQ(16U, file_size);
|
||||
//
|
||||
// EXPECT_EQ(0, close(file));
|
||||
//
|
||||
// file_size = 0U;
|
||||
// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size));
|
||||
// EXPECT_EQ(16U, file_size);
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// static void test_file_getattr(const std::string & /* api_path */,
|
||||
// const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// auto fd =
|
||||
// open(file_path.c_str(), O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR |
|
||||
// S_IRGRP);
|
||||
// EXPECT_LE(1, fd);
|
||||
//
|
||||
// EXPECT_EQ(0, close(fd));
|
||||
//
|
||||
// struct stat64 unix_st {};
|
||||
// EXPECT_EQ(0, stat64(file_path.c_str(), &unix_st));
|
||||
// EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR | S_IRGRP),
|
||||
// ACCESSPERMS & unix_st.st_mode);
|
||||
// EXPECT_FALSE(S_ISDIR(unix_st.st_mode));
|
||||
// EXPECT_TRUE(S_ISREG(unix_st.st_mode));
|
||||
// }
|
||||
//
|
||||
// static void test_directory_getattr(const std::string & /* api_path */,
|
||||
// const std::string &directory_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// EXPECT_EQ(0, mkdir(directory_path.c_str(),
|
||||
// S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP));
|
||||
//
|
||||
// struct stat64 unix_st {};
|
||||
// EXPECT_EQ(0, stat64(directory_path.c_str(), &unix_st));
|
||||
// EXPECT_EQ(static_cast<std::uint32_t>(S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP
|
||||
// |
|
||||
// S_IXGRP),
|
||||
// ACCESSPERMS & unix_st.st_mode);
|
||||
// EXPECT_TRUE(S_ISDIR(unix_st.st_mode));
|
||||
// EXPECT_FALSE(S_ISREG(unix_st.st_mode));
|
||||
// }
|
||||
//
|
||||
// static void
|
||||
// test_write_operations_fail_if_read_only(const std::string & /* api_path */,
|
||||
// const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// auto fd =
|
||||
// open(file_path.c_str(), O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR |
|
||||
// S_IRGRP);
|
||||
// EXPECT_LE(1, fd);
|
||||
//
|
||||
// std::string data = "TestData";
|
||||
// EXPECT_EQ(-1, write(fd, data.data(), data.size()));
|
||||
//
|
||||
// EXPECT_EQ(-1, ftruncate(fd, 9u));
|
||||
//
|
||||
// #if !defined(__APPLE__)
|
||||
// EXPECT_EQ(-1, fallocate(fd, 0, 0, 16));
|
||||
// #endif
|
||||
//
|
||||
// EXPECT_EQ(0, close(fd));
|
||||
//
|
||||
// std::this_thread::sleep_for(SLEEP_SECONDS);
|
||||
//
|
||||
// std::uint64_t file_size{};
|
||||
// EXPECT_TRUE(utils::file::get_file_size(file_path, file_size));
|
||||
// EXPECT_EQ(std::size_t(0u), file_size);
|
||||
//
|
||||
// // filesystem_item fsi{};
|
||||
// // EXPECT_EQ(api_error::success,
|
||||
// // provider.get_filesystem_item(api_path, false, fsi));
|
||||
// // EXPECT_TRUE(utils::file::get_file_size(fsi.source_path, file_size));
|
||||
// // EXPECT_EQ(std::size_t(0u), file_size);
|
||||
// }
|
||||
//
|
||||
// #if !__APPLE__ && HAS_SETXATTR
|
||||
// static void test_xattr_invalid_parameters(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(-1, setxattr(nullptr, "user.test_attr", attr.data(), attr.size(),
|
||||
// XATTR_CREATE));
|
||||
// EXPECT_EQ(errno, EFAULT);
|
||||
// EXPECT_EQ(-1, setxattr(file_path.c_str(), nullptr, attr.data(),
|
||||
// attr.size(),
|
||||
// XATTR_CREATE));
|
||||
// EXPECT_EQ(errno, EFAULT);
|
||||
// EXPECT_EQ(-1, setxattr(file_path.c_str(), "user.test_attr", nullptr,
|
||||
// attr.size(), XATTR_CREATE));
|
||||
// EXPECT_EQ(errno, EFAULT);
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", nullptr, 0,
|
||||
// XATTR_CREATE));
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_create_and_get(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(attr.size(),
|
||||
// static_cast<std::size_t>(getxattr(
|
||||
// file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size())));
|
||||
// EXPECT_STREQ(attr.c_str(), val.c_str());
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_listxattr(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr2", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(attr.size(),
|
||||
// static_cast<std::size_t>(getxattr(
|
||||
// file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size())));
|
||||
// EXPECT_STREQ(attr.c_str(), val.c_str());
|
||||
//
|
||||
// std::string data;
|
||||
// auto size = listxattr(file_path.c_str(), data.data(), 0U);
|
||||
// EXPECT_EQ(31, size);
|
||||
//
|
||||
// data.resize(static_cast<std::size_t>(size));
|
||||
// EXPECT_EQ(size, listxattr(file_path.c_str(), data.data(),
|
||||
// static_cast<std::size_t>(size)));
|
||||
//
|
||||
// auto *ptr = data.data();
|
||||
// EXPECT_STREQ("user.test_attr", ptr);
|
||||
//
|
||||
// ptr += strlen(ptr) + 1;
|
||||
// EXPECT_STREQ("user.test_attr2", ptr);
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_replace(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
//
|
||||
// attr = "cow";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_REPLACE));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(attr.size(),
|
||||
// static_cast<std::size_t>(getxattr(
|
||||
// file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size())));
|
||||
// EXPECT_STREQ(attr.c_str(), val.c_str());
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_default_create(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), 0));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(attr.size(),
|
||||
// static_cast<std::size_t>(getxattr(
|
||||
// file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size())));
|
||||
// EXPECT_STREQ(attr.c_str(), val.c_str());
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_default_replace(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), 0));
|
||||
//
|
||||
// attr = "cow";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), 0));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(attr.size(),
|
||||
// static_cast<std::size_t>(getxattr(
|
||||
// file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size())));
|
||||
// EXPECT_STREQ(attr.c_str(), val.c_str());
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_create_fails_if_exists(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), 0));
|
||||
// EXPECT_EQ(-1, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
// EXPECT_EQ(EEXIST, errno);
|
||||
// }
|
||||
//
|
||||
// static void
|
||||
// test_xattr_create_fails_if_not_exists(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(-1, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_REPLACE));
|
||||
// EXPECT_EQ(ENODATA, errno);
|
||||
// }
|
||||
//
|
||||
// static void test_xattr_removexattr(const std::string &file_path) {
|
||||
// std::cout << __FUNCTION__ << std::endl;
|
||||
// std::string attr = "moose";
|
||||
// EXPECT_EQ(0, setxattr(file_path.c_str(), "user.test_attr", attr.data(),
|
||||
// attr.size(), XATTR_CREATE));
|
||||
//
|
||||
// EXPECT_EQ(0, removexattr(file_path.c_str(), "user.test_attr"));
|
||||
//
|
||||
// std::string val;
|
||||
// val.resize(attr.size());
|
||||
// EXPECT_EQ(-1, getxattr(file_path.c_str(), "user.test_attr", val.data(),
|
||||
// val.size()));
|
||||
// EXPECT_EQ(ENODATA, errno);
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "write_read_test");
|
||||
// // test_write_and_read(utils::path::create_api_path("write_read_test"),
|
||||
// // file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location, "from_rename_file_test");
|
||||
// // auto to_file_path =
|
||||
// // utils::path::combine(mount_location, {"to_rename_file_test"});
|
||||
// // test_rename_file(file_path, to_file_path,
|
||||
// // provider_ptr->is_rename_supported());
|
||||
// // EXPECT_TRUE(utils::file::file(file_path).remove());
|
||||
// // EXPECT_TRUE(utils::file::file(to_file_path).remove());
|
||||
// //
|
||||
// // file_path =
|
||||
// // utils::path::combine(mount_location,
|
||||
// {"from_rename_dir_test"});
|
||||
// // to_file_path =
|
||||
// // utils::path::combine(mount_location, {"to_rename_dir_test"});
|
||||
// // test_rename_directory(file_path, to_file_path,
|
||||
// // provider_ptr->is_rename_supported());
|
||||
// // EXPECT_TRUE(utils::file::retry_delete_directory(file_path.c_str()));
|
||||
// // EXPECT_TRUE(utils::file::retry_delete_directory(to_file_path.c_str()));
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "truncate_file_test"); test_truncate(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "ftruncate_file_test"); test_ftruncate(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // #if !defined(__APPLE__)
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "fallocate_file_test");
|
||||
// // test_fallocate(utils::path::create_api_path("fallocate_file_test"),
|
||||
// // file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// // #endif
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "write_fails_ro_test"); test_write_operations_fail_if_read_only(
|
||||
// // utils::path::create_api_path("write_fails_ro_test"),
|
||||
// // file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location, "getattr.txt");
|
||||
// // test_file_getattr(utils::path::create_api_path("getattr.txt"),
|
||||
// // file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = utils::path::combine(mount_location, {"getattr_dir"});
|
||||
// // test_directory_getattr(utils::path::create_api_path("getattr_dir"),
|
||||
// // file_path);
|
||||
// // rmdir_and_test(file_path);
|
||||
// //
|
||||
// // #if !__APPLE__ && HAS_SETXATTR
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location,
|
||||
// // "xattr_invalid_names_test");
|
||||
// // test_xattr_invalid_parameters(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location, "xattr_create_get_test");
|
||||
// // test_xattr_create_and_get(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location, "xattr_listxattr_test");
|
||||
// // test_xattr_listxattr(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "xattr_replace_test"); test_xattr_replace(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location,
|
||||
// // "xattr_default_create_test");
|
||||
// // test_xattr_default_create(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location,
|
||||
// // "xattr_default_replace_test");
|
||||
// // test_xattr_default_replace(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "xattr_create_fails_exists_test");
|
||||
// // test_xattr_create_fails_if_exists(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path = create_file_and_test(mount_location,
|
||||
// // "xattr_create_fails_not_exists_test");
|
||||
// // test_xattr_create_fails_if_not_exists(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// //
|
||||
// // file_path =
|
||||
// // create_file_and_test(mount_location,
|
||||
// "xattr_removexattr_test");
|
||||
// // test_xattr_removexattr(file_path);
|
||||
// // unlink_file_and_test(file_path);
|
||||
// // #endif
|
248
repertory/repertory_test/src/json_serialize_test.cpp
Normal file
248
repertory/repertory_test/src/json_serialize_test.cpp
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "types/remote.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(json_serialize, can_handle_directory_item) {
|
||||
directory_item cfg{
|
||||
"api", "parent", true, 2U, {{META_DIRECTORY, "true"}},
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("api", data.at(JSON_API_PATH).get<std::string>().c_str());
|
||||
EXPECT_STREQ("parent", data.at(JSON_API_PARENT).get<std::string>().c_str());
|
||||
EXPECT_TRUE(data.at(JSON_DIRECTORY).get<bool>());
|
||||
EXPECT_STREQ(
|
||||
"true", data.at(JSON_META).at(META_DIRECTORY).get<std::string>().c_str());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<directory_item>();
|
||||
EXPECT_STREQ(cfg2.api_path.c_str(), cfg.api_path.c_str());
|
||||
EXPECT_STREQ(cfg2.api_parent.c_str(), cfg.api_parent.c_str());
|
||||
EXPECT_EQ(cfg2.directory, cfg.directory);
|
||||
EXPECT_STREQ(cfg2.meta.at(META_DIRECTORY).c_str(),
|
||||
cfg.meta.at(META_DIRECTORY).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_encrypt_config) {
|
||||
encrypt_config cfg{
|
||||
"token",
|
||||
"path",
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("token",
|
||||
data.at(JSON_ENCRYPTION_TOKEN).get<std::string>().c_str());
|
||||
EXPECT_STREQ("path", data.at(JSON_PATH).get<std::string>().c_str());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<encrypt_config>();
|
||||
EXPECT_STREQ(cfg2.encryption_token.c_str(), cfg.encryption_token.c_str());
|
||||
EXPECT_STREQ(cfg2.path.c_str(), cfg.path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_host_config) {
|
||||
host_config cfg{
|
||||
"agent", "pwd", "user", 1024U, "host", "path", "http", 11U,
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("agent", data.at(JSON_AGENT_STRING).get<std::string>().c_str());
|
||||
EXPECT_STREQ("pwd", data.at(JSON_API_PASSWORD).get<std::string>().c_str());
|
||||
EXPECT_STREQ("user", data.at(JSON_API_USER).get<std::string>().c_str());
|
||||
EXPECT_EQ(1024U, data.at(JSON_API_PORT).get<std::uint16_t>());
|
||||
EXPECT_STREQ("host",
|
||||
data.at(JSON_HOST_NAME_OR_IP).get<std::string>().c_str());
|
||||
EXPECT_STREQ("path", data.at(JSON_PATH).get<std::string>().c_str());
|
||||
EXPECT_STREQ("http", data.at(JSON_PROTOCOL).get<std::string>().c_str());
|
||||
EXPECT_EQ(11U, data.at(JSON_TIMEOUT_MS).get<std::uint16_t>());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<host_config>();
|
||||
EXPECT_STREQ(cfg2.agent_string.c_str(), cfg.agent_string.c_str());
|
||||
EXPECT_STREQ(cfg2.api_password.c_str(), cfg.api_password.c_str());
|
||||
EXPECT_STREQ(cfg2.api_user.c_str(), cfg.api_user.c_str());
|
||||
EXPECT_EQ(cfg2.api_port, cfg.api_port);
|
||||
EXPECT_STREQ(cfg2.host_name_or_ip.c_str(), cfg.host_name_or_ip.c_str());
|
||||
EXPECT_STREQ(cfg2.path.c_str(), cfg.path.c_str());
|
||||
EXPECT_STREQ(cfg2.protocol.c_str(), cfg.protocol.c_str());
|
||||
EXPECT_EQ(cfg2.timeout_ms, cfg.timeout_ms);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_remote_config) {
|
||||
remote::remote_config cfg{
|
||||
1024U, "token", "host", 11U, 20U, 21U,
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_EQ(1024U, data.at(JSON_API_PORT).get<std::uint16_t>());
|
||||
EXPECT_STREQ("token",
|
||||
data.at(JSON_ENCRYPTION_TOKEN).get<std::string>().c_str());
|
||||
EXPECT_STREQ("host",
|
||||
data.at(JSON_HOST_NAME_OR_IP).get<std::string>().c_str());
|
||||
EXPECT_EQ(11U, data.at(JSON_MAX_CONNECTIONS).get<std::uint16_t>());
|
||||
EXPECT_EQ(20U, data.at(JSON_RECV_TIMEOUT_MS).get<std::uint32_t>());
|
||||
EXPECT_EQ(21U, data.at(JSON_SEND_TIMEOUT_MS).get<std::uint32_t>());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<remote::remote_config>();
|
||||
EXPECT_EQ(cfg2.api_port, cfg.api_port);
|
||||
EXPECT_STREQ(cfg2.encryption_token.c_str(), cfg.encryption_token.c_str());
|
||||
EXPECT_STREQ(cfg2.host_name_or_ip.c_str(), cfg.host_name_or_ip.c_str());
|
||||
EXPECT_EQ(cfg2.max_connections, cfg.max_connections);
|
||||
EXPECT_EQ(cfg2.recv_timeout_ms, cfg.recv_timeout_ms);
|
||||
EXPECT_EQ(cfg2.send_timeout_ms, cfg.send_timeout_ms);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_remote_mount) {
|
||||
remote::remote_mount cfg{1024U, 21U, true, "token"};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_EQ(1024U, data.at(JSON_API_PORT).get<std::uint16_t>());
|
||||
EXPECT_EQ(21U, data.at(JSON_CLIENT_POOL_SIZE).get<std::uint16_t>());
|
||||
EXPECT_TRUE(data.at(JSON_ENABLE_REMOTE_MOUNT).get<bool>());
|
||||
EXPECT_STREQ("token",
|
||||
data.at(JSON_ENCRYPTION_TOKEN).get<std::string>().c_str());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<remote::remote_mount>();
|
||||
EXPECT_EQ(cfg2.api_port, cfg.api_port);
|
||||
EXPECT_EQ(cfg2.client_pool_size, cfg.client_pool_size);
|
||||
EXPECT_EQ(cfg2.enable, cfg.enable);
|
||||
EXPECT_STREQ(cfg2.encryption_token.c_str(), cfg.encryption_token.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_s3_config) {
|
||||
s3_config cfg{
|
||||
"access", "bucket", "token", "region", "secret", 31U, "url", true, false,
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("access", data.at(JSON_ACCESS_KEY).get<std::string>().c_str());
|
||||
EXPECT_STREQ("bucket", data.at(JSON_BUCKET).get<std::string>().c_str());
|
||||
EXPECT_STREQ("token",
|
||||
data.at(JSON_ENCRYPTION_TOKEN).get<std::string>().c_str());
|
||||
EXPECT_STREQ("region", data.at(JSON_REGION).get<std::string>().c_str());
|
||||
EXPECT_STREQ("secret", data.at(JSON_SECRET_KEY).get<std::string>().c_str());
|
||||
EXPECT_EQ(31U, data.at(JSON_TIMEOUT_MS).get<std::uint32_t>());
|
||||
EXPECT_STREQ("url", data.at(JSON_URL).get<std::string>().c_str());
|
||||
EXPECT_TRUE(data.at(JSON_USE_PATH_STYLE).get<bool>());
|
||||
EXPECT_FALSE(data.at(JSON_USE_REGION_IN_URL).get<bool>());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<s3_config>();
|
||||
EXPECT_STREQ(cfg2.access_key.c_str(), cfg.access_key.c_str());
|
||||
EXPECT_STREQ(cfg2.bucket.c_str(), cfg.bucket.c_str());
|
||||
EXPECT_STREQ(cfg2.encryption_token.c_str(), cfg.encryption_token.c_str());
|
||||
EXPECT_STREQ(cfg2.region.c_str(), cfg.region.c_str());
|
||||
EXPECT_STREQ(cfg2.secret_key.c_str(), cfg.secret_key.c_str());
|
||||
EXPECT_EQ(cfg2.timeout_ms, cfg.timeout_ms);
|
||||
EXPECT_STREQ(cfg2.url.c_str(), cfg.url.c_str());
|
||||
EXPECT_EQ(cfg2.use_path_style, cfg.use_path_style);
|
||||
EXPECT_EQ(cfg2.use_region_in_url, cfg.use_region_in_url);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_sia_config) {
|
||||
sia_config cfg{
|
||||
"bucket",
|
||||
};
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("bucket", data.at(JSON_BUCKET).get<std::string>().c_str());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<sia_config>();
|
||||
EXPECT_STREQ(cfg2.bucket.c_str(), cfg.bucket.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_atomic) {
|
||||
atomic<sia_config> cfg({
|
||||
"bucket",
|
||||
});
|
||||
|
||||
json data(cfg);
|
||||
EXPECT_STREQ("bucket", data.at(JSON_BUCKET).get<std::string>().c_str());
|
||||
|
||||
{
|
||||
auto cfg2 = data.get<atomic<sia_config>>();
|
||||
EXPECT_STREQ(cfg2.load().bucket.c_str(), cfg.load().bucket.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_database_type) {
|
||||
json data(database_type::rocksdb);
|
||||
EXPECT_EQ(database_type::rocksdb, data.get<database_type>());
|
||||
EXPECT_STREQ("rocksdb", data.get<std::string>().c_str());
|
||||
|
||||
data = database_type::sqlite;
|
||||
EXPECT_EQ(database_type::sqlite, data.get<database_type>());
|
||||
EXPECT_STREQ("sqlite", data.get<std::string>().c_str());
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_download_type) {
|
||||
json data(download_type::direct);
|
||||
EXPECT_EQ(download_type::direct, data.get<download_type>());
|
||||
EXPECT_STREQ("direct", data.get<std::string>().c_str());
|
||||
|
||||
data = download_type::default_;
|
||||
EXPECT_EQ(download_type::default_, data.get<download_type>());
|
||||
EXPECT_STREQ("default", data.get<std::string>().c_str());
|
||||
|
||||
data = download_type::ring_buffer;
|
||||
EXPECT_EQ(download_type::ring_buffer, data.get<download_type>());
|
||||
EXPECT_STREQ("ring_buffer", data.get<std::string>().c_str());
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_atomic_database_type) {
|
||||
json data(atomic<database_type>{database_type::rocksdb});
|
||||
EXPECT_EQ(database_type::rocksdb, data.get<atomic<database_type>>());
|
||||
EXPECT_STREQ("rocksdb", data.get<std::string>().c_str());
|
||||
|
||||
data = atomic<database_type>(database_type::sqlite);
|
||||
EXPECT_EQ(database_type::sqlite, data.get<atomic<database_type>>());
|
||||
EXPECT_STREQ("sqlite", data.get<std::string>().c_str());
|
||||
}
|
||||
|
||||
TEST(json_serialize, can_handle_atomic_download_type) {
|
||||
json data(atomic<download_type>{download_type::direct});
|
||||
EXPECT_EQ(download_type::direct, data.get<atomic<download_type>>());
|
||||
EXPECT_STREQ("direct", data.get<std::string>().c_str());
|
||||
|
||||
data = atomic<download_type>{download_type::default_};
|
||||
EXPECT_EQ(download_type::default_, data.get<download_type>());
|
||||
EXPECT_STREQ("default", data.get<std::string>().c_str());
|
||||
|
||||
data = atomic<download_type>{download_type::ring_buffer};
|
||||
EXPECT_EQ(download_type::ring_buffer, data.get<atomic<download_type>>());
|
||||
EXPECT_STREQ("ring_buffer", data.get<std::string>().c_str());
|
||||
}
|
||||
} // namespace repertory
|
104
repertory/repertory_test/src/lock_data_test.cpp
Normal file
104
repertory/repertory_test/src/lock_data_test.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(lock_data, lock_and_unlock) {
|
||||
{
|
||||
lock_data l(provider_type::sia, "1");
|
||||
EXPECT_EQ(lock_result::success, l.grab_lock());
|
||||
|
||||
std::thread([]() {
|
||||
lock_data l2(provider_type::sia, "1");
|
||||
EXPECT_EQ(lock_result::locked, l2.grab_lock(10));
|
||||
}).join();
|
||||
}
|
||||
|
||||
std::thread([]() {
|
||||
lock_data l(provider_type::sia, "1");
|
||||
EXPECT_EQ(lock_result::success, l.grab_lock(10));
|
||||
}).join();
|
||||
|
||||
#if defined(_WIN32)
|
||||
lock_data l2(provider_type::remote, "1");
|
||||
EXPECT_EQ(lock_result::success, l2.grab_lock());
|
||||
|
||||
lock_data l3(provider_type::remote, "2");
|
||||
EXPECT_EQ(lock_result::success, l3.grab_lock());
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
TEST(lock_data, set_and_unset_mount_state) {
|
||||
lock_data l(provider_type::sia, "1");
|
||||
EXPECT_TRUE(l.set_mount_state(true, "C:", 99));
|
||||
|
||||
lock_data l2(provider_type::remote, "1");
|
||||
EXPECT_TRUE(l2.set_mount_state(true, "D:", 97));
|
||||
|
||||
lock_data l3(provider_type::remote, "2");
|
||||
EXPECT_TRUE(l3.set_mount_state(true, "E:", 96));
|
||||
|
||||
json mount_state;
|
||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||
|
||||
EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
|
||||
mount_state["Sia1"].dump().c_str());
|
||||
EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
|
||||
mount_state["Remote1"].dump().c_str());
|
||||
EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
|
||||
mount_state["Remote2"].dump().c_str());
|
||||
|
||||
EXPECT_TRUE(l.set_mount_state(false, "C:", 99));
|
||||
EXPECT_TRUE(l2.set_mount_state(false, "D:", 98));
|
||||
EXPECT_TRUE(l3.set_mount_state(false, "E:", 97));
|
||||
|
||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||
mount_state["Sia1"].dump().c_str());
|
||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||
mount_state["Remote1"].dump().c_str());
|
||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||
mount_state["Remote2"].dump().c_str());
|
||||
}
|
||||
#else
|
||||
TEST(lock_data, set_and_unset_mount_state) {
|
||||
lock_data l(provider_type::sia, "1");
|
||||
EXPECT_TRUE(l.set_mount_state(true, "/mnt/1", 99));
|
||||
|
||||
json mount_state;
|
||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||
|
||||
EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
|
||||
mount_state["Sia1"].dump().c_str());
|
||||
|
||||
EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99));
|
||||
|
||||
EXPECT_TRUE(l.get_mount_state(mount_state));
|
||||
|
||||
EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
|
||||
mount_state["Sia1"].dump().c_str());
|
||||
}
|
||||
#endif
|
||||
} // namespace repertory
|
618
repertory/repertory_test/src/meta_db_test.cpp
Normal file
618
repertory/repertory_test/src/meta_db_test.cpp
Normal file
@@ -0,0 +1,618 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "fixtures/meta_db_fixture.hpp"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] auto create_test_file() -> std::string {
|
||||
static std::atomic<std::uint64_t> idx{};
|
||||
return "/test" + std::to_string(++idx);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(meta_db_test, meta_db_types);
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_api_path_from_source_path) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_api_path(test_source, api_path));
|
||||
EXPECT_STREQ(test_file.c_str(), api_path.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_change_source_path) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
auto test_source2 = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(test_file, META_SOURCE, test_source2));
|
||||
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_api_path(test_source2, api_path));
|
||||
EXPECT_STREQ(test_file.c_str(), api_path.c_str());
|
||||
|
||||
std::string api_path2;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_api_path(test_source, api_path2));
|
||||
EXPECT_TRUE(api_path2.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test,
|
||||
get_api_path_returns_item_not_found_if_source_does_not_exist) {
|
||||
std::string api_path;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_api_path(create_test_file(), api_path));
|
||||
EXPECT_TRUE(api_path.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_api_file_list) {
|
||||
std::vector<std::string> directories{};
|
||||
for (auto idx = 0U; idx < 5U; ++idx) {
|
||||
auto test_dir = create_test_file();
|
||||
directories.push_back(test_dir);
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
}));
|
||||
}
|
||||
|
||||
std::vector<std::string> files{};
|
||||
for (auto idx = 0U; idx < 5U; ++idx) {
|
||||
auto test_file = create_test_file();
|
||||
files.push_back(test_file);
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
}));
|
||||
}
|
||||
|
||||
auto file_list = this->meta_db->get_api_path_list();
|
||||
for (const auto &api_path : directories) {
|
||||
EXPECT_TRUE(utils::collection::includes(file_list, api_path));
|
||||
}
|
||||
|
||||
for (const auto &api_path : files) {
|
||||
EXPECT_TRUE(utils::collection::includes(file_list, api_path));
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test,
|
||||
full_get_item_meta_returns_item_not_found_if_item_does_not_exist) {
|
||||
auto api_path = create_test_file();
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_item_meta(api_path, meta));
|
||||
EXPECT_TRUE(meta.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(
|
||||
meta_db_test,
|
||||
individual_get_item_meta_returns_item_not_found_if_item_does_not_exist) {
|
||||
auto api_path = create_test_file();
|
||||
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_item_meta(api_path, META_DIRECTORY, value));
|
||||
EXPECT_TRUE(value.empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_full_item_meta_for_directory) {
|
||||
auto api_path = create_test_file();
|
||||
auto source_path = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
api_path, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_PINNED, utils::string::from_bool(true)},
|
||||
{META_SIZE, std::to_string(2ULL)},
|
||||
{META_SOURCE, source_path},
|
||||
}));
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(api_path, meta));
|
||||
|
||||
EXPECT_TRUE(utils::string::to_bool(meta[META_DIRECTORY]));
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_PINNED]));
|
||||
EXPECT_EQ(0U, utils::string::to_uint64(meta[META_SIZE]));
|
||||
EXPECT_TRUE(meta[META_SOURCE].empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_full_item_meta_for_file) {
|
||||
auto api_path = create_test_file();
|
||||
auto source_path = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
api_path, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_PINNED, utils::string::from_bool(true)},
|
||||
{META_SIZE, std::to_string(2ULL)},
|
||||
{META_SOURCE, source_path},
|
||||
}));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(api_path, meta));
|
||||
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY]));
|
||||
EXPECT_TRUE(utils::string::to_bool(meta[META_PINNED]));
|
||||
EXPECT_EQ(2ULL, utils::string::to_uint64(meta[META_SIZE]));
|
||||
EXPECT_STREQ(source_path.c_str(), meta[META_SOURCE].c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_individual_item_meta_for_directory) {
|
||||
auto api_path = create_test_file();
|
||||
auto source_path = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
api_path, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_PINNED, utils::string::from_bool(true)},
|
||||
{META_SIZE, std::to_string(2ULL)},
|
||||
{META_SOURCE, source_path},
|
||||
}));
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_DIRECTORY, value));
|
||||
EXPECT_TRUE(utils::string::to_bool(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_PINNED, value));
|
||||
EXPECT_FALSE(utils::string::to_bool(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_SIZE, value));
|
||||
EXPECT_EQ(0ULL, utils::string::to_uint64(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_SOURCE, value));
|
||||
EXPECT_TRUE(value.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_individual_item_meta_for_file) {
|
||||
auto api_path = create_test_file();
|
||||
auto source_path = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
api_path, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_PINNED, utils::string::from_bool(true)},
|
||||
{META_SIZE, std::to_string(2ULL)},
|
||||
{META_SOURCE, source_path},
|
||||
}));
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_DIRECTORY, value));
|
||||
EXPECT_FALSE(utils::string::to_bool(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_PINNED, value));
|
||||
EXPECT_TRUE(utils::string::to_bool(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_SIZE, value));
|
||||
EXPECT_EQ(2ULL, utils::string::to_uint64(value));
|
||||
}
|
||||
|
||||
{
|
||||
std::string value;
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->get_item_meta(api_path, META_SOURCE, value));
|
||||
EXPECT_STREQ(source_path.c_str(), value.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_pinned_files) {
|
||||
std::vector<std::string> pinned_files{};
|
||||
std::vector<std::string> unpinned_files{};
|
||||
for (auto idx = 0U; idx < 20U; ++idx) {
|
||||
auto test_file = create_test_file();
|
||||
auto pinned = idx % 2U == 0U;
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_PINNED, utils::string::from_bool(pinned)},
|
||||
}));
|
||||
if (pinned) {
|
||||
pinned_files.push_back(test_file);
|
||||
continue;
|
||||
}
|
||||
unpinned_files.push_back(test_file);
|
||||
}
|
||||
|
||||
auto pinned = this->meta_db->get_pinned_files();
|
||||
EXPECT_GE(pinned.size(), pinned_files.size());
|
||||
for (const auto &api_path : pinned_files) {
|
||||
EXPECT_TRUE(utils::collection::includes(pinned, api_path));
|
||||
}
|
||||
|
||||
for (const auto &api_path : unpinned_files) {
|
||||
EXPECT_TRUE(utils::collection::excludes(pinned, api_path));
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_total_item_count) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
}));
|
||||
|
||||
EXPECT_EQ(2U, this->meta_db->get_total_item_count());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test,
|
||||
get_total_item_count_decreases_after_directory_is_removed) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
}));
|
||||
|
||||
this->meta_db->remove_api_path(test_dir);
|
||||
EXPECT_EQ(1U, this->meta_db->get_total_item_count());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, get_total_item_count_decreases_after_file_is_removed) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
}));
|
||||
|
||||
this->meta_db->remove_api_path(test_file);
|
||||
EXPECT_EQ(1U, this->meta_db->get_total_item_count());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_get_total_size) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
|
||||
test_file = create_test_file();
|
||||
test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "1"},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_SIZE, "7"},
|
||||
}));
|
||||
|
||||
EXPECT_EQ(3U, this->meta_db->get_total_size());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test,
|
||||
total_size_does_not_decrease_after_directory_is_removed) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
|
||||
test_file = create_test_file();
|
||||
test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "1"},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_SIZE, "7"},
|
||||
}));
|
||||
this->meta_db->remove_api_path(test_dir);
|
||||
|
||||
EXPECT_EQ(3U, this->meta_db->get_total_size());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, total_size_decreases_after_file_is_removed) {
|
||||
this->meta_db->clear();
|
||||
EXPECT_EQ(0U, this->meta_db->get_total_item_count());
|
||||
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
|
||||
test_file = create_test_file();
|
||||
test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "1"},
|
||||
}));
|
||||
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_SIZE, "7"},
|
||||
}));
|
||||
this->meta_db->remove_api_path(test_file);
|
||||
|
||||
EXPECT_EQ(2U, this->meta_db->get_total_size());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_remove_api_path) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
this->meta_db->remove_api_path(test_file);
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_item_meta(test_file, meta));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, can_rename_item_meta) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
{META_SOURCE, test_source},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
auto test_file2 = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->rename_item_meta(test_file, test_file2));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->get_item_meta(test_file, meta));
|
||||
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_file2, meta));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, rename_item_meta_fails_if_not_found) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_file2 = create_test_file();
|
||||
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
this->meta_db->rename_item_meta(test_file, test_file2));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, set_item_meta_fails_with_missing_directory_meta) {
|
||||
auto test_file = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(api_error::error, this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
EXPECT_EQ(api_error::error,
|
||||
this->meta_db->set_item_meta(test_file, META_SOURCE, test_source));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, check_size_is_ignored_for_directory) {
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_SIZE, "2"},
|
||||
}));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_dir, meta));
|
||||
EXPECT_EQ(0U, utils::string::to_uint64(meta[META_SIZE]));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, check_pinned_is_ignored_for_directory) {
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_PINNED, utils::string::from_bool(true)},
|
||||
}));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_dir, meta));
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_PINNED]));
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, check_source_is_ignored_for_directory) {
|
||||
auto test_dir = create_test_file();
|
||||
auto test_source = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
{META_SOURCE, test_source},
|
||||
}));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_dir, meta));
|
||||
EXPECT_TRUE(meta[META_SOURCE].empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, check_set_item_meta_directory_defaults) {
|
||||
auto test_dir = create_test_file();
|
||||
EXPECT_EQ(api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_dir, {
|
||||
{META_DIRECTORY, utils::string::from_bool(true)},
|
||||
}));
|
||||
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_dir, meta));
|
||||
|
||||
EXPECT_TRUE(utils::string::to_bool(meta[META_DIRECTORY]));
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_PINNED]));
|
||||
EXPECT_EQ(0U, utils::string::to_uint64(meta[META_SIZE]));
|
||||
EXPECT_TRUE(meta[META_SOURCE].empty());
|
||||
}
|
||||
|
||||
TYPED_TEST(meta_db_test, check_set_item_meta_file_defaults) {
|
||||
auto test_file = create_test_file();
|
||||
EXPECT_EQ(
|
||||
api_error::success,
|
||||
this->meta_db->set_item_meta(
|
||||
test_file, {
|
||||
{META_DIRECTORY, utils::string::from_bool(false)},
|
||||
}));
|
||||
api_meta_map meta;
|
||||
EXPECT_EQ(api_error::success, this->meta_db->get_item_meta(test_file, meta));
|
||||
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_DIRECTORY]));
|
||||
EXPECT_FALSE(utils::string::to_bool(meta[META_PINNED]));
|
||||
EXPECT_EQ(0U, utils::string::to_uint64(meta[META_SIZE]));
|
||||
EXPECT_TRUE(meta[META_SOURCE].empty());
|
||||
}
|
||||
} // namespace repertory
|
720
repertory/repertory_test/src/open_file_test.cpp
Normal file
720
repertory/repertory_test/src/open_file_test.cpp
Normal file
@@ -0,0 +1,720 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "app_config.hpp"
|
||||
#include "file_manager/cache_size_mgr.hpp"
|
||||
#include "file_manager/open_file.hpp"
|
||||
#include "mocks/mock_provider.hpp"
|
||||
#include "mocks/mock_upload_manager.hpp"
|
||||
#include "types/repertory.hpp"
|
||||
#include "utils/event_capture.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr const std::size_t test_chunk_size{1024U};
|
||||
}
|
||||
|
||||
namespace repertory {
|
||||
class open_file_test : public ::testing::Test {
|
||||
public:
|
||||
console_consumer con_consumer;
|
||||
std::unique_ptr<app_config> cfg;
|
||||
static std::atomic<std::size_t> inst;
|
||||
mock_provider provider;
|
||||
mock_upload_manager upload_mgr;
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
event_system::instance().start();
|
||||
|
||||
auto open_file_dir = repertory::utils::path::combine(
|
||||
repertory::test::get_test_output_dir(),
|
||||
{"open_file_test" + std::to_string(++inst)});
|
||||
|
||||
cfg = std::make_unique<app_config>(provider_type::sia, open_file_dir);
|
||||
|
||||
cache_size_mgr::instance().initialize(cfg.get());
|
||||
}
|
||||
|
||||
void TearDown() override { event_system::instance().stop(); }
|
||||
};
|
||||
|
||||
std::atomic<std::size_t> open_file_test::inst{0U};
|
||||
|
||||
static void test_closeable_open_file(const open_file &file, bool directory,
|
||||
const api_error &err, std::uint64_t size,
|
||||
const std::string &source_path) {
|
||||
EXPECT_EQ(directory, file.is_directory());
|
||||
EXPECT_EQ(err, file.get_api_error());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_open_file_count());
|
||||
EXPECT_EQ(std::uint64_t(size), file.get_file_size());
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
EXPECT_TRUE(file.can_close());
|
||||
}
|
||||
|
||||
static void validate_write(open_file &file, std::size_t offset,
|
||||
data_buffer data, std::size_t bytes_written) {
|
||||
EXPECT_EQ(data.size(), bytes_written);
|
||||
|
||||
data_buffer read_data{};
|
||||
EXPECT_EQ(api_error::success, file.read(data.size(), offset, read_data));
|
||||
|
||||
EXPECT_TRUE(std::equal(data.begin(), data.end(), read_data.begin()));
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, properly_initializes_state_for_0_byte_file) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = 0U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
EXPECT_EQ(std::size_t(0U), file.get_read_state().size());
|
||||
EXPECT_FALSE(file.is_modified());
|
||||
EXPECT_EQ(test_chunk_size, file.get_chunk_size());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, properly_initializes_state_based_on_chunk_size) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = 8U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
open_file file(1U, 0U, fsi, provider, upload_mgr);
|
||||
EXPECT_EQ(std::size_t(8U), file.get_read_state().size());
|
||||
EXPECT_TRUE(file.get_read_state().none());
|
||||
|
||||
EXPECT_FALSE(file.is_modified());
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, META_SOURCE, _))
|
||||
.WillOnce(Return(api_error::success));
|
||||
EXPECT_EQ(std::size_t(1U), file.get_chunk_size());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, will_not_change_source_path_for_0_byte_file) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = 0U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
open_file file(0U, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, 0U, source_path);
|
||||
|
||||
file.close();
|
||||
EXPECT_EQ(api_error::success, file.get_api_error());
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
EXPECT_TRUE(utils::file::file(fsi.source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, will_change_source_path_if_file_size_is_greater_than_0) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, META_SOURCE, _))
|
||||
.WillOnce([&fsi](const std::string &, const std::string &,
|
||||
const std::string &source_path2) -> api_error {
|
||||
EXPECT_STRNE(fsi.source_path.c_str(), source_path2.c_str());
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, test_chunk_size,
|
||||
source_path);
|
||||
|
||||
file.close();
|
||||
EXPECT_EQ(api_error::download_stopped, file.get_api_error());
|
||||
EXPECT_STRNE(source_path.c_str(), file.get_source_path().c_str());
|
||||
EXPECT_FALSE(utils::file::file(source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test,
|
||||
will_not_change_source_path_if_file_size_matches_existing_source) {
|
||||
auto &rf = test::create_random_file(test_chunk_size);
|
||||
const auto source_path = rf.get_path();
|
||||
rf.close();
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, test_chunk_size,
|
||||
source_path);
|
||||
|
||||
file.close();
|
||||
EXPECT_EQ(api_error::success, file.get_api_error());
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
EXPECT_TRUE(utils::file::file(source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, write_with_incomplete_download) {
|
||||
const auto source_path = test::generate_test_file_name("test");
|
||||
auto &nf = test::create_random_file(test_chunk_size * 2u);
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 2u;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success,
|
||||
test_chunk_size * 2u, source_path);
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, _))
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
EXPECT_CALL(provider, read_file_bytes)
|
||||
.WillRepeatedly([&nf](const std::string & /* api_path */,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
if (stop_requested) {
|
||||
return api_error::download_stopped;
|
||||
}
|
||||
|
||||
if (offset == 0U) {
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = nf.read(data, offset, &bytes_read) ? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (not stop_requested) {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
return api_error::download_stopped;
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_upload)
|
||||
.WillOnce([&fsi](const std::string &api_path) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, store_resume)
|
||||
.Times(2)
|
||||
.WillRepeatedly([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
|
||||
data_buffer data = {10, 9, 8};
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_EQ(api_error::success, file.write(0U, data, bytes_written));
|
||||
validate_write(file, 0U, data, bytes_written);
|
||||
|
||||
const auto test_state = [&]() {
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
|
||||
EXPECT_FALSE(file.can_close());
|
||||
|
||||
EXPECT_TRUE(file.is_modified());
|
||||
|
||||
EXPECT_TRUE(file.get_read_state(0U));
|
||||
EXPECT_FALSE(file.get_read_state(1u));
|
||||
};
|
||||
test_state();
|
||||
|
||||
file.close();
|
||||
nf.close();
|
||||
|
||||
test_state();
|
||||
|
||||
EXPECT_EQ(api_error::download_incomplete, file.get_api_error());
|
||||
|
||||
EXPECT_TRUE(utils::file::file(fsi.source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, write_new_file) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = 0U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, store_resume)
|
||||
.WillOnce([&fsi](const i_open_file &file) {
|
||||
EXPECT_EQ(fsi.api_path, file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, file.get_source_path());
|
||||
});
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, 0U, source_path);
|
||||
data_buffer data = {10, 9, 8};
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, _))
|
||||
.WillOnce([&data](const std::string &,
|
||||
const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_SIZE).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
EXPECT_EQ(data.size(), utils::string::to_size_t(meta.at(META_SIZE)));
|
||||
return api_error::success;
|
||||
})
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_upload)
|
||||
.WillOnce([&fsi](const std::string &api_path) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, queue_upload)
|
||||
.WillOnce([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_EQ(api_error::success, file.write(0U, data, bytes_written));
|
||||
|
||||
const auto test_state = [&]() {
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
|
||||
EXPECT_FALSE(file.can_close());
|
||||
EXPECT_TRUE(file.is_modified());
|
||||
|
||||
EXPECT_TRUE(file.get_read_state(0U));
|
||||
EXPECT_EQ(std::size_t(1u), file.get_read_state().size());
|
||||
EXPECT_EQ(data.size(), file.get_file_size());
|
||||
};
|
||||
test_state();
|
||||
|
||||
file.close();
|
||||
|
||||
test_state();
|
||||
|
||||
EXPECT_EQ(api_error::success, file.get_api_error());
|
||||
|
||||
EXPECT_TRUE(utils::file::file(fsi.source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, write_new_file_multiple_chunks) {
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = 0U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_CALL(upload_mgr, store_resume)
|
||||
.WillOnce([&fsi](const i_open_file &file) {
|
||||
EXPECT_EQ(fsi.api_path, file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, file.get_source_path());
|
||||
});
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, 0U, source_path);
|
||||
data_buffer data = {10, 9, 8};
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, _))
|
||||
.WillOnce([&data](const std::string &,
|
||||
const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_SIZE).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
EXPECT_EQ(data.size(), utils::string::to_size_t(meta.at(META_SIZE)));
|
||||
return api_error::success;
|
||||
})
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
return api_error::success;
|
||||
})
|
||||
.WillOnce(
|
||||
[&data](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_SIZE).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
EXPECT_EQ(data.size() + test_chunk_size,
|
||||
utils::string::to_size_t(meta.at(META_SIZE)));
|
||||
return api_error::success;
|
||||
})
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_upload)
|
||||
.WillOnce([&fsi](const std::string &api_path) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, queue_upload)
|
||||
.WillOnce([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_EQ(api_error::success, file.write(0U, data, bytes_written));
|
||||
EXPECT_EQ(api_error::success,
|
||||
file.write(test_chunk_size, data, bytes_written));
|
||||
|
||||
const auto test_state = [&]() {
|
||||
EXPECT_STREQ(source_path.c_str(), file.get_source_path().c_str());
|
||||
|
||||
EXPECT_FALSE(file.can_close());
|
||||
EXPECT_TRUE(file.is_modified());
|
||||
|
||||
EXPECT_EQ(std::size_t(2u), file.get_read_state().size());
|
||||
for (std::size_t i = 0U; i < 2u; i++) {
|
||||
EXPECT_TRUE(file.get_read_state(i));
|
||||
}
|
||||
|
||||
EXPECT_EQ(data.size() + test_chunk_size, file.get_file_size());
|
||||
};
|
||||
test_state();
|
||||
|
||||
file.close();
|
||||
|
||||
test_state();
|
||||
|
||||
EXPECT_EQ(api_error::success, file.get_api_error());
|
||||
|
||||
EXPECT_TRUE(utils::file::file(fsi.source_path).exists());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, resize_file_to_0_bytes) {
|
||||
auto &r_file = test::create_random_file(test_chunk_size * 4U);
|
||||
const auto source_path = r_file.get_path();
|
||||
r_file.close();
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_EQ(api_error::success, cache_size_mgr::instance().expand(fsi.size));
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, fsi.size,
|
||||
source_path);
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, _))
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_SIZE).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
EXPECT_EQ(std::size_t(0U),
|
||||
utils::string::to_size_t(meta.at(META_SIZE)));
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_upload)
|
||||
.WillOnce([&fsi](const std ::string &api_path) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, queue_upload)
|
||||
.WillOnce([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
EXPECT_CALL(upload_mgr, store_resume)
|
||||
.WillOnce([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
|
||||
EXPECT_EQ(api_error::success, file.resize(0U));
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_file_size());
|
||||
EXPECT_FALSE(file.can_close());
|
||||
EXPECT_TRUE(file.is_modified());
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_read_state().size());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, resize_file_by_full_chunk) {
|
||||
auto &r_file = test::create_random_file(test_chunk_size * 4U);
|
||||
const auto source_path = r_file.get_path();
|
||||
r_file.close();
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
EXPECT_EQ(api_error::success, cache_size_mgr::instance().expand(fsi.size));
|
||||
|
||||
EXPECT_CALL(upload_mgr, store_resume)
|
||||
.WillOnce([&fsi](const i_open_file &file) {
|
||||
EXPECT_EQ(fsi.api_path, file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, file.get_source_path());
|
||||
});
|
||||
|
||||
open_file file(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
test_closeable_open_file(file, false, api_error::success, fsi.size,
|
||||
source_path);
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, _))
|
||||
.WillOnce([](const std::string &, const api_meta_map &meta) -> api_error {
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_CHANGED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_MODIFIED).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_SIZE).empty()));
|
||||
EXPECT_NO_THROW(EXPECT_FALSE(meta.at(META_WRITTEN).empty()));
|
||||
EXPECT_EQ(std::size_t(test_chunk_size * 3U),
|
||||
utils::string::to_size_t(meta.at(META_SIZE)));
|
||||
return api_error::success;
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_upload)
|
||||
.WillOnce([&fsi](const std::string &api_path) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, queue_upload)
|
||||
.WillOnce([&fsi](const i_open_file &cur_file) {
|
||||
EXPECT_EQ(fsi.api_path, cur_file.get_api_path());
|
||||
EXPECT_EQ(fsi.source_path, cur_file.get_source_path());
|
||||
});
|
||||
|
||||
EXPECT_EQ(api_error::success, file.resize(test_chunk_size * 3U));
|
||||
|
||||
EXPECT_EQ(std::size_t(test_chunk_size * 3U), file.get_file_size());
|
||||
EXPECT_FALSE(file.can_close());
|
||||
EXPECT_TRUE(file.is_modified());
|
||||
EXPECT_EQ(std::size_t(3U), file.get_read_state().size());
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, can_add_handle) {
|
||||
event_system::instance().start();
|
||||
console_consumer c;
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4u;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
event_consumer ec("filesystem_item_opened", [&fsi](const event &e) {
|
||||
const auto &ee = dynamic_cast<const filesystem_item_opened &>(e);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
ee.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
ee.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", ee.get_directory().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
event_consumer ec2("filesystem_item_handle_opened", [&fsi](const event &e) {
|
||||
const auto &ee = dynamic_cast<const filesystem_item_handle_opened &>(e);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
ee.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
ee.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", ee.get_directory().get<std::string>().c_str());
|
||||
EXPECT_STREQ("1", ee.get_handle().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, META_SOURCE, _))
|
||||
.WillOnce(Return(api_error::success));
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
|
||||
event_capture capture(
|
||||
{"filesystem_item_opened", "filesystem_item_handle_opened"});
|
||||
|
||||
open_file o(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
#if defined(_WIN32)
|
||||
o.add(1u, {});
|
||||
EXPECT_EQ(nullptr, o.get_open_data(1u).directory_buffer);
|
||||
#else
|
||||
o.add(1u, O_RDWR | O_SYNC);
|
||||
EXPECT_EQ(O_RDWR | O_SYNC, o.get_open_data(1u));
|
||||
#endif
|
||||
|
||||
capture.wait_for_empty();
|
||||
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, can_remove_handle) {
|
||||
event_system::instance().start();
|
||||
console_consumer c;
|
||||
|
||||
const auto source_path =
|
||||
test::generate_test_file_name("file_manager_open_file_test");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4u;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
event_consumer ec("filesystem_item_closed", [&fsi](const event &e) {
|
||||
const auto &ee = dynamic_cast<const filesystem_item_closed &>(e);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
ee.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
ee.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", ee.get_directory().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
event_consumer ec2("filesystem_item_handle_closed", [&fsi](const event &e) {
|
||||
const auto &ee = dynamic_cast<const filesystem_item_handle_closed &>(e);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
ee.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
ee.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", ee.get_directory().get<std::string>().c_str());
|
||||
EXPECT_STREQ("1", ee.get_handle().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
EXPECT_CALL(upload_mgr, remove_resume)
|
||||
.WillOnce(
|
||||
[&fsi](const std::string &api_path, const std::string &source_path2) {
|
||||
EXPECT_EQ(fsi.api_path, api_path);
|
||||
EXPECT_EQ(fsi.source_path, source_path2);
|
||||
});
|
||||
EXPECT_CALL(provider, set_item_meta(fsi.api_path, META_SOURCE, _))
|
||||
.WillOnce(Return(api_error::success));
|
||||
|
||||
event_capture capture({
|
||||
"filesystem_item_opened",
|
||||
"filesystem_item_handle_opened",
|
||||
"filesystem_item_handle_closed",
|
||||
"filesystem_item_closed",
|
||||
});
|
||||
|
||||
open_file o(test_chunk_size, 0U, fsi, provider, upload_mgr);
|
||||
#if defined(_WIN32)
|
||||
o.add(1u, {});
|
||||
#else
|
||||
o.add(1u, O_RDWR | O_SYNC);
|
||||
#endif
|
||||
o.remove(1u);
|
||||
|
||||
capture.wait_for_empty();
|
||||
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST_F(open_file_test,
|
||||
can_read_locally_after_write_with_file_size_greater_than_existing_size) {
|
||||
}
|
||||
|
||||
TEST_F(open_file_test, test_valid_download_chunks) {}
|
||||
|
||||
TEST_F(open_file_test, test_full_download_with_partial_chunk) {}
|
||||
|
||||
TEST_F(open_file_test, source_is_read_after_full_download) {}
|
||||
} // namespace repertory
|
41
repertory/repertory_test/src/packet_test.cpp
Normal file
41
repertory/repertory_test/src/packet_test.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "comm/packet/packet.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(packet, encrypt_and_decrypt) {
|
||||
packet test_packet;
|
||||
test_packet.encode("test");
|
||||
test_packet.encrypt("moose");
|
||||
|
||||
std::uint32_t size{};
|
||||
EXPECT_EQ(0, test_packet.decode(size));
|
||||
EXPECT_EQ(0, test_packet.decrypt("moose"));
|
||||
|
||||
std::string data;
|
||||
EXPECT_EQ(0, test_packet.decode(data));
|
||||
|
||||
EXPECT_STREQ("test", data.c_str());
|
||||
}
|
||||
} // namespace repertory
|
751
repertory/repertory_test/src/providers_test.cpp
Normal file
751
repertory/repertory_test/src/providers_test.cpp
Normal file
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
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
|
||||
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "comm/curl/curl_comm.hpp"
|
||||
#include "events/event_system.hpp"
|
||||
#include "file_manager/file_manager.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "providers/encrypt/encrypt_provider.hpp"
|
||||
#include "providers/i_provider.hpp"
|
||||
#include "providers/s3/s3_provider.hpp"
|
||||
#include "providers/sia/sia_provider.hpp"
|
||||
#include "utils/collection.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path.hpp"
|
||||
#include "utils/string.hpp"
|
||||
#include "utils/time.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
|
||||
namespace {
|
||||
#if defined(_WIN32)
|
||||
using gid_t = std::uint32_t;
|
||||
using uid_t = std::uint32_t;
|
||||
static constexpr auto getgid() -> gid_t { return 0U; }
|
||||
static constexpr auto getuid() -> uid_t { return 0U; }
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
const auto check_forced_dirs = [](const repertory::directory_item_list &list) {
|
||||
static auto forced_dirs = std::array<std::string, 2>{".", ".."};
|
||||
for (std::size_t i = 0U; i < forced_dirs.size(); ++i) {
|
||||
const auto &item = list.at(i);
|
||||
EXPECT_TRUE(item.directory);
|
||||
EXPECT_STREQ(forced_dirs.at(i).c_str(), item.api_path.c_str());
|
||||
EXPECT_STREQ("", item.api_parent.c_str());
|
||||
EXPECT_EQ(std::size_t(0U), item.size);
|
||||
}
|
||||
};
|
||||
|
||||
const auto create_directory = [](repertory::i_provider &provider,
|
||||
const std::string &api_path) {
|
||||
auto date = repertory::utils::time::get_time_now();
|
||||
auto meta = repertory::create_meta_attributes(
|
||||
date, 1U, date + 1U, date + 2U, true, getgid(), "", 0700, date + 3U, 2U,
|
||||
3U, 0U, api_path + "_src", getuid(), date + 4U);
|
||||
EXPECT_EQ(repertory::api_error::success,
|
||||
provider.create_directory(api_path, meta));
|
||||
|
||||
bool exists{};
|
||||
EXPECT_EQ(repertory::api_error::success,
|
||||
provider.is_directory(api_path, exists));
|
||||
EXPECT_TRUE(exists);
|
||||
|
||||
repertory::api_meta_map meta2{};
|
||||
EXPECT_EQ(repertory::api_error::success,
|
||||
provider.get_item_meta(api_path, meta2));
|
||||
|
||||
EXPECT_EQ(date, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_ACCESSED]));
|
||||
EXPECT_EQ(1U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_ATTRIBUTES]));
|
||||
EXPECT_EQ(date + 1U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_CHANGED]));
|
||||
EXPECT_EQ(date + 2U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_CREATION]));
|
||||
EXPECT_TRUE(
|
||||
repertory::utils::string::to_bool(meta2.at(repertory::META_DIRECTORY)));
|
||||
EXPECT_EQ(getgid(), static_cast<gid_t>(repertory::utils::string::to_uint32(
|
||||
meta2[repertory::META_GID])));
|
||||
EXPECT_EQ(std::uint32_t(0700),
|
||||
repertory::utils::string::to_uint32(meta2[repertory::META_MODE]));
|
||||
EXPECT_EQ(date + 3U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_MODIFIED]));
|
||||
EXPECT_EQ(2U,
|
||||
repertory::utils::string::to_uint64(meta2[repertory::META_BACKUP]));
|
||||
EXPECT_EQ(
|
||||
3U, repertory::utils::string::to_uint64(meta2[repertory::META_OSXFLAGS]));
|
||||
EXPECT_FALSE(
|
||||
repertory::utils::string::to_bool(meta2[repertory::META_PINNED]));
|
||||
EXPECT_EQ(std::uint64_t(0U),
|
||||
repertory::utils::string::to_uint64(meta2[repertory::META_SIZE]));
|
||||
EXPECT_STREQ((api_path + "_src").c_str(),
|
||||
meta2[repertory::META_SOURCE].c_str());
|
||||
EXPECT_EQ(getuid(), static_cast<uid_t>(repertory::utils::string::to_uint32(
|
||||
meta2[repertory::META_UID])));
|
||||
EXPECT_EQ(date + 4U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_WRITTEN]));
|
||||
};
|
||||
|
||||
const auto create_file = [](repertory::i_provider &provider,
|
||||
const std::string &api_path) {
|
||||
auto source_path = repertory::test::generate_test_file_name("providers_test");
|
||||
|
||||
auto date = repertory::utils::time::get_time_now();
|
||||
auto meta = repertory::create_meta_attributes(
|
||||
date, 1U, date + 1U, date + 2U, false, getgid(), "", 0700, date + 3U, 2U,
|
||||
3U, 0U, source_path, getuid(), date + 4U);
|
||||
EXPECT_EQ(repertory::api_error::success,
|
||||
provider.create_file(api_path, meta));
|
||||
|
||||
bool exists{};
|
||||
EXPECT_EQ(repertory::api_error::success, provider.is_file(api_path, exists));
|
||||
EXPECT_TRUE(exists);
|
||||
|
||||
EXPECT_TRUE(repertory::utils::file::file{source_path}.remove());
|
||||
|
||||
repertory::api_meta_map meta2{};
|
||||
EXPECT_EQ(repertory::api_error::success,
|
||||
provider.get_item_meta(api_path, meta2));
|
||||
|
||||
EXPECT_EQ(date, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_ACCESSED]));
|
||||
EXPECT_EQ(1U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_ATTRIBUTES]));
|
||||
EXPECT_EQ(date + 1U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_CHANGED]));
|
||||
EXPECT_EQ(date + 2U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_CREATION]));
|
||||
EXPECT_FALSE(
|
||||
repertory::utils::string::to_bool(meta2.at(repertory::META_DIRECTORY)));
|
||||
EXPECT_EQ(getgid(), static_cast<gid_t>(repertory::utils::string::to_uint32(
|
||||
meta2[repertory::META_GID])));
|
||||
EXPECT_EQ(std::uint32_t(0700),
|
||||
repertory::utils::string::to_uint32(meta2[repertory::META_MODE]));
|
||||
EXPECT_EQ(date + 3U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_MODIFIED]));
|
||||
EXPECT_EQ(2U,
|
||||
repertory::utils::string::to_uint64(meta2[repertory::META_BACKUP]));
|
||||
EXPECT_EQ(
|
||||
3U, repertory::utils::string::to_uint64(meta2[repertory::META_OSXFLAGS]));
|
||||
EXPECT_FALSE(
|
||||
repertory::utils::string::to_bool(meta2[repertory::META_PINNED]));
|
||||
EXPECT_EQ(std::uint64_t(0U),
|
||||
repertory::utils::string::to_uint64(meta2[repertory::META_SIZE]));
|
||||
EXPECT_STREQ(source_path.c_str(), meta2[repertory::META_SOURCE].c_str());
|
||||
EXPECT_EQ(getuid(), static_cast<uid_t>(repertory::utils::string::to_uint32(
|
||||
meta2[repertory::META_UID])));
|
||||
EXPECT_EQ(date + 4U, repertory::utils::string::to_uint64(
|
||||
meta2[repertory::META_WRITTEN]));
|
||||
};
|
||||
|
||||
const auto decrypt_parts = [](const repertory::app_config &cfg,
|
||||
std::string &path) {
|
||||
if (path != "/" && path != "." && path != "..") {
|
||||
auto parts = repertory::utils::string::split(path, '/', false);
|
||||
for (auto &part : parts) {
|
||||
if (part.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
EXPECT_EQ(true, repertory::utils::encryption::decrypt_file_name(
|
||||
cfg.get_encrypt_config().encryption_token, part));
|
||||
}
|
||||
path = repertory::utils::string::join(parts, '/');
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
static void can_create_and_remove_directory(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::not_implemented,
|
||||
provider.create_directory("/moose", meta));
|
||||
|
||||
EXPECT_EQ(api_error::not_implemented, provider.remove_directory("/moose"));
|
||||
return;
|
||||
}
|
||||
|
||||
create_directory(provider, "/pt01");
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/pt01"));
|
||||
|
||||
bool exists{};
|
||||
EXPECT_EQ(api_error::success, provider.is_directory("/pt01", exists));
|
||||
EXPECT_FALSE(exists);
|
||||
}
|
||||
|
||||
static void create_directory_fails_if_already_exists(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_directory(provider, "/pt01");
|
||||
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::directory_exists,
|
||||
provider.create_directory("/pt01", meta));
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/pt01"));
|
||||
}
|
||||
|
||||
static void
|
||||
create_directory_fails_if_file_already_exists(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_file(provider, "/pt01");
|
||||
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::item_exists, provider.create_directory("/pt01", meta));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_file("/pt01"));
|
||||
}
|
||||
|
||||
static void create_directory_clone_source_meta(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
EXPECT_EQ(api_error::not_implemented,
|
||||
provider.create_directory_clone_source_meta("/moose", "/moose"));
|
||||
return;
|
||||
}
|
||||
create_directory(provider, "/clone");
|
||||
|
||||
api_meta_map meta_orig{};
|
||||
EXPECT_EQ(api_error::success, provider.get_item_meta("/clone", meta_orig));
|
||||
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.create_directory_clone_source_meta("/clone", "/clone2"));
|
||||
|
||||
api_meta_map meta_clone{};
|
||||
EXPECT_EQ(api_error::success, provider.get_item_meta("/clone2", meta_clone));
|
||||
|
||||
EXPECT_EQ(meta_orig.size(), meta_clone.size());
|
||||
for (const auto &item : meta_orig) {
|
||||
if (item.first == META_KEY) {
|
||||
if (item.second.empty() && meta_clone[item.first].empty()) {
|
||||
continue;
|
||||
}
|
||||
EXPECT_STRNE(item.second.c_str(), meta_clone[item.first].c_str());
|
||||
continue;
|
||||
}
|
||||
EXPECT_STREQ(item.second.c_str(), meta_clone[item.first].c_str());
|
||||
}
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/clone"));
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/clone2"));
|
||||
}
|
||||
|
||||
static void create_directory_clone_source_meta_fails_if_already_exists(
|
||||
i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
create_directory(provider, "/clone");
|
||||
create_directory(provider, "/clone2");
|
||||
|
||||
EXPECT_EQ(api_error::directory_exists,
|
||||
provider.create_directory_clone_source_meta("/clone", "/clone2"));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/clone"));
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/clone2"));
|
||||
}
|
||||
|
||||
static void create_directory_clone_source_meta_fails_if_directory_not_found(
|
||||
i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(api_error::directory_not_found,
|
||||
provider.create_directory_clone_source_meta("/clone", "/clone2"));
|
||||
}
|
||||
|
||||
static void create_directory_clone_source_meta_fails_if_file_already_exists(
|
||||
i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_directory(provider, "/clone");
|
||||
create_file(provider, "/clone2");
|
||||
|
||||
EXPECT_EQ(api_error::item_exists,
|
||||
provider.create_directory_clone_source_meta("/clone", "/clone2"));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/clone"));
|
||||
EXPECT_EQ(api_error::success, provider.remove_file("/clone2"));
|
||||
}
|
||||
|
||||
static void can_create_and_remove_file(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::not_implemented,
|
||||
provider.create_file("/moose.txt", meta));
|
||||
return;
|
||||
}
|
||||
|
||||
create_file(provider, "/pt01.txt");
|
||||
|
||||
bool exists{};
|
||||
EXPECT_EQ(api_error::success, provider.is_file("/pt01.txt", exists));
|
||||
EXPECT_TRUE(exists);
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt"));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.is_file("/pt01.txt", exists));
|
||||
EXPECT_FALSE(exists);
|
||||
}
|
||||
|
||||
static void create_file_fails_if_already_exists(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_file(provider, "/pt01.txt");
|
||||
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::item_exists, provider.create_file("/pt01.txt", meta));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt"));
|
||||
}
|
||||
|
||||
static void
|
||||
create_file_fails_if_directory_already_exists(i_provider &provider) {
|
||||
if (provider.is_read_only()) {
|
||||
return;
|
||||
}
|
||||
|
||||
create_directory(provider, "/pt01");
|
||||
|
||||
api_meta_map meta{};
|
||||
EXPECT_EQ(api_error::directory_exists, provider.create_file("/pt01", meta));
|
||||
|
||||
EXPECT_EQ(api_error::success, provider.remove_directory("/pt01"));
|
||||
}
|
||||
|
||||
static void get_api_path_from_source(const app_config &cfg,
|
||||
i_provider &provider) {
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
const auto source_path =
|
||||
utils::path::combine("./test_date/encrypt", {"test.txt"});
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
std::string file_name{api_path.substr(1U)};
|
||||
decrypt_parts(cfg, file_name);
|
||||
EXPECT_STREQ("test.txt", file_name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
create_file(provider, "/pt01.txt");
|
||||
|
||||
filesystem_item fsi{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_filesystem_item("/pt01.txt", false, fsi));
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(fsi.source_path, api_path));
|
||||
|
||||
EXPECT_STREQ("/pt01.txt", api_path.c_str());
|
||||
EXPECT_EQ(api_error::success, provider.remove_file("/pt01.txt"));
|
||||
}
|
||||
|
||||
static void
|
||||
get_api_path_from_source_fails_if_file_not_found(const app_config &cfg,
|
||||
i_provider &provider) {
|
||||
std::string source_path{};
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
source_path = utils::path::combine(cfg.get_encrypt_config().path,
|
||||
{"test_not_found.txt"});
|
||||
} else {
|
||||
source_path = utils::path::combine("./", {"test_not_found.txt"});
|
||||
}
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::item_not_found,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
EXPECT_TRUE(api_path.empty());
|
||||
}
|
||||
|
||||
static void get_directory_item_count(const app_config &cfg,
|
||||
i_provider &provider) {
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
EXPECT_EQ(std::size_t(2U), provider.get_directory_item_count("/"));
|
||||
EXPECT_EQ(std::size_t(0U), provider.get_directory_item_count("/not_found"));
|
||||
|
||||
const auto source_path =
|
||||
utils::path::combine(test::get_test_input_dir(), {"encrypt", "sub10"});
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
EXPECT_EQ(std::size_t(1U), provider.get_directory_item_count(api_path));
|
||||
}
|
||||
}
|
||||
|
||||
static void get_directory_items(const app_config &cfg, i_provider &provider) {
|
||||
directory_item_list list{};
|
||||
EXPECT_EQ(api_error::success, provider.get_directory_items("/", list));
|
||||
check_forced_dirs(list);
|
||||
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
EXPECT_EQ(std::size_t(4U), list.size());
|
||||
|
||||
directory_item_list list_decrypted{list.begin() + 2U, list.end()};
|
||||
for (auto &dir_item : list_decrypted) {
|
||||
decrypt_parts(cfg, dir_item.api_parent);
|
||||
decrypt_parts(cfg, dir_item.api_path);
|
||||
}
|
||||
|
||||
auto dir = std::find_if(list_decrypted.begin(), list_decrypted.end(),
|
||||
[](const directory_item &dir_item) -> bool {
|
||||
return dir_item.directory;
|
||||
});
|
||||
EXPECT_LT(dir, list_decrypted.end());
|
||||
EXPECT_STREQ("/sub10", dir->api_path.c_str());
|
||||
EXPECT_STREQ("/", dir->api_parent.c_str());
|
||||
EXPECT_EQ(std::size_t(0U), dir->size);
|
||||
|
||||
auto file = std::find_if(list_decrypted.begin(), list_decrypted.end(),
|
||||
[](const directory_item &dir_item) -> bool {
|
||||
return not dir_item.directory;
|
||||
});
|
||||
EXPECT_LT(file, list_decrypted.end());
|
||||
EXPECT_STREQ("/test.txt", file->api_path.c_str());
|
||||
EXPECT_STREQ("/", file->api_parent.c_str());
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(std::size_t(47U), file->size);
|
||||
#else
|
||||
EXPECT_EQ(std::size_t(46U), file->size);
|
||||
#endif
|
||||
|
||||
const auto source_path =
|
||||
utils::path::combine(cfg.get_encrypt_config().path, {"sub10"});
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
list.clear();
|
||||
EXPECT_EQ(api_error::success, provider.get_directory_items(api_path, list));
|
||||
check_forced_dirs(list);
|
||||
EXPECT_EQ(std::size_t(3U), list.size());
|
||||
|
||||
directory_item_list list_decrypted2{list.begin() + 2U, list.end()};
|
||||
for (auto &dir_item : list_decrypted2) {
|
||||
decrypt_parts(cfg, dir_item.api_parent);
|
||||
decrypt_parts(cfg, dir_item.api_path);
|
||||
}
|
||||
|
||||
auto file2 = std::find_if(list_decrypted2.begin(), list_decrypted2.end(),
|
||||
[](const directory_item &dir_item) -> bool {
|
||||
return not dir_item.directory;
|
||||
});
|
||||
EXPECT_LT(file2, list_decrypted2.end());
|
||||
EXPECT_STREQ("/sub10/moose.txt", file2->api_path.c_str());
|
||||
EXPECT_STREQ("/sub10", file2->api_parent.c_str());
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(std::size_t(46U), file2->size);
|
||||
#else
|
||||
EXPECT_EQ(std::size_t(45U), file2->size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_directory_items_fails_if_directory_not_found(i_provider &provider) {
|
||||
directory_item_list list{};
|
||||
EXPECT_EQ(api_error::directory_not_found,
|
||||
provider.get_directory_items("/not_found", list));
|
||||
EXPECT_TRUE(list.empty());
|
||||
}
|
||||
|
||||
static void get_directory_items_fails_if_item_is_file(const app_config &cfg,
|
||||
i_provider &provider) {
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
const auto source_path =
|
||||
utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"});
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
directory_item_list list{};
|
||||
EXPECT_EQ(api_error::item_exists,
|
||||
provider.get_directory_items(api_path, list));
|
||||
EXPECT_TRUE(list.empty());
|
||||
}
|
||||
}
|
||||
|
||||
static void get_file(const app_config &cfg, i_provider &provider) {
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
const auto source_path =
|
||||
utils::path::combine(cfg.get_encrypt_config().path, {"test.txt"});
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
api_file file{};
|
||||
EXPECT_EQ(api_error::success, provider.get_file(api_path, file));
|
||||
decrypt_parts(cfg, file.api_path);
|
||||
decrypt_parts(cfg, file.api_parent);
|
||||
|
||||
EXPECT_STREQ("/test.txt", file.api_path.c_str());
|
||||
EXPECT_STREQ("/", file.api_parent.c_str());
|
||||
#if defined(_WIN32)
|
||||
EXPECT_EQ(std::size_t(47U), file.file_size);
|
||||
#else
|
||||
EXPECT_EQ(std::size_t(46U), file.file_size);
|
||||
#endif
|
||||
EXPECT_STREQ(source_path.c_str(), file.source_path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void get_file_fails_if_file_not_found(i_provider &provider) {
|
||||
api_file file{};
|
||||
EXPECT_EQ(api_error::item_not_found, provider.get_file("/not_found", file));
|
||||
}
|
||||
|
||||
static void get_file_fails_if_item_is_directory(const app_config &cfg,
|
||||
i_provider &provider) {
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
const auto source_path =
|
||||
utils::path::combine(cfg.get_encrypt_config().path, {"sub10"});
|
||||
|
||||
std::string api_path{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
provider.get_api_path_from_source(source_path, api_path));
|
||||
|
||||
api_file file{};
|
||||
EXPECT_EQ(api_error::directory_exists, provider.get_file(api_path, file));
|
||||
}
|
||||
}
|
||||
|
||||
static void get_file_list(const app_config &cfg, i_provider &provider) {
|
||||
api_file_list list{};
|
||||
std::string marker;
|
||||
EXPECT_EQ(api_error::success, provider.get_file_list(list, marker));
|
||||
if (provider.get_provider_type() == provider_type::encrypt) {
|
||||
EXPECT_EQ(std::size_t(2U), list.size());
|
||||
|
||||
std::vector<std::string> expected_parents{
|
||||
{"/"},
|
||||
{"/sub10"},
|
||||
};
|
||||
std::vector<std::string> expected_paths{
|
||||
{"/test.txt"},
|
||||
{"/sub10/moose.txt"},
|
||||
};
|
||||
|
||||
for (auto &file : list) {
|
||||
decrypt_parts(cfg, file.api_parent);
|
||||
decrypt_parts(cfg, file.api_path);
|
||||
utils::collection::remove_element(expected_parents, file.api_parent);
|
||||
utils::collection::remove_element(expected_paths, file.api_path);
|
||||
}
|
||||
EXPECT_TRUE(expected_parents.empty());
|
||||
EXPECT_TRUE(expected_paths.empty());
|
||||
}
|
||||
}
|
||||
|
||||
static void run_tests(const app_config &cfg, i_provider &provider) {
|
||||
get_file_list(cfg, provider);
|
||||
ASSERT_FALSE(::testing::Test::HasFailure());
|
||||
|
||||
can_create_and_remove_directory(provider);
|
||||
can_create_and_remove_file(provider);
|
||||
|
||||
create_directory_fails_if_already_exists(provider);
|
||||
create_directory_fails_if_file_already_exists(provider);
|
||||
|
||||
create_directory_clone_source_meta(provider);
|
||||
create_directory_clone_source_meta_fails_if_already_exists(provider);
|
||||
create_directory_clone_source_meta_fails_if_directory_not_found(provider);
|
||||
create_directory_clone_source_meta_fails_if_file_already_exists(provider);
|
||||
|
||||
create_file_fails_if_already_exists(provider);
|
||||
create_file_fails_if_directory_already_exists(provider);
|
||||
|
||||
get_api_path_from_source(cfg, provider);
|
||||
get_api_path_from_source_fails_if_file_not_found(cfg, provider);
|
||||
|
||||
// TODO: continue here
|
||||
get_directory_items(cfg, provider);
|
||||
get_directory_items_fails_if_directory_not_found(provider);
|
||||
get_directory_items_fails_if_item_is_file(cfg, provider);
|
||||
|
||||
get_directory_item_count(cfg, provider);
|
||||
|
||||
get_file(cfg, provider);
|
||||
get_file_fails_if_file_not_found(provider);
|
||||
get_file_fails_if_item_is_directory(cfg, provider);
|
||||
|
||||
// TODO need to test read when file size changes for encrypt provider
|
||||
/* get_file_list(provider);
|
||||
get_file_size(provider);
|
||||
get_filesystem_item(provider);
|
||||
get_filesystem_item_and_file(provider);
|
||||
get_filesystem_item_from_source_path(provider);
|
||||
get_item_meta(provider);
|
||||
get_item_meta2(provider);
|
||||
get_pinned_files(provider);
|
||||
get_total_drive_space(provider);
|
||||
get_total_item_count(provider);
|
||||
get_used_drive_space(provider);
|
||||
is_directory(provider);
|
||||
is_file(provider);
|
||||
is_file_writeable(provider);
|
||||
read_file_bytes(provider);
|
||||
remove_directory(provider);
|
||||
remove_file(provider);
|
||||
remove_item_meta(provider);
|
||||
rename_file(provider);
|
||||
set_item_meta(provider);
|
||||
set_item_meta2(provider);
|
||||
upload_file(provider); */
|
||||
}
|
||||
|
||||
TEST(providers, encrypt_provider) {
|
||||
const auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {"encrypt_provider"});
|
||||
|
||||
console_consumer consumer{};
|
||||
event_system::instance().start();
|
||||
{
|
||||
app_config cfg(provider_type::encrypt, config_path);
|
||||
|
||||
const auto encrypt_path =
|
||||
utils::path::combine(test::get_test_input_dir(), {"encrypt"});
|
||||
|
||||
EXPECT_STREQ(
|
||||
encrypt_path.c_str(),
|
||||
cfg.set_value_by_name("EncryptConfig.Path", encrypt_path).c_str());
|
||||
EXPECT_STREQ(
|
||||
"test_token",
|
||||
cfg.set_value_by_name("EncryptConfig.EncryptionToken", "test_token")
|
||||
.c_str());
|
||||
|
||||
encrypt_provider provider{cfg};
|
||||
file_manager mgr(cfg, provider);
|
||||
mgr.start();
|
||||
|
||||
EXPECT_TRUE(provider.start(
|
||||
[&provider](bool directory, api_file &file) -> api_error {
|
||||
return provider_meta_handler(provider, directory, file);
|
||||
},
|
||||
&mgr));
|
||||
EXPECT_EQ(provider_type::encrypt, provider.get_provider_type());
|
||||
EXPECT_TRUE(provider.is_read_only());
|
||||
EXPECT_TRUE(provider.is_online());
|
||||
EXPECT_FALSE(provider.is_rename_supported());
|
||||
|
||||
run_tests(cfg, provider);
|
||||
|
||||
provider.stop();
|
||||
mgr.stop();
|
||||
}
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST(providers, s3_provider) {
|
||||
const auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {"s3_provider"});
|
||||
|
||||
console_consumer consumer{};
|
||||
event_system::instance().start();
|
||||
{
|
||||
app_config cfg(provider_type::s3, config_path);
|
||||
{
|
||||
app_config src_cfg(
|
||||
provider_type::s3,
|
||||
utils::path::combine(test::get_test_config_dir(), {"storj"}));
|
||||
cfg.set_s3_config(src_cfg.get_s3_config());
|
||||
}
|
||||
|
||||
curl_comm comm{cfg.get_s3_config()};
|
||||
s3_provider provider{cfg, comm};
|
||||
file_manager mgr(cfg, provider);
|
||||
mgr.start();
|
||||
|
||||
EXPECT_TRUE(provider.start(
|
||||
[&provider](bool directory, api_file &file) -> api_error {
|
||||
return provider_meta_handler(provider, directory, file);
|
||||
},
|
||||
&mgr));
|
||||
EXPECT_EQ(provider_type::s3, provider.get_provider_type());
|
||||
EXPECT_FALSE(provider.is_read_only());
|
||||
EXPECT_TRUE(provider.is_online());
|
||||
EXPECT_FALSE(provider.is_rename_supported());
|
||||
|
||||
run_tests(cfg, provider);
|
||||
|
||||
provider.stop();
|
||||
mgr.stop();
|
||||
}
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST(providers, sia_provider) {
|
||||
const auto config_path =
|
||||
utils::path::combine(test::get_test_output_dir(), {"sia_provider"});
|
||||
|
||||
console_consumer consumer{};
|
||||
event_system::instance().start();
|
||||
{
|
||||
app_config cfg(provider_type::sia, config_path);
|
||||
{
|
||||
app_config src_cfg(
|
||||
provider_type::sia,
|
||||
utils::path::combine(test::get_test_config_dir(), {"sia"}));
|
||||
cfg.set_host_config(src_cfg.get_host_config());
|
||||
}
|
||||
|
||||
curl_comm comm{cfg.get_host_config()};
|
||||
sia_provider provider{cfg, comm};
|
||||
file_manager mgr(cfg, provider);
|
||||
mgr.start();
|
||||
|
||||
EXPECT_TRUE(provider.start(
|
||||
[&provider](bool directory, api_file &file) -> api_error {
|
||||
return provider_meta_handler(provider, directory, file);
|
||||
},
|
||||
&mgr));
|
||||
EXPECT_EQ(provider_type::sia, provider.get_provider_type());
|
||||
EXPECT_FALSE(provider.is_read_only());
|
||||
EXPECT_TRUE(provider.is_online());
|
||||
EXPECT_TRUE(provider.is_rename_supported());
|
||||
|
||||
run_tests(cfg, provider);
|
||||
|
||||
provider.stop();
|
||||
mgr.stop();
|
||||
}
|
||||
event_system::instance().stop();
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // 0
|
566
repertory/repertory_test/src/ring_buffer_open_file_test.cpp
Normal file
566
repertory/repertory_test/src/ring_buffer_open_file_test.cpp
Normal file
@@ -0,0 +1,566 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "file_manager/ring_buffer_open_file.hpp"
|
||||
#include "mocks/mock_provider.hpp"
|
||||
#include "mocks/mock_upload_manager.hpp"
|
||||
#include "platform/platform.hpp"
|
||||
#include "utils/file_utils.hpp"
|
||||
#include "utils/path.hpp"
|
||||
|
||||
namespace {
|
||||
constexpr const std::size_t test_chunk_size{1024U};
|
||||
|
||||
const auto ring_buffer_dir{
|
||||
repertory::utils::path::combine(
|
||||
repertory::test::get_test_output_dir(),
|
||||
{"file_manager_ring_buffer_open_file_test"}),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace repertory {
|
||||
class ring_buffer_open_file_test : public ::testing::Test {
|
||||
public:
|
||||
console_consumer con_consumer;
|
||||
mock_provider provider;
|
||||
|
||||
protected:
|
||||
void SetUp() override { event_system::instance().start(); }
|
||||
|
||||
void TearDown() override { event_system::instance().stop(); }
|
||||
};
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_forward_to_last_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(0U, 3U);
|
||||
file.forward(4U);
|
||||
|
||||
EXPECT_EQ(std::size_t(7U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(7U), file.get_last_chunk());
|
||||
for (std::size_t chunk = 0U; chunk < 8U; chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test,
|
||||
can_forward_to_last_chunk_if_count_is_greater_than_remaining) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(0U, 3U);
|
||||
file.forward(100U);
|
||||
|
||||
EXPECT_EQ(std::size_t(15U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(8U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(15U), file.get_last_chunk());
|
||||
for (std::size_t chunk = 8U; chunk <= 15U; chunk++) {
|
||||
EXPECT_FALSE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_forward_after_last_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(0U, 3U);
|
||||
file.forward(5U);
|
||||
|
||||
EXPECT_EQ(std::size_t(8U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(1U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(8U), file.get_last_chunk());
|
||||
EXPECT_FALSE(file.get_read_state(8U));
|
||||
for (std::size_t chunk = 1U; chunk < 8U; chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_forward_and_rollover_after_last_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(16U, 20U);
|
||||
file.forward(8U);
|
||||
|
||||
EXPECT_EQ(std::size_t(28U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(21U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(28U), file.get_last_chunk());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_reverse_to_first_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(0U, 3U);
|
||||
file.reverse(3U);
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(7U), file.get_last_chunk());
|
||||
for (std::size_t chunk = 0U; chunk < 8U; chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test,
|
||||
can_reverse_to_first_chunk_if_count_is_greater_than_remaining) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(0U, 3U);
|
||||
file.reverse(13U);
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(7U), file.get_last_chunk());
|
||||
for (std::size_t chunk = 0U; chunk < 8U; chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_reverse_before_first_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 16U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(1U, 3U);
|
||||
file.reverse(3U);
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(7U), file.get_last_chunk());
|
||||
EXPECT_FALSE(file.get_read_state(0U));
|
||||
for (std::size_t chunk = 1U; chunk < 8U; chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test,
|
||||
can_reverse_and_rollover_before_first_chunk) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(16U, 20U);
|
||||
file.reverse(8U);
|
||||
|
||||
EXPECT_EQ(std::size_t(12U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(12U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(19U), file.get_last_chunk());
|
||||
|
||||
EXPECT_FALSE(file.get_read_state(12U));
|
||||
EXPECT_FALSE(file.get_read_state(13U));
|
||||
EXPECT_FALSE(file.get_read_state(14U));
|
||||
EXPECT_FALSE(file.get_read_state(15U));
|
||||
for (std::size_t chunk = 16U; chunk <= file.get_last_chunk(); chunk++) {
|
||||
EXPECT_TRUE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, can_reverse_full_ring) {
|
||||
auto source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
EXPECT_CALL(provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
{
|
||||
ring_buffer_open_file file(ring_buffer_dir, test_chunk_size, 30U, fsi,
|
||||
provider, 8U);
|
||||
file.set(8U, 15U);
|
||||
file.reverse(16U);
|
||||
|
||||
EXPECT_EQ(std::size_t(0U), file.get_current_chunk());
|
||||
EXPECT_EQ(std::size_t(0U), file.get_first_chunk());
|
||||
EXPECT_EQ(std::size_t(7U), file.get_last_chunk());
|
||||
|
||||
for (std::size_t chunk = 0U; chunk <= file.get_last_chunk(); chunk++) {
|
||||
EXPECT_FALSE(file.get_read_state(chunk));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, read_full_file) {
|
||||
auto &nf = test::create_random_file(test_chunk_size * 33u + 11u);
|
||||
auto download_source_path = nf.get_path();
|
||||
|
||||
auto dest_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
mock_provider mp;
|
||||
|
||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 33u + 11u;
|
||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(mp, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = nf.read(data, offset, &bytes_read) ? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp,
|
||||
8u);
|
||||
|
||||
auto ptr = utils::file::file::open_or_create_file(dest_path);
|
||||
auto &nf2 = *ptr;
|
||||
EXPECT_TRUE(nf2);
|
||||
|
||||
auto to_read = fsi.size;
|
||||
std::size_t chunk = 0u;
|
||||
while (to_read) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
rb.read(test_chunk_size, chunk * test_chunk_size, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(nf2.write(data, chunk * test_chunk_size, &bytes_written));
|
||||
chunk++;
|
||||
to_read -= data.size();
|
||||
}
|
||||
nf2.close();
|
||||
nf.close();
|
||||
|
||||
auto hash1 = utils::file::file(download_source_path).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, read_full_file_in_reverse) {
|
||||
auto &nf = test::create_random_file(test_chunk_size * 32u);
|
||||
auto download_source_path = nf.get_path();
|
||||
|
||||
auto dest_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
mock_provider mp;
|
||||
|
||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32u;
|
||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(mp, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = nf.read(data, offset, &bytes_read) ? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp,
|
||||
8u);
|
||||
|
||||
auto ptr = utils::file::file::open_or_create_file(dest_path);
|
||||
auto &nf2 = *ptr;
|
||||
EXPECT_TRUE(nf2);
|
||||
|
||||
auto to_read = fsi.size;
|
||||
std::size_t chunk = rb.get_total_chunks() - 1U;
|
||||
while (to_read > 0U) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
rb.read(test_chunk_size, chunk * test_chunk_size, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(nf2.write(data, chunk * test_chunk_size, &bytes_written));
|
||||
--chunk;
|
||||
to_read -= data.size();
|
||||
}
|
||||
nf2.close();
|
||||
nf.close();
|
||||
|
||||
auto hash1 = utils::file::file(download_source_path).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test, read_full_file_in_partial_chunks) {
|
||||
auto &nf = test::create_random_file(test_chunk_size * 32u);
|
||||
auto download_source_path = nf.get_path();
|
||||
|
||||
auto dest_path = test::generate_test_file_name("test");
|
||||
|
||||
mock_provider mp;
|
||||
|
||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32u;
|
||||
fsi.source_path = test::generate_test_file_name("test");
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(mp, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = nf.read(data, offset, &bytes_read) ? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp,
|
||||
8u);
|
||||
|
||||
auto ptr = utils::file::file::open_or_create_file(dest_path);
|
||||
auto &nf2 = *ptr;
|
||||
EXPECT_TRUE(nf2);
|
||||
// EXPECT_EQ(api_error::success, native_file::create_or_open(dest_path,
|
||||
// nf2));
|
||||
|
||||
auto total_read = std::uint64_t(0u);
|
||||
|
||||
while (total_read < fsi.size) {
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success, rb.read(3u, total_read, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(
|
||||
nf2.write(data.data(), data.size(), total_read, &bytes_written));
|
||||
total_read += data.size();
|
||||
}
|
||||
nf2.close();
|
||||
nf.close();
|
||||
|
||||
auto hash1 = utils::file::file(download_source_path).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ring_buffer_open_file_test,
|
||||
read_full_file_in_partial_chunks_in_reverse) {
|
||||
auto &nf = test::create_random_file(test_chunk_size * 32u);
|
||||
auto download_source_path = nf.get_path();
|
||||
|
||||
auto dest_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
mock_provider mp;
|
||||
|
||||
EXPECT_CALL(mp, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.directory = false;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 32u;
|
||||
fsi.source_path = test::generate_test_file_name("ring_buffer_open_file");
|
||||
|
||||
std::mutex read_mtx;
|
||||
EXPECT_CALL(mp, read_file_bytes)
|
||||
.WillRepeatedly([&read_mtx, &nf](const std::string & /* api_path */,
|
||||
std::size_t size, std::uint64_t offset,
|
||||
data_buffer &data,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
mutex_lock lock(read_mtx);
|
||||
|
||||
EXPECT_FALSE(stop_requested);
|
||||
std::size_t bytes_read{};
|
||||
data.resize(size);
|
||||
auto ret = nf.read(data, offset, &bytes_read) ? api_error::success
|
||||
: api_error::os_error;
|
||||
EXPECT_EQ(bytes_read, data.size());
|
||||
return ret;
|
||||
});
|
||||
{
|
||||
ring_buffer_open_file rb(ring_buffer_dir, test_chunk_size, 30U, fsi, mp,
|
||||
8u);
|
||||
|
||||
auto ptr = utils::file::file::open_or_create_file(dest_path);
|
||||
auto &nf2 = *ptr;
|
||||
EXPECT_TRUE(nf2);
|
||||
|
||||
std::uint64_t total_read{0U};
|
||||
auto read_size{3U};
|
||||
|
||||
while (total_read < fsi.size) {
|
||||
auto offset = fsi.size - total_read - read_size;
|
||||
auto remain = fsi.size - total_read;
|
||||
|
||||
data_buffer data{};
|
||||
EXPECT_EQ(api_error::success,
|
||||
rb.read(static_cast<std::size_t>(
|
||||
std::min(remain, std::uint64_t(read_size))),
|
||||
(remain >= read_size) ? offset : 0u, data));
|
||||
|
||||
std::size_t bytes_written{};
|
||||
EXPECT_TRUE(
|
||||
nf2.write(data, (remain >= read_size) ? offset : 0u, &bytes_written));
|
||||
total_read += data.size();
|
||||
}
|
||||
nf2.close();
|
||||
nf.close();
|
||||
|
||||
auto hash1 = utils::file::file(download_source_path).sha256();
|
||||
auto hash2 = utils::file::file(dest_path).sha256();
|
||||
|
||||
EXPECT_TRUE(hash1.has_value());
|
||||
EXPECT_TRUE(hash2.has_value());
|
||||
if (hash1.has_value() && hash2.has_value()) {
|
||||
EXPECT_STREQ(hash1.value().c_str(), hash2.value().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
184
repertory/repertory_test/src/upload_test.cpp
Normal file
184
repertory/repertory_test/src/upload_test.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "file_manager/upload.hpp"
|
||||
#include "mocks/mock_provider.hpp"
|
||||
#include "utils/event_capture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
static constexpr const std::size_t test_chunk_size{1024U};
|
||||
|
||||
TEST(upload, can_upload_a_valid_file) {
|
||||
console_consumer con;
|
||||
|
||||
event_system::instance().start();
|
||||
|
||||
const auto source_path = test::generate_test_file_name("upload_test");
|
||||
|
||||
mock_provider mock_prov;
|
||||
|
||||
EXPECT_CALL(mock_prov, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
event_consumer evt_com("file_upload_completed", [&fsi](const event &evt) {
|
||||
const auto &comp_evt = dynamic_cast<const file_upload_completed &>(evt);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
comp_evt.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
comp_evt.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("success", comp_evt.get_result().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", comp_evt.get_cancelled().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
EXPECT_CALL(mock_prov, upload_file(fsi.api_path, fsi.source_path, _))
|
||||
.WillOnce([](const std::string &, const std::string &,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
EXPECT_FALSE(stop_requested);
|
||||
return api_error::success;
|
||||
});
|
||||
upload upload(fsi, mock_prov);
|
||||
|
||||
event_capture evt_cap({"file_upload_completed"});
|
||||
evt_cap.wait_for_empty();
|
||||
|
||||
EXPECT_EQ(api_error::success, upload.get_api_error());
|
||||
EXPECT_FALSE(upload.is_cancelled());
|
||||
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST(upload, can_cancel_upload) {
|
||||
console_consumer con;
|
||||
|
||||
event_system::instance().start();
|
||||
|
||||
const auto source_path = test::generate_test_file_name("upload_test");
|
||||
|
||||
mock_provider mock_provider;
|
||||
|
||||
EXPECT_CALL(mock_provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
event_consumer evt_con("file_upload_completed", [&fsi](const event &evt) {
|
||||
const auto &comp_evt = dynamic_cast<const file_upload_completed &>(evt);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
comp_evt.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
comp_evt.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("comm_error",
|
||||
comp_evt.get_result().get<std::string>().c_str());
|
||||
EXPECT_STREQ("1", comp_evt.get_cancelled().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
std::mutex mtx;
|
||||
std::condition_variable notify;
|
||||
|
||||
EXPECT_CALL(mock_provider, upload_file(fsi.api_path, fsi.source_path, _))
|
||||
.WillOnce([¬ify, &mtx](const std::string &, const std::string &,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
EXPECT_FALSE(stop_requested);
|
||||
|
||||
unique_mutex_lock lock(mtx);
|
||||
notify.notify_one();
|
||||
lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
notify.wait(lock);
|
||||
lock.unlock();
|
||||
|
||||
EXPECT_TRUE(stop_requested);
|
||||
|
||||
return api_error::comm_error;
|
||||
});
|
||||
|
||||
unique_mutex_lock lock(mtx);
|
||||
upload upload(fsi, mock_provider);
|
||||
notify.wait(lock);
|
||||
|
||||
upload.cancel();
|
||||
|
||||
notify.notify_one();
|
||||
lock.unlock();
|
||||
|
||||
event_capture evt_cap({"file_upload_completed"});
|
||||
evt_cap.wait_for_empty();
|
||||
|
||||
EXPECT_EQ(api_error::comm_error, upload.get_api_error());
|
||||
EXPECT_TRUE(upload.is_cancelled());
|
||||
|
||||
event_system::instance().stop();
|
||||
}
|
||||
|
||||
TEST(upload, can_stop_upload) {
|
||||
console_consumer con;
|
||||
|
||||
event_system::instance().start();
|
||||
|
||||
const auto source_path = test::generate_test_file_name("upload_test");
|
||||
|
||||
mock_provider mock_provider;
|
||||
|
||||
EXPECT_CALL(mock_provider, is_read_only()).WillRepeatedly(Return(false));
|
||||
|
||||
filesystem_item fsi;
|
||||
fsi.api_path = "/test.txt";
|
||||
fsi.size = test_chunk_size * 4U;
|
||||
fsi.source_path = source_path;
|
||||
|
||||
event_consumer evt_con("file_upload_completed", [&fsi](const event &evt) {
|
||||
const auto &evt_com = dynamic_cast<const file_upload_completed &>(evt);
|
||||
EXPECT_STREQ(fsi.api_path.c_str(),
|
||||
evt_com.get_api_path().get<std::string>().c_str());
|
||||
EXPECT_STREQ(fsi.source_path.c_str(),
|
||||
evt_com.get_source().get<std::string>().c_str());
|
||||
EXPECT_STREQ("comm_error", evt_com.get_result().get<std::string>().c_str());
|
||||
EXPECT_STREQ("0", evt_com.get_cancelled().get<std::string>().c_str());
|
||||
});
|
||||
|
||||
EXPECT_CALL(mock_provider, upload_file(fsi.api_path, fsi.source_path, _))
|
||||
.WillOnce([](const std::string &, const std::string &,
|
||||
stop_type &stop_requested) -> api_error {
|
||||
std::this_thread::sleep_for(3s);
|
||||
EXPECT_TRUE(stop_requested);
|
||||
return api_error::comm_error;
|
||||
});
|
||||
|
||||
event_capture evt_cap({"file_upload_completed"});
|
||||
|
||||
{
|
||||
upload upload(fsi, mock_provider);
|
||||
}
|
||||
|
||||
evt_cap.wait_for_empty();
|
||||
|
||||
event_system::instance().stop();
|
||||
}
|
||||
} // namespace repertory
|
73
repertory/repertory_test/src/utils_test.cpp
Normal file
73
repertory/repertory_test/src/utils_test.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
#include "test_common.hpp"
|
||||
|
||||
#include "providers/s3/s3_provider.hpp"
|
||||
#include "utils/file.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TEST(utils, convert_api_date) {
|
||||
#if defined(_WIN32)
|
||||
auto file_time = utils::time::unix_time_to_filetime(
|
||||
s3_provider::convert_api_date("2009-10-12T17:50:30.111Z"));
|
||||
|
||||
SYSTEMTIME st{};
|
||||
FileTimeToSystemTime(&file_time, &st);
|
||||
|
||||
EXPECT_EQ(2009, st.wYear);
|
||||
EXPECT_EQ(10, st.wMonth);
|
||||
EXPECT_EQ(12, st.wDay);
|
||||
|
||||
EXPECT_EQ(17, st.wHour);
|
||||
EXPECT_EQ(50, st.wMinute);
|
||||
EXPECT_EQ(30, st.wSecond);
|
||||
EXPECT_EQ(111, st.wMilliseconds);
|
||||
#else // !defined(_WIN32)
|
||||
auto unix_time = s3_provider::convert_api_date("2009-10-12T17:50:30.111Z") /
|
||||
utils::time::NANOS_PER_SECOND;
|
||||
auto *tm_data = gmtime(reinterpret_cast<time_t *>(&unix_time));
|
||||
EXPECT_TRUE(tm_data != nullptr);
|
||||
if (tm_data != nullptr) {
|
||||
EXPECT_EQ(2009, tm_data->tm_year + 1900);
|
||||
EXPECT_EQ(10, tm_data->tm_mon + 1);
|
||||
EXPECT_EQ(12, tm_data->tm_mday);
|
||||
|
||||
EXPECT_EQ(17, tm_data->tm_hour);
|
||||
EXPECT_EQ(50, tm_data->tm_min);
|
||||
EXPECT_EQ(30, tm_data->tm_sec);
|
||||
|
||||
auto millis = (s3_provider::convert_api_date("2009-10-12T17:50:30.111Z") %
|
||||
utils::time::NANOS_PER_SECOND) /
|
||||
1000000UL;
|
||||
EXPECT_EQ(111U, millis);
|
||||
}
|
||||
#endif // defined(_WIN32)
|
||||
}
|
||||
|
||||
TEST(utils, generate_sha256) {
|
||||
auto res = utils::file::file{__FILE__}.sha256();
|
||||
EXPECT_TRUE(res.has_value());
|
||||
if (res.has_value()) {
|
||||
std::cout << res.value() << std::endl;
|
||||
}
|
||||
}
|
||||
} // namespace repertory
|
221
repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp
Normal file
221
repertory/repertory_test/src/winfsp_drive_create_attr_test.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_normal_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, attr);
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_read_only_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_READONLY, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY, attr);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(), FILE_ATTRIBUTE_NORMAL));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
// TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_system_attribute) {
|
||||
// auto file_path{
|
||||
// utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
// };
|
||||
//
|
||||
// auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ |
|
||||
// GENERIC_WRITE,
|
||||
// FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
// CREATE_NEW, FILE_ATTRIBUTE_SYSTEM, nullptr);
|
||||
// ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
// ::CloseHandle(handle);
|
||||
//
|
||||
// auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
// EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_SYSTEM, attr);
|
||||
//
|
||||
// EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(),
|
||||
// FILE_ATTRIBUTE_NORMAL));
|
||||
//
|
||||
// EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
// }
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_new_file_with_hidden_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_HIDDEN, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN, attr);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(), FILE_ATTRIBUTE_NORMAL));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_always_file_with_normal_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, attr);
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_file_with_read_only_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY, attr);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(), FILE_ATTRIBUTE_NORMAL));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
// TYPED_TEST(winfsp_test,
|
||||
// cr8_attr_can_create_always_file_with_system_attribute) {
|
||||
// auto file_path{
|
||||
// utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
// };
|
||||
//
|
||||
// auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ |
|
||||
// GENERIC_WRITE,
|
||||
// FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
// CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, nullptr);
|
||||
// ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
// ::CloseHandle(handle);
|
||||
//
|
||||
// auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
// EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_SYSTEM, attr);
|
||||
//
|
||||
// EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(),
|
||||
// FILE_ATTRIBUTE_NORMAL));
|
||||
//
|
||||
// EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
// }
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_create_always_file_with_hidden_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_1"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
auto attr = ::GetFileAttributesA(file_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_HIDDEN, attr);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(), FILE_ATTRIBUTE_NORMAL));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_attr_can_handle_read_only_directory) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_1"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_1"}),
|
||||
};
|
||||
EXPECT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(
|
||||
dir_path.c_str(), FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY));
|
||||
|
||||
auto attr = ::GetFileAttributesA(dir_path.c_str());
|
||||
EXPECT_EQ((FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY), attr);
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
|
||||
EXPECT_FALSE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(dir_path.c_str(), FILE_ATTRIBUTE_DIRECTORY));
|
||||
|
||||
attr = ::GetFileAttributesA(dir_path.c_str());
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_DIRECTORY, attr);
|
||||
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
83
repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp
Normal file
83
repertory/repertory_test/src/winfsp_drive_create_nl_test.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_nl_can_create_file_of_max_component_length) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD max_length{};
|
||||
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr, 0,
|
||||
nullptr, &max_length, nullptr, nullptr,
|
||||
0));
|
||||
EXPECT_EQ(255U, max_length);
|
||||
|
||||
auto file_path = utils::path::combine(this->mount_location,
|
||||
{
|
||||
std::string(max_length, 'a'),
|
||||
});
|
||||
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
cr8_nl_can_not_create_file_greater_than_max_component_length) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD max_length{};
|
||||
EXPECT_TRUE(::GetVolumeInformationA(this->mount_location.c_str(), nullptr, 0,
|
||||
nullptr, &max_length, nullptr, nullptr,
|
||||
0));
|
||||
EXPECT_EQ(255U, max_length);
|
||||
|
||||
auto file_path = utils::path::combine(this->mount_location,
|
||||
{
|
||||
std::string(max_length + 1U, 'a'),
|
||||
});
|
||||
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_INVALID_NAME, ::GetLastError());
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
191
repertory/repertory_test/src/winfsp_drive_create_test.cpp
Normal file
191
repertory/repertory_test/src/winfsp_drive_create_test.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_can_create_file) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_0"}),
|
||||
};
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_create_new_fails_when_file_exists) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_0"}),
|
||||
};
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_EXISTS, ::GetLastError());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_can_open_existing_file) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_0"}),
|
||||
};
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_create_always_succeeds_when_file_exists) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_0"}),
|
||||
};
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
// EXPECT file_size is 0
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_can_delete_file_after_close) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_0"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
|
||||
// EXPECT file not found
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
cr8_file_cannot_create_files_with_invalid_characters_in_path) {
|
||||
for (auto &&invalid_char : std::array<std::string, 7U>{
|
||||
{"*", ":", "<", ">", "?", "|", "\""},
|
||||
}) {
|
||||
auto handle = ::CreateFileA(
|
||||
(this->mount_location + "\\" + invalid_char + "\\test_file_0").c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
std::cout << "char: " << invalid_char << std::endl;
|
||||
}
|
||||
EXPECT_EQ(ERROR_INVALID_NAME, ::GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
cr8_file_cannot_create_stream_files_with_extra_component_in_path) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location,
|
||||
{
|
||||
"test_file_0:test",
|
||||
"moose",
|
||||
}),
|
||||
};
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_INVALID_NAME, ::GetLastError());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_can_create_directory) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location,
|
||||
{
|
||||
"test_dir_0",
|
||||
}),
|
||||
};
|
||||
EXPECT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
EXPECT_FALSE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, cr8_file_directory_delete_fails_if_not_empty) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location,
|
||||
{
|
||||
"test_dir_0",
|
||||
}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path,
|
||||
{
|
||||
"test_file_0",
|
||||
}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(
|
||||
dir_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(
|
||||
dir_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(
|
||||
dir_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
279
repertory/repertory_test/src/winfsp_drive_delete_test.cpp
Normal file
279
repertory/repertory_test/src/winfsp_drive_delete_test.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, delete_directory_fails_if_directory_not_empty) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_3"}),
|
||||
};
|
||||
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_3"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_FALSE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_EQ(ERROR_DIR_NOT_EMPTY, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
EXPECT_FALSE(::DeleteFileA(file_path.c_str()));
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_FALSE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
delete_read_file_attributes_fails_if_delete_is_pending) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_3"}),
|
||||
};
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), DELETE, FILE_SHARE_DELETE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
typedef struct {
|
||||
BOOLEAN Disposition;
|
||||
} MY_FILE_DISPOSITION_INFO;
|
||||
|
||||
MY_FILE_DISPOSITION_INFO disp_info{TRUE};
|
||||
EXPECT_TRUE(::SetFileInformationByHandle(handle, FileDispositionInfo,
|
||||
&disp_info, sizeof disp_info));
|
||||
auto handle2 = ::CreateFileA(file_path.c_str(), FILE_READ_ATTRIBUTES, 0,
|
||||
nullptr, OPEN_EXISTING, 0, nullptr);
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle2);
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_FALSE(::DeleteFileA(file_path.c_str()));
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, delete_can_handle_mmap_after_file_deletion) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_3"}),
|
||||
};
|
||||
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
auto *mapping =
|
||||
::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
sys_info.dwAllocationGranularity, nullptr);
|
||||
EXPECT_TRUE(mapping != nullptr);
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, delete_can_delete_after_mapping) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_3"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_3"}),
|
||||
};
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_3"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto seed = static_cast<unsigned>(std::time(nullptr));
|
||||
srand(seed);
|
||||
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
{
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto *mapping =
|
||||
::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
16U * sys_info.dwAllocationGranularity, nullptr);
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
ASSERT_TRUE(mapping != nullptr);
|
||||
|
||||
auto *view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
ASSERT_TRUE(view != nullptr);
|
||||
|
||||
for (PUINT8 ptr = reinterpret_cast<PUINT8>(view),
|
||||
end = ptr + 16U * sys_info.dwAllocationGranularity;
|
||||
end > ptr; ++ptr) {
|
||||
*ptr = rand() & 0xFF;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
}
|
||||
|
||||
{
|
||||
auto handle =
|
||||
::CreateFileA(file_path2.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto mapping =
|
||||
::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
16U * sys_info.dwAllocationGranularity, nullptr);
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
ASSERT_TRUE(mapping != nullptr);
|
||||
|
||||
auto view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
EXPECT_TRUE(view != nullptr);
|
||||
|
||||
for (PUINT8 ptr = reinterpret_cast<PUINT8>(view),
|
||||
end = ptr + 16U * sys_info.dwAllocationGranularity;
|
||||
end > ptr; ++ptr) {
|
||||
*ptr = rand() & 0xFF;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
ASSERT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
|
||||
ASSERT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
ASSERT_FALSE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, delete_can_delete_on_close_after_mapping) {
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_3"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_3"}),
|
||||
};
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_3"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto seed = static_cast<unsigned>(std::time(nullptr));
|
||||
srand(seed);
|
||||
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
{
|
||||
auto handle = ::CreateFileA(
|
||||
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto *mapping =
|
||||
::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
16U * sys_info.dwAllocationGranularity, nullptr);
|
||||
EXPECT_TRUE(mapping != nullptr);
|
||||
|
||||
auto *view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
EXPECT_TRUE(view != nullptr);
|
||||
for (PUINT8 ptr = reinterpret_cast<PUINT8>(view),
|
||||
end = ptr + 16U * sys_info.dwAllocationGranularity;
|
||||
end > ptr; ++ptr) {
|
||||
*ptr = rand() & 0xFF;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
{
|
||||
auto handle = ::CreateFileA(
|
||||
file_path2.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto *mapping =
|
||||
::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
16U * sys_info.dwAllocationGranularity, nullptr);
|
||||
EXPECT_TRUE(mapping != nullptr);
|
||||
|
||||
auto *view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
EXPECT_TRUE(view != nullptr);
|
||||
|
||||
for (PUINT8 ptr = reinterpret_cast<PUINT8>(view),
|
||||
end = ptr + 16U * sys_info.dwAllocationGranularity;
|
||||
end > ptr; ++ptr) {
|
||||
*ptr = rand() & 0xFF;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
ASSERT_FALSE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
ASSERT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
406
repertory/repertory_test/src/winfsp_drive_info_test.cpp
Normal file
406
repertory/repertory_test/src/winfsp_drive_info_test.cpp
Normal file
@@ -0,0 +1,406 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_tag_info) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
FILE_ATTRIBUTE_TAG_INFO tag_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandleEx(handle, FileAttributeTagInfo,
|
||||
&tag_info, sizeof tag_info));
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, tag_info.FileAttributes);
|
||||
EXPECT_EQ(0, tag_info.ReparseTag);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_basic_info) {
|
||||
FILETIME file_time{};
|
||||
::GetSystemTimeAsFileTime(&file_time);
|
||||
|
||||
auto time_low = reinterpret_cast<PLARGE_INTEGER>(&file_time)->QuadPart;
|
||||
auto time_high = time_low + 10000 * 10000 /* 10 seconds */;
|
||||
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
FILE_BASIC_INFO basic_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandleEx(handle, FileBasicInfo, &basic_info,
|
||||
sizeof basic_info));
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, basic_info.FileAttributes);
|
||||
|
||||
EXPECT_LE(time_low, basic_info.CreationTime.QuadPart);
|
||||
EXPECT_GT(time_high, basic_info.CreationTime.QuadPart);
|
||||
|
||||
EXPECT_LE(time_low, basic_info.LastAccessTime.QuadPart);
|
||||
EXPECT_GT(time_high, basic_info.LastAccessTime.QuadPart);
|
||||
|
||||
EXPECT_LE(time_low, basic_info.LastWriteTime.QuadPart);
|
||||
EXPECT_GT(time_high, basic_info.LastWriteTime.QuadPart);
|
||||
|
||||
EXPECT_LE(time_low, basic_info.ChangeTime.QuadPart);
|
||||
EXPECT_GT(time_high, basic_info.ChangeTime.QuadPart);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_standard_info) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
FILE_STANDARD_INFO std_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandleEx(handle, FileStandardInfo,
|
||||
&std_info, sizeof std_info));
|
||||
EXPECT_EQ(0, std_info.AllocationSize.QuadPart);
|
||||
EXPECT_EQ(0, std_info.EndOfFile.QuadPart);
|
||||
EXPECT_EQ(1, std_info.NumberOfLinks);
|
||||
EXPECT_FALSE(std_info.DeletePending);
|
||||
EXPECT_FALSE(std_info.Directory);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_file_name_info) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
std::array<std::uint8_t, sizeof(FILE_NAME_INFO) + MAX_PATH> name_info{};
|
||||
EXPECT_TRUE(
|
||||
::GetFileInformationByHandleEx(handle, FileNameInfo, name_info.data(),
|
||||
static_cast<DWORD>(name_info.size())));
|
||||
|
||||
auto *info = reinterpret_cast<FILE_NAME_INFO *>(name_info.data());
|
||||
auto expected_name{
|
||||
std::string{"\\repertory\\"} + this->mount_location.at(0U) +
|
||||
"\\test_file_2",
|
||||
};
|
||||
|
||||
EXPECT_EQ(info->FileNameLength,
|
||||
static_cast<DWORD>(expected_name.size() * 2U));
|
||||
EXPECT_STREQ(expected_name.c_str(),
|
||||
utils::string::to_utf8(info->FileName).c_str());
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_get_file_name_info_buffer_too_small) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
std::array<std::uint8_t, sizeof(FILE_NAME_INFO)> name_info{};
|
||||
EXPECT_FALSE(
|
||||
::GetFileInformationByHandleEx(handle, FileNameInfo, name_info.data(),
|
||||
static_cast<DWORD>(name_info.size())));
|
||||
EXPECT_EQ(ERROR_MORE_DATA, ::GetLastError());
|
||||
|
||||
auto *info = reinterpret_cast<FILE_NAME_INFO *>(name_info.data());
|
||||
EXPECT_EQ('\\', info->FileName[0U]);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_file_info) {
|
||||
FILETIME file_time{};
|
||||
::GetSystemTimeAsFileTime(&file_time);
|
||||
|
||||
auto time_low = reinterpret_cast<PLARGE_INTEGER>(&file_time)->QuadPart;
|
||||
auto time_high = time_low + 10000 * 10000 /* 10 seconds */;
|
||||
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
|
||||
EXPECT_LE(
|
||||
time_low,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftCreationTime)->QuadPart);
|
||||
EXPECT_GT(
|
||||
time_high,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftCreationTime)->QuadPart);
|
||||
|
||||
EXPECT_LE(
|
||||
time_low,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftLastAccessTime)->QuadPart);
|
||||
EXPECT_GT(
|
||||
time_high,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftLastAccessTime)->QuadPart);
|
||||
|
||||
EXPECT_LE(
|
||||
time_low,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftLastWriteTime)->QuadPart);
|
||||
EXPECT_GT(
|
||||
time_high,
|
||||
reinterpret_cast<PLARGE_INTEGER>(&file_info.ftLastWriteTime)->QuadPart);
|
||||
|
||||
EXPECT_EQ(0U, file_info.nFileSizeHigh);
|
||||
EXPECT_EQ(0U, file_info.nFileSizeLow);
|
||||
|
||||
EXPECT_EQ(1U, file_info.nNumberOfLinks);
|
||||
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_ARCHIVE, file_info.dwFileAttributes);
|
||||
|
||||
EXPECT_EQ(0U, file_info.dwVolumeSerialNumber);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_get_file_path) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
std::string final_path;
|
||||
final_path.resize(MAX_PATH + 1U);
|
||||
|
||||
auto result = ::GetFinalPathNameByHandleA(
|
||||
handle, final_path.data(), final_path.size() - 1U,
|
||||
VOLUME_NAME_NONE | FILE_NAME_OPENED);
|
||||
|
||||
auto expected_name{
|
||||
std::string{"\\repertory\\"} + this->mount_location.at(0U) +
|
||||
"\\test_file_2",
|
||||
};
|
||||
EXPECT_EQ(result, static_cast<DWORD>(expected_name.size()));
|
||||
EXPECT_STREQ(expected_name.c_str(), final_path.c_str());
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_set_file_info_attributes_to_hidden) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(file_path.c_str(), FILE_ATTRIBUTE_HIDDEN));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN, file_info.dwFileAttributes);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(
|
||||
winfsp_test,
|
||||
info_can_set_file_info_attributes_to_hidden_ignoring_directory_attribute) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
EXPECT_TRUE(::SetFileAttributesA(
|
||||
file_path.c_str(), FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
EXPECT_EQ(FILE_ATTRIBUTE_HIDDEN, file_info.dwFileAttributes);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_set_creation_file_time) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION orig_file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &orig_file_info));
|
||||
|
||||
static constexpr const UINT64 file_time{
|
||||
116444736000000000ULL + 0x4200000042ULL,
|
||||
};
|
||||
|
||||
EXPECT_TRUE(::SetFileTime(handle,
|
||||
reinterpret_cast<const FILETIME *>(&file_time),
|
||||
nullptr, nullptr));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
|
||||
EXPECT_EQ(file_time, *reinterpret_cast<PUINT64>(&file_info.ftCreationTime));
|
||||
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftLastAccessTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftLastAccessTime));
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftLastWriteTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftLastWriteTime));
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_set_accessed_file_time) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION orig_file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &orig_file_info));
|
||||
|
||||
static constexpr const UINT64 file_time{
|
||||
116444736000000000ULL + 0x4200000042ULL,
|
||||
};
|
||||
|
||||
EXPECT_TRUE(::SetFileTime(handle, nullptr,
|
||||
reinterpret_cast<const FILETIME *>(&file_time),
|
||||
nullptr));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
|
||||
EXPECT_EQ(file_time, *reinterpret_cast<PUINT64>(&file_info.ftLastAccessTime));
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftCreationTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftCreationTime));
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftLastWriteTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftLastWriteTime));
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_set_written_file_time) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION orig_file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &orig_file_info));
|
||||
|
||||
static constexpr const UINT64 file_time{
|
||||
116444736000000000ULL + 0x4200000042ULL,
|
||||
};
|
||||
|
||||
EXPECT_TRUE(::SetFileTime(handle, nullptr, nullptr,
|
||||
reinterpret_cast<const FILETIME *>(&file_time)));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
|
||||
EXPECT_EQ(file_time, *reinterpret_cast<PUINT64>(&file_info.ftLastWriteTime));
|
||||
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftLastAccessTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftLastAccessTime));
|
||||
EXPECT_EQ(*reinterpret_cast<PUINT64>(&orig_file_info.ftCreationTime),
|
||||
*reinterpret_cast<PUINT64>(&file_info.ftCreationTime));
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, info_can_set_file_size) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_2"}),
|
||||
};
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto offset = ::SetFilePointer(handle, 42, nullptr, FILE_BEGIN);
|
||||
EXPECT_EQ(42U, offset);
|
||||
|
||||
EXPECT_TRUE(::SetEndOfFile(handle));
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION file_info{};
|
||||
EXPECT_TRUE(::GetFileInformationByHandle(handle, &file_info));
|
||||
|
||||
EXPECT_EQ(0U, file_info.nFileSizeHigh);
|
||||
EXPECT_EQ(42U, file_info.nFileSizeLow);
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
854
repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp
Normal file
854
repertory/repertory_test/src/winfsp_drive_rdrw_test.cpp
Normal file
@@ -0,0 +1,854 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
static void test_file(auto &&mount_location, auto &&file_path, auto &&flags) {
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||
&bytes_per_sector, &free_clusters,
|
||||
&total_clusters));
|
||||
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | flags, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
DWORD bytes_written{};
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle, 2U * bytes_per_sector, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, 0));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
data_buffer read_buffer{};
|
||||
read_buffer.resize(buffer_size);
|
||||
DWORD bytes_read{};
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
for (auto idx = 0; idx < 2; ++idx) {
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 2U * bytes_per_sector, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, 0));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
}
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 3U * bytes_per_sector, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(3U * bytes_per_sector, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(0U, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_read, 0U));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = ::CreateFileA(
|
||||
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | flags | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read, ::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
static void test_append_file(auto &&mount_location, auto &&file_path,
|
||||
auto &&flags, bool should_fail = false) {
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||
&bytes_per_sector, &free_clusters,
|
||||
&total_clusters));
|
||||
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
auto handle =
|
||||
CreateFileA(file_path.c_str(), FILE_APPEND_DATA, FILE_SHARE_READ, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL | flags, nullptr);
|
||||
if (should_fail) {
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
DWORD bytes_written{};
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data() + bytes_per_sector,
|
||||
bytes_per_sector, &bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = CreateFileA(
|
||||
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | flags | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
auto pointer = ::SetFilePointer(handle, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
data_buffer read_buffer{};
|
||||
read_buffer.resize(buffer_size);
|
||||
DWORD bytes_read{};
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), 2U * bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(2U * bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
static void test_overlapped_file(auto &&mount_location, auto &&file_path,
|
||||
auto &&flags) {
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||
&bytes_per_sector, &free_clusters,
|
||||
&total_clusters));
|
||||
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
OVERLAPPED overlapped{};
|
||||
overlapped.hEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
ASSERT_NE(nullptr, overlapped.hEvent);
|
||||
|
||||
auto handle = ::CreateFileA(
|
||||
file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_NORMAL | flags | FILE_FLAG_OVERLAPPED, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
overlapped.Offset = 0U;
|
||||
DWORD bytes_written{};
|
||||
auto ret = ::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
|
||||
overlapped.Offset = 2U * bytes_per_sector;
|
||||
ret = ::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
|
||||
data_buffer read_buffer{};
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 0U;
|
||||
DWORD bytes_read{};
|
||||
ret = ::ReadFile(handle, read_buffer.data(), bytes_per_sector, &bytes_read,
|
||||
&overlapped);
|
||||
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 2U * bytes_per_sector;
|
||||
ret = ::ReadFile(handle, read_buffer.data(), bytes_per_sector, &bytes_read,
|
||||
&overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 2U * bytes_per_sector;
|
||||
ret = ::ReadFile(handle, read_buffer.data(), 2U * bytes_per_sector,
|
||||
&bytes_read, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 3U * bytes_per_sector;
|
||||
ret = ::ReadFile(handle, read_buffer.data(), bytes_per_sector, &bytes_read,
|
||||
&overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError() ||
|
||||
ERROR_HANDLE_EOF == ::GetLastError());
|
||||
if (ERROR_HANDLE_EOF != ::GetLastError()) {
|
||||
ret = ::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE);
|
||||
EXPECT_FALSE(ret);
|
||||
EXPECT_EQ(ERROR_HANDLE_EOF, ::GetLastError());
|
||||
}
|
||||
EXPECT_EQ(0U, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::WriteFile(handle, write_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_written, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_written);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::ReadFile(handle, read_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_read, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::WriteFile(handle, write_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_written, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_written);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::ReadFile(handle, read_buffer.data(), 2U * sys_info.dwPageSize,
|
||||
&bytes_read, &overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::WriteFile(handle, write_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector, &bytes_written,
|
||||
&overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_written, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_written);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::ReadFile(handle, read_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector, &bytes_read,
|
||||
&overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | flags | FILE_FLAG_OVERLAPPED |
|
||||
FILE_FLAG_DELETE_ON_CLOSE,
|
||||
nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
overlapped.Offset = 0U;
|
||||
ret = ::ReadFile(handle, read_buffer.data(),
|
||||
2U * sys_info.dwPageSize + bytes_per_sector, &bytes_read,
|
||||
&overlapped);
|
||||
EXPECT_TRUE(ret || ERROR_IO_PENDING == ::GetLastError());
|
||||
EXPECT_TRUE(::GetOverlappedResult(handle, &overlapped, &bytes_read, TRUE));
|
||||
EXPECT_EQ(2U * sys_info.dwPageSize + bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(overlapped.hEvent));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, 0, nullptr);
|
||||
EXPECT_EQ(INVALID_HANDLE_VALUE, handle);
|
||||
EXPECT_EQ(ERROR_FILE_NOT_FOUND, ::GetLastError());
|
||||
}
|
||||
|
||||
static void test_mixed_file(auto &&mount_location, auto &&file_path) {
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||
&bytes_per_sector, &free_clusters,
|
||||
&total_clusters));
|
||||
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
auto handle0 = CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||
|
||||
auto handle1 =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||
|
||||
auto pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
DWORD bytes_written{};
|
||||
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_per_sector,
|
||||
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle1, 2 * bytes_per_sector, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||
|
||||
EXPECT_TRUE(::WriteFile(handle1, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
data_buffer read_buffer{};
|
||||
read_buffer.resize(buffer_size);
|
||||
DWORD bytes_read{};
|
||||
EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle0));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle1));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
|
||||
handle0 = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
EXPECT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||
|
||||
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle0, 2 * bytes_per_sector, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(2U * bytes_per_sector, pointer);
|
||||
EXPECT_TRUE(::WriteFile(handle0, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||
|
||||
pointer = ::SetFilePointer(handle0, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
EXPECT_TRUE(::ReadFile(handle0, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle0, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle0));
|
||||
|
||||
handle1 =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING, nullptr);
|
||||
EXPECT_NE(INVALID_HANDLE_VALUE, handle0);
|
||||
|
||||
pointer = ::SetFilePointer(handle1, 0, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(0U, pointer);
|
||||
|
||||
read_buffer.clear();
|
||||
read_buffer.resize(buffer_size);
|
||||
EXPECT_TRUE(::ReadFile(handle1, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle1, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(handle1));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
static void test_mmap_file(auto &&mount_location, auto &&file_path,
|
||||
auto &&flags, auto &&early_close) {
|
||||
SYSTEM_INFO sys_info{};
|
||||
::GetSystemInfo(&sys_info);
|
||||
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(mount_location.c_str(), §ors_per_cluster,
|
||||
&bytes_per_sector, &free_clusters,
|
||||
&total_clusters));
|
||||
const auto buffer_size = 16U * sys_info.dwPageSize;
|
||||
auto write_buffer = utils::generate_secure_random<data_buffer>(buffer_size);
|
||||
|
||||
auto handle =
|
||||
::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL | flags, nullptr);
|
||||
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
DWORD file_size0{
|
||||
2U * sys_info.dwAllocationGranularity,
|
||||
};
|
||||
DWORD file_size1{100U};
|
||||
auto *mapping = ::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
file_size0 + file_size1, nullptr);
|
||||
ASSERT_NE(nullptr, mapping);
|
||||
|
||||
if (early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
auto *view = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
ASSERT_NE(nullptr, view);
|
||||
|
||||
auto seed = static_cast<unsigned>(std::time(nullptr));
|
||||
srand(seed);
|
||||
for (PUINT8 begin = reinterpret_cast<PUINT8>(view) + file_size1 / 2U,
|
||||
end = begin + file_size0;
|
||||
end > begin; ++begin) {
|
||||
*begin = rand() & 0xFF;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
|
||||
view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
ASSERT_NE(nullptr, view);
|
||||
|
||||
srand(seed);
|
||||
for (PUINT8 begin = reinterpret_cast<PUINT8>(view) + file_size1 / 2U,
|
||||
end = begin + file_size0;
|
||||
end > begin; begin++) {
|
||||
EXPECT_EQ(*begin, (rand() & 0xFF));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
|
||||
if (not early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | flags, nullptr);
|
||||
EXPECT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
mapping = ::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
file_size0 + file_size1, nullptr);
|
||||
ASSERT_NE(nullptr, mapping);
|
||||
|
||||
if (early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
ASSERT_NE(nullptr, view);
|
||||
|
||||
srand(seed);
|
||||
for (PUINT8 begin = reinterpret_cast<PUINT8>(view) + file_size1 / 2U,
|
||||
end = begin + file_size0;
|
||||
end > begin; ++begin) {
|
||||
EXPECT_EQ(*begin, (rand() & 0xFF));
|
||||
}
|
||||
|
||||
if (not early_close) {
|
||||
auto pointer = ::SetFilePointer(handle, file_size0 / 2U, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(file_size0 / 2U, pointer);
|
||||
|
||||
DWORD bytes_written{};
|
||||
EXPECT_TRUE(::WriteFile(handle, write_buffer.data(), bytes_per_sector,
|
||||
&bytes_written, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_written);
|
||||
EXPECT_EQ(pointer + bytes_written,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
|
||||
if (not early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | flags, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
mapping = ::CreateFileMappingA(handle, nullptr, PAGE_READWRITE, 0,
|
||||
file_size0 + file_size1, nullptr);
|
||||
ASSERT_NE(nullptr, mapping);
|
||||
|
||||
if (early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
view = ::MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
||||
ASSERT_NE(nullptr, view);
|
||||
|
||||
if (not early_close) {
|
||||
auto pointer = ::SetFilePointer(handle, file_size0 / 2U, 0, FILE_BEGIN);
|
||||
EXPECT_EQ(file_size0 / 2U, pointer);
|
||||
|
||||
data_buffer read_buffer{};
|
||||
read_buffer.resize(buffer_size);
|
||||
DWORD bytes_read{};
|
||||
EXPECT_TRUE(::ReadFile(handle, read_buffer.data(), bytes_per_sector,
|
||||
&bytes_read, nullptr));
|
||||
EXPECT_EQ(bytes_per_sector, bytes_read);
|
||||
EXPECT_EQ(pointer + bytes_read,
|
||||
::SetFilePointer(handle, 0, 0, FILE_CURRENT));
|
||||
EXPECT_EQ(0,
|
||||
std::memcmp(write_buffer.data(), read_buffer.data(), bytes_read));
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
for (PUINT8 begin = reinterpret_cast<PUINT8>(view) + file_size1 / 2U,
|
||||
end = reinterpret_cast<PUINT8>(view) + file_size0 / 2U;
|
||||
end > begin; begin++) {
|
||||
EXPECT_EQ(*begin, (rand() & 0xFF));
|
||||
}
|
||||
|
||||
if (not early_close) {
|
||||
EXPECT_EQ(0, std::memcmp(write_buffer.data(), view + file_size0 / 2U,
|
||||
bytes_per_sector));
|
||||
}
|
||||
|
||||
for (size_t idx = 0U; bytes_per_sector > idx; ++idx) {
|
||||
rand();
|
||||
}
|
||||
|
||||
for (PUINT8
|
||||
begin = reinterpret_cast<PUINT8>(view) + file_size0 / 2U +
|
||||
bytes_per_sector,
|
||||
end = reinterpret_cast<PUINT8>(view) + file_size1 / 2U + file_size0;
|
||||
end > begin; ++begin) {
|
||||
EXPECT_EQ(*begin, (rand() & 0xFF));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(::UnmapViewOfFile(view));
|
||||
|
||||
EXPECT_TRUE(::CloseHandle(mapping));
|
||||
|
||||
if (not early_close) {
|
||||
EXPECT_TRUE(::CloseHandle(handle));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(DeleteFileA(file_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_flags) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_file(this->mount_location, file_path, 0U);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_no_buffering) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_write_through) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_append_file_no_flags) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_append_file(this->mount_location, file_path, 0U);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_append_file_no_buffering) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_append_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING,
|
||||
true);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_append_file_write_through) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_append_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_overlapped_file_no_flags) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_overlapped_file(this->mount_location, file_path, 0U);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_overlapped_file_no_buffering) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_overlapped_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_overlapped_write_through) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_overlapped_file(this->mount_location, file_path,
|
||||
FILE_FLAG_WRITE_THROUGH);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_flags) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_mmap_file(this->mount_location, file_path, 0U, false);
|
||||
test_mmap_file(this->mount_location, file_path, 0U, true);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mmap_file_no_buffering) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_mmap_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING,
|
||||
false);
|
||||
test_mmap_file(this->mount_location, file_path, FILE_FLAG_NO_BUFFERING, true);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_file_mmap_write_through) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_mmap_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH,
|
||||
false);
|
||||
test_mmap_file(this->mount_location, file_path, FILE_FLAG_WRITE_THROUGH,
|
||||
true);
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rdrw_can_read_and_write_mixed_file) {
|
||||
auto file_path{
|
||||
utils::path::combine(this->mount_location, {"test_file_5"}),
|
||||
};
|
||||
test_mixed_file(this->mount_location, file_path);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
260
repertory/repertory_test/src/winfsp_drive_rename_test.cpp
Normal file
260
repertory/repertory_test/src/winfsp_drive_rename_test.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_can_rename_file_if_dest_does_not_exist) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_4"}),
|
||||
};
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_4"}),
|
||||
};
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
EXPECT_EQ(ERROR_ALREADY_EXISTS, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_4"}),
|
||||
};
|
||||
auto file_path2{
|
||||
utils::path::combine(dir_path, {"test_file2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
|
||||
|
||||
handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING));
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_can_rename_dir_if_dest_does_not_exist) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto dir_path2{
|
||||
utils::path::combine(this->mount_location, {"test_dir2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
EXPECT_TRUE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
|
||||
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_false) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto dir_path2{
|
||||
utils::path::combine(this->mount_location, {"test_dir2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path2.c_str(), nullptr));
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_true) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto dir_path2{
|
||||
utils::path::combine(this->mount_location, {"test_dir2_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path2.c_str(), nullptr));
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING));
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
rename_dir_fails_directory_is_not_empty_and_replace_is_false) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto dir_path2{
|
||||
utils::path::combine(this->mount_location, {"test_dir2_4"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test,
|
||||
rename_dir_fails_directory_is_not_empty_and_replace_is_true) {
|
||||
if (this->current_provider == provider_type::s3) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto dir_path{
|
||||
utils::path::combine(this->mount_location, {"test_dir_4"}),
|
||||
};
|
||||
auto dir_path2{
|
||||
utils::path::combine(this->mount_location, {"test_dir2_4"}),
|
||||
};
|
||||
auto file_path{
|
||||
utils::path::combine(dir_path, {"test_file_4"}),
|
||||
};
|
||||
|
||||
ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
|
||||
|
||||
auto handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
::CloseHandle(handle);
|
||||
|
||||
EXPECT_FALSE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING));
|
||||
EXPECT_EQ(ERROR_ACCESS_DENIED, ::GetLastError());
|
||||
|
||||
EXPECT_TRUE(::DeleteFileA(file_path.c_str()));
|
||||
EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
61
repertory/repertory_test/src/winfsp_drive_test.cpp
Normal file
61
repertory/repertory_test/src/winfsp_drive_test.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
// TODO revisit create_allocation
|
||||
// TODO revisit create_backup
|
||||
// TODO revisit create_notraverse
|
||||
// TODO revisit create_related
|
||||
// TODO revisit create_restore
|
||||
// TODO revisit create_sd
|
||||
// TODO revisit create_share
|
||||
// TODO revisit delete_access_test
|
||||
// TODO revisit delete_ex_test
|
||||
// TODO revisit getfileattr_test
|
||||
// TODO revisit query_winfsp_test
|
||||
// TODO revisit rename_backslash_test
|
||||
// TODO revisit rename_caseins_test
|
||||
// TODO revisit rename_ex_test
|
||||
// TODO revisit rename_flipflop_test
|
||||
// TODO revisit rename_mmap_test
|
||||
// TODO revisit rename_open_test
|
||||
// TODO revisit rename_pid_test
|
||||
// TODO revisit rename_standby_test
|
||||
// TODO revisit setvolinfo_test
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, can_set_current_directory_to_mount_location) {
|
||||
EXPECT_TRUE(::SetCurrentDirectoryA(this->mount_location.c_str()));
|
||||
|
||||
EXPECT_TRUE(::SetCurrentDirectoryA(this->current_directory.string().c_str()));
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
100
repertory/repertory_test/src/winfsp_drive_volume_test.cpp
Normal file
100
repertory/repertory_test/src/winfsp_drive_volume_test.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
//
|
||||
// Implemented test cases based on WinFsp tests:
|
||||
// https://github.com/winfsp/winfsp/blob/v2.0/tst/winfsp-tests
|
||||
//
|
||||
#include "fixtures/winfsp_fixture.hpp"
|
||||
|
||||
namespace repertory {
|
||||
TYPED_TEST_CASE(winfsp_test, winfsp_provider_types);
|
||||
|
||||
TYPED_TEST(winfsp_test, volume_can_get_volume_info) {
|
||||
std::string volume_label;
|
||||
volume_label.resize(MAX_PATH + 1U);
|
||||
|
||||
std::string fs_name;
|
||||
fs_name.resize(MAX_PATH + 1U);
|
||||
|
||||
DWORD flags{};
|
||||
DWORD max_component_length{};
|
||||
DWORD serial_num{};
|
||||
EXPECT_TRUE(::GetVolumeInformationA(
|
||||
this->mount_location.c_str(), volume_label.data(),
|
||||
static_cast<DWORD>(volume_label.size()), &serial_num,
|
||||
&max_component_length, &flags, fs_name.data(),
|
||||
static_cast<DWORD>(fs_name.size())));
|
||||
EXPECT_EQ(FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES |
|
||||
FILE_UNICODE_ON_DISK,
|
||||
flags);
|
||||
EXPECT_EQ(255U, max_component_length);
|
||||
EXPECT_EQ(0U, serial_num);
|
||||
EXPECT_STREQ(
|
||||
("repertory_" + app_config::get_provider_name(this->current_provider))
|
||||
.c_str(),
|
||||
volume_label.c_str());
|
||||
EXPECT_STREQ(this->mount_location.c_str(), fs_name.c_str());
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, volume_can_get_size_info) {
|
||||
{
|
||||
DWORD bytes_per_sector{};
|
||||
DWORD free_clusters{};
|
||||
DWORD sectors_per_cluster{};
|
||||
DWORD total_clusters{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceA(this->mount_location.c_str(),
|
||||
§ors_per_cluster, &bytes_per_sector,
|
||||
&free_clusters, &total_clusters));
|
||||
EXPECT_NE(0U, bytes_per_sector);
|
||||
EXPECT_NE(0U, free_clusters);
|
||||
EXPECT_NE(0U, sectors_per_cluster);
|
||||
EXPECT_NE(0U, total_clusters);
|
||||
}
|
||||
|
||||
{
|
||||
ULARGE_INTEGER caller_free_bytes{};
|
||||
ULARGE_INTEGER free_bytes{};
|
||||
ULARGE_INTEGER total_bytes{};
|
||||
EXPECT_TRUE(::GetDiskFreeSpaceExA(this->mount_location.c_str(),
|
||||
&caller_free_bytes, &total_bytes,
|
||||
&free_bytes));
|
||||
EXPECT_NE(0U, caller_free_bytes.QuadPart);
|
||||
EXPECT_NE(0U, total_bytes.QuadPart);
|
||||
EXPECT_NE(0U, free_bytes.QuadPart);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(winfsp_test, volume_can_get_file_type) {
|
||||
auto handle = ::CreateFileA(
|
||||
this->mount_location.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
|
||||
ASSERT_NE(INVALID_HANDLE_VALUE, handle);
|
||||
|
||||
EXPECT_EQ(FILE_TYPE_DISK, ::GetFileType(handle));
|
||||
|
||||
::CloseHandle(handle);
|
||||
}
|
||||
} // namespace repertory
|
||||
|
||||
#endif // defined(_WIN32)
|
@@ -0,0 +1 @@
|
||||
test
|
1
repertory/repertory_test/test_input/encrypt/test.txt
Normal file
1
repertory/repertory_test/test_input/encrypt/test.txt
Normal file
@@ -0,0 +1 @@
|
||||
moose
|
Reference in New Issue
Block a user