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