mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 21:51:06 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
722af0df61 | ||
|
|
4c2ae6d202 | ||
|
|
ac022b56ad | ||
|
|
b9a5556724 | ||
|
|
d3699dd9f6 | ||
|
|
a315a96c5c | ||
|
|
24cf1c6bcc |
No files matched your search
@@ -10657,6 +10657,9 @@ msgstr ""
|
|||||||
msgid "Send Failed"
|
msgid "Send Failed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No available printers"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "The name length exceeds the limit."
|
msgid "The name length exceeds the limit."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@@ -988,7 +988,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
|
|
||||||
MetadataList metadata;
|
MetadataList metadata;
|
||||||
VolumeMetadataList volumes;
|
VolumeMetadataList volumes;
|
||||||
bool auto_drop { true };
|
std::optional<bool> auto_drop;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CutObjectInfo
|
struct CutObjectInfo
|
||||||
@@ -1208,6 +1208,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
//IdToGeometryMap m_orig_geometries; // backup & restore
|
//IdToGeometryMap m_orig_geometries; // backup & restore
|
||||||
CurrentConfig m_curr_config;
|
CurrentConfig m_curr_config;
|
||||||
IdToMetadataMap m_objects_metadata;
|
IdToMetadataMap m_objects_metadata;
|
||||||
|
std::set<int> m_items_auto_drop_off; // object ids with a build item auto_drop="0" (2.4.x layout)
|
||||||
IdToCutObjectInfoMap m_cut_object_infos;
|
IdToCutObjectInfoMap m_cut_object_infos;
|
||||||
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
IdToLayerHeightsProfileMap m_layer_heights_profiles;
|
||||||
IdToLayerConfigRangesMap m_layer_config_ranges;
|
IdToLayerConfigRangesMap m_layer_config_ranges;
|
||||||
@@ -1463,6 +1464,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
m_objects.clear();
|
m_objects.clear();
|
||||||
m_instances.clear();
|
m_instances.clear();
|
||||||
m_objects_metadata.clear();
|
m_objects_metadata.clear();
|
||||||
|
m_items_auto_drop_off.clear();
|
||||||
m_curr_metadata_name.clear();
|
m_curr_metadata_name.clear();
|
||||||
m_curr_characters.clear();
|
m_curr_characters.clear();
|
||||||
|
|
||||||
@@ -1502,6 +1504,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
m_curr_config.object_id = -1;
|
m_curr_config.object_id = -1;
|
||||||
m_curr_config.volume_id = -1;
|
m_curr_config.volume_id = -1;
|
||||||
m_objects_metadata.clear();
|
m_objects_metadata.clear();
|
||||||
|
m_items_auto_drop_off.clear();
|
||||||
m_layer_heights_profiles.clear();
|
m_layer_heights_profiles.clear();
|
||||||
m_layer_config_ranges.clear();
|
m_layer_config_ranges.clear();
|
||||||
m_brim_ear_points.clear();
|
m_brim_ear_points.clear();
|
||||||
@@ -2257,11 +2260,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
ObjectMetadata::VolumeMetadataList volumes;
|
ObjectMetadata::VolumeMetadataList volumes;
|
||||||
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr;
|
||||||
|
|
||||||
|
// 2.4.x stored auto_drop on build items, shared by all instances of an object; any "off" wins
|
||||||
|
const bool legacy_auto_drop = !m_items_auto_drop_off.count(object.first.second);
|
||||||
|
|
||||||
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
|
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second);
|
||||||
if (obj_metadata != m_objects_metadata.end()) {
|
if (obj_metadata != m_objects_metadata.end()) {
|
||||||
// config data has been found, this model was saved using slic3r pe
|
// config data has been found, this model was saved using slic3r pe
|
||||||
|
|
||||||
model_object->auto_drop = obj_metadata->second.auto_drop;
|
model_object->auto_drop = obj_metadata->second.auto_drop.value_or(legacy_auto_drop);
|
||||||
|
|
||||||
// apply object's name and config data
|
// apply object's name and config data
|
||||||
for (const Metadata& metadata : obj_metadata->second.metadata) {
|
for (const Metadata& metadata : obj_metadata->second.metadata) {
|
||||||
@@ -2279,6 +2285,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// config data not found, this model was not saved using slic3r pe
|
// config data not found, this model was not saved using slic3r pe
|
||||||
|
model_object->auto_drop = legacy_auto_drop;
|
||||||
|
|
||||||
// add the entire geometry as the single volume to generate
|
// add the entire geometry as the single volume to generate
|
||||||
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
|
//volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
|
||||||
@@ -4121,6 +4128,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
|
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
|
||||||
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
|
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
|
||||||
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
|
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
|
||||||
|
if (const char* auto_drop = bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR); auto_drop != nullptr && !::atoi(auto_drop))
|
||||||
|
m_items_auto_drop_off.insert(object_id);
|
||||||
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
|
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4489,7 +4498,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||||||
// int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
|
// int instances_count_id = bbs_get_attribute_value_int(attributes, num_attributes, INSTANCESCOUNT_ATTR);
|
||||||
|
|
||||||
ObjectMetadata metadata;
|
ObjectMetadata metadata;
|
||||||
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
if (bbs_get_attribute_value_charptr(attributes, num_attributes, AUTO_DROP_ATTR) != nullptr)
|
||||||
|
metadata.auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
|
||||||
m_objects_metadata.insert({ object_id, std::move(metadata) });
|
m_objects_metadata.insert({ object_id, std::move(metadata) });
|
||||||
m_curr_config.object_id = object_id;
|
m_curr_config.object_id = object_id;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -756,7 +756,14 @@ public:
|
|||||||
Preset& get_edited_preset() { return m_edited_preset; }
|
Preset& get_edited_preset() { return m_edited_preset; }
|
||||||
const Preset& get_edited_preset() const { return m_edited_preset; }
|
const Preset& get_edited_preset() const { return m_edited_preset; }
|
||||||
|
|
||||||
const Preset& get_selected_preset_base() const { return *get_preset_base(m_presets[m_idx_selected]); }
|
// The root of the selected preset's inherits chain, or the selected preset itself when
|
||||||
|
// the chain names a preset that is not loaded (e.g. a vendor that is not installed).
|
||||||
|
const Preset& get_selected_preset_base() const
|
||||||
|
{
|
||||||
|
const Preset &selected = m_presets[m_idx_selected];
|
||||||
|
const Preset *base = get_preset_base(selected);
|
||||||
|
return base ? *base : selected;
|
||||||
|
}
|
||||||
|
|
||||||
// Return the last saved preset.
|
// Return the last saved preset.
|
||||||
// const Preset& get_saved_preset() const { return m_saved_preset; }
|
// const Preset& get_saved_preset() const { return m_saved_preset; }
|
||||||
|
|||||||
@@ -3151,7 +3151,7 @@ void CreatePrinterPresetDialog::set_current_visible_printer()
|
|||||||
m_printer_name_to_preset.clear();
|
m_printer_name_to_preset.clear();
|
||||||
for (const Preset &printer_preset : printer_presets) {
|
for (const Preset &printer_preset : printer_presets) {
|
||||||
if (!printer_preset.is_visible) continue;
|
if (!printer_preset.is_visible) continue;
|
||||||
if (preset_bundle->printers.get_preset_base(printer_preset)->name != printer_preset.name) continue;
|
if (preset_bundle->printers.get_preset_base(printer_preset) != &printer_preset) continue;
|
||||||
if (auto printer_model = dynamic_cast<ConfigOptionString*>(const_cast<Preset&>(printer_preset).config.option("printer_model", false))) {
|
if (auto printer_model = dynamic_cast<ConfigOptionString*>(const_cast<Preset&>(printer_preset).config.option("printer_model", false))) {
|
||||||
if (m_printer_name_to_preset.find(printer_model->value) == m_printer_name_to_preset.end()) {
|
if (m_printer_name_to_preset.find(printer_model->value) == m_printer_name_to_preset.end()) {
|
||||||
printer_choice.push_back(from_u8(printer_model->value));
|
printer_choice.push_back(from_u8(printer_model->value));
|
||||||
@@ -4962,7 +4962,7 @@ void CreatePresetForPrinterDialog::get_visible_printer_and_compatible_filament_p
|
|||||||
if (filament_preset.is_default || !filament_preset.is_compatible || filament_preset.is_project_embedded) continue;
|
if (filament_preset.is_default || !filament_preset.is_compatible || filament_preset.is_project_embedded) continue;
|
||||||
ConfigOptionStrings *filament_types;
|
ConfigOptionStrings *filament_types;
|
||||||
const Preset * filament_preset_base = m_preset_bundle->filaments.get_preset_base(filament_preset);
|
const Preset * filament_preset_base = m_preset_bundle->filaments.get_preset_base(filament_preset);
|
||||||
if (filament_preset_base == &filament_preset) {
|
if (filament_preset_base == &filament_preset || filament_preset_base == nullptr) {
|
||||||
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(&filament_preset)->config.option("filament_type"));
|
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(&filament_preset)->config.option("filament_type"));
|
||||||
} else {
|
} else {
|
||||||
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(filament_preset_base)->config.option("filament_type"));
|
filament_types = dynamic_cast<ConfigOptionStrings *>(const_cast<Preset *>(filament_preset_base)->config.option("filament_type"));
|
||||||
|
|||||||
@@ -4087,7 +4087,7 @@ void SelectMachineDialog::update_user_printer()
|
|||||||
{
|
{
|
||||||
m_printer_last_select = "";
|
m_printer_last_select = "";
|
||||||
update_select_layout(nullptr);
|
update_select_layout(nullptr);
|
||||||
m_printer_box->GetPrinterComboBox()->SetTextLabel("");
|
m_printer_box->GetPrinterComboBox()->SetValue(_L("No available printers"));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
||||||
|
|||||||
@@ -1229,7 +1229,7 @@ void SendToPrinterDialog::update_user_printer()
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_printer_last_select = "";
|
m_printer_last_select = "";
|
||||||
m_comboBox_printer->SetTextLabel("");
|
m_comboBox_printer->SetValue(_L("No available printers"));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "for send task, current printer id = " << m_printer_last_select << std::endl;
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ void ComboBox::DoDeleteOneItem(unsigned int pos)
|
|||||||
|
|
||||||
void ComboBox::ForceDropdownOpen()
|
void ComboBox::ForceDropdownOpen()
|
||||||
{
|
{
|
||||||
if (!IsEnabled())
|
if (!IsEnabled() || items.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!drop_down) {
|
if (!drop_down) {
|
||||||
@@ -398,7 +398,7 @@ void ComboBox::mouseDown(wxMouseEvent &event)
|
|||||||
SetFocus();
|
SetFocus();
|
||||||
if (drop_down) {
|
if (drop_down) {
|
||||||
drop.Hide();
|
drop.Hide();
|
||||||
} else if (drop.HasDismissLongTime()) {
|
} else if (!items.empty() && drop.HasDismissLongTime()) { // DropDown paints nothing for an empty list
|
||||||
drop.autoPosition();
|
drop.autoPosition();
|
||||||
drop_down = true;
|
drop_down = true;
|
||||||
drop.Popup(&drop);
|
drop.Popup(&drop);
|
||||||
@@ -426,7 +426,7 @@ void ComboBox::keyDown(wxKeyEvent& event)
|
|||||||
case WXK_SPACE:
|
case WXK_SPACE:
|
||||||
if (drop_down) {
|
if (drop_down) {
|
||||||
drop.DismissAndNotify();
|
drop.DismissAndNotify();
|
||||||
} else if (drop.HasDismissLongTime()) {
|
} else if (!items.empty() && drop.HasDismissLongTime()) {
|
||||||
drop.autoPosition();
|
drop.autoPosition();
|
||||||
drop_down = true;
|
drop_down = true;
|
||||||
drop.Popup();
|
drop.Popup();
|
||||||
|
|||||||
@@ -296,16 +296,18 @@ void PluginAuditManager::add_denied_filename(const std::string& filename)
|
|||||||
std::vector<std::string> PluginAuditManager::default_denied_filenames()
|
std::vector<std::string> PluginAuditManager::default_denied_filenames()
|
||||||
{
|
{
|
||||||
// AppConfig::config_path() picks .conf vs .ini on the USE_JSON_CONFIG ifdef and the app key
|
// AppConfig::config_path() picks .conf vs .ini on the USE_JSON_CONFIG ifdef and the app key
|
||||||
// on its mode, and is a non-static member we cannot call without an instance. Denying all
|
// on its mode, and is a non-static member we cannot call without an instance. Denying all
|
||||||
// four names is cheaper and more robust than replicating that; the two unused names cost one
|
// four names is cheaper and more robust than replicating that; the two unused names cost one
|
||||||
// string comparison each. Single-sourced here so install_hook() and the tests seed from the
|
// string comparison each. Keep the plugin install-state sidecar here too: it controls which
|
||||||
// same list and cannot drift apart.
|
// packages load on startup and must remain host-managed. Single-sourced so install_hook() and
|
||||||
|
// the tests seed from the same list and cannot drift apart.
|
||||||
return {
|
return {
|
||||||
SLIC3R_APP_KEY ".conf",
|
SLIC3R_APP_KEY ".conf",
|
||||||
GCODEVIEWER_APP_KEY ".conf",
|
GCODEVIEWER_APP_KEY ".conf",
|
||||||
SLIC3R_APP_KEY ".ini",
|
SLIC3R_APP_KEY ".ini",
|
||||||
GCODEVIEWER_APP_KEY ".ini",
|
GCODEVIEWER_APP_KEY ".ini",
|
||||||
secret_constants::USER_SECRET_FILENAME,
|
secret_constants::USER_SECRET_FILENAME,
|
||||||
|
".install_state.json",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1076,8 +1078,9 @@ void PluginAuditManager::install_hook()
|
|||||||
// "cert" denied-path keyword seeded below.
|
// "cert" denied-path keyword seeded below.
|
||||||
add_global_allowed_root(resources_dir(), /*allow_write=*/false);
|
add_global_allowed_root(resources_dir(), /*allow_write=*/false);
|
||||||
|
|
||||||
// The user's app config and cloud credentials live directly inside data_dir(), so the
|
// The user's app config and cloud credentials live directly inside data_dir(), and plugin
|
||||||
// root just granted would otherwise expose them to any plugin. Deny them by name.
|
// install state lives beneath it, so the root just granted would otherwise expose them to
|
||||||
|
// plugins. Deny them by name.
|
||||||
//
|
//
|
||||||
// Seeded here rather than by each secret's owner because install_hook() runs during lazy
|
// Seeded here rather than by each secret's owner because install_hook() runs during lazy
|
||||||
// interpreter init and therefore provably precedes any plugin bytecode, whereas
|
// interpreter init and therefore provably precedes any plugin bytecode, whereas
|
||||||
|
|||||||
@@ -584,6 +584,19 @@ bool PythonInterpreter::initialize()
|
|||||||
}
|
}
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Program name set successfully";
|
BOOST_LOG_TRIVIAL(debug) << "Program name set successfully";
|
||||||
|
|
||||||
|
// Keep bytecode out of the Python home. The shipped .pyc files are stale against their
|
||||||
|
// sources, so a writable install would rewrite them inside the app (breaking a signed
|
||||||
|
// macOS bundle's seal) and a read-only one would recompile them on every launch.
|
||||||
|
const std::string pycache_prefix = data_dir() + "/python/pycache";
|
||||||
|
#ifdef _WIN32
|
||||||
|
status = PyConfig_SetString(&config, &config.pycache_prefix, boost::nowide::widen(pycache_prefix).c_str());
|
||||||
|
#else
|
||||||
|
status = PyConfig_SetBytesString(&config, &config.pycache_prefix, pycache_prefix.c_str());
|
||||||
|
#endif
|
||||||
|
if (PyStatus_Exception(status))
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "Failed to set the Python bytecode cache to " << pycache_prefix << ": "
|
||||||
|
<< (status.err_msg ? status.err_msg : "unknown error");
|
||||||
|
|
||||||
// Use pybind11's scoped_interpreter which properly initializes
|
// Use pybind11's scoped_interpreter which properly initializes
|
||||||
// pybind11 internals for multi-threaded use — raw Py_InitializeFromConfig
|
// pybind11 internals for multi-threaded use — raw Py_InitializeFromConfig
|
||||||
// does not set up thread state tracking that pybind11 requires.
|
// does not set up thread state tracking that pybind11 requires.
|
||||||
|
|||||||
@@ -505,6 +505,28 @@ TEST_CASE("A project whose components reference themselves fails to load", "[3mf
|
|||||||
REQUIRE_FALSE(loaded);
|
REQUIRE_FALSE(loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("A project in the 2.4.x layout keeps auto drop off from its build item", "[3mf][Regression]")
|
||||||
|
{
|
||||||
|
// 2.4.x wrote auto_drop on the build item and left it off the object, so the object is re-laid out here.
|
||||||
|
ScopedTemporaryFile temp(".3mf");
|
||||||
|
{
|
||||||
|
Model model;
|
||||||
|
ModelObject* object = model.add_object();
|
||||||
|
object->add_volume(make_cube(10., 10., 10.));
|
||||||
|
object->add_instance();
|
||||||
|
object->auto_drop = false;
|
||||||
|
store_project(model, temp.string(), SaveStrategy::Zip64 | SaveStrategy::Silence);
|
||||||
|
}
|
||||||
|
REQUIRE(replace_in_3mf_entry(temp.string(), "model_settings.config", " auto_drop=\"0\"", ""));
|
||||||
|
REQUIRE(replace_in_3mf_entry(temp.string(), "3dmodel.model", "printable=\"1\"/>", "printable=\"1\" auto_drop=\"0\"/>"));
|
||||||
|
|
||||||
|
ScopedTemporaryDir backup_dir("orca_auto_drop_dst");
|
||||||
|
Model model;
|
||||||
|
REQUIRE(load_project(temp.string(), model, backup_dir));
|
||||||
|
REQUIRE(model.objects.size() == 1);
|
||||||
|
CHECK_FALSE(model.objects.front()->auto_drop);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("An object loads up to the component reference budget and fails past it", "[3mf][Regression]")
|
TEST_CASE("An object loads up to the component reference budget and fails past it", "[3mf][Regression]")
|
||||||
{
|
{
|
||||||
// The importer queues at most 100000 component references per object. Every reference besides the
|
// The importer queues at most 100000 component references per object. Every reference besides the
|
||||||
|
|||||||
@@ -1219,6 +1219,24 @@ TEST_CASE("find_preset2 auto-matches removed Generic vendor profiles to the libr
|
|||||||
CHECK(bundle.filaments.find_preset2("BrandX Generic PETG", /*auto_match=*/true) == nullptr);
|
CHECK(bundle.filaments.find_preset2("BrandX Generic PETG", /*auto_match=*/true) == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("The selected preset is its own base when its parent is not loaded", "[Preset]")
|
||||||
|
{
|
||||||
|
PresetCollection printers(Preset::TYPE_PRINTER, Preset::printer_options(),
|
||||||
|
static_cast<const PrintRegionConfig &>(FullPrintConfig::defaults()));
|
||||||
|
add_inmemory_preset(printers, "Printer");
|
||||||
|
add_inmemory_preset(printers, "User Printer", "Printer");
|
||||||
|
add_inmemory_preset(printers, "Orphan Printer", "Uninstalled Vendor Printer");
|
||||||
|
|
||||||
|
printers.select_preset_by_name("User Printer", true);
|
||||||
|
REQUIRE(printers.get_selected_preset_name() == "User Printer");
|
||||||
|
CHECK(printers.get_selected_preset_base().name == "Printer");
|
||||||
|
|
||||||
|
printers.select_preset_by_name("Orphan Printer", true);
|
||||||
|
REQUIRE(printers.get_selected_preset_name() == "Orphan Printer");
|
||||||
|
REQUIRE(printers.get_preset_base(printers.get_selected_preset()) == nullptr);
|
||||||
|
CHECK(printers.get_selected_preset_base().name == "Orphan Printer");
|
||||||
|
}
|
||||||
|
|
||||||
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]")
|
||||||
{
|
{
|
||||||
ScopedTemporaryDir temp_dir;
|
ScopedTemporaryDir temp_dir;
|
||||||
|
|||||||
@@ -150,6 +150,13 @@ public:
|
|||||||
return m_close_frames;
|
return m_close_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The printer answers a close frame before its own read completes, so the agent's stop can return first.
|
||||||
|
bool wait_for_close_frames(int count)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(m_mutex);
|
||||||
|
return m_changed.wait_for(lock, 10s, [&] { return m_close_frames >= count; });
|
||||||
|
}
|
||||||
|
|
||||||
// Hangs up every connection that is waiting.
|
// Hangs up every connection that is waiting.
|
||||||
void release()
|
void release()
|
||||||
{
|
{
|
||||||
@@ -277,6 +284,7 @@ private:
|
|||||||
if (ec == websocket::error::closed) {
|
if (ec == websocket::error::closed) {
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
++m_close_frames;
|
++m_close_frames;
|
||||||
|
m_changed.notify_all();
|
||||||
}
|
}
|
||||||
if (ec)
|
if (ec)
|
||||||
reading = false;
|
reading = false;
|
||||||
@@ -449,6 +457,7 @@ TEST_CASE("Stopping the status stream sends the printer a close frame", "[Moonra
|
|||||||
REQUIRE(f.printer.wait_for_ready_connections(1));
|
REQUIRE(f.printer.wait_for_ready_connections(1));
|
||||||
|
|
||||||
CHECK(f.stops_within(3s));
|
CHECK(f.stops_within(3s));
|
||||||
|
CHECK(f.printer.wait_for_close_frames(1));
|
||||||
CHECK(f.printer.close_frames() == 1);
|
CHECK(f.printer.close_frames() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ void seed_denied_keywords()
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Plugin audit denies app config and token filenames anywhere", "[audit]")
|
TEST_CASE("Plugin audit denies host-managed filenames anywhere", "[audit]")
|
||||||
{
|
{
|
||||||
seed_denied_names();
|
seed_denied_names();
|
||||||
const PluginAuditManager& mgr = PluginAuditManager::instance();
|
const PluginAuditManager& mgr = PluginAuditManager::instance();
|
||||||
@@ -55,6 +55,7 @@ TEST_CASE("Plugin audit denies app config and token filenames anywhere", "[audit
|
|||||||
CHECK(mgr.is_denied_filename(fs::path(SLIC3R_APP_KEY ".ini")));
|
CHECK(mgr.is_denied_filename(fs::path(SLIC3R_APP_KEY ".ini")));
|
||||||
CHECK(mgr.is_denied_filename(fs::path(GCODEVIEWER_APP_KEY ".ini")));
|
CHECK(mgr.is_denied_filename(fs::path(GCODEVIEWER_APP_KEY ".ini")));
|
||||||
CHECK(mgr.is_denied_filename(fs::path(secret_constants::USER_SECRET_FILENAME)));
|
CHECK(mgr.is_denied_filename(fs::path(secret_constants::USER_SECRET_FILENAME)));
|
||||||
|
CHECK(mgr.is_denied_filename(fs::path(".install_state.json")));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("companions holding the same secrets are denied by the prefix rule")
|
SECTION("companions holding the same secrets are denied by the prefix rule")
|
||||||
@@ -110,6 +111,7 @@ TEST_CASE("Plugin audit deny beats allowed roots", "[audit]")
|
|||||||
|
|
||||||
const fs::path conf = fs::path(data_dir()) / (SLIC3R_APP_KEY ".conf");
|
const fs::path conf = fs::path(data_dir()) / (SLIC3R_APP_KEY ".conf");
|
||||||
const fs::path token = fs::path(data_dir()) / secret_constants::USER_SECRET_FILENAME;
|
const fs::path token = fs::path(data_dir()) / secret_constants::USER_SECRET_FILENAME;
|
||||||
|
const fs::path install_state = fs::path(data_dir()) / "plugins" / "example" / ".install_state.json";
|
||||||
|
|
||||||
SECTION("a non-denied file inside the allowed root is writable (root really grants writes)")
|
SECTION("a non-denied file inside the allowed root is writable (root really grants writes)")
|
||||||
{
|
{
|
||||||
@@ -150,6 +152,17 @@ TEST_CASE("Plugin audit deny beats allowed roots", "[audit]")
|
|||||||
CHECK_FALSE(decision.allowed);
|
CHECK_FALSE(decision.allowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("plugin install state is blocked for reads and writes despite the allowed root")
|
||||||
|
{
|
||||||
|
AuditDecision read_decision = mgr.check_open(install_state.string(), "r");
|
||||||
|
CHECK_FALSE(read_decision.allowed);
|
||||||
|
CHECK(read_decision.reason == "denied filename");
|
||||||
|
|
||||||
|
AuditDecision write_decision = mgr.check_open(install_state.string(), "w");
|
||||||
|
CHECK_FALSE(write_decision.allowed);
|
||||||
|
CHECK(write_decision.reason == "denied filename");
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("a traversal path resolving to the config is blocked")
|
SECTION("a traversal path resolving to the config is blocked")
|
||||||
{
|
{
|
||||||
const fs::path traversal = fs::path(data_dir()) / "plugins" / ".." / (SLIC3R_APP_KEY ".conf");
|
const fs::path traversal = fs::path(data_dir()) / "plugins" / ".." / (SLIC3R_APP_KEY ".conf");
|
||||||
|
|||||||
@@ -615,8 +615,22 @@ TEST_CASE("Python module release removes package submodules and owned sys.path",
|
|||||||
|
|
||||||
// Ensure the next import executes the new submodule rather than reusing a stale package child.
|
// Ensure the next import executes the new submodule rather than reusing a stale package child.
|
||||||
write_package("'new'");
|
write_package("'new'");
|
||||||
boost::system::error_code ec;
|
{
|
||||||
fs::remove_all(package_root / "__pycache__", ec);
|
// The rewrite can keep the old mtime and size, so drop the bytecode wherever the
|
||||||
|
// interpreter caches it.
|
||||||
|
PythonGILState gil;
|
||||||
|
REQUIRE(gil);
|
||||||
|
PyObjectPtr util(PyImport_ImportModule("importlib.util"));
|
||||||
|
REQUIRE(util);
|
||||||
|
for (const char* file : {"__init__.py", "sub.py"}) {
|
||||||
|
PyObjectPtr source(PyUnicode_DecodeFSDefault((package_root / file).string().c_str()));
|
||||||
|
REQUIRE(source);
|
||||||
|
PyObjectPtr cached(PyObject_CallMethod(util.get(), "cache_from_source", "O", source.get()));
|
||||||
|
REQUIRE(cached);
|
||||||
|
boost::system::error_code ec;
|
||||||
|
fs::remove(fs::path(PyUnicode_AsUTF8(cached.get())), ec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
paths.clear();
|
paths.clear();
|
||||||
modules.clear();
|
modules.clear();
|
||||||
|
|||||||
Reference in new issue
Block a user