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.
This commit is contained in:
Kris Austin
2026-08-07 11:33:37 -05:00
committed by GitHub
parent 8bff9aaf32
commit 74aed7a2bb
9 changed files with 106 additions and 153 deletions

View File

@@ -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; // 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). // point it at a writable temp dir (the default lives under a read-only root in CI).
std::string backup_dir = ScopedTemporaryDir backup_dir("orca_mn");
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_mn_%%%%%%%%")).string(); model.set_backup_path(backup_dir.string());
boost::filesystem::create_directories(backup_dir);
model.set_backup_path(backup_dir);
// Global (printer) config: give nozzle_volume_type a non-default value so the slice_info // Global (printer) config: give nozzle_volume_type a non-default value so the slice_info
// read-back is a meaningful assertion (High Flow == 1). // 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)); plate->config.set_key_value("enable_filament_dynamic_map", new ConfigOptionBool(true));
WHEN("stored to and reloaded from a .3mf") { 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; StoreParams store_params;
store_params.path = test_file.c_str(); 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, 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, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr,
LoadStrategy::LoadModel | LoadStrategy::LoadConfig); LoadStrategy::LoadModel | LoadStrategy::LoadConfig);
boost::filesystem::remove(test_file);
THEN("every multi-nozzle key round-trips as expected") { THEN("every multi-nozzle key round-trips as expected") {
REQUIRE(loaded); REQUIRE(loaded);
REQUIRE(dst_plates.size() >= 1); REQUIRE(dst_plates.size() >= 1);
@@ -233,7 +230,6 @@ SCENARIO("H2C multi-nozzle .3mf round-trip", "[3mf][MultiNozzle]") {
release_PlateData_list(dst_plates); release_PlateData_list(dst_plates);
} }
delete plate; // store_bbs_3mf does not take ownership of the source plate 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)); REQUIRE(load_stl(src_file.c_str(), &model));
model.add_default_instances(); model.add_default_instances();
std::string backup_dir = ScopedTemporaryDir backup_dir("orca_nd");
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_nd_%%%%%%%%")).string(); model.set_backup_path(backup_dir.string());
boost::filesystem::create_directories(backup_dir);
model.set_backup_path(backup_dir);
// Single extruder with a non-standard 0.5 mm nozzle; extruder_max_nozzle_count stays at its // 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. // 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); plate->slice_filaments_info.push_back(fi);
WHEN("stored to and reloaded from a .3mf") { 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; StoreParams store_params;
store_params.path = test_file.c_str(); 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, 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, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr,
LoadStrategy::LoadModel | LoadStrategy::LoadConfig); LoadStrategy::LoadModel | LoadStrategy::LoadConfig);
boost::filesystem::remove(test_file);
THEN("the saved nozzle diameter is the exact 0.5, not the rounded 0.4") { THEN("the saved nozzle diameter is the exact 0.5, not the rounded 0.4") {
REQUIRE(loaded); REQUIRE(loaded);
REQUIRE(dst_plates.size() >= 1); 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); release_PlateData_list(dst_plates);
} }
delete plate; // store_bbs_3mf does not take ownership of the source plate 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)); REQUIRE(load_stl(src_file.c_str(), &model));
model.add_default_instances(); model.add_default_instances();
std::string backup_dir = ScopedTemporaryDir backup_dir("orca_ng");
(boost::filesystem::temp_directory_path() / boost::filesystem::unique_path("orca_ng_%%%%%%%%")).string(); model.set_backup_path(backup_dir.string());
boost::filesystem::create_directories(backup_dir);
model.set_backup_path(backup_dir);
DynamicPrintConfig config = DynamicPrintConfig::full_print_config(); 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 })); plate->config.set_key_value("filament_map", new ConfigOptionInts({ 1, 2, 1 }));
WHEN("stored to and reloaded from a .3mf") { 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; StoreParams store_params;
store_params.path = test_file.c_str(); 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, 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, &project_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr,
LoadStrategy::LoadModel | LoadStrategy::LoadConfig); LoadStrategy::LoadModel | LoadStrategy::LoadConfig);
boost::filesystem::remove(test_file);
THEN("the <nozzle> tags round-trip into the loaded plate's nozzles_info") { THEN("the <nozzle> tags round-trip into the loaded plate's nozzles_info") {
REQUIRE(loaded); REQUIRE(loaded);
REQUIRE(dst_plates.size() >= 1); REQUIRE(dst_plates.size() >= 1);
@@ -506,6 +495,5 @@ SCENARIO("Nozzle-group metadata .3mf round-trip", "[3mf][MultiNozzle]") {
release_PlateData_list(dst_plates); release_PlateData_list(dst_plates);
} }
delete plate; delete plate;
boost::filesystem::remove_all(backup_dir);
} }
} }

View File

@@ -4,6 +4,8 @@
#include "libslic3r/PrintConfigConstants.hpp" #include "libslic3r/PrintConfigConstants.hpp"
#include "libslic3r/LocalesUtils.hpp" #include "libslic3r/LocalesUtils.hpp"
#include "test_utils.hpp"
#include <cereal/types/polymorphic.hpp> #include <cereal/types/polymorphic.hpp>
#include <cereal/types/string.hpp> #include <cereal/types/string.hpp>
#include <cereal/types/vector.hpp> #include <cereal/types/vector.hpp>
@@ -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]") { TEST_CASE("save_to_json round-trips plugin capability references as strings", "[Config][plugins]") {
namespace fs = boost::filesystem; ScopedTemporaryFile tmp(".json");
const fs::path tmp = fs::temp_directory_path() / fs::unique_path("orca_plugins_%%%%-%%%%.json");
const std::vector<std::string> refs = { const std::vector<std::string> refs = {
"local_plugin;;inset", "local_plugin;;inset",
"cloud_plugin;550e8400-e29b-41d4-a716-446655440000;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); REQUIRE(reloaded.load_from_json(tmp.string(), substitutions, true, key_values, reason) == 0);
CHECK(reason.empty()); CHECK(reason.empty());
CHECK(reloaded.option<ConfigOptionStrings>("slicing_pipeline_plugin")->values == refs); CHECK(reloaded.option<ConfigOptionStrings>("slicing_pipeline_plugin")->values == refs);
fs::remove(tmp);
} }
TEST_CASE("plugin capability references survive string-map serialization", "[Config][plugins]") { TEST_CASE("plugin capability references survive string-map serialization", "[Config][plugins]") {

View File

@@ -5,28 +5,14 @@
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "test_utils.hpp"
using namespace Slic3r; using namespace Slic3r;
namespace { namespace {
namespace fs = boost::filesystem; 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 = {}) void write_print_preset(const DynamicPrintConfig &default_config, const fs::path &file, const std::string &name, const std::string &inherits = {})
{ {
DynamicPrintConfig config(default_config); DynamicPrintConfig config(default_config);
@@ -82,17 +68,17 @@ struct RenameTestCollection : public PresetCollection
TEST_CASE("Preset identity is canonicalized from load path", "[Preset][Identity]") TEST_CASE("Preset identity is canonicalized from load path", "[Preset][Identity]")
{ {
TempPresetDir temp_dir; ScopedTemporaryDir temp_dir;
PresetBundle bundle; PresetBundle bundle;
PresetsConfigSubstitutions substitutions; 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_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_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_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().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_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() / PRESET_SUBSCRIBED_DIR / "remote-1").string(), PRESET_PRINT_NAME, substitutions, ForwardCompatibilitySubstitutionRule::Disable);
const Preset *root_user = bundle.prints.find_preset("User"); const Preset *root_user = bundle.prints.find_preset("User");
REQUIRE(root_user != nullptr); 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]") TEST_CASE("Legacy bundle import without bundle metadata stays in the user preset directory", "[Preset][Identity]")
{ {
TempPresetDir temp_dir; ScopedTemporaryDir temp_dir;
PresetBundle bundle; PresetBundle bundle;
PresetsConfigSubstitutions substitutions; PresetsConfigSubstitutions substitutions;
std::vector<std::string> result; std::vector<std::string> result;
int overwrite = 0; int overwrite = 0;
std::string file = (temp_dir.path / "legacy-bundle" / "Imported.json").string(); std::string file = (temp_dir.path() / "legacy-bundle" / "Imported.json").string();
const fs::path user_root = temp_dir.path / "user"; const fs::path user_root = temp_dir.path() / "user";
write_print_preset(bundle.prints.default_preset().config, file, "Imported"); write_print_preset(bundle.prints.default_preset().config, file, "Imported");
fs::create_directories(user_root); 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]") TEST_CASE("Renamed parent is normalized into a loaded preset's inherits", "[Preset][Rename]")
{ {
TempPresetDir temp_dir; ScopedTemporaryDir temp_dir;
RenameTestCollection coll; RenameTestCollection coll;
// Current parent, renamed from "Old Process". // 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. // A user preset on disk that still inherits the OLD name.
write_preset_with_inherits(coll.default_preset().config, 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; 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); ForwardCompatibilitySubstitutionRule::Disable);
const Preset *child = coll.find_preset("Child"); 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]") TEST_CASE("Removed Generic parent is normalized into a loaded filament's inherits", "[Preset][Rename]")
{ {
TempPresetDir temp_dir; ScopedTemporaryDir temp_dir;
PresetBundle bundle; PresetBundle bundle;
add_inmemory_preset(bundle.filaments, "Generic PLA @System"); add_inmemory_preset(bundle.filaments, "Generic PLA @System");
// A user filament that still inherits a removed "<vendor> Generic PLA" profile. // A user filament that still inherits a removed "<vendor> Generic PLA" profile.
write_preset_with_inherits(bundle.filaments.default_preset().config, 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; 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); ForwardCompatibilitySubstitutionRule::Disable);
const Preset *child = bundle.filaments.find_preset("MyPLA"); const Preset *child = bundle.filaments.find_preset("MyPLA");

View File

@@ -8,6 +8,8 @@
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/TriangleMesh.hpp" #include "libslic3r/TriangleMesh.hpp"
#include "test_utils.hpp"
#include <algorithm> #include <algorithm>
#include <map> #include <map>
#include <set> #include <set>
@@ -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()); 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. // 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"; ScopedTemporaryFile gcode(".gcode");
REQUIRE_NOTHROW(print.export_gcode(gcode_path.string(), nullptr, nullptr)); REQUIRE_NOTHROW(print.export_gcode(gcode.string(), nullptr, nullptr));
REQUIRE(boost::filesystem::exists(gcode_path)); REQUIRE(boost::filesystem::exists(gcode.path()));
boost::filesystem::remove(gcode_path);
} }
TEST_CASE("Per-variant expansion gives migrating filaments one slot per variant", "[PrintConfig][H2C][Dynamic]") TEST_CASE("Per-variant expansion gives migrating filaments one slot per variant", "[PrintConfig][H2C][Dynamic]")

View File

@@ -6,6 +6,8 @@
#include <string> #include <string>
#include "test_utils.hpp"
namespace Slic3r { namespace Slic3r {
// Point data_dir() at a throwaway directory for the lifetime of a test and // 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. // disposable tree and tests don't leak state into each other.
struct ScopedDataDir 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; std::string previous;
boost::filesystem::path dir;
explicit ScopedDataDir(const std::string& tag) 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()); set_data_dir(dir.string());
} }
~ScopedDataDir() ~ScopedDataDir() { set_data_dir(previous); } // tmp removes the directory
{
set_data_dir(previous); // The plugin manager scans {data_dir}/orca_plugins.
boost::system::error_code ec; boost::filesystem::path plugins_dir() const { return dir / "orca_plugins"; }
boost::filesystem::remove_all(dir, ec);
}
ScopedDataDir(const ScopedDataDir&) = delete; ScopedDataDir(const ScopedDataDir&) = delete;
ScopedDataDir& operator=(const ScopedDataDir&) = delete; ScopedDataDir& operator=(const ScopedDataDir&) = delete;

View File

@@ -6,6 +6,8 @@
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "slic3r/Utils/bambu_networking.hpp" #include "slic3r/Utils/bambu_networking.hpp"
#include "plugin_test_utils.hpp"
using namespace Slic3r; using namespace Slic3r;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@@ -25,27 +27,16 @@ static const char* PLUGIN_EXT = ".so";
struct PluginFolderFixture struct PluginFolderFixture
{ {
fs::path root; ScopedDataDir data{"netver"};
std::string previous_data_dir;
PluginFolderFixture() PluginFolderFixture()
{ {
previous_data_dir = data_dir(); fs::create_directories(data.dir / "plugins");
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);
} }
void add_plugin(const std::string& version) 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"; f << "stub";
} }
}; };

View File

@@ -6,6 +6,8 @@
#include <slic3r/plugin/PluginFsUtils.hpp> #include <slic3r/plugin/PluginFsUtils.hpp>
#include <slic3r/plugin/PythonInterpreter.hpp> #include <slic3r/plugin/PythonInterpreter.hpp>
#include "plugin_test_utils.hpp"
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <algorithm> #include <algorithm>
@@ -25,32 +27,6 @@ namespace fs = boost::filesystem;
namespace { 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. // 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 // Shutting the interpreter down here, rather than leaving it to PythonInterpreter's static

View File

@@ -16,6 +16,8 @@ TEST_CASE("SlicingPipeline capability-type string maps round-trip", "[slicing_pi
#include "libslic3r/Point.hpp" #include "libslic3r/Point.hpp"
#include "libslic3r/ExPolygon.hpp" #include "libslic3r/ExPolygon.hpp"
#include "libslic3r/Surface.hpp" #include "libslic3r/Surface.hpp"
#include "test_utils.hpp"
#include "libslic3r/Layer.hpp" #include "libslic3r/Layer.hpp"
#include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp"
@@ -142,7 +144,7 @@ TEST_CASE("orca.slicing psGCodePostProcess context: file edit in place + config
import_orca_module(); import_orca_module();
py::gil_scoped_acquire gil; 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()); boost::nowide::ofstream ofs(gpath.string());
ofs << "; header\nG1 X0 Y0\n"; ofs << "; header\nG1 X0 Y0\n";
@@ -196,9 +198,7 @@ _pp_result = Stamp().execute(_pp_ctx)
boost::nowide::ifstream ifs(gpath.string()); boost::nowide::ifstream ifs(gpath.string());
std::stringstream ss; ss << ifs.rdbuf(); contents = ss.str(); std::stringstream ss; ss << ifs.rdbuf(); contents = ss.str();
} }
CHECK(contents.find("; stamped by File") != std::string::npos); CHECK(contents.find("; stamped by File") != std::string::npos);}
fs::remove(gpath);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Toolpath helpers for the raw-graph tests. // Toolpath helpers for the raw-graph tests.

View File

@@ -27,26 +27,47 @@ inline Slic3r::TriangleMesh load_model(const std::string &obj_filename)
return mesh; 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 // Scoped temporary paths
// 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 // Owns a unique path under the system temp dir, "<prefix>-<unique>[<extension>]"
// (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: public:
explicit ScopedTemporaryFile(const std::string &extension = ".tmp") explicit ScopedTemporaryFile(const std::string &extension = ".tmp")
: m_path(boost::filesystem::temp_directory_path() : ScopedTemporaryPath("orca", extension) {}
/ boost::filesystem::unique_path("orca-%%%%-%%%%-%%%%" + extension))
{}
~ScopedTemporaryFile() { boost::system::error_code ec; boost::filesystem::remove(m_path, ec); } ~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; } // A temp directory created on construction and removed recursively on scope exit.
std::string string() const { return m_path.string(); } class ScopedTemporaryDir : public ScopedTemporaryPath
{
private: public:
boost::filesystem::path m_path; 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::path dir = boost::filesystem::temp_directory_path()
/ boost::filesystem::unique_path("orca-test-artifacts-%%%%-%%%%"); / boost::filesystem::unique_path("orca-test-artifacts-%%%%-%%%%");
boost::filesystem::create_directories(dir); 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; return dir;
}(); }();
boost::filesystem::path full = root / name; 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. // 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 #ifndef NDEBUG
mesh.WriteOBJFile(debug_artifact_path(name).c_str()); mesh.WriteOBJFile(debug_artifact_path(name).c_str());
#else
(void) name; (void) mesh;
#endif #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 #ifndef NDEBUG
its_write_obj(its, debug_artifact_path(name).c_str()); its_write_obj(its, debug_artifact_path(name).c_str());
#else
(void) name; (void) its;
#endif #endif
} }
// Dump a mesh as ASCII STL. // 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 #ifndef NDEBUG
mesh.write_ascii(debug_artifact_path(name).c_str()); mesh.write_ascii(debug_artifact_path(name).c_str());
#else
(void) name; (void) mesh;
#endif #endif
} }
// Draw an SVG artifact through a callback that receives the open SVG. Second // Draw an SVG artifact through a callback that receives the open SVG. Second
// overload takes a BoundingBox when the drawing needs one. // overload takes a BoundingBox when the drawing needs one.
template<class Draw> template<class Draw>
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 #ifndef NDEBUG
Slic3r::SVG svg(debug_artifact_path(name)); Slic3r::SVG svg(debug_artifact_path(name));
draw(svg); draw(svg);
svg.Close(); svg.Close();
#else
(void) name; (void) draw;
#endif #endif
} }
template<class Draw> template<class Draw>
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 #ifndef NDEBUG
Slic3r::SVG svg(debug_artifact_path(name), bbox); Slic3r::SVG svg(debug_artifact_path(name), bbox);
draw(svg); draw(svg);
svg.Close(); svg.Close();
#else
(void) name; (void) bbox; (void) draw;
#endif #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 // artifact. operator<< is resolved by ADL at the call site, so this header needn't
// include the producer's headers. // include the producer's headers.
template<class Produce> template<class Produce>
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 #ifndef NDEBUG
std::ofstream out(debug_artifact_path(name), std::ios::out | std::ios::binary); std::ofstream out(debug_artifact_path(name), std::ios::out | std::ios::binary);
out << produce(); out << produce();
#else
(void) name; (void) produce;
#endif #endif
} }