From 74aed7a2bb4bb2452ca0048ecfb658c55c503d72 Mon Sep 17 00:00:00 2001 From: Kris Austin Date: Fri, 7 Aug 2026 11:33:37 -0500 Subject: [PATCH] test: finish the temp-file cleanup (#14976) Follow-up to #14785. Routes the tests that still hand-rolled temp paths through the shared helpers and unifies the temp guards. - Add ScopedTemporaryDir and a shared ScopedTemporaryPath base under it and ScopedTemporaryFile. - Move test_3mf's round-trip .3mf output out of the TEST_DATA_DIR source tree (a fixed-name leak) and test_toolordering's fixed-name temp .gcode (a sharding collision) onto ScopedTemporaryFile. - Move test_config, test_slicing_pipeline_bindings, the test_3mf backup dirs, and test_preset_bundle_loading onto the guards. - Make slic3rutils ScopedDataDir compose ScopedTemporaryDir; dedupe test_network_versions' fixture and delete test_plugin_lifecycle's duplicate. --- tests/libslic3r/test_3mf.cpp | 36 +++----- tests/libslic3r/test_config.cpp | 7 +- .../libslic3r/test_preset_bundle_loading.cpp | 50 ++++------- .../test_toolordering_nozzle_group.cpp | 9 +- tests/slic3rutils/plugin_test_utils.hpp | 20 ++--- tests/slic3rutils/test_network_versions.cpp | 19 ++--- tests/slic3rutils/test_plugin_lifecycle.cpp | 28 +------ .../test_slicing_pipeline_bindings.cpp | 8 +- tests/test_utils.hpp | 82 +++++++++++-------- 9 files changed, 106 insertions(+), 153 deletions(-) diff --git a/tests/libslic3r/test_3mf.cpp b/tests/libslic3r/test_3mf.cpp index a6fe3ed460..c839149f5f 100644 --- a/tests/libslic3r/test_3mf.cpp +++ b/tests/libslic3r/test_3mf.cpp @@ -155,10 +155,8 @@ SCENARIO("H2C multi-nozzle .3mf round-trip", "[3mf][MultiNozzle]") { // store_bbs_3mf stages Metadata/project_settings.config through the model's backup path; // point it at a writable temp dir (the default lives under a read-only root in CI). - std::string backup_dir = - (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_mn_%%%%%%%%")).string(); - boost::filesystem::create_directories(backup_dir); - model.set_backup_path(backup_dir); + ScopedTemporaryDir backup_dir("orca_mn"); + model.set_backup_path(backup_dir.string()); // Global (printer) config: give nozzle_volume_type a non-default value so the slice_info // read-back is a meaningful assertion (High Flow == 1). @@ -180,7 +178,8 @@ SCENARIO("H2C multi-nozzle .3mf round-trip", "[3mf][MultiNozzle]") { plate->config.set_key_value("enable_filament_dynamic_map", new ConfigOptionBool(true)); WHEN("stored to and reloaded from a .3mf") { - std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/mn_roundtrip.3mf"; + ScopedTemporaryFile temp(".3mf"); + const std::string test_file = temp.string(); StoreParams store_params; store_params.path = test_file.c_str(); @@ -202,8 +201,6 @@ SCENARIO("H2C multi-nozzle .3mf round-trip", "[3mf][MultiNozzle]") { bool loaded = load_bbs_3mf(test_file.c_str(), &dst_config, &ctxt, &dst_model, &dst_plates, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr, LoadStrategy::LoadModel | LoadStrategy::LoadConfig); - boost::filesystem::remove(test_file); - THEN("every multi-nozzle key round-trips as expected") { REQUIRE(loaded); REQUIRE(dst_plates.size() >= 1); @@ -233,7 +230,6 @@ SCENARIO("H2C multi-nozzle .3mf round-trip", "[3mf][MultiNozzle]") { release_PlateData_list(dst_plates); } delete plate; // store_bbs_3mf does not take ownership of the source plate - boost::filesystem::remove_all(backup_dir); } } @@ -250,10 +246,8 @@ SCENARIO("Non-standard nozzle diameter survives .3mf save on a single-nozzle pri REQUIRE(load_stl(src_file.c_str(), &model)); model.add_default_instances(); - std::string backup_dir = - (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_nd_%%%%%%%%")).string(); - boost::filesystem::create_directories(backup_dir); - model.set_backup_path(backup_dir); + ScopedTemporaryDir backup_dir("orca_nd"); + model.set_backup_path(backup_dir.string()); // Single extruder with a non-standard 0.5 mm nozzle; extruder_max_nozzle_count stays at its // default (no nozzle cluster), so the writer must emit the exact config diameter. @@ -276,7 +270,8 @@ SCENARIO("Non-standard nozzle diameter survives .3mf save on a single-nozzle pri plate->slice_filaments_info.push_back(fi); WHEN("stored to and reloaded from a .3mf") { - std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/nd_roundtrip.3mf"; + ScopedTemporaryFile temp(".3mf"); + const std::string test_file = temp.string(); StoreParams store_params; store_params.path = test_file.c_str(); @@ -296,8 +291,6 @@ SCENARIO("Non-standard nozzle diameter survives .3mf save on a single-nozzle pri bool loaded = load_bbs_3mf(test_file.c_str(), &dst_config, &ctxt, &dst_model, &dst_plates, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr, LoadStrategy::LoadModel | LoadStrategy::LoadConfig); - boost::filesystem::remove(test_file); - THEN("the saved nozzle diameter is the exact 0.5, not the rounded 0.4") { REQUIRE(loaded); REQUIRE(dst_plates.size() >= 1); @@ -315,7 +308,6 @@ SCENARIO("Non-standard nozzle diameter survives .3mf save on a single-nozzle pri release_PlateData_list(dst_plates); } delete plate; // store_bbs_3mf does not take ownership of the source plate - boost::filesystem::remove_all(backup_dir); } } @@ -436,10 +428,8 @@ SCENARIO("Nozzle-group metadata .3mf round-trip", "[3mf][MultiNozzle]") { REQUIRE(load_stl(src_file.c_str(), &model)); model.add_default_instances(); - std::string backup_dir = - (boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_ng_%%%%%%%%")).string(); - boost::filesystem::create_directories(backup_dir); - model.set_backup_path(backup_dir); + ScopedTemporaryDir backup_dir("orca_ng"); + model.set_backup_path(backup_dir.string()); DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); @@ -459,7 +449,8 @@ SCENARIO("Nozzle-group metadata .3mf round-trip", "[3mf][MultiNozzle]") { plate->config.set_key_value("filament_map", new ConfigOptionInts({ 1, 2, 1 })); WHEN("stored to and reloaded from a .3mf") { - std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/ng_roundtrip.3mf"; + ScopedTemporaryFile temp(".3mf"); + const std::string test_file = temp.string(); StoreParams store_params; store_params.path = test_file.c_str(); @@ -479,8 +470,6 @@ SCENARIO("Nozzle-group metadata .3mf round-trip", "[3mf][MultiNozzle]") { bool loaded = load_bbs_3mf(test_file.c_str(), &dst_config, &ctxt, &dst_model, &dst_plates, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr, LoadStrategy::LoadModel | LoadStrategy::LoadConfig); - boost::filesystem::remove(test_file); - THEN("the tags round-trip into the loaded plate's nozzles_info") { REQUIRE(loaded); REQUIRE(dst_plates.size() >= 1); @@ -506,6 +495,5 @@ SCENARIO("Nozzle-group metadata .3mf round-trip", "[3mf][MultiNozzle]") { release_PlateData_list(dst_plates); } delete plate; - boost::filesystem::remove_all(backup_dir); } } diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index f256ae3442..5bc825c3b2 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -4,6 +4,8 @@ #include "libslic3r/PrintConfigConstants.hpp" #include "libslic3r/LocalesUtils.hpp" +#include "test_utils.hpp" + #include #include #include @@ -407,8 +409,7 @@ SCENARIO("update_diff_values_to_child_config tolerates legacy machine-limit vect // } TEST_CASE("save_to_json round-trips plugin capability references as strings", "[Config][plugins]") { - namespace fs = boost::filesystem; - const fs::path tmp = fs::temp_directory_path() / fs::unique_path("orca_plugins_%%%%-%%%%.json"); + ScopedTemporaryFile tmp(".json"); const std::vector refs = { "local_plugin;;inset", "cloud_plugin;550e8400-e29b-41d4-a716-446655440000;inset" @@ -435,8 +436,6 @@ TEST_CASE("save_to_json round-trips plugin capability references as strings", "[ REQUIRE(reloaded.load_from_json(tmp.string(), substitutions, true, key_values, reason) == 0); CHECK(reason.empty()); CHECK(reloaded.option("slicing_pipeline_plugin")->values == refs); - - fs::remove(tmp); } TEST_CASE("plugin capability references survive string-map serialization", "[Config][plugins]") { diff --git a/tests/libslic3r/test_preset_bundle_loading.cpp b/tests/libslic3r/test_preset_bundle_loading.cpp index c697c4461c..844ccb6a8b 100644 --- a/tests/libslic3r/test_preset_bundle_loading.cpp +++ b/tests/libslic3r/test_preset_bundle_loading.cpp @@ -5,28 +5,14 @@ #include "libslic3r/PresetBundle.hpp" #include "libslic3r/AppConfig.hpp" +#include "test_utils.hpp" + using namespace Slic3r; namespace { namespace fs = boost::filesystem; -struct TempPresetDir { - fs::path path; - - TempPresetDir() - { - path = fs::temp_directory_path() / fs::unique_path("orcaslicer-preset-%%%%-%%%%-%%%%"); - fs::create_directories(path); - } - - ~TempPresetDir() - { - boost::system::error_code ec; - fs::remove_all(path, ec); - } -}; - void write_print_preset(const DynamicPrintConfig &default_config, const fs::path &file, const std::string &name, const std::string &inherits = {}) { DynamicPrintConfig config(default_config); @@ -82,17 +68,17 @@ struct RenameTestCollection : public PresetCollection TEST_CASE("Preset identity is canonicalized from load path", "[Preset][Identity]") { - TempPresetDir temp_dir; + ScopedTemporaryDir temp_dir; PresetBundle bundle; PresetsConfigSubstitutions substitutions; - write_print_preset(bundle.prints.default_preset().config, temp_dir.path / PRESET_PRINT_NAME / "User.json", "User"); - write_print_preset(bundle.prints.default_preset().config, temp_dir.path / PRESET_LOCAL_DIR / "bundle-1" / PRESET_PRINT_NAME / "LocalBundle.json", "LocalBundle"); - write_print_preset(bundle.prints.default_preset().config, temp_dir.path / PRESET_SUBSCRIBED_DIR / "remote-1" / PRESET_PRINT_NAME / "Subscribed.json", "Subscribed"); + write_print_preset(bundle.prints.default_preset().config, temp_dir.path() / PRESET_PRINT_NAME / "User.json", "User"); + write_print_preset(bundle.prints.default_preset().config, temp_dir.path() / PRESET_LOCAL_DIR / "bundle-1" / PRESET_PRINT_NAME / "LocalBundle.json", "LocalBundle"); + write_print_preset(bundle.prints.default_preset().config, temp_dir.path() / PRESET_SUBSCRIBED_DIR / "remote-1" / PRESET_PRINT_NAME / "Subscribed.json", "Subscribed"); - bundle.prints.load_presets(temp_dir.path.string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); - bundle.prints.load_presets((temp_dir.path / PRESET_LOCAL_DIR / "bundle-1").string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); - bundle.prints.load_presets((temp_dir.path / PRESET_SUBSCRIBED_DIR / "remote-1").string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); + bundle.prints.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); + bundle.prints.load_presets((temp_dir.path() / PRESET_LOCAL_DIR / "bundle-1").string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); + bundle.prints.load_presets((temp_dir.path() / PRESET_SUBSCRIBED_DIR / "remote-1").string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); const Preset *root_user = bundle.prints.find_preset("User"); REQUIRE(root_user != nullptr); @@ -112,14 +98,14 @@ TEST_CASE("Preset identity is canonicalized from load path", "[Preset][Identity] TEST_CASE("Legacy bundle import without bundle metadata stays in the user preset directory", "[Preset][Identity]") { - TempPresetDir temp_dir; + ScopedTemporaryDir temp_dir; PresetBundle bundle; PresetsConfigSubstitutions substitutions; std::vector result; int overwrite = 0; - std::string file = (temp_dir.path / "legacy-bundle" / "Imported.json").string(); - const fs::path user_root = temp_dir.path / "user"; + std::string file = (temp_dir.path() / "legacy-bundle" / "Imported.json").string(); + const fs::path user_root = temp_dir.path() / "user"; write_print_preset(bundle.prints.default_preset().config, file, "Imported"); fs::create_directories(user_root); @@ -252,7 +238,7 @@ TEST_CASE("find_preset2 auto-matches removed Generic vendor profiles to the libr TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Preset][Rename]") { - TempPresetDir temp_dir; + ScopedTemporaryDir temp_dir; RenameTestCollection coll; // Current parent, renamed from "Old Process". @@ -262,10 +248,10 @@ TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Pres // A user preset on disk that still inherits the OLD name. write_preset_with_inherits(coll.default_preset().config, - temp_dir.path / PRESET_PRINT_NAME / "Child.json", "Child", "Old Process"); + temp_dir.path() / PRESET_PRINT_NAME / "Child.json", "Child", "Old Process"); PresetsConfigSubstitutions substitutions; - coll.load_presets(temp_dir.path.string(), PRESET_PRINT_NAME, substitutions, + coll.load_presets(temp_dir.path().string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); const Preset *child = coll.find_preset("Child"); @@ -279,17 +265,17 @@ TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Pres TEST_CASE("Removed Generic parent is normalized into a loaded filament's inherits", "[Preset][Rename]") { - TempPresetDir temp_dir; + ScopedTemporaryDir temp_dir; PresetBundle bundle; add_inmemory_preset(bundle.filaments, "Generic PLA @System"); // A user filament that still inherits a removed " Generic PLA" profile. write_preset_with_inherits(bundle.filaments.default_preset().config, - temp_dir.path / PRESET_FILAMENT_NAME / "MyPLA.json", "MyPLA", "Voron Generic PLA"); + temp_dir.path() / PRESET_FILAMENT_NAME / "MyPLA.json", "MyPLA", "Voron Generic PLA"); PresetsConfigSubstitutions substitutions; - bundle.filaments.load_presets(temp_dir.path.string(), PRESET_FILAMENT_NAME, substitutions, + bundle.filaments.load_presets(temp_dir.path().string(), PRESET_FILAMENT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable); const Preset *child = bundle.filaments.find_preset("MyPLA"); diff --git a/tests/libslic3r/test_toolordering_nozzle_group.cpp b/tests/libslic3r/test_toolordering_nozzle_group.cpp index dc54aae80a..26e36c0dbf 100644 --- a/tests/libslic3r/test_toolordering_nozzle_group.cpp +++ b/tests/libslic3r/test_toolordering_nozzle_group.cpp @@ -8,6 +8,8 @@ #include "libslic3r/Print.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "test_utils.hpp" + #include #include #include @@ -708,10 +710,9 @@ TEST_CASE("Sequential selector prints publish a stitched result and cache the pl REQUIRE(print.config().filament_self_index.values.size() >= print.config().filament_map.values.size()); // Export must consume the cached plans and produce g-code without throwing. - boost::filesystem::path gcode_path = boost::filesystem::temp_directory_path() / "orca_seq_dynamic_publish_test.gcode"; - REQUIRE_NOTHROW(print.export_gcode(gcode_path.string(), nullptr, nullptr)); - REQUIRE(boost::filesystem::exists(gcode_path)); - boost::filesystem::remove(gcode_path); + ScopedTemporaryFile gcode(".gcode"); + REQUIRE_NOTHROW(print.export_gcode(gcode.string(), nullptr, nullptr)); + REQUIRE(boost::filesystem::exists(gcode.path())); } TEST_CASE("Per-variant expansion gives migrating filaments one slot per variant", "[PrintConfig][H2C][Dynamic]") diff --git a/tests/slic3rutils/plugin_test_utils.hpp b/tests/slic3rutils/plugin_test_utils.hpp index 52e503f6f4..d60b3441c8 100644 --- a/tests/slic3rutils/plugin_test_utils.hpp +++ b/tests/slic3rutils/plugin_test_utils.hpp @@ -6,6 +6,8 @@ #include +#include "test_utils.hpp" + namespace Slic3r { // Point data_dir() at a throwaway directory for the lifetime of a test and @@ -13,24 +15,20 @@ namespace Slic3r { // disposable tree and tests don't leak state into each other. struct ScopedDataDir { + ScopedTemporaryDir tmp; // owns the temp dir (create + recursive remove) + boost::filesystem::path dir; // = tmp.path(); kept as a member for callers std::string previous; - boost::filesystem::path dir; explicit ScopedDataDir(const std::string& tag) + : tmp("orca-" + tag), dir(tmp.path()), previous(data_dir()) { - namespace fs = boost::filesystem; - previous = data_dir(); - dir = fs::temp_directory_path() / fs::unique_path("orca-" + tag + "-%%%%-%%%%"); - fs::create_directories(dir); set_data_dir(dir.string()); } - ~ScopedDataDir() - { - set_data_dir(previous); - boost::system::error_code ec; - boost::filesystem::remove_all(dir, ec); - } + ~ScopedDataDir() { set_data_dir(previous); } // tmp removes the directory + + // The plugin manager scans {data_dir}/orca_plugins. + boost::filesystem::path plugins_dir() const { return dir / "orca_plugins"; } ScopedDataDir(const ScopedDataDir&) = delete; ScopedDataDir& operator=(const ScopedDataDir&) = delete; diff --git a/tests/slic3rutils/test_network_versions.cpp b/tests/slic3rutils/test_network_versions.cpp index efe8b5d831..349082a965 100644 --- a/tests/slic3rutils/test_network_versions.cpp +++ b/tests/slic3rutils/test_network_versions.cpp @@ -6,6 +6,8 @@ #include "libslic3r/Utils.hpp" #include "slic3r/Utils/bambu_networking.hpp" +#include "plugin_test_utils.hpp" + using namespace Slic3r; namespace fs = boost::filesystem; @@ -25,27 +27,16 @@ static const char* PLUGIN_EXT = ".so"; struct PluginFolderFixture { - fs::path root; - std::string previous_data_dir; + ScopedDataDir data{"netver"}; PluginFolderFixture() { - previous_data_dir = data_dir(); - root = fs::temp_directory_path() / fs::unique_path("orca-netver-%%%%%%%%"); - fs::create_directories(root / "plugins"); - set_data_dir(root.string()); - } - - ~PluginFolderFixture() - { - set_data_dir(previous_data_dir); - boost::system::error_code ec; - fs::remove_all(root, ec); + fs::create_directories(data.dir / "plugins"); } void add_plugin(const std::string& version) { - boost::nowide::ofstream f((root / "plugins" / (PLUGIN_PREFIX + version + PLUGIN_EXT)).string()); + boost::nowide::ofstream f((data.dir / "plugins" / (PLUGIN_PREFIX + version + PLUGIN_EXT)).string()); f << "stub"; } }; diff --git a/tests/slic3rutils/test_plugin_lifecycle.cpp b/tests/slic3rutils/test_plugin_lifecycle.cpp index c49471de28..63a4e6827b 100644 --- a/tests/slic3rutils/test_plugin_lifecycle.cpp +++ b/tests/slic3rutils/test_plugin_lifecycle.cpp @@ -6,6 +6,8 @@ #include #include +#include "plugin_test_utils.hpp" + #include #include @@ -25,32 +27,6 @@ namespace fs = boost::filesystem; namespace { -// Point data_dir() at a throwaway directory for the lifetime of a test and restore the previous -// value afterwards, so discovery scans a disposable {data_dir}/orca_plugins tree and tests don't -// leak state into each other. -struct ScopedDataDir -{ - std::string previous; - fs::path dir; - - explicit ScopedDataDir(const std::string& tag) - { - previous = data_dir(); - dir = fs::temp_directory_path() / fs::unique_path("orca-" + tag + "-%%%%-%%%%"); - fs::create_directories(dir); - set_data_dir(dir.string()); - } - - ~ScopedDataDir() - { - set_data_dir(previous); - boost::system::error_code ec; - fs::remove_all(dir, ec); - } - - fs::path plugins_dir() const { return dir / "orca_plugins"; } -}; - // Brings the plugin system up, and tears it down explicitly at the end of the test. // // Shutting the interpreter down here, rather than leaving it to PythonInterpreter's static diff --git a/tests/slic3rutils/test_slicing_pipeline_bindings.cpp b/tests/slic3rutils/test_slicing_pipeline_bindings.cpp index 8f00b6f31b..5e819c09e0 100644 --- a/tests/slic3rutils/test_slicing_pipeline_bindings.cpp +++ b/tests/slic3rutils/test_slicing_pipeline_bindings.cpp @@ -16,6 +16,8 @@ TEST_CASE("SlicingPipeline capability-type string maps round-trip", "[slicing_pi #include "libslic3r/Point.hpp" #include "libslic3r/ExPolygon.hpp" #include "libslic3r/Surface.hpp" + +#include "test_utils.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" @@ -142,7 +144,7 @@ TEST_CASE("orca.slicing psGCodePostProcess context: file edit in place + config import_orca_module(); py::gil_scoped_acquire gil; - const fs::path gpath = fs::temp_directory_path() / fs::unique_path("orca_pp_%%%%-%%%%.gcode"); + ScopedTemporaryFile gpath(".gcode"); { boost::nowide::ofstream ofs(gpath.string()); ofs << "; header\nG1 X0 Y0\n"; @@ -196,9 +198,7 @@ _pp_result = Stamp().execute(_pp_ctx) boost::nowide::ifstream ifs(gpath.string()); std::stringstream ss; ss << ifs.rdbuf(); contents = ss.str(); } - CHECK(contents.find("; stamped by File") != std::string::npos); - fs::remove(gpath); -} + CHECK(contents.find("; stamped by File") != std::string::npos);} // --------------------------------------------------------------------------- // Toolpath helpers for the raw-graph tests. diff --git a/tests/test_utils.hpp b/tests/test_utils.hpp index d928f2f41e..97e684fd6e 100644 --- a/tests/test_utils.hpp +++ b/tests/test_utils.hpp @@ -27,26 +27,47 @@ inline Slic3r::TriangleMesh load_model(const std::string &obj_filename) return mesh; } -// RAII holder for a unique temporary file path, removed when the guard goes out -// of scope so a failing assertion never leaks it. Uses the system temp dir with -// a unique name (parallel-safe, cross-platform). The file itself is created by -// whoever writes to path()/string(); this only reserves the name and cleans up. -class ScopedTemporaryFile +// --------------------------------------------------------------------------- +// Scoped temporary paths +// --------------------------------------------------------------------------- + +// Owns a unique path under the system temp dir, "-[]" +// (parallel-safe, cross-platform). Shared base for the two RAII temp guards below. +class ScopedTemporaryPath +{ +public: + const boost::filesystem::path &path() const { return m_path; } + std::string string() const { return m_path.string(); } + ScopedTemporaryPath(const ScopedTemporaryPath &) = delete; + ScopedTemporaryPath &operator=(const ScopedTemporaryPath &) = delete; + +protected: + ScopedTemporaryPath(const std::string &prefix, const std::string &extension) + : m_path(boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(prefix + "-%%%%-%%%%-%%%%" + extension)) + {} + ~ScopedTemporaryPath() = default; // non-virtual: never deleted through a base pointer + + boost::filesystem::path m_path; +}; + +// A temp file the caller creates by writing to path()/string(); the guard only +// reserves the name and removes the file on scope exit. +class ScopedTemporaryFile : public ScopedTemporaryPath { public: explicit ScopedTemporaryFile(const std::string &extension = ".tmp") - : m_path(boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path("orca-%%%%-%%%%-%%%%" + extension)) - {} + : ScopedTemporaryPath("orca", extension) {} ~ScopedTemporaryFile() { boost::system::error_code ec; boost::filesystem::remove(m_path, ec); } - ScopedTemporaryFile(const ScopedTemporaryFile &) = delete; - ScopedTemporaryFile &operator=(const ScopedTemporaryFile &) = delete; +}; - const boost::filesystem::path &path() const { return m_path; } - std::string string() const { return m_path.string(); } - -private: - boost::filesystem::path m_path; +// A temp directory created on construction and removed recursively on scope exit. +class ScopedTemporaryDir : public ScopedTemporaryPath +{ +public: + explicit ScopedTemporaryDir(const std::string &prefix = "orca") + : ScopedTemporaryPath(prefix, "") { boost::filesystem::create_directories(m_path); } + ~ScopedTemporaryDir() { boost::system::error_code ec; boost::filesystem::remove_all(m_path, ec); } }; // --------------------------------------------------------------------------- @@ -66,7 +87,7 @@ inline std::string debug_artifact_path(const std::string &name) boost::filesystem::path dir = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca-test-artifacts-%%%%-%%%%"); boost::filesystem::create_directories(dir); - std::printf("Debug test artifacts will be written to %s\n", dir.string().c_str()); + std::fprintf(stderr, "Debug test artifacts will be written to %s\n", dir.string().c_str()); return dir; }(); boost::filesystem::path full = root / name; @@ -75,57 +96,52 @@ inline std::string debug_artifact_path(const std::string &name) } // Dump a mesh as OBJ. -inline void write_debug_obj(const std::string &name, const Slic3r::TriangleMesh &mesh) +inline void write_debug_obj([[maybe_unused]] const std::string &name, + [[maybe_unused]] const Slic3r::TriangleMesh &mesh) { #ifndef NDEBUG mesh.WriteOBJFile(debug_artifact_path(name).c_str()); -#else - (void) name; (void) mesh; #endif } -inline void write_debug_obj(const std::string &name, const indexed_triangle_set &its) +inline void write_debug_obj([[maybe_unused]] const std::string &name, + [[maybe_unused]] const indexed_triangle_set &its) { #ifndef NDEBUG its_write_obj(its, debug_artifact_path(name).c_str()); -#else - (void) name; (void) its; #endif } // Dump a mesh as ASCII STL. -inline void write_debug_stl(const std::string &name, const Slic3r::TriangleMesh &mesh) +inline void write_debug_stl([[maybe_unused]] const std::string &name, + [[maybe_unused]] const Slic3r::TriangleMesh &mesh) { #ifndef NDEBUG mesh.write_ascii(debug_artifact_path(name).c_str()); -#else - (void) name; (void) mesh; #endif } // Draw an SVG artifact through a callback that receives the open SVG. Second // overload takes a BoundingBox when the drawing needs one. template -inline void write_debug_svg(const std::string &name, Draw &&draw) +inline void write_debug_svg([[maybe_unused]] const std::string &name, [[maybe_unused]] Draw &&draw) { #ifndef NDEBUG Slic3r::SVG svg(debug_artifact_path(name)); draw(svg); svg.Close(); -#else - (void) name; (void) draw; #endif } template -inline void write_debug_svg(const std::string &name, const Slic3r::BoundingBox &bbox, Draw &&draw) +inline void write_debug_svg([[maybe_unused]] const std::string &name, + [[maybe_unused]] const Slic3r::BoundingBox &bbox, + [[maybe_unused]] Draw &&draw) { #ifndef NDEBUG Slic3r::SVG svg(debug_artifact_path(name), bbox); draw(svg); svg.Close(); -#else - (void) name; (void) bbox; (void) draw; #endif } @@ -133,13 +149,11 @@ inline void write_debug_svg(const std::string &name, const Slic3r::BoundingBox & // artifact. operator<< is resolved by ADL at the call site, so this header needn't // include the producer's headers. template -inline void write_debug_stream(const std::string &name, Produce &&produce) +inline void write_debug_stream([[maybe_unused]] const std::string &name, [[maybe_unused]] Produce &&produce) { #ifndef NDEBUG std::ofstream out(debug_artifact_path(name), std::ios::out | std::ios::binary); out << produce(); -#else - (void) name; (void) produce; #endif }