mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-26 10:21:00 +00:00
Move a Refused Target Aside Rather Than Removing It and Wait Only for a Reader
The two-step rename fallback removed the target before its second try, and rename(2) reports the same errors for a source it cannot move, so a refusal about the source cost the caller its existing file. The target is moved aside and put back if the second step fails too. The wait for a reader holding the target open was layered on the write helper and ran on every platform and for every refusal, so a read-only target on Windows cost half a second before failing anyway, while G-code exports through rename_file() got no wait at all. It lives in rename_file() now, on Windows only, and only for a target this process could write. A failed config write clears the dirty flag as it always did, so the idle handler does not repeat it on every tick. The lock file is created readable and writable by every user, since another user sharing the data dir has to open it read-write; the check that the file behind the path is still the one opened runs at most every few seconds rather than once per preset during a scan; the physical printer loader reads under the lock; the stat headers join the existing platform include block; and the utility tests keep their file's tag.
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("per_user_temp_dir composes a per-user temp root", "[Utils]") {
|
||||
TEST_CASE("per_user_temp_dir composes a per-user temp root", "[utils]") {
|
||||
const std::string base = "/tmp";
|
||||
|
||||
SECTION("an empty id returns base unchanged") {
|
||||
@@ -34,7 +34,7 @@ TEST_CASE("per_user_temp_dir composes a per-user temp root", "[Utils]") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("per_user_temp_id follows the platform contract", "[Utils]") {
|
||||
TEST_CASE("per_user_temp_id follows the platform contract", "[utils]") {
|
||||
const std::string id = per_user_temp_id();
|
||||
|
||||
SECTION("stable across calls") {
|
||||
@@ -54,7 +54,7 @@ TEST_CASE("per_user_temp_id follows the platform contract", "[Utils]") {
|
||||
|
||||
// The end-to-end contract callers depend on: the temp root is left alone on
|
||||
// Windows and isolated per user on Linux/macOS.
|
||||
TEST_CASE("per-user temp root is unchanged on Windows, isolated elsewhere", "[Utils]") {
|
||||
TEST_CASE("per-user temp root is unchanged on Windows, isolated elsewhere", "[utils]") {
|
||||
const std::string base = "/tmp";
|
||||
const std::string root = per_user_temp_dir(base, per_user_temp_id());
|
||||
#ifdef _WIN32
|
||||
@@ -65,7 +65,7 @@ TEST_CASE("per-user temp root is unchanged on Windows, isolated elsewhere", "[Ut
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("write_file_atomically replaces the target and leaves no temporary file", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically replaces the target and leaves no temporary file", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
const boost::filesystem::path target = dir.path() / "preset.json";
|
||||
|
||||
@@ -83,7 +83,7 @@ TEST_CASE("write_file_atomically replaces the target and leaves no temporary fil
|
||||
REQUIRE(entries == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("write_file_atomically reports a missing directory and writes nothing", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically reports a missing directory and writes nothing", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
const boost::filesystem::path target = dir.path() / "missing" / "preset.json";
|
||||
|
||||
@@ -92,7 +92,7 @@ TEST_CASE("write_file_atomically reports a missing directory and writes nothing"
|
||||
REQUIRE_FALSE(boost::filesystem::exists(target));
|
||||
}
|
||||
|
||||
TEST_CASE("write_file_atomically refuses a read-only target and leaves it untouched", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically refuses a read-only target and leaves it untouched", "[utils]") {
|
||||
#ifndef _WIN32
|
||||
if (::geteuid() == 0)
|
||||
SKIP("a read-only file does not stop root");
|
||||
@@ -111,7 +111,7 @@ TEST_CASE("write_file_atomically refuses a read-only target and leaves it untouc
|
||||
REQUIRE(content == "pinned");
|
||||
}
|
||||
|
||||
TEST_CASE("write_file_atomically replaces a read-only target when asked to", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically replaces a read-only target when asked to", "[utils]") {
|
||||
#ifndef _WIN32
|
||||
if (::geteuid() == 0)
|
||||
SKIP("a read-only file does not stop root");
|
||||
@@ -130,7 +130,7 @@ TEST_CASE("write_file_atomically replaces a read-only target when asked to", "[U
|
||||
REQUIRE(content == "replaced");
|
||||
}
|
||||
|
||||
TEST_CASE("write_file_atomically keeps bytes intact in binary mode", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically keeps bytes intact in binary mode", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
const boost::filesystem::path target = dir.path() / "blob.bin";
|
||||
const std::string bytes("a\r\nb\0c", 6);
|
||||
@@ -140,7 +140,7 @@ TEST_CASE("write_file_atomically keeps bytes intact in binary mode", "[Utils]")
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
TEST_CASE("write_file_atomically writes through a symlink and keeps the target's permissions", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically writes through a symlink and keeps the target's permissions", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
const boost::filesystem::path real = dir.path() / "real.json";
|
||||
const boost::filesystem::path link = dir.path() / "link.json";
|
||||
@@ -161,7 +161,7 @@ TEST_CASE("write_file_atomically writes through a symlink and keeps the target's
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("write_file_atomically survives two threads writing one target", "[Utils]") {
|
||||
TEST_CASE("write_file_atomically survives two threads writing one target", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
const boost::filesystem::path target = dir.path() / "shared.json";
|
||||
const std::string a(20000, 'a'), b(20000, 'b');
|
||||
@@ -187,7 +187,7 @@ TEST_CASE("write_file_atomically survives two threads writing one target", "[Uti
|
||||
REQUIRE(temporaries == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("remove_stale_temp_files removes only old <name>.<pid>.<n>.tmp files", "[Utils]") {
|
||||
TEST_CASE("remove_stale_temp_files removes only old <name>.<pid>.<n>.tmp files", "[utils]") {
|
||||
ScopedTemporaryDir dir;
|
||||
for (const char *name : { "a.json.123.7.tmp", "b.info.4.0.tmp", "c.json", "d.tmp", "e.json.x.1.tmp", "f.json..tmp", "a.json.99", "a.json.12.tmp" }) {
|
||||
REQUIRE_FALSE(write_file_atomically((dir.path() / name).string(), "x"));
|
||||
@@ -217,7 +217,7 @@ TEST_CASE("remove_stale_temp_files removes only old <name>.<pid>.<n>.tmp files",
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("copy_file reports the OS error when the destination cannot be written", "[Utils]") {
|
||||
TEST_CASE("copy_file reports the OS error when the destination cannot be written", "[utils]") {
|
||||
ScopedTemporaryFile source(".txt");
|
||||
{
|
||||
std::ofstream ofs(source.string(), std::ios::binary);
|
||||
@@ -246,7 +246,7 @@ TEST_CASE("copy_file reports the OS error when the destination cannot be written
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
TEST_CASE("A resolved input path still names the same file after the working directory changes", "[Utils]") {
|
||||
TEST_CASE("A resolved input path still names the same file after the working directory changes", "[utils]") {
|
||||
ScopedTemporaryFile model(".3mf");
|
||||
{ std::ofstream out(model.string()); out << "3mf"; }
|
||||
const std::string name = model.path().filename().string();
|
||||
@@ -263,7 +263,7 @@ TEST_CASE("A resolved input path still names the same file after the working dir
|
||||
REQUIRE_FALSE(boost::filesystem::exists(name));
|
||||
}
|
||||
|
||||
TEST_CASE("resolve_cli_input_path completes a relative path against the working directory", "[Utils]") {
|
||||
TEST_CASE("resolve_cli_input_path completes a relative path against the working directory", "[utils]") {
|
||||
ScopedWorkingDirectory cwd(boost::filesystem::temp_directory_path());
|
||||
// Read back rather than reusing temp_directory_path(): changing to it resolves any symlink.
|
||||
const boost::filesystem::path here = boost::filesystem::current_path();
|
||||
@@ -279,7 +279,7 @@ TEST_CASE("resolve_cli_input_path completes a relative path against the working
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("resolve_cli_input_path leaves inputs that must not be completed unchanged", "[Utils]") {
|
||||
TEST_CASE("resolve_cli_input_path leaves inputs that must not be completed unchanged", "[utils]") {
|
||||
SECTION("an absolute path") {
|
||||
const boost::filesystem::path absolute = (boost::filesystem::temp_directory_path() / "model.3mf").make_preferred();
|
||||
REQUIRE(resolve_cli_input_path(absolute.string()) == absolute.string());
|
||||
|
||||
Reference in New Issue
Block a user