Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/Progmasoft/PropertyEngine.cpp
diff options
context:
space:
mode:
authorLeitwolf2026-10-24 19:03:40 +0300
committerGitHub2026-10-24 19:03:40 +0300
commit4f1c759cffcb14bd6511e1d7c5fbace682e24f2d (patch)
tree415fe5d48a5b48650bba3747beaf245d727c4f61 /src/Progmasoft/PropertyEngine.cpp
parent83c6fc3ac13abe83fa39f7b5f8559ccaf4412233 (diff)
parent622188f9c08e4af35941c146979883ed4882410e (diff)
downloadcatch3-4f1c759cffcb14bd6511e1d7c5fbace682e24f2d.tar.gz
Merge pull request #15 from Progmasoft/feature/catch3-runner
Replace Catch2's runner with the Catch3 runner
Diffstat (limited to 'src/Progmasoft/PropertyEngine.cpp')
-rw-r--r--src/Progmasoft/PropertyEngine.cpp71
1 files changed, 56 insertions, 15 deletions
diff --git a/src/Progmasoft/PropertyEngine.cpp b/src/Progmasoft/PropertyEngine.cpp
index c42b235f..571a5ac3 100644
--- a/src/Progmasoft/PropertyEngine.cpp
+++ b/src/Progmasoft/PropertyEngine.cpp
@@ -2,7 +2,12 @@
// SPDX-License-Identifier: MPL-2.0 WITH AdditionRef-Progmasoft-Exception-1.1
#include <Progmasoft/Catch3/Strategy/Engine.hpp>
+#include <Progmasoft/Runner/Files.hpp>
+
#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <fstream>
@@ -10,6 +15,7 @@
#include <sstream>
#include <string>
#include <system_error>
+#include <thread>
#include <utility>
namespace Progmasoft::Catch3::Detail
@@ -75,24 +81,53 @@ namespace Progmasoft::Catch3::Detail
return tokens;
}
+ /// Changes the stored tokens under a lock: the file is read
+ /// again, `change` edits what it holds, and the result replaces
+ /// the file in one rename. Workers of a parallel run, retries and
+ /// repeats of one property may write at the same time; none of
+ /// them loses what another stored.
+ template<typename Change>
void
- Save(const std::vector<std::string> &tokens) const
+ Update(Change change) const
{
if (file_.empty())
{
return;
}
std::error_code error;
+ std::filesystem::create_directories(file_.parent_path(), error);
+ const Runner::FileLock lock(file_, std::chrono::seconds(2),
+ std::chrono::seconds(10));
+ auto tokens = Load();
+ change(tokens);
if (tokens.empty())
{
std::filesystem::remove(file_, error);
return;
}
- std::filesystem::create_directories(file_.parent_path(), error);
- std::ofstream output(file_, std::ios::trunc);
- for (const auto &token : tokens)
+ const auto temporary = std::filesystem::path(
+ file_.string() + "." + Runner::UniqueName() + ".tmp");
+ {
+ std::ofstream output(temporary,
+ std::ios::binary | std::ios::trunc);
+ for (const auto &token : tokens)
+ {
+ output << token << '\n';
+ }
+ if (!output)
+ {
+ output.close();
+ std::filesystem::remove(temporary, error);
+ return;
+ }
+ }
+ // rename replaces the target atomically on every platform
+ // the library supports, so a reader sees the old or the new
+ // file, never a half-written one.
+ std::filesystem::rename(temporary, file_, error);
+ if (error)
{
- output << token << '\n';
+ std::filesystem::remove(temporary, error);
}
}
@@ -243,8 +278,7 @@ namespace Progmasoft::Catch3::Detail
std::optional<PropertyResult>
ReplayDatabase()
{
- auto tokens = database_.Load();
- std::vector<std::string> kept;
+ const auto tokens = database_.Load();
for (const auto &token : tokens)
{
const auto decoded = DecodeChoices(token);
@@ -265,7 +299,14 @@ namespace Progmasoft::Catch3::Detail
}
if (!tokens.empty())
{
- database_.Save(kept);
+ // Only the examples checked here are dropped; another
+ // process may have stored new ones meanwhile.
+ database_.Update([&](std::vector<std::string> &stored) {
+ std::erase_if(stored, [&](const std::string &token) {
+ return std::find(tokens.begin(), tokens.end(), token)
+ != tokens.end();
+ });
+ });
}
return std::nullopt;
}
@@ -474,13 +515,13 @@ namespace Progmasoft::Catch3::Detail
failure.Replay = EncodeChoices(size, failing.Choices);
failure.Notes = std::move(failing.Outcome.Record.Notes);
- auto stored = database_.Load();
- if (std::find(stored.begin(), stored.end(), failure.Replay)
- == stored.end())
- {
- stored.insert(stored.begin(), failure.Replay);
- database_.Save(stored);
- }
+ database_.Update([&](std::vector<std::string> &stored) {
+ if (std::find(stored.begin(), stored.end(), failure.Replay)
+ == stored.end())
+ {
+ stored.insert(stored.begin(), failure.Replay);
+ }
+ });
PropertyResult result;
result.Status = PropertyStatus::kFailed;