From 7567e3289c0bbda4697a99b5d15fb89280d09077 Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Sun, 8 Dec 2024 10:29:53 -0600 Subject: [PATCH] refactor file manager db --- .../librepertory/include/db/i_file_mgr_db.hpp | 87 +++++ .../include/db/rdb_file_mgr_db.hpp | 91 +++++ .../include/db/sqlite_file_mgr_db.hpp | 82 +++++ .../include/file_manager/file_manager.hpp | 22 +- .../librepertory/src/db/rdb_file_mgr_db.cpp | 116 +++++++ .../src/db/sqlite_file_mgr_db.cpp | 274 +++++++++++++++ .../librepertory/src/db/sqlite_meta_db.cpp | 3 +- .../src/file_manager/file_manager.cpp | 328 ++++-------------- .../include/fixtures/file_mgr_db_fixture.hpp | 72 ++++ .../repertory_test/src/file_manager_test.cpp | 15 +- .../repertory_test/src/file_mgr_db_test.cpp | 96 +++++ 11 files changed, 914 insertions(+), 272 deletions(-) create mode 100644 repertory/librepertory/include/db/i_file_mgr_db.hpp create mode 100644 repertory/librepertory/include/db/rdb_file_mgr_db.hpp create mode 100644 repertory/librepertory/include/db/sqlite_file_mgr_db.hpp create mode 100644 repertory/librepertory/src/db/rdb_file_mgr_db.cpp create mode 100644 repertory/librepertory/src/db/sqlite_file_mgr_db.cpp create mode 100644 repertory/repertory_test/include/fixtures/file_mgr_db_fixture.hpp create mode 100644 repertory/repertory_test/src/file_mgr_db_test.cpp diff --git a/repertory/librepertory/include/db/i_file_mgr_db.hpp b/repertory/librepertory/include/db/i_file_mgr_db.hpp new file mode 100644 index 00000000..8cb06519 --- /dev/null +++ b/repertory/librepertory/include/db/i_file_mgr_db.hpp @@ -0,0 +1,87 @@ +/* + Copyright <2018-2024> + + 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_INCLUDE_DB_I_FILE_MGR_DB_HPP_ +#define REPERTORY_INCLUDE_DB_I_FILE_MGR_DB_HPP_ + +#include "types/repertory.hpp" + +namespace repertory { +class i_file_mgr_db { + INTERFACE_SETUP(i_file_mgr_db); + +public: + struct resume_entry final { + std::string api_path; + std::uint64_t chunk_size{}; + boost::dynamic_bitset<> read_state; + std::string source_path; + }; + + struct upload_active_entry final { + std::string api_path; + std::string source_path; + }; + + struct upload_entry final { + std::string api_path; + std::uint64_t date_time{}; + std::string source_path; + }; + +public: + [[nodiscard]] virtual auto add_resume(resume_entry entry) -> bool = 0; + + [[nodiscard]] virtual auto add_upload(upload_entry entry) -> bool = 0; + + [[nodiscard]] virtual auto + add_upload_active(upload_active_entry entry) -> bool = 0; + + virtual void clear() = 0; + + [[nodiscard]] virtual auto + get_next_upload() const -> std::optional = 0; + + [[nodiscard]] virtual auto + get_resume_list() const -> std::vector = 0; + + [[nodiscard]] virtual auto get_upload(const std::string &api_path) const + -> std::optional = 0; + + [[nodiscard]] virtual auto + get_upload_active_list() const -> std::vector = 0; + + [[nodiscard]] virtual auto + remove_resume(const std::string &api_path) -> bool = 0; + + [[nodiscard]] virtual auto + remove_upload(const std::string &api_path) -> bool = 0; + + [[nodiscard]] virtual auto + remove_upload_active(const std::string &api_path) -> bool = 0; + + [[nodiscard]] virtual auto + rename_resume(const std::string &from_api_path, + const std::string &to_api_path) -> bool = 0; +}; +} // namespace repertory + +#endif // REPERTORY_INCLUDE_DB_I_FILE_MGR_DB_HPP_ diff --git a/repertory/librepertory/include/db/rdb_file_mgr_db.hpp b/repertory/librepertory/include/db/rdb_file_mgr_db.hpp new file mode 100644 index 00000000..dc4f6a0b --- /dev/null +++ b/repertory/librepertory/include/db/rdb_file_mgr_db.hpp @@ -0,0 +1,91 @@ +/* + Copyright <2018-2024> + + 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_INCLUDE_DB_RDB_FILE_MGR_DB_HPP_ +#define REPERTORY_INCLUDE_DB_RDB_FILE_MGR_DB_HPP_ + +#include "db/i_file_mgr_db.hpp" + +namespace repertory { +class app_config; + +class rdb_file_mgr_db final : public i_file_mgr_db { +public: + rdb_file_mgr_db(const app_config &cfg); + ~rdb_file_mgr_db() override; + + rdb_file_mgr_db(const rdb_file_mgr_db &) = delete; + rdb_file_mgr_db(rdb_file_mgr_db &&) = delete; + auto operator=(const rdb_file_mgr_db &) -> rdb_file_mgr_db & = delete; + auto operator=(rdb_file_mgr_db &&) -> rdb_file_mgr_db & = delete; + +private: + const app_config &cfg_; + +private: + std::unique_ptr db_; + std::atomic id_{0U}; + rocksdb::ColumnFamilyHandle *resume_family_{}; + rocksdb::ColumnFamilyHandle *upload_active_family_{}; + rocksdb::ColumnFamilyHandle *upload_family_{}; + +private: + void create_or_open(bool clear); + +public: + [[nodiscard]] auto add_resume(resume_entry entry) -> bool override; + + [[nodiscard]] auto add_upload(upload_entry entry) -> bool override; + + [[nodiscard]] auto + add_upload_active(upload_active_entry entry) -> bool override; + + void clear() override; + + [[nodiscard]] auto + get_next_upload() const -> std::optional override; + + [[nodiscard]] auto + get_resume_list() const -> std::vector override; + + [[nodiscard]] auto get_upload(const std::string &api_path) const + -> std::optional override; + + [[nodiscard]] auto + get_upload_active_list() const -> std::vector override; + + [[nodiscard]] auto + remove_resume(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + remove_upload(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + remove_upload_active(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + rename_resume(const std::string &from_api_path, + const std::string &to_api_path) -> bool override; +}; + +} // namespace repertory + +#endif // REPERTORY_INCLUDE_DB_RDB_FILE_MGR_DB_HPP_ diff --git a/repertory/librepertory/include/db/sqlite_file_mgr_db.hpp b/repertory/librepertory/include/db/sqlite_file_mgr_db.hpp new file mode 100644 index 00000000..7a891429 --- /dev/null +++ b/repertory/librepertory/include/db/sqlite_file_mgr_db.hpp @@ -0,0 +1,82 @@ +/* + Copyright <2018-2024> + + 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_INCLUDE_DB_SQLITE_FILE_MGR_DB_HPP_ +#define REPERTORY_INCLUDE_DB_SQLITE_FILE_MGR_DB_HPP_ + +#include "db/i_file_mgr_db.hpp" +#include "utils/db/sqlite/db_common.hpp" + +namespace repertory { +class app_config; + +class sqlite_file_mgr_db final : public i_file_mgr_db { +public: + sqlite_file_mgr_db(const app_config &cfg); + ~sqlite_file_mgr_db() override; + + sqlite_file_mgr_db(const sqlite_file_mgr_db &) = delete; + sqlite_file_mgr_db(sqlite_file_mgr_db &&) = delete; + auto operator=(const sqlite_file_mgr_db &) -> sqlite_file_mgr_db & = delete; + auto operator=(sqlite_file_mgr_db &&) -> sqlite_file_mgr_db & = delete; + +private: + utils::db::sqlite::db3_t db_; + +public: + [[nodiscard]] auto add_resume(resume_entry entry) -> bool override; + + [[nodiscard]] auto add_upload(upload_entry entry) -> bool override; + + [[nodiscard]] auto + add_upload_active(upload_active_entry entry) -> bool override; + + void clear() override; + + [[nodiscard]] auto + get_next_upload() const -> std::optional override; + + [[nodiscard]] auto + get_resume_list() const -> std::vector override; + + [[nodiscard]] auto get_upload(const std::string &api_path) const + -> std::optional override; + + [[nodiscard]] auto + get_upload_active_list() const -> std::vector override; + + [[nodiscard]] auto + remove_resume(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + remove_upload(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + remove_upload_active(const std::string &api_path) -> bool override; + + [[nodiscard]] auto + rename_resume(const std::string &from_api_path, + const std::string &to_api_path) -> bool override; +}; + +} // namespace repertory + +#endif // REPERTORY_INCLUDE_DB_SQLITE_FILE_MGR_DB_HPP_ diff --git a/repertory/librepertory/include/file_manager/file_manager.hpp b/repertory/librepertory/include/file_manager/file_manager.hpp index 412dc981..3abde31e 100644 --- a/repertory/librepertory/include/file_manager/file_manager.hpp +++ b/repertory/librepertory/include/file_manager/file_manager.hpp @@ -22,15 +22,14 @@ #ifndef REPERTORY_INCLUDE_FILE_MANAGER_FILE_MANAGER_HPP_ #define REPERTORY_INCLUDE_FILE_MANAGER_FILE_MANAGER_HPP_ +#include "db/i_file_mgr_db.hpp" #include "events/event_system.hpp" #include "events/events.hpp" #include "file_manager/i_file_manager.hpp" #include "file_manager/i_open_file.hpp" #include "file_manager/i_upload_manager.hpp" #include "file_manager/upload.hpp" -#include "platform/platform.hpp" #include "types/repertory.hpp" -#include "utils/db/sqlite/db_common.hpp" #include "utils/file.hpp" namespace repertory { @@ -57,7 +56,7 @@ private: i_provider &provider_; private: - utils::db::sqlite::db3_t db_; + std::unique_ptr mgr_db_; std::atomic next_handle_{0U}; mutable std::recursive_mutex open_file_mtx_; std::unordered_map> @@ -132,12 +131,13 @@ public: [[nodiscard]] auto get_open_handle_count() const -> std::size_t; - [[nodiscard]] auto get_stored_downloads() const -> std::vector; + [[nodiscard]] auto + get_stored_downloads() const -> std::vector; [[nodiscard]] auto has_no_open_file_handles() const -> bool override; - [[nodiscard]] auto is_processing(const std::string &api_path) const - -> bool override; + [[nodiscard]] auto + is_processing(const std::string &api_path) const -> bool override; #if defined(PROJECT_TESTING) [[nodiscard]] auto open(std::shared_ptr of, @@ -150,13 +150,13 @@ public: [[nodiscard]] auto remove_file(const std::string &api_path) -> api_error; - [[nodiscard]] auto rename_directory(const std::string &from_api_path, - const std::string &to_api_path) - -> api_error; + [[nodiscard]] auto + rename_directory(const std::string &from_api_path, + const std::string &to_api_path) -> api_error; [[nodiscard]] auto rename_file(const std::string &from_api_path, - const std::string &to_api_path, bool overwrite) - -> api_error; + const std::string &to_api_path, + bool overwrite) -> api_error; void start(); diff --git a/repertory/librepertory/src/db/rdb_file_mgr_db.cpp b/repertory/librepertory/src/db/rdb_file_mgr_db.cpp new file mode 100644 index 00000000..c52a2700 --- /dev/null +++ b/repertory/librepertory/src/db/rdb_file_mgr_db.cpp @@ -0,0 +1,116 @@ +/* + Copyright <2018-2024> + + 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 "db/rdb_file_mgr_db.hpp" + +#include "app_config.hpp" +#include "types/startup_exception.hpp" +#include "utils/config.hpp" +#include "utils/error_utils.hpp" +#include "utils/file.hpp" +#include "utils/path.hpp" +#include "utils/string.hpp" + +namespace { +[[nodiscard]] auto +create_rocksdb(const repertory::app_config &cfg, const std::string &name, + const std::vector &families, + std::vector &handles, + bool clear) -> std::unique_ptr { + REPERTORY_USES_FUNCTION_NAME(); + + auto path = repertory::utils::path::combine(cfg.get_data_directory(), {name}); + if (clear && + not repertory::utils::file::directory{path}.remove_recursively()) { + repertory::utils::error::raise_error( + function_name, "failed to remove file mgr db|" + path); + } + + rocksdb::Options options{}; + options.create_if_missing = true; + options.create_missing_column_families = true; + options.db_log_dir = cfg.get_log_directory(); + options.keep_log_file_num = 10; + + rocksdb::DB *ptr{}; + auto status = rocksdb::DB::Open(options, path, families, &handles, &ptr); + if (not status.ok()) { + repertory::utils::error::raise_error(function_name, status.ToString()); + throw repertory::startup_exception(status.ToString()); + } + + return std::unique_ptr(ptr); +} +} // namespace + +namespace repertory { +rdb_file_mgr_db::rdb_file_mgr_db(const app_config &cfg) : cfg_(cfg) { + create_or_open(false); +} + +rdb_file_mgr_db::~rdb_file_mgr_db() { db_.reset(); } + +void rdb_file_mgr_db::create_or_open(bool clear) { + db_.reset(); + + auto families = std::vector(); + families.emplace_back(rocksdb::kDefaultColumnFamilyName, + rocksdb::ColumnFamilyOptions()); + families.emplace_back("upload_active", rocksdb::ColumnFamilyOptions()); + families.emplace_back("upload", rocksdb::ColumnFamilyOptions()); + + auto handles = std::vector(); + db_ = create_rocksdb(cfg_, "mgr", families, handles, clear); + + std::size_t idx{}; + resume_family_ = handles[idx++]; + upload_active_family_ = handles[idx++]; + upload_family_ = handles[idx++]; +} + +auto rdb_file_mgr_db::add_resume(resume_entry entry) -> bool {} + +auto rdb_file_mgr_db::add_upload(upload_entry entry) -> bool {} + +auto rdb_file_mgr_db::add_upload_active(upload_active_entry entry) -> bool {} + +void rdb_file_mgr_db::clear() {} + +auto rdb_file_mgr_db::get_next_upload() const -> std::optional {} + +auto rdb_file_mgr_db::get_resume_list() const -> std::vector {} + +auto rdb_file_mgr_db::get_upload(const std::string &api_path) const + -> std::optional {} + +auto rdb_file_mgr_db::get_upload_active_list() const + -> std::vector {} + +auto rdb_file_mgr_db::remove_resume(const std::string &api_path) -> bool {} + +auto rdb_file_mgr_db::remove_upload(const std::string &api_path) -> bool {} + +auto rdb_file_mgr_db::remove_upload_active(const std::string &api_path) + -> bool {} + +auto rdb_file_mgr_db::rename_resume(const std::string &from_api_path, + const std::string &to_api_path) -> bool {} +} // namespace repertory diff --git a/repertory/librepertory/src/db/sqlite_file_mgr_db.cpp b/repertory/librepertory/src/db/sqlite_file_mgr_db.cpp new file mode 100644 index 00000000..ea6045fa --- /dev/null +++ b/repertory/librepertory/src/db/sqlite_file_mgr_db.cpp @@ -0,0 +1,274 @@ +/* + Copyright <2018-2024> + + 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 "db/sqlite_file_mgr_db.hpp" + +#include "app_config.hpp" +#include "utils/config.hpp" +#include "utils/db/sqlite/db_common.hpp" +#include "utils/db/sqlite/db_delete.hpp" +#include "utils/db/sqlite/db_insert.hpp" +#include "utils/db/sqlite/db_select.hpp" +#include "utils/db/sqlite/db_update.hpp" +#include "utils/error_utils.hpp" +#include "utils/path.hpp" +#include "utils/string.hpp" + +namespace { +const std::string resume_table = "resume"; +const std::string upload_table = "upload"; +const std::string upload_active_table = "upload_active"; +const std::map sql_create_tables{ + { + {resume_table}, + { + "CREATE TABLE IF NOT EXISTS " + resume_table + + "(" + "api_path TEXT PRIMARY KEY ASC, " + "chunk_size INTEGER, " + "read_state TEXT, " + "source_path TEXT" + ");", + }, + }, + { + {upload_table}, + { + "CREATE TABLE IF NOT EXISTS " + upload_table + + "(" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "api_path TEXT UNIQUE, " + "date_time INTEGER, " + "source_path TEXT" + ");", + }, + }, + { + {upload_active_table}, + { + "CREATE TABLE IF NOT EXISTS " + upload_active_table + + "(" + "api_path TEXT PRIMARY KEY ASC, " + "source_path TEXT" + ");", + }, + }, +}; +} // namespace + +namespace repertory { +sqlite_file_mgr_db::sqlite_file_mgr_db(const app_config &cfg) { + db_ = utils::db::sqlite::create_db( + utils::path::combine(cfg.get_data_directory(), {"mgr.db"}), + sql_create_tables); +} + +sqlite_file_mgr_db::~sqlite_file_mgr_db() { db_.reset(); } + +auto sqlite_file_mgr_db::add_resume(resume_entry entry) -> bool { + return utils::db::sqlite::db_insert{*db_, resume_table} + .or_replace() + .column_value("api_path", entry.api_path) + .column_value("chunk_size", static_cast(entry.chunk_size)) + .column_value("read_state", + utils::string::from_dynamic_bitset(entry.read_state)) + .column_value("source_path", entry.source_path) + .go() + .ok(); +} + +auto sqlite_file_mgr_db::add_upload(upload_entry entry) -> bool { + return utils::db::sqlite::db_insert{*db_, upload_table} + .or_replace() + .column_value("api_path", entry.api_path) + .column_value("date_time", static_cast(entry.date_time)) + .column_value("source_path", entry.source_path) + .go() + .ok(); +} + +auto sqlite_file_mgr_db::add_upload_active(upload_active_entry entry) -> bool { + return utils::db::sqlite::db_insert{*db_, upload_table} + .or_replace() + .column_value("api_path", entry.api_path) + .column_value("source_path", entry.source_path) + .go() + .ok(); +} + +void sqlite_file_mgr_db::clear() { + REPERTORY_USES_FUNCTION_NAME(); + + auto result = utils::db::sqlite::db_delete{*db_, resume_table}.go(); + if (not result.ok()) { + utils::error::raise_error(function_name, + "failed to clear resume table|" + + std::to_string(result.get_error())); + } + + result = utils::db::sqlite::db_delete{*db_, upload_active_table}.go(); + if (not result.ok()) { + utils::error::raise_error(function_name, + "failed to clear upload active table|" + + std::to_string(result.get_error())); + } + + result = utils::db::sqlite::db_delete{*db_, upload_table}.go(); + if (not result.ok()) { + utils::error::raise_error(function_name, + "failed to clear upload table|" + + std::to_string(result.get_error())); + } +} + +auto sqlite_file_mgr_db::get_next_upload() const + -> std::optional { + auto result = utils::db::sqlite::db_select{*db_, upload_table} + .order_by("id", true) + .limit(1) + .go(); + std::optional row; + if (not result.get_row(row) || not row.has_value()) { + return std::nullopt; + } + + return upload_entry{ + row->get_column("api_path").get_value(), + static_cast( + row->get_column("date_time").get_value()), + row->get_column("source_path").get_value(), + }; +} + +auto sqlite_file_mgr_db::get_resume_list() const -> std::vector { + REPERTORY_USES_FUNCTION_NAME(); + + std::vector ret; + auto result = utils::db::sqlite::db_select{*db_, resume_table}.go(); + while (result.has_row()) { + try { + std::optional row; + if (not result.get_row(row)) { + continue; + } + if (not row.has_value()) { + continue; + } + + ret.push_back(resume_entry{ + row->get_column("api_path").get_value(), + static_cast( + row->get_column("chunk_size").get_value()), + utils::string::to_dynamic_bitset( + row->get_column("read_state").get_value()), + row->get_column("source_path").get_value(), + }); + } catch (const std::exception &ex) { + utils::error::raise_error(function_name, ex, "query error"); + } + } + + return ret; +} + +auto sqlite_file_mgr_db::get_upload(const std::string &api_path) const + -> std::optional { + auto result = utils::db::sqlite::db_select{*db_, upload_table} + .column("source_path") + .where("api_path") + .equals(api_path) + .go(); + std::optional row; + if (not result.get_row(row) || not row.has_value()) { + return std::nullopt; + } + + return upload_entry{ + row->get_column("api_path").get_value(), + static_cast( + row->get_column("date_time").get_value()), + row->get_column("source_path").get_value(), + }; +} + +auto sqlite_file_mgr_db::get_upload_active_list() const + -> std::vector { + REPERTORY_USES_FUNCTION_NAME(); + + std::vector ret; + auto result = utils::db::sqlite::db_select{*db_, upload_active_table}.go(); + while (result.has_row()) { + try { + std::optional row; + if (not result.get_row(row)) { + continue; + } + if (not row.has_value()) { + continue; + } + + ret.push_back(upload_active_entry{ + row->get_column("api_path").get_value(), + row->get_column("source_path").get_value(), + }); + } catch (const std::exception &ex) { + utils::error::raise_error(function_name, ex, "query error"); + } + } + + return ret; +} + +auto sqlite_file_mgr_db::remove_resume(const std::string &api_path) -> bool { + return utils::db::sqlite::db_delete{*db_, resume_table} + .where("api_path") + .equals(api_path) + .go() + .ok(); +} + +auto sqlite_file_mgr_db::remove_upload(const std::string &api_path) -> bool { + return utils::db::sqlite::db_delete{*db_, upload_table} + .where("api_path") + .equals(api_path) + .go() + .ok(); +} + +auto sqlite_file_mgr_db::remove_upload_active(const std::string &api_path) + -> bool { + return utils::db::sqlite::db_delete{*db_, upload_active_table} + .where("api_path") + .equals(api_path) + .go() + .ok(); +} + +auto sqlite_file_mgr_db::rename_resume(const std::string &from_api_path, + const std::string &to_api_path) -> bool { + return utils::db::sqlite::db_update{*db_, resume_table} + .column_value("api_path", to_api_path) + .where("api_path") + .equals(from_api_path) + .go() + .ok(); +} +} // namespace repertory diff --git a/repertory/librepertory/src/db/sqlite_meta_db.cpp b/repertory/librepertory/src/db/sqlite_meta_db.cpp index f1fd5650..96ad97c0 100644 --- a/repertory/librepertory/src/db/sqlite_meta_db.cpp +++ b/repertory/librepertory/src/db/sqlite_meta_db.cpp @@ -64,7 +64,8 @@ void sqlite_meta_db::clear() { } utils::error::raise_error(function_name, - "failed to clear meta db|" + result.get_error()); + "failed to clear meta db|" + + std::to_string(result.get_error())); } auto sqlite_meta_db::get_api_path(const std::string &source_path, diff --git a/repertory/librepertory/src/file_manager/file_manager.cpp b/repertory/librepertory/src/file_manager/file_manager.cpp index 0e46a11e..1767e56f 100644 --- a/repertory/librepertory/src/file_manager/file_manager.cpp +++ b/repertory/librepertory/src/file_manager/file_manager.cpp @@ -22,19 +22,16 @@ #include "file_manager/file_manager.hpp" #include "app_config.hpp" +#include "db/sqlite_file_mgr_db.hpp" #include "file_manager/events.hpp" #include "file_manager/open_file.hpp" #include "file_manager/open_file_base.hpp" #include "file_manager/ring_buffer_open_file.hpp" #include "file_manager/upload.hpp" +#include "platform/platform.hpp" #include "providers/i_provider.hpp" #include "types/repertory.hpp" #include "utils/common.hpp" -#include "utils/db/sqlite/db_common.hpp" -#include "utils/db/sqlite/db_delete.hpp" -#include "utils/db/sqlite/db_insert.hpp" -#include "utils/db/sqlite/db_select.hpp" -#include "utils/db/sqlite/db_update.hpp" #include "utils/encrypting_reader.hpp" #include "utils/error_utils.hpp" #include "utils/file.hpp" @@ -42,85 +39,24 @@ #include "utils/polling.hpp" #include "utils/time.hpp" -namespace { -[[nodiscard]] auto -create_resume_entry(const repertory::i_open_file &file) -> json { - return { - {"chunk_size", file.get_chunk_size()}, - {"path", file.get_api_path()}, - {"read_state", - repertory::utils::string::from_dynamic_bitset(file.get_read_state())}, - {"source", file.get_source_path()}, - }; -} - -void restore_resume_entry(const json &resume_entry, std::string &api_path, - std::size_t &chunk_size, - boost::dynamic_bitset<> &read_state, - std::string &source_path) { - api_path = resume_entry["path"].get(); - chunk_size = resume_entry["chunk_size"].get(); - read_state = repertory::utils::string::to_dynamic_bitset( - resume_entry["read_state"].get()); - source_path = resume_entry["source"].get(); -} - -const std::string resume_table = "resume"; -const std::string upload_table = "upload"; -const std::string upload_active_table = "upload_active"; -const std::map sql_create_tables{ - { - {resume_table}, - { - "CREATE TABLE IF NOT EXISTS " + resume_table + - "(" - "api_path TEXT PRIMARY KEY ASC, " - "data TEXT" - ");", - }, - }, - { - {upload_table}, - { - "CREATE TABLE IF NOT EXISTS " + upload_table + - "(" - "api_path TEXT PRIMARY KEY ASC, " - "date_time INTEGER, " - "source_path TEXT" - ");", - }, - }, - { - {upload_active_table}, - { - "CREATE TABLE IF NOT EXISTS " + upload_active_table + - "(" - "api_path TEXT PRIMARY KEY ASC, " - "source_path TEXT" - ");", - }, - }, -}; -} // namespace - namespace repertory { file_manager::file_manager(app_config &config, i_provider &provider) : config_(config), provider_(provider) { - db_ = utils::db::sqlite::create_db( - utils::path::combine(config_.get_data_directory(), {"file_manager.db"}), - sql_create_tables); + mgr_db_ = std::make_unique(config_); - if (not provider_.is_read_only()) { - E_SUBSCRIBE_EXACT(file_upload_completed, - [this](const file_upload_completed &completed) { - this->upload_completed(completed); - }); + if (provider_.is_read_only()) { + return; } + + E_SUBSCRIBE_EXACT(file_upload_completed, + [this](const file_upload_completed &completed) { + this->upload_completed(completed); + }); } file_manager::~file_manager() { stop(); - db_.reset(); + mgr_db_.reset(); E_CONSUMER_RELEASE(); } @@ -336,32 +272,15 @@ auto file_manager::get_open_handle_count() const -> std::size_t { }); } -auto file_manager::get_stored_downloads() const -> std::vector { +auto file_manager::get_stored_downloads() const + -> std::vector { REPERTORY_USES_FUNCTION_NAME(); if (provider_.is_read_only()) { return {}; } - std::vector ret; - auto result = utils::db::sqlite::db_select{*db_, resume_table}.go(); - while (result.has_row()) { - try { - std::optional row; - if (not result.get_row(row)) { - continue; - } - if (not row.has_value()) { - continue; - } - - ret.push_back(row.value().get_column("data").get_value_as_json()); - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "query error"); - } - } - - return ret; + return mgr_db_->get_resume_list(); } auto file_manager::handle_file_rename(const std::string &from_api_path, @@ -379,15 +298,10 @@ auto file_manager::handle_file_rename(const std::string &from_api_path, source_path = upload_lookup_.at(from_api_path)->get_source_path(); } } else { - auto result = utils::db::sqlite::db_select{*db_, upload_table} - .column("source_path") - .where("api_path") - .equals(from_api_path) - .go(); - std::optional row; - should_upload = result.get_row(row) && row.has_value(); + auto upload = mgr_db_->get_upload(from_api_path); + should_upload = upload.has_value(); if (should_upload && source_path.empty()) { - source_path = row->get_column("source_path").get_value(); + source_path = upload->source_path; } } @@ -427,8 +341,8 @@ auto file_manager::is_processing(const std::string &api_path) const -> bool { } upload_lock.unlock(); - utils::db::sqlite::db_select query{*db_, upload_table}; - if (query.where("api_path").equals(api_path).go().has_row()) { + auto upload = mgr_db_->get_upload(api_path); + if (upload.has_value()) { return true; }; @@ -510,21 +424,16 @@ void file_manager::queue_upload(const std::string &api_path, remove_upload(api_path, true); - auto result = - utils::db::sqlite::db_insert{*db_, upload_table} - .or_replace() - .column_value("api_path", api_path) - .column_value("date_time", - static_cast(utils::time::get_time_now())) - .column_value("source_path", source_path) - .go(); - if (result.ok()) { + if (mgr_db_->add_upload({ + api_path, + utils::time::get_time_now(), + source_path, + })) { remove_resume(api_path, source_path); event_system::instance().raise(api_path, source_path); } else { event_system::instance().raise( - api_path, source_path, - std::to_string(result.get_error()) + '|' + result.get_error_str()); + api_path, source_path, "failed to queue upload"); } if (not no_lock) { @@ -561,14 +470,12 @@ auto file_manager::remove_file(const std::string &api_path) -> api_error { void file_manager::remove_resume(const std::string &api_path, const std::string &source_path) { - auto result = utils::db::sqlite::db_delete{*db_, resume_table} - .where("api_path") - .equals(api_path) - .go(); - if (result.ok()) { - event_system::instance().raise(api_path, - source_path); + if (not mgr_db_->remove_resume(api_path)) { + return; } + + event_system::instance().raise(api_path, + source_path); } void file_manager::remove_upload(const std::string &api_path) { @@ -587,21 +494,13 @@ void file_manager::remove_upload(const std::string &api_path, bool no_lock) { lock = std::make_unique(upload_mtx_); } - auto result = utils::db::sqlite::db_delete{*db_, upload_table} - .where("api_path") - .equals(api_path) - .go(); - if (not result.ok()) { - utils::error::raise_api_path_error(function_name, api_path, - api_error::error, - "failed to remove from upload table"); + if (not mgr_db_->remove_upload(api_path)) { + utils::error::raise_api_path_error( + function_name, api_path, api_error::error, "failed to remove upload"); } - result = utils::db::sqlite::db_delete{*db_, upload_active_table} - .where("api_path") - .equals(api_path) - .go(); - if (not result.ok()) { + auto removed = not mgr_db_->remove_upload_active(api_path); + if (not removed) { utils::error::raise_api_path_error( function_name, api_path, api_error::error, "failed to remove from upload_active table"); @@ -612,7 +511,7 @@ void file_manager::remove_upload(const std::string &api_path, bool no_lock) { upload_lookup_.erase(api_path); } - if (result.ok()) { + if (removed) { event_system::instance().raise(api_path); } @@ -779,15 +678,6 @@ void file_manager::start() { stop_requested_ = false; - polling::instance().set_callback({ - "db_cleanup", - polling::frequency::medium, - [this](auto && /* stop_requested */) { - mutex_lock lock(upload_mtx_); - sqlite3_db_release_memory(db_.get()); - }, - }); - polling::instance().set_callback({ "timed_out_close", polling::frequency::second, @@ -799,67 +689,25 @@ void file_manager::start() { return; } - struct active_item final { - std::string api_path; - std::string source_path; - }; + for (const auto &entry : mgr_db_->get_upload_active_list()) { + queue_upload(entry.api_path, entry.source_path, false); + } - std::vector active_items{}; - - auto result = utils::db::sqlite::db_select{*db_, upload_active_table}.go(); - while (result.has_row()) { + for (const auto &entry : mgr_db_->get_resume_list()) { try { - std::optional row; - if (result.get_row(row) && row.has_value()) { - active_items.emplace_back(active_item{ - row->get_column("api_path").get_value(), - row->get_column("source_path").get_value(), - }); - } - } catch (const std::exception &ex) { - utils::error::raise_error(function_name, ex, "query error"); - } - } - - for (auto &&active_item : active_items) { - queue_upload(active_item.api_path, active_item.source_path, false); - } - active_items.clear(); - - result = utils::db::sqlite::db_select{*db_, resume_table}.go(); - if (not result.ok()) { - return; - } - - while (result.has_row()) { - try { - std::optional row; - if (not(result.get_row(row) && row.has_value())) { - return; - } - - auto resume_entry = row.value().get_column("data").get_value_as_json(); - - std::string api_path; - std::string source_path; - std::size_t chunk_size{}; - boost::dynamic_bitset<> read_state; - restore_resume_entry(resume_entry, api_path, chunk_size, read_state, - source_path); - filesystem_item fsi{}; - auto res = provider_.get_filesystem_item(api_path, false, fsi); + auto res = provider_.get_filesystem_item(entry.api_path, false, fsi); if (res != api_error::success) { event_system::instance().raise( - api_path, source_path, + entry.api_path, entry.source_path, "failed to get filesystem item|" + api_error_to_string(res)); continue; } - if (source_path != fsi.source_path) { + if (entry.source_path != fsi.source_path) { event_system::instance().raise( fsi.api_path, fsi.source_path, - "source path mismatch|expected|" + source_path + "|actual|" + + "source path mismatch|expected|" + entry.source_path + "|actual|" + fsi.source_path); continue; } @@ -883,12 +731,12 @@ void file_manager::start() { } auto closeable_file = std::make_shared( - chunk_size, + entry.chunk_size, config_.get_enable_chunk_download_timeout() ? config_.get_chunk_downloader_timeout_secs() : 0U, - fsi, provider_, read_state, *this); - open_file_lookup_[api_path] = closeable_file; + fsi, provider_, entry.read_state, *this); + open_file_lookup_[entry.api_path] = closeable_file; event_system::instance().raise(fsi.api_path, fsi.source_path); } catch (const std::exception &ex) { @@ -948,21 +796,19 @@ void file_manager::store_resume(const i_open_file &file) { return; } - auto result = utils::db::sqlite::db_insert{*db_, resume_table} - .or_replace() - .column_value("api_path", file.get_api_path()) - .column_value("data", create_resume_entry(file).dump()) - .go(); - if (result.ok()) { + if (mgr_db_->add_resume({ + file.get_api_path(), + file.get_chunk_size(), + file.get_read_state(), + file.get_source_path(), + })) { event_system::instance().raise( file.get_api_path(), file.get_source_path()); return; } event_system::instance().raise( - file.get_api_path(), file.get_source_path(), - "failed to insert|" + std::to_string(result.get_error()) + '|' + - result.get_error_str()); + file.get_api_path(), file.get_source_path(), "failed to store resume"); } void file_manager::swap_renamed_items(std::string from_api_path, @@ -981,16 +827,13 @@ void file_manager::swap_renamed_items(std::string from_api_path, return; } - auto result = utils::db::sqlite::db_update{*db_, resume_table} - .column_value("api_path", to_api_path) - .where("api_path") - .equals(from_api_path) - .go(); - if (not result.ok()) { - utils::error::raise_api_path_error(function_name, to_api_path, - api_error::error, - "failed to update resume table"); + if (mgr_db_->rename_resume(from_api_path, to_api_path)) { + return; } + + utils::error::raise_api_path_error(function_name, to_api_path, + api_error::error, + "failed to update resume table"); } void file_manager::upload_completed(const file_upload_completed &evt) { @@ -1001,11 +844,8 @@ void file_manager::upload_completed(const file_upload_completed &evt) { if (not utils::string::to_bool(evt.get_cancelled().get())) { auto err = api_error_from_string(evt.get_result().get()); if (err == api_error::success) { - auto result = utils::db::sqlite::db_delete{*db_, upload_active_table} - .where("api_path") - .equals(evt.get_api_path().get()) - .go(); - if (not result.ok()) { + if (not mgr_db_->remove_upload_active( + evt.get_api_path().get())) { utils::error::raise_api_path_error( function_name, evt.get_api_path().get(), evt.get_source().get(), @@ -1046,25 +886,17 @@ void file_manager::upload_handler() { } if (upload_lookup_.size() < config_.get_max_upload_count()) { - auto result = utils::db::sqlite::db_select{*db_, upload_table} - .order_by("api_path", true) - .limit(1) - .go(); try { - std::optional row; - if (result.get_row(row) && row.has_value()) { - auto api_path = row->get_column("api_path").get_value(); - auto source_path = - row->get_column("source_path").get_value(); - + auto entry = mgr_db_->get_next_upload(); + if (entry.has_value()) { filesystem_item fsi{}; - auto res = provider_.get_filesystem_item(api_path, false, fsi); + auto res = provider_.get_filesystem_item(entry->api_path, false, fsi); switch (res) { case api_error::item_not_found: { should_wait = false; - event_system::instance().raise(api_path, - source_path); - remove_upload(api_path, true); + event_system::instance().raise( + entry->api_path, entry->source_path); + remove_upload(entry->api_path, true); } break; case api_error::success: { @@ -1072,28 +904,22 @@ void file_manager::upload_handler() { upload_lookup_[fsi.api_path] = std::make_unique(fsi, provider_); - auto del_res = utils::db::sqlite::db_delete{*db_, upload_table} - .where("api_path") - .equals(api_path) - .go(); - if (del_res.ok()) { - auto ins_res = - utils::db::sqlite::db_insert{*db_, upload_active_table} - .column_value("api_path", api_path) - .column_value("source_path", source_path) - .go(); - if (not ins_res.ok()) { + if (mgr_db_->remove_upload(entry->api_path)) { + if (not mgr_db_->add_upload_active({ + entry->api_path, + entry->source_path, + })) { utils::error::raise_api_path_error( - function_name, api_path, source_path, + function_name, entry->api_path, entry->source_path, "failed to add to upload_active table"); } } } break; default: { - event_system::instance().raise(api_path, - source_path, res); - queue_upload(api_path, source_path, true); + event_system::instance().raise( + entry->api_path, entry->source_path, res); + queue_upload(entry->api_path, entry->source_path, true); } break; } } diff --git a/repertory/repertory_test/include/fixtures/file_mgr_db_fixture.hpp b/repertory/repertory_test/include/fixtures/file_mgr_db_fixture.hpp new file mode 100644 index 00000000..a9c10692 --- /dev/null +++ b/repertory/repertory_test/include/fixtures/file_mgr_db_fixture.hpp @@ -0,0 +1,72 @@ +/* + Copyright <2018-2024> + + 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/rdb_meta_db.hpp" +#include "db/sqlite_file_mgr_db.hpp" +#include "events/consumers/console_consumer.hpp" +#include "events/event_system.hpp" + +namespace repertory { +template class file_mgr_db_test : public ::testing::Test { +protected: + static std::unique_ptr config; + static console_consumer console_; + static std::unique_ptr 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(provider_type::s3, cfg_directory); + file_mgr_db = std::make_unique(*config); + } + + static void TearDownTestCase() { + file_mgr_db.reset(); + config.reset(); + event_system::instance().stop(); + } +}; + +using file_mgr_db_types = ::testing::Types; + +template +std::unique_ptr file_mgr_db_test::config; + +template console_consumer file_mgr_db_test::console_; + +template +std::unique_ptr file_mgr_db_test::file_mgr_db; +} // namespace repertory + +#endif // REPERTORY_TEST_INCLUDE_FIXTURES_FILE_MGR_DB_FIXTURE_HPP diff --git a/repertory/repertory_test/src/file_manager_test.cpp b/repertory/repertory_test/src/file_manager_test.cpp index 2c28c6f9..ed361351 100644 --- a/repertory/repertory_test/src/file_manager_test.cpp +++ b/repertory/repertory_test/src/file_manager_test.cpp @@ -496,20 +496,17 @@ TEST_F(file_manager_test, auto stored_downloads = mgr.get_stored_downloads(); EXPECT_EQ(std::size_t(1u), stored_downloads.size()); - std::cout << stored_downloads[0u].dump(2) << std::endl; EXPECT_STREQ("/test_write_partial_download.txt", - stored_downloads[0u]["path"].get().c_str()); + stored_downloads[0U].api_path.c_str()); EXPECT_EQ(utils::encryption::encrypting_reader::get_data_chunk_size(), - stored_downloads[0u]["chunk_size"].get()); - auto read_state = utils::string::to_dynamic_bitset( - stored_downloads[0u]["read_state"].get()); - EXPECT_TRUE(read_state[0u]); - for (std::size_t i = 1u; i < read_state.size(); i++) { + stored_downloads[0U].chunk_size); + auto read_state = stored_downloads[0U].read_state; + EXPECT_TRUE(read_state[0U]); + for (std::size_t i = 1U; i < read_state.size(); i++) { EXPECT_FALSE(read_state[i]); } - EXPECT_STREQ(source_path.c_str(), - stored_downloads[0u]["source"].get().c_str()); + EXPECT_STREQ(source_path.c_str(), stored_downloads[0u].source_path.c_str()); mgr.start(); diff --git a/repertory/repertory_test/src/file_mgr_db_test.cpp b/repertory/repertory_test/src/file_mgr_db_test.cpp new file mode 100644 index 00000000..7610f9b7 --- /dev/null +++ b/repertory/repertory_test/src/file_mgr_db_test.cpp @@ -0,0 +1,96 @@ +/* + Copyright <2018-2024> + + 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" + +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")); +} +} // namespace repertory