| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include <gtest/gtest.h> |
| | #include <gmock/gmock.h> |
| |
|
| | #include "InitApplication.h" |
| |
|
| | #include <App/BackupPolicy.h> |
| |
|
| | #include <filesystem> |
| | #include <array> |
| | #include <fstream> |
| | #include <random> |
| | #include <regex> |
| | #include <string> |
| |
|
| | #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L && defined(_LIBCPP_VERSION) \ |
| | && _LIBCPP_VERSION >= 13000 |
| | |
| | |
| | |
| | # define CAN_USE_CHRONO_AND_FORMAT |
| | # include <chrono> |
| | # include <format> |
| | #endif |
| |
|
| |
|
| | class BackupPolicyTest: public ::testing::Test |
| | { |
| | protected: |
| | static void SetUpTestSuite() |
| | { |
| | tests::initApplication(); |
| | } |
| |
|
| | void SetUp() override |
| | { |
| | _tempDir = std::filesystem::temp_directory_path() / ("fc_backup_policy-" + randomString(16)); |
| | std::filesystem::create_directory(_tempDir); |
| | } |
| |
|
| | void TearDown() override |
| | { |
| | std::filesystem::remove_all(_tempDir); |
| | } |
| |
|
| | void apply(const std::string& sourcename, const std::string& targetname) |
| | { |
| | _policy.apply(sourcename, targetname); |
| | } |
| |
|
| | void setPolicyTerms(App::BackupPolicy::Policy p, int count, bool useExt, const std::string& fmt) |
| | { |
| | _policy.setPolicy(p); |
| | _policy.setNumberOfFiles(count); |
| | _policy.useBackupExtension(useExt); |
| | _policy.setDateFormat(fmt); |
| | } |
| |
|
| | |
| | |
| | std::filesystem::path createTempFile(const std::string& filename) |
| | { |
| | std::filesystem::path p = _tempDir / filename; |
| | std::ofstream fileStream(p.string()); |
| | fileStream << "Test data"; |
| | fileStream.close(); |
| | return p; |
| | } |
| |
|
| |
|
| | protected: |
| | std::string randomString(size_t length) |
| | { |
| | static constexpr std::string_view chars = "0123456789" |
| | "abcdefghijklmnopqrstuvwxyz" |
| | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
| |
|
| | std::random_device rd; |
| | std::mt19937 gen(rd()); |
| | std::uniform_int_distribution<> dis(0, static_cast<int>(chars.size()) - 1); |
| |
|
| | std::string result; |
| | result.reserve(length); |
| |
|
| | std::ranges::generate_n(std::back_inserter(result), length, [&]() { return chars[dis(gen)]; }); |
| |
|
| | return result; |
| | } |
| |
|
| | std::string filenameFromDateFormatString(const std::string& fmt) |
| | { |
| | #if CAN_USE_CHRONO_AND_FORMAT |
| | std::chrono::zoned_time local_time { |
| | std::chrono::current_zone(), |
| | std::chrono::system_clock::now() |
| | }; |
| | std::string fmt_str = "{:" + fmt + "}"; |
| | std::string result = std::vformat(fmt_str, std::make_format_args(local_time)); |
| | #else |
| | std::time_t now = std::time(nullptr); |
| | std::tm local_tm {}; |
| | # if defined(_WIN32) |
| | localtime_s(&local_tm, &now); |
| | # else |
| | localtime_r(&now, &local_tm); |
| | # endif |
| | constexpr size_t bufferLength = 128; |
| | std::array<char, bufferLength> buffer {}; |
| | size_t bytes = std::strftime(buffer.data(), bufferLength, fmt.c_str(), &local_tm); |
| | if (bytes == 0) { |
| | throw std::runtime_error("failed to format time"); |
| | } |
| | std::string result {buffer.data()}; |
| | #endif |
| |
|
| | return result; |
| | } |
| |
|
| | std::filesystem::path getTempPath() |
| | { |
| | return _tempDir; |
| | } |
| |
|
| | private: |
| | App::BackupPolicy _policy; |
| | std::filesystem::path _tempDir; |
| | }; |
| |
|
| | TEST_F(BackupPolicyTest, StandardSourceDoesNotExist) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| |
|
| | |
| | EXPECT_THROW(apply("nonexistent.fcstd", "backup.fcstd"), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithZeroFilesDeletesExisting) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 0, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | GTEST_SKIP(); |
| | EXPECT_FALSE(std::filesystem::exists(target)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithOneFileNoPreviousBackups) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(target.string() + "1")); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithOneFileOnePreviousBackup) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd1"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | EXPECT_FALSE(std::filesystem::exists(target.string() + "2")); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithTwoFilesOnePreviousBackup) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 2, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd1"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | EXPECT_TRUE(std::filesystem::exists(target.string() + "2")); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithTwoFilesOnePreviousBackupUnexpectedSuffix) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 2, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd1"); |
| | auto weird = createTempFile("target.fcstd2a"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | EXPECT_TRUE(std::filesystem::exists(target.string() + "2")); |
| | EXPECT_TRUE(std::filesystem::exists(weird)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithTwoFilesOnePreviousBackupOutOfSequenceNumber) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 2, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd1"); |
| | auto weird = createTempFile("target.fcstd999"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | bool check1 = std::filesystem::exists(target.string() + "2"); |
| | bool check2 = std::filesystem::exists(weird); |
| | EXPECT_NE(check1, check2); |
| | |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, StandardWithFCBakSet) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::Standard, 1, true, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(target.string() + "1")); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampSourceDoesNotExist) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| |
|
| | |
| | EXPECT_THROW(apply("nonexistent.fcstd", "backup.fcstd"), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampNoSourceGiven) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | EXPECT_THROW(apply("nonexistent.fcstd", target.string()), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampNoTargetGiven) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| |
|
| | |
| | EXPECT_THROW(apply(source.string(), ""), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampWithZeroFilesDeletesExisting) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 0, false, "%Y-%m-%d_%H-%M-%S"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | GTEST_SKIP(); |
| | EXPECT_FALSE(std::filesystem::exists(target)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampWithOneFileAndNoneExistingNotFCBakCreatesNumberedFile) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, false, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | |
| | |
| | EXPECT_TRUE(std::filesystem::exists(target.string() + "1")); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampSourceHasNoExtension) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | auto expected = "target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"; |
| | EXPECT_TRUE(std::filesystem::exists(getTempPath() / expected)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampTargetHasNoExtension) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | auto expected = "target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"; |
| | EXPECT_TRUE(std::filesystem::exists(getTempPath() / expected)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampWithOneFileAndNoneExistingFCBakCreatesDatedFile) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | auto expected = "target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"; |
| | EXPECT_TRUE(std::filesystem::exists(getTempPath() / expected)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampReplacesDotsWithDashes) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y.%m.%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | auto expected = "target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"; |
| | EXPECT_TRUE(std::filesystem::exists(getTempPath() / expected)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, DISABLED_TimestampWithInvalidFormatStringThrows) |
| | { |
| | |
| | |
| |
|
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Q-%W-%E"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | EXPECT_THROW(apply(source.string(), target.string()), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, DISABLED_TimestampWithAbsurdlyLongFormatStringThrows) |
| | { |
| | |
| | |
| |
|
| | |
| | setPolicyTerms( |
| | App::BackupPolicy::Policy::TimeStamp, |
| | 1, |
| | true, |
| | "%A, %B %d, %Y at %H:%M:%S %Z (Day %j of the year, Week %U/%W) — This is a " |
| | "verbose date string for demonstration purposes." |
| | ); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | EXPECT_THROW(apply(source.string(), target.string()), Base::FileException); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampDetectsOldBackupFormat) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd12345"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | bool check1 = std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak") |
| | ); |
| | bool check2 = std::filesystem::exists(backup); |
| | EXPECT_NE(check1, check2); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampDetectsOldBackupFormatIgnoresOther) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd12345"); |
| | auto weird = createTempFile("target.fcstd12345abc"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | bool check1 = std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak") |
| | ); |
| | bool check2 = std::filesystem::exists(backup); |
| | EXPECT_NE(check1, check2); |
| | EXPECT_TRUE(std::filesystem::exists(weird)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampDetectsAndRetainsOldBackupWhenAllowed) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 2, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target.fcstd12345"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE( |
| | std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak") |
| | ) |
| | ); |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampFormatStringEndsWithSpace) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d "); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE( |
| | std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + "1.FCBak") |
| | ) |
| | ); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampFormatStringEndsWithDash) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 1, true, "%Y-%m-%d-"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE( |
| | std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + "-1.FCBak") |
| | ) |
| | ); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampFormatFileAlreadyExists) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 2, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | EXPECT_TRUE( |
| | std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + "-1.FCBak") |
| | ) |
| | ); |
| | } |
| |
|
| | TEST_F(BackupPolicyTest, TimestampFormatFileAlreadyExistsMultipleTimes) |
| | { |
| | |
| | setPolicyTerms(App::BackupPolicy::Policy::TimeStamp, 5, true, "%Y-%m-%d"); |
| | auto source = createTempFile("source.fcstd"); |
| | auto target = createTempFile("target.fcstd"); |
| | auto backup = createTempFile("target." + filenameFromDateFormatString("%Y-%m-%d") + ".FCBak"); |
| | auto backup1 = createTempFile("target." + filenameFromDateFormatString("%Y-%m-%d") + "-1.FCBak"); |
| | auto backup2 = createTempFile("target." + filenameFromDateFormatString("%Y-%m-%d") + "-2.FCBak"); |
| | auto backup3 = createTempFile("target." + filenameFromDateFormatString("%Y-%m-%d") + "-3.FCBak"); |
| |
|
| | |
| | apply(source.string(), target.string()); |
| |
|
| | |
| | EXPECT_TRUE(std::filesystem::exists(backup)); |
| | EXPECT_TRUE(std::filesystem::exists(backup1)); |
| | EXPECT_TRUE(std::filesystem::exists(backup2)); |
| | EXPECT_TRUE(std::filesystem::exists(backup3)); |
| | EXPECT_TRUE( |
| | std::filesystem::exists( |
| | getTempPath() / ("target." + filenameFromDateFormatString("%Y-%m-%d") + "-4.FCBak") |
| | ) |
| | ); |
| | } |
| |
|