mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-11 19:17:33 +00:00
Fix conflicts. Update unit tests
This commit is contained in:
@@ -46,8 +46,6 @@ class Button : public StaticBox
|
|||||||
bool isCenter = true;
|
bool isCenter = true;
|
||||||
bool vertical = false;
|
bool vertical = false;
|
||||||
|
|
||||||
wxTipWindow* tipWindow = nullptr;
|
|
||||||
|
|
||||||
static const int buttonWidth = 200;
|
static const int buttonWidth = 200;
|
||||||
static const int buttonHeight = 50;
|
static const int buttonHeight = 50;
|
||||||
|
|
||||||
|
|||||||
@@ -795,12 +795,10 @@ TEST_CASE("Published 3MF applies positional material keys onto the receiver's ma
|
|||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "full publish" slot serializes the whole filament. On load the slot is matched positionally
|
// A "full publish" slot serializes the whole filament. On load the slot always receives a
|
||||||
// against the published type: a matching receiver type still receives the author's full values
|
// standalone detached copy of the author's material - created even when the receiver's own
|
||||||
// (like a normal save/load of the filament), a mismatched type replaces it with the first
|
// material matches the published type - and no receiver library preset is ever mutated.
|
||||||
// same-type visible preset (applying the author's full values on top), and a type not in the
|
TEST_CASE("Published 3MF full-published slots are imported as standalone detached copies", "[Preset][Bundle][Published]")
|
||||||
// receiver's library falls back to the first available visible preset.
|
|
||||||
TEST_CASE("Published 3MF full-published slots replace or ignore the receiver material by type", "[Preset][Bundle][Published]")
|
|
||||||
{
|
{
|
||||||
auto make_file_config = [] {
|
auto make_file_config = [] {
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
@@ -826,7 +824,7 @@ TEST_CASE("Published 3MF full-published slots replace or ignore the receiver mat
|
|||||||
return entry;
|
return entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("type match applies the full dump onto the receiver's material") {
|
SECTION("type match still creates a detached copy instead of mutating the receiver material") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
||||||
pla.config.opt_string("filament_type", 0u) = "PLA";
|
pla.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
@@ -843,14 +841,20 @@ TEST_CASE("Published 3MF full-published slots replace or ignore the receiver mat
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
// The type matches, but a full publish behaves like a normal save: the author's values
|
// The type matches, but the import still detaches: the slot lands on a fresh copy
|
||||||
// are written onto the slot's preset wholesale.
|
// named from the published type (no identity fields in this entry), carrying the
|
||||||
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
// author's values; the receiver's own material is untouched.
|
||||||
|
CHECK(bundle.filament_presets[0] == "PLA");
|
||||||
|
Preset *copy = bundle.filaments.find_preset("PLA", false, true);
|
||||||
|
REQUIRE(copy != nullptr);
|
||||||
|
CHECK(copy->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
CHECK(pub.material_replacements.empty());
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
|
CHECK(pub.material_replacements[0] == "slot 0: My PLA -> PLA (published material imported)");
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("type mismatch replaces the slot with the first same-type preset and applies the full dump") {
|
SECTION("type mismatch also detaches: a fresh same-type copy replaces the slot") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
||||||
pla.config.opt_string("filament_type", 0u) = "PLA";
|
pla.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
@@ -868,19 +872,20 @@ TEST_CASE("Published 3MF full-published slots replace or ignore the receiver mat
|
|||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
REQUIRE(bundle.filament_presets.size() == 1);
|
REQUIRE(bundle.filament_presets.size() == 1);
|
||||||
// The slot is re-pointed at the library's ABS preset and the author's full values are
|
// No substitution search runs: a brand-new copy named after the published type is
|
||||||
// written onto it in place (the original 0.3 is overwritten); the receiver's own
|
// created and both pre-existing presets stay exactly as they were.
|
||||||
// material is untouched.
|
CHECK(bundle.filament_presets[0] == "ABS");
|
||||||
CHECK(bundle.filament_presets[0] == "My ABS");
|
Preset *copy = bundle.filaments.find_preset("ABS", false, true);
|
||||||
CHECK(bundle.filaments.find_preset("My ABS", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
REQUIRE(copy != nullptr);
|
||||||
|
CHECK(copy->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(bundle.filaments.find_preset("My ABS", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.3 });
|
||||||
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
// The entry carries no identity fields, so the pick cannot be judged as a substitute.
|
CHECK(pub.material_replacements[0] == "slot 0: My PLA -> ABS (published material imported)");
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PLA -> My ABS");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("no same-type match falls back to the first available visible preset") {
|
SECTION("the author's identity rides on the copy when the type has no library match") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
||||||
pla.config.opt_string("filament_type", 0u) = "PLA";
|
pla.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
@@ -891,8 +896,8 @@ TEST_CASE("Published 3MF full-published slots replace or ignore the receiver mat
|
|||||||
bundle.filament_presets = { "My PLA" };
|
bundle.filament_presets = { "My PLA" };
|
||||||
|
|
||||||
PublishedMaterialEntry full = make_full_abs_entry();
|
PublishedMaterialEntry full = make_full_abs_entry();
|
||||||
// The dump carries the identity too, so the fallback preset must take the author's type
|
// The dump carries the identity too, so the created copy takes the author's type
|
||||||
// and vendor.
|
// and vendor instead of the baseline clone's.
|
||||||
full.full_keys = { "filament_retraction_length", "filament_type", "filament_vendor" };
|
full.full_keys = { "filament_retraction_length", "filament_type", "filament_vendor" };
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
@@ -904,97 +909,27 @@ TEST_CASE("Published 3MF full-published slots replace or ignore the receiver mat
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
// No ABS in the library: the slot falls back to the first available visible preset (the
|
// No ABS preset needs to exist in the library: the copy carries the author's values,
|
||||||
// unused "Other PLA") and the author's full values are written onto it, type and vendor
|
// type and vendor included. Neither receiver preset was touched.
|
||||||
// included. The receiver's own material is untouched.
|
CHECK(bundle.filament_presets[0] == "ABS");
|
||||||
CHECK(bundle.filament_presets[0] == "Other PLA");
|
Preset *copy = bundle.filaments.find_preset("ABS", false, true);
|
||||||
Preset *fallback = bundle.filaments.find_preset("Other PLA", false, true);
|
REQUIRE(copy != nullptr);
|
||||||
REQUIRE(fallback != nullptr);
|
CHECK(copy->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
CHECK(fallback->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
CHECK(copy->config.opt_string("filament_type", 0u) == "ABS");
|
||||||
CHECK(fallback->config.opt_string("filament_type", 0u) == "ABS");
|
CHECK(copy->config.opt_string("filament_vendor", 0u) == "Generic");
|
||||||
CHECK(fallback->config.opt_string("filament_vendor", 0u) == "Generic");
|
|
||||||
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt_string("filament_type", 0u) == "PLA");
|
CHECK(bundle.filaments.find_preset("My PLA", false, true)->config.opt_string("filament_type", 0u) == "PLA");
|
||||||
|
CHECK(bundle.filaments.find_preset("Other PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.7 });
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PLA -> Other PLA (substitute: no ABS available)");
|
CHECK(pub.material_replacements[0] == "slot 0: My PLA -> ABS (published material imported)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regression for the author's published material being skipped by a type-only replacement
|
// The author's preset name travels in the file and names the created standalone copy (variant
|
||||||
// search: the slot must prefer the exact published material (filament_id) over the first other
|
// tail stripped), regardless of what the receiver's library holds: an exact-name library preset
|
||||||
// same-type preset, even when the exact preset is already referenced by another slot.
|
// is never reused nor mutated.
|
||||||
TEST_CASE("Published 3MF replaces a mismatched slot with the exact published material when available", "[Preset][Bundle][Published]")
|
TEST_CASE("Published 3MF imports a full material under the author's stripped name", "[Preset][Bundle][Published]")
|
||||||
{
|
|
||||||
auto make_file_config = [] {
|
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
|
||||||
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75 };
|
|
||||||
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1 };
|
|
||||||
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard" };
|
|
||||||
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000" };
|
|
||||||
config.opt<ConfigOptionStrings>("filament_type")->values = { "PLA" };
|
|
||||||
config.opt<ConfigOptionStrings>("filament_vendor")->values = { "Generic" };
|
|
||||||
config.opt<ConfigOptionStrings>("filament_ids")->values = { "GFL99" };
|
|
||||||
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.9 };
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
// The receiver's second slot already uses the exact published material.
|
|
||||||
Preset &generic_pla = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
|
||||||
generic_pla.filament_id = "GFL99";
|
|
||||||
generic_pla.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
generic_pla.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
generic_pla.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
// An unrelated PLA, unreferenced: a type-only search picks it because Generic PLA is
|
|
||||||
// referenced by slot 1.
|
|
||||||
Preset &bambu = add_inmemory_preset(bundle.filaments, "Bambu PLA Basic");
|
|
||||||
bambu.filament_id = "GFB00";
|
|
||||||
bambu.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
bambu.config.opt_string("filament_vendor", 0u) = "Bambu Lab";
|
|
||||||
bambu.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.4 };
|
|
||||||
bundle.filament_presets = { "My PETG", "Generic PLA" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.filament_id = "GFL99";
|
|
||||||
entry.filament_vendor = "Generic";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = make_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
// The exact published material (id GFL99) wins over the unreferenced type-only preset.
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA");
|
|
||||||
CHECK(bundle.filament_presets[1] == "Generic PLA");
|
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
|
||||||
// The receiver's own material is untouched; the unrelated PLA too.
|
|
||||||
CHECK(bundle.filaments.find_preset("My PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.6 });
|
|
||||||
CHECK(bundle.filaments.find_preset("Bambu PLA Basic", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.4 });
|
|
||||||
// Accepted mutate tradeoff: the shared exact-material preset was mutated, so slot 1 also
|
|
||||||
// carries the author's values (the leak is documented, not accidental).
|
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
|
||||||
// An exact-material pick is reported without a substitute qualifier.
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA");
|
|
||||||
CHECK(pub.skipped_keys.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// The author's preset name travels in the file, so a type mismatch resolves to the exact
|
|
||||||
// published material even when the identity fields are absent or stale (older files): a
|
|
||||||
// "Generic PLA" author must land on the receiver's "Generic PLA", never on a same-type
|
|
||||||
// substitute like "Bambu PLA Basic".
|
|
||||||
TEST_CASE("Published 3MF replaces a mismatched slot with the exact preset by name", "[Preset][Bundle][Published]")
|
|
||||||
{
|
{
|
||||||
auto make_file_config = [] {
|
auto make_file_config = [] {
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
@@ -1019,7 +954,7 @@ TEST_CASE("Published 3MF replaces a mismatched slot with the exact preset by nam
|
|||||||
return &preset;
|
return &preset;
|
||||||
};
|
};
|
||||||
|
|
||||||
SECTION("full identity (name, setting_id, filament_id): the name match wins") {
|
SECTION("an exact-name library preset exists: a detached copy is created beside it") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
@@ -1046,15 +981,18 @@ TEST_CASE("Published 3MF replaces a mismatched slot with the exact preset by nam
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @System");
|
// The copy is named after the stripped author name; the receiver's exact-name preset
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
// keeps its own values.
|
||||||
|
CHECK(bundle.filament_presets[0] == "Generic PLA");
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
CHECK(bundle.filaments.find_preset("My PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.6 });
|
CHECK(bundle.filaments.find_preset("My PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.6 });
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @System");
|
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA (published material imported)");
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("only the name is present (broken/stale ids): still the exact preset") {
|
SECTION("only the name is present (broken/stale ids): the copy is still created") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
@@ -1078,43 +1016,14 @@ TEST_CASE("Published 3MF replaces a mismatched slot with the exact preset by nam
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @System");
|
CHECK(bundle.filament_presets[0] == "Generic PLA");
|
||||||
CHECK(bundle.filaments.find_preset("Bambu PLA Basic @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
CHECK(bundle.filaments.find_preset("Bambu PLA Basic @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @System");
|
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA (published material imported)");
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("the exact preset is hidden in the library: the exact match still wins") {
|
SECTION("grown slot (author slot 1) receives its own detached copy") {
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
Preset &generic_pla = *add_pla(bundle, "Generic PLA @System", "OGFL99", "Generic", "RcBNzytWgwRrwXXz");
|
|
||||||
generic_pla.is_visible = false;
|
|
||||||
add_pla(bundle, "Bambu PLA Basic @System", "OGFA00", "Bambu Lab", "zkc85XTKi4cb6cOw");
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.preset_name = "Generic PLA @System";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = make_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @System");
|
|
||||||
CHECK(pub.skipped_keys.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("grown slot (author slot 1) is seeded with the exact preset by name") {
|
|
||||||
auto two_slot_config = [] {
|
auto two_slot_config = [] {
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75 };
|
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75 };
|
||||||
@@ -1153,53 +1062,27 @@ TEST_CASE("Published 3MF replaces a mismatched slot with the exact preset by nam
|
|||||||
|
|
||||||
REQUIRE(bundle.filament_presets.size() == 2);
|
REQUIRE(bundle.filament_presets.size() == 2);
|
||||||
CHECK(bundle.filament_presets[0] == "My PETG");
|
CHECK(bundle.filament_presets[0] == "My PETG");
|
||||||
CHECK(bundle.filament_presets[1] == "Generic PLA @System");
|
// The grown slot lands on a freshly created copy carrying the author's slot-1 value;
|
||||||
// The full dump applies the author's slot-1 value onto the grown slot's preset.
|
// the library preset that seeded it stays untouched.
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.8 });
|
CHECK(bundle.filament_presets[1] == "Generic PLA");
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.8 });
|
||||||
}
|
CHECK(bundle.filaments.find_preset("Generic PLA @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
|
|
||||||
SECTION("no exact preset in the library: falls back to the substitute") {
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
add_pla(bundle, "Bambu PLA Basic @System", "OGFA00", "Bambu Lab", "zkc85XTKi4cb6cOw");
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.preset_name = "Generic PLA @System";
|
|
||||||
entry.filament_id = "OGFL99";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = make_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Bambu PLA Basic @System");
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Bambu PLA Basic @System (substitute: no exact material match)");
|
CHECK(pub.material_replacements[0] == "slot 1: Generic PLA @System -> Generic PLA (published material imported)");
|
||||||
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The receiver library can hold several presets that all look like "Generic PLA": a bare-named
|
// The stripped author name can collide with an existing library preset ("Generic PLA"): the
|
||||||
// legacy copy, the author's exact vendor preset and the Orca library preset (whose alias is the
|
// created copy must uniquify with the "(Published)" suffix rather than overwrite, reuse or
|
||||||
// bare name). The exact preset name must outrank the fuzzy bare/alias tier, so the author's
|
// mutate any of the receiver's own presets.
|
||||||
// "Generic PLA @Qidi Q2 0.4 nozzle" wins regardless of collection order.
|
TEST_CASE("Published 3MF uniquifies an imported full material name on collision", "[Preset][Bundle][Published]")
|
||||||
TEST_CASE("Published 3MF prefers the exact preset over same-bare-name presets", "[Preset][Bundle][Published]")
|
|
||||||
{
|
{
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
||||||
// A legacy bundle preset literally named "Generic PLA": bare-name match, sorts first.
|
// A legacy bundle preset literally named "Generic PLA" - collides with the stripped name.
|
||||||
Preset &bare = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
Preset &bare = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
||||||
bare.config.opt_string("filament_type", 0u) = "PLA";
|
bare.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
bare.config.opt_string("filament_vendor", 0u) = "Generic";
|
bare.config.opt_string("filament_vendor", 0u) = "Generic";
|
||||||
@@ -1209,7 +1092,7 @@ TEST_CASE("Published 3MF prefers the exact preset over same-bare-name presets",
|
|||||||
qidi.config.opt_string("filament_type", 0u) = "PLA";
|
qidi.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
qidi.config.opt_string("filament_vendor", 0u) = "Generic";
|
qidi.config.opt_string("filament_vendor", 0u) = "Generic";
|
||||||
qidi.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
qidi.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
||||||
// The Orca library preset: alias "Generic PLA" matches the bare-name tier too.
|
// The Orca library preset.
|
||||||
Preset &sys = add_inmemory_preset(bundle.filaments, "Generic PLA @System");
|
Preset &sys = add_inmemory_preset(bundle.filaments, "Generic PLA @System");
|
||||||
sys.config.opt_string("filament_type", 0u) = "PLA";
|
sys.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
sys.config.opt_string("filament_vendor", 0u) = "Generic";
|
sys.config.opt_string("filament_vendor", 0u) = "Generic";
|
||||||
@@ -1231,103 +1114,15 @@ TEST_CASE("Published 3MF prefers the exact preset over same-bare-name presets",
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
// The exact preset beats the bare/alias matches even though "Generic PLA" sorts first.
|
// The copy lands beside the collision, suffixed; every pre-existing preset keeps its own
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @Qidi Q2 0.4 nozzle");
|
// values.
|
||||||
|
CHECK(bundle.filament_presets[0] == "Generic PLA (Published)");
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA (Published)", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA @Qidi Q2 0.4 nozzle", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Generic PLA @System", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @Qidi Q2 0.4 nozzle");
|
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA (Published) (published material imported)");
|
||||||
CHECK(pub.skipped_keys.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// The author's exact preset exists on the receiver but is incompatible with the active printer
|
|
||||||
// (a Qidi bundle preset on a non-Qidi printer); a same-family preset that IS compatible must
|
|
||||||
// win instead, so the slot never ends up with a filament the printer cannot use.
|
|
||||||
TEST_CASE("Published 3MF prefers a compatible preset over an exact but incompatible one", "[Preset][Bundle][Published]")
|
|
||||||
{
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
// The bare-named legacy preset (Bambu-printer only) and the exact author preset are both
|
|
||||||
// incompatible with the receiver's printer. update_compatible() recomputes is_compatible
|
|
||||||
// inside load_config_model, so the incompatibility is expressed the way production derives
|
|
||||||
// it: a compatible_printers constraint no receiver printer satisfies (the fresh bundle's
|
|
||||||
// active printer is the "Default Printer" placeholder).
|
|
||||||
Preset &bare = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
|
||||||
bare.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
bare.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
bare.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
bare.config.set_key_value("compatible_printers", new ConfigOptionStrings({ "Unrelated Printer" }));
|
|
||||||
Preset &qidi = add_inmemory_preset(bundle.filaments, "Generic PLA @Qidi Q2 0.4 nozzle");
|
|
||||||
qidi.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
qidi.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
qidi.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
qidi.config.set_key_value("compatible_printers", new ConfigOptionStrings({ "Unrelated Printer" }));
|
|
||||||
// The receiver's own compatible library preset.
|
|
||||||
Preset &sys = add_inmemory_preset(bundle.filaments, "Generic PLA @System");
|
|
||||||
sys.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
sys.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
sys.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.preset_name = "Generic PLA @Qidi Q2 0.4 nozzle";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = published_pla_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @System");
|
|
||||||
// A same-family name-tier pick is not reported as a substitute.
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @System");
|
|
||||||
CHECK(pub.skipped_keys.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// When every candidate is incompatible with the receiver's printer, the search still falls back
|
|
||||||
// to the best (exact) match rather than leaving the slot on the mismatched type.
|
|
||||||
TEST_CASE("Published 3MF falls back to an incompatible exact preset when no compatible candidate exists", "[Preset][Bundle][Published]")
|
|
||||||
{
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
// The exact author preset is incompatible with the receiver's printer. update_compatible()
|
|
||||||
// recomputes is_compatible inside load_config_model, so the incompatibility is expressed the
|
|
||||||
// way production derives it: a compatible_printers constraint the receiver's printer (the
|
|
||||||
// fresh bundle's "Default Printer" placeholder) does not satisfy.
|
|
||||||
Preset &qidi = add_inmemory_preset(bundle.filaments, "Generic PLA @Qidi Q2 0.4 nozzle");
|
|
||||||
qidi.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
qidi.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
qidi.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
qidi.config.set_key_value("compatible_printers", new ConfigOptionStrings({ "Unrelated Printer" }));
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.preset_name = "Generic PLA @Qidi Q2 0.4 nozzle";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = published_pla_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @Qidi Q2 0.4 nozzle");
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @Qidi Q2 0.4 nozzle");
|
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1393,35 +1188,46 @@ TEST_CASE("Published 3MF writes to the stored preset when the edited layer is re
|
|||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The author published "Generic PLA @System"; the receiver's copy "Generic PLA" drops the
|
// The created copy is named after the author's preset with the "@variant" tail stripped;
|
||||||
// "@System" suffix (and has no setting_id, a different filament_id), so only the trimmed
|
// trailing whitespace left behind by the truncation must be trimmed away, and names without
|
||||||
// bare-name tier can reach it - the truncated form carries trailing whitespace that must not
|
// a tail pass through unchanged.
|
||||||
// defeat the match.
|
TEST_CASE("publish_material_base_name strips the variant tail from a published preset name", "[Preset][Bundle][Published]")
|
||||||
TEST_CASE("Published 3MF name matching accepts suffix-less receiver presets", "[Preset][Bundle][Published]")
|
{
|
||||||
|
CHECK(publish_material_base_name("Generic PLA @System") == "Generic PLA");
|
||||||
|
CHECK(publish_material_base_name("Generic PLA @Qidi Q2 0.4 nozzle") == "Generic PLA");
|
||||||
|
// Truncation at '@' leaves the space before the tail; it must not survive.
|
||||||
|
CHECK(publish_material_base_name("Generic PLA @System") == "Generic PLA");
|
||||||
|
CHECK(publish_material_base_name("Voron Generic PLA") == "Voron Generic PLA");
|
||||||
|
CHECK(publish_material_base_name("") == "");
|
||||||
|
// A tail-only name strips to nothing; the caller falls back to identity fields.
|
||||||
|
CHECK(publish_material_base_name("@System").empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A full-published material arrives as a brand-new standalone preset: parentless, visible,
|
||||||
|
// project-embedded ("Preset Inside Project"), carrying the author's values and colour - and
|
||||||
|
// never touching any of the receiver's own presets.
|
||||||
|
TEST_CASE("Published 3MF imports a full material as a detached project-embedded preset", "[Preset][Bundle][Published]")
|
||||||
{
|
{
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
||||||
Preset &plain = add_inmemory_preset(bundle.filaments, "Generic PLA");
|
Preset &spare = add_inmemory_preset(bundle.filaments, "Spare PLA");
|
||||||
plain.config.opt_string("filament_type", 0u) = "PLA";
|
spare.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
plain.config.opt_string("filament_vendor", 0u) = "Generic";
|
spare.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.4 };
|
||||||
plain.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
// An unrelated same-type preset that would win a type-only search.
|
|
||||||
Preset &bambu = add_inmemory_preset(bundle.filaments, "Bambu PLA Basic");
|
|
||||||
bambu.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
bambu.config.opt_string("filament_vendor", 0u) = "Bambu Lab";
|
|
||||||
bambu.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.4 };
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
bundle.filament_presets = { "My PETG" };
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
PublishedMaterialEntry entry;
|
||||||
entry.slot = 0;
|
entry.slot = 0;
|
||||||
entry.full = true;
|
entry.full = true;
|
||||||
entry.publish_type = true;
|
entry.publish_type = true;
|
||||||
entry.publish_type_value = "PLA";
|
entry.publish_type_value = "PLA";
|
||||||
entry.preset_name = "Generic PLA @System";
|
entry.publish_color = true;
|
||||||
entry.filament_id = "GFL99";
|
entry.color = "#ABCDEF";
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
entry.filament_id = "AFL01";
|
||||||
|
entry.setting_id = "Sid000111222";
|
||||||
|
entry.preset_name = "Author PLA @Vendor";
|
||||||
|
entry.full_keys = { "filament_retraction_length" };
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
pub.published = true;
|
pub.published = true;
|
||||||
@@ -1430,37 +1236,57 @@ TEST_CASE("Published 3MF name matching accepts suffix-less receiver presets", "[
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
// The trimmed bare-name tier fires: the suffix-less copy wins over the type-only
|
// The slot lands on the freshly created copy, named after the stripped author name.
|
||||||
// candidate, without a substitute qualifier.
|
CHECK(bundle.filament_presets[0] == "Author PLA");
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA");
|
Preset *copy = bundle.filaments.find_preset("Author PLA", false, true);
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
REQUIRE(copy != nullptr);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA");
|
// Detached + project-embedded contract.
|
||||||
|
CHECK(copy->is_project_embedded);
|
||||||
|
CHECK(copy->inherits().empty());
|
||||||
|
CHECK(copy->setting_id.empty());
|
||||||
|
CHECK(copy->vendor == nullptr);
|
||||||
|
CHECK_FALSE(copy->is_system);
|
||||||
|
CHECK_FALSE(copy->is_default);
|
||||||
|
CHECK_FALSE(copy->is_external);
|
||||||
|
CHECK(copy->is_visible);
|
||||||
|
CHECK(copy->filament_id == "AFL01");
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("filament_settings_id")->values == std::vector<std::string>{ "Author PLA" });
|
||||||
|
// The published values and colour live on the copy.
|
||||||
|
CHECK(copy->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("filament_colour")->values == std::vector<std::string>{ "#ABCDEF" });
|
||||||
|
// Universally compatible: no printer/print restrictions survive the import.
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("compatible_printers")->values.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("compatible_prints")->values.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionString>("compatible_printers_condition")->value.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionString>("compatible_prints_condition")->value.empty());
|
||||||
|
// Nothing pre-existing was touched.
|
||||||
|
CHECK(bundle.filaments.find_preset("My PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.6 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Spare PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.4 });
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
|
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Author PLA (published material imported)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// The receiver's vendor renamed "Generic PLA @System" to "PLA Generic @System" (renamed_from
|
// Compatibility restrictions riding on the receiver's baseline preset must not leak onto the
|
||||||
// records the old name); the published file still names the old preset, which resolves through
|
// imported copy: a detached full material is usable with every printer and print profile.
|
||||||
// the collection's rename map instead of degrading to a same-type substitute.
|
TEST_CASE("Published 3MF clears printer restrictions on the imported full material", "[Preset][Bundle][Published]")
|
||||||
TEST_CASE("Published 3MF name matching follows the receiver's preset renames", "[Preset][Bundle][Published]")
|
|
||||||
{
|
{
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &restricted = add_inmemory_preset(bundle.filaments, "Restricted PLA");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
restricted.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
restricted.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
||||||
add_inmemory_preset(bundle.filaments, "PLA Generic @System");
|
restricted.config.set_key_value("compatible_printers", new ConfigOptionStrings({ "Unrelated Printer" }));
|
||||||
bundle.filament_presets = { "My PETG" };
|
restricted.config.set_key_value("compatible_prints", new ConfigOptionStrings({ "Unrelated Print" }));
|
||||||
|
restricted.config.option<ConfigOptionString>("compatible_printers_condition", true)->value = "printer_settings_id==\"Nope\"";
|
||||||
set_renamed_from(bundle.filaments, "PLA Generic @System", { "Generic PLA @System" });
|
restricted.config.option<ConfigOptionString>("compatible_prints_condition", true)->value = "print_settings_id==\"Nope\"";
|
||||||
AppConfig app_config;
|
bundle.filament_presets = { "Restricted PLA" };
|
||||||
bundle.load_installed_printers(app_config); // rebuild the rename map
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
PublishedMaterialEntry entry;
|
||||||
entry.slot = 0;
|
entry.slot = 0;
|
||||||
entry.full = true;
|
entry.full = true;
|
||||||
entry.publish_type = true;
|
entry.publish_type = true;
|
||||||
entry.publish_type_value = "PLA";
|
entry.publish_type_value = "PLA";
|
||||||
entry.preset_name = "Generic PLA @System"; // the pre-rename name
|
entry.full_keys = { "filament_retraction_length" };
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
pub.published = true;
|
pub.published = true;
|
||||||
@@ -1469,215 +1295,206 @@ TEST_CASE("Published 3MF name matching follows the receiver's preset renames", "
|
|||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
// find_preset2 resolves the pre-rename name through the collection's rename map.
|
// The copy (named from the published type; no identity fields) has no restrictions left.
|
||||||
CHECK(bundle.filament_presets[0] == "PLA Generic @System");
|
CHECK(bundle.filament_presets[0] == "PLA");
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
Preset *copy = bundle.filaments.find_preset("PLA", false, true);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> PLA Generic @System");
|
REQUIRE(copy != nullptr);
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("compatible_printers")->values.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionStrings>("compatible_prints")->values.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionString>("compatible_printers_condition")->value.empty());
|
||||||
|
CHECK(copy->config.opt<ConfigOptionString>("compatible_prints_condition")->value.empty());
|
||||||
|
// The receiver's own restricted preset keeps its restrictions.
|
||||||
|
const Preset *original = bundle.filaments.find_preset("Restricted PLA", false, true);
|
||||||
|
REQUIRE(original != nullptr);
|
||||||
|
CHECK(original->config.opt<ConfigOptionStrings>("compatible_printers")->values == std::vector<std::string>{ "Unrelated Printer" });
|
||||||
|
CHECK(original->config.opt<ConfigOptionString>("compatible_printers_condition")->value == "printer_settings_id==\"Nope\"");
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The author's preset "Voron Generic PLA" (a vendor-specific generic) is gone from the
|
// Identical Full materials (same setting_id + preset_name identity) share one created
|
||||||
// receiver's profile set; find_preset2 auto-matches it to the Orca Filament Library instead of
|
// instance: an author who pointed several slots at one material gets one standalone copy,
|
||||||
// falling back to a same-type substitute.
|
// and the first entry's slot values win.
|
||||||
TEST_CASE("Published 3MF name matching falls back to the library for removed vendor generics", "[Preset][Bundle][Published]")
|
TEST_CASE("Published 3MF shares one imported copy between identical full slots", "[Preset][Bundle][Published]")
|
||||||
{
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
add_inmemory_preset(bundle.filaments, "Generic PLA @System");
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.preset_name = "Voron Generic PLA";
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = published_pla_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
|
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA @System");
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA @System");
|
|
||||||
CHECK(pub.skipped_keys.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// The replacement search prefers the published identity: exact filament_id, then vendor+type,
|
|
||||||
// then type only (collection order decides equal scores; the pick is reported as a substitute
|
|
||||||
// when it is not the exact published material).
|
|
||||||
TEST_CASE("Published 3MF prefers the published material identity when replacing a slot", "[Preset][Bundle][Published]")
|
|
||||||
{
|
{
|
||||||
auto make_file_config = [] {
|
auto make_file_config = [] {
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75 };
|
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75 };
|
||||||
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1 };
|
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1, 2 };
|
||||||
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard" };
|
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard", "Direct Drive Standard" };
|
||||||
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000" };
|
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000", "#00FF00" };
|
||||||
config.opt<ConfigOptionStrings>("filament_type")->values = { "PLA" };
|
config.opt<ConfigOptionStrings>("filament_type")->values = { "PLA", "PLA" };
|
||||||
config.opt<ConfigOptionStrings>("filament_vendor")->values = { "Generic" };
|
config.opt<ConfigOptionStrings>("filament_vendor")->values = { "Generic", "Generic" };
|
||||||
config.opt<ConfigOptionStrings>("filament_ids")->values = { "GFL99" };
|
config.opt<ConfigOptionStrings>("filament_ids")->values = { "AFL01", "AFL01" };
|
||||||
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.9 };
|
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.9, 0.8 };
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
auto make_entry = [] {
|
auto make_entry = [](int slot) {
|
||||||
PublishedMaterialEntry entry;
|
PublishedMaterialEntry entry;
|
||||||
entry.slot = 0;
|
entry.slot = slot;
|
||||||
entry.full = true;
|
entry.full = true;
|
||||||
entry.publish_type = true;
|
entry.publish_type = true;
|
||||||
entry.publish_type_value = "PLA";
|
entry.publish_type_value = "PLA";
|
||||||
entry.filament_id = "GFL99";
|
entry.filament_id = "AFL01";
|
||||||
entry.filament_vendor = "Generic";
|
entry.setting_id = "Sid000111222";
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
entry.preset_name = "Author PLA @Vendor";
|
||||||
|
entry.full_keys = { "filament_retraction_length" };
|
||||||
return entry;
|
return entry;
|
||||||
};
|
};
|
||||||
auto add_pla = [](PresetBundle &bundle, const char *name, const char *id, const char *vendor) {
|
|
||||||
Preset &preset = add_inmemory_preset(bundle.filaments, name);
|
|
||||||
preset.filament_id = id;
|
|
||||||
preset.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
preset.config.opt_string("filament_vendor", 0u) = vendor;
|
|
||||||
preset.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
return &preset;
|
|
||||||
};
|
|
||||||
auto load = [&](PresetBundle &bundle, PublishedConfig &pub) {
|
|
||||||
PublishedMaterialEntry entry = make_entry();
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = make_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
};
|
|
||||||
|
|
||||||
SECTION("exact filament_id beats collection order") {
|
PresetBundle bundle;
|
||||||
PresetBundle bundle;
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
Preset &other = add_inmemory_preset(bundle.filaments, "Other PETG");
|
||||||
// "Bambu PLA Basic" sorts before "Zebra PLA"; only the latter carries the published id.
|
other.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
add_pla(bundle, "Bambu PLA Basic", "GFB00", "Bambu Lab");
|
other.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.65 };
|
||||||
add_pla(bundle, "Zebra PLA", "GFL99", "Generic");
|
bundle.filament_presets = { "My PETG", "Other PETG" };
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
load(bundle, pub);
|
pub.published = true;
|
||||||
CHECK(bundle.filament_presets[0] == "Zebra PLA");
|
pub.material_keys = { make_entry(0), make_entry(1) };
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
DynamicPrintConfig config = make_file_config();
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Zebra PLA");
|
Preset::normalize(config);
|
||||||
}
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
SECTION("vendor and type beat a type-only preset, reported as a substitute") {
|
// Both slots point at the single shared copy; no second "(Published)" instance exists.
|
||||||
PresetBundle bundle;
|
REQUIRE(bundle.filament_presets.size() == 2);
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
CHECK(bundle.filament_presets[0] == "Author PLA");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
CHECK(bundle.filament_presets[1] == "Author PLA");
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
CHECK(bundle.filaments.find_preset("Author PLA", false, true) != nullptr);
|
||||||
add_pla(bundle, "Bambu PLA Basic", "GFB00", "Bambu Lab");
|
CHECK(bundle.filaments.find_preset("Author PLA (Published)", false, true) == nullptr);
|
||||||
add_pla(bundle, "Zebra PLA", "ZZZ99", "Generic"); // same vendor+type, different id
|
// The first entry's slot values won.
|
||||||
bundle.filament_presets = { "My PETG" };
|
CHECK(bundle.filaments.find_preset("Author PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
// Both slots reported, same target; originals untouched.
|
||||||
PublishedConfig pub;
|
REQUIRE(pub.material_replacements.size() == 2);
|
||||||
load(bundle, pub);
|
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Author PLA (published material imported)");
|
||||||
CHECK(bundle.filament_presets[0] == "Zebra PLA");
|
CHECK(pub.material_replacements[1] == "slot 1: Other PETG -> Author PLA (published material imported)");
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
CHECK(bundle.filaments.find_preset("My PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.6 });
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Zebra PLA (substitute: no exact material match)");
|
CHECK(bundle.filaments.find_preset("Other PETG", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.65 });
|
||||||
}
|
CHECK(pub.skipped_keys.empty());
|
||||||
|
|
||||||
SECTION("type-only candidates keep collection order and are reported as substitutes") {
|
|
||||||
PresetBundle bundle;
|
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
|
||||||
add_pla(bundle, "Bambu PLA Basic", "GFB00", "Bambu Lab");
|
|
||||||
add_pla(bundle, "Zebra PLA", "ZZZ99", "Acme"); // no identity match at all
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
|
||||||
|
|
||||||
PublishedConfig pub;
|
|
||||||
load(bundle, pub);
|
|
||||||
CHECK(bundle.filament_presets[0] == "Bambu PLA Basic");
|
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Bambu PLA Basic (substitute: no exact material match)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Generic PLA" and "Generic PLA Matte" share their inherited filament_id (OGFL99), so the
|
// Without a preset name the copy falls back to the stable material id; fully anonymous
|
||||||
// exact variant can only be matched via the preset setting_id carried in the published file.
|
// hand-crafted entries never share instances between slots (their dedup key is slot-scoped).
|
||||||
TEST_CASE("Published 3MF matches the exact published variant via setting_id", "[Preset][Bundle][Published]")
|
TEST_CASE("Published 3MF names unidentified full materials from their fallback fields", "[Preset][Bundle][Published]")
|
||||||
{
|
{
|
||||||
auto make_file_config = [] {
|
auto make_file_config = [] {
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75 };
|
config.opt<ConfigOptionFloats>("filament_diameter")->values = { 1.75, 1.75 };
|
||||||
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1 };
|
config.opt<ConfigOptionInts>("filament_self_index")->values = { 1, 2 };
|
||||||
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard" };
|
config.opt<ConfigOptionStrings>("filament_extruder_variant")->values = { "Direct Drive Standard", "Direct Drive Standard" };
|
||||||
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000" };
|
config.opt<ConfigOptionStrings>("filament_colour")->values = { "#FF0000", "#00FF00" };
|
||||||
config.opt<ConfigOptionStrings>("filament_type")->values = { "PLA" };
|
config.opt<ConfigOptionStrings>("filament_type")->values = { "PLA", "PLA" };
|
||||||
config.opt<ConfigOptionStrings>("filament_vendor")->values = { "Generic" };
|
config.opt<ConfigOptionStrings>("filament_vendor")->values = { "Generic", "Generic" };
|
||||||
config.opt<ConfigOptionStrings>("filament_ids")->values = { "OGFL99" };
|
config.opt<ConfigOptionStrings>("filament_ids")->values = { "GFL99", "GFT99" };
|
||||||
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.9 };
|
config.option<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.9, 1.2 };
|
||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
auto add_pla = [](PresetBundle &bundle, const char *name, const char *setting_id) {
|
|
||||||
Preset &preset = add_inmemory_preset(bundle.filaments, name);
|
|
||||||
preset.setting_id = setting_id;
|
|
||||||
preset.filament_id = "OGFL99"; // shared by all Generic PLA variants
|
|
||||||
preset.config.opt_string("filament_type", 0u) = "PLA";
|
|
||||||
preset.config.opt_string("filament_vendor", 0u) = "Generic";
|
|
||||||
preset.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
|
||||||
return &preset;
|
|
||||||
};
|
|
||||||
auto load = [&](PresetBundle &bundle, PublishedConfig &pub, const std::string &setting_id) {
|
|
||||||
PublishedMaterialEntry entry;
|
|
||||||
entry.slot = 0;
|
|
||||||
entry.full = true;
|
|
||||||
entry.publish_type = true;
|
|
||||||
entry.publish_type_value = "PLA";
|
|
||||||
entry.filament_id = "OGFL99";
|
|
||||||
entry.filament_vendor = "Generic";
|
|
||||||
entry.setting_id = setting_id;
|
|
||||||
entry.full_keys = { "filament_retraction_length" };
|
|
||||||
pub.published = true;
|
|
||||||
pub.material_keys = { entry };
|
|
||||||
DynamicPrintConfig config = make_file_config();
|
|
||||||
Preset::normalize(config);
|
|
||||||
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
|
||||||
};
|
|
||||||
|
|
||||||
SECTION("the published variant wins over its same-id sibling") {
|
SECTION("empty preset_name falls back to the filament_id") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &pla = add_inmemory_preset(bundle.filaments, "My PLA");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
pla.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
pla.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
||||||
add_pla(bundle, "Generic PLA", "RcBNzytWgwRrwXXz");
|
bundle.filament_presets = { "My PLA" };
|
||||||
add_pla(bundle, "Generic PLA Matte", "RFs9eCKYOMUSmvZf");
|
|
||||||
bundle.filament_presets = { "My PETG" };
|
PublishedMaterialEntry entry;
|
||||||
|
entry.slot = 0;
|
||||||
|
entry.full = true;
|
||||||
|
entry.publish_type = true;
|
||||||
|
entry.publish_type_value = "PLA";
|
||||||
|
entry.filament_id = "AFL01";
|
||||||
|
entry.full_keys = { "filament_retraction_length" };
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
load(bundle, pub, "RFs9eCKYOMUSmvZf"); // the author published "Generic PLA Matte"
|
pub.published = true;
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA Matte");
|
pub.material_keys = { entry };
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA Matte", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
DynamicPrintConfig config = published_pla_file_config();
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.5 });
|
Preset::normalize(config);
|
||||||
REQUIRE(pub.material_replacements.size() == 1);
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
CHECK(pub.material_replacements[0] == "slot 0: My PETG -> Generic PLA Matte");
|
|
||||||
|
CHECK(bundle.filament_presets[0] == "AFL01");
|
||||||
|
CHECK(bundle.filaments.find_preset("AFL01", false, true) != nullptr);
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("without a setting_id the same-id siblings fall back to collection order") {
|
SECTION("fully anonymous entries get slot-scoped copies") {
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
Preset &first = add_inmemory_preset(bundle.filaments, "First PLA");
|
||||||
petg.config.opt_string("filament_type", 0u) = "PETG";
|
first.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
first.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.5 };
|
||||||
add_pla(bundle, "Generic PLA", "RcBNzytWgwRrwXXz");
|
Preset &second = add_inmemory_preset(bundle.filaments, "Second PLA");
|
||||||
add_pla(bundle, "Generic PLA Matte", "RFs9eCKYOMUSmvZf");
|
second.config.opt_string("filament_type", 0u) = "PLA";
|
||||||
bundle.filament_presets = { "My PETG" };
|
second.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.55 };
|
||||||
|
bundle.filament_presets = { "First PLA", "Second PLA" };
|
||||||
|
|
||||||
|
PublishedMaterialEntry entry0;
|
||||||
|
entry0.slot = 0;
|
||||||
|
entry0.full = true;
|
||||||
|
entry0.publish_type = true;
|
||||||
|
entry0.publish_type_value = "ABS"; // the only naming field present
|
||||||
|
entry0.full_keys = { "filament_retraction_length" };
|
||||||
|
PublishedMaterialEntry entry1 = entry0;
|
||||||
|
entry1.slot = 1;
|
||||||
|
|
||||||
PublishedConfig pub;
|
PublishedConfig pub;
|
||||||
load(bundle, pub, ""); // legacy file without the field
|
pub.published = true;
|
||||||
CHECK(bundle.filament_presets[0] == "Generic PLA");
|
pub.material_keys = { entry0, entry1 };
|
||||||
CHECK(bundle.filaments.find_preset("Generic PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
DynamicPrintConfig config = make_file_config();
|
||||||
|
Preset::normalize(config);
|
||||||
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
|
// No shared identity: each slot gets its own uniquified copy.
|
||||||
|
CHECK(bundle.filament_presets[0] == "ABS");
|
||||||
|
CHECK(bundle.filament_presets[1] == "ABS (Published)");
|
||||||
|
CHECK(bundle.filaments.find_preset("ABS", false, true) != nullptr);
|
||||||
|
CHECK(bundle.filaments.find_preset("ABS (Published)", false, true) != nullptr);
|
||||||
|
CHECK(pub.skipped_keys.empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Within-load dedup does not span loads: importing the same published file again into the same
|
||||||
|
// session creates a second, uniquified copy instead of mutating or reusing the first.
|
||||||
|
TEST_CASE("Re-importing a published full material uniquifies the second copy", "[Preset][Bundle][Published]")
|
||||||
|
{
|
||||||
|
PresetBundle bundle;
|
||||||
|
Preset &petg = add_inmemory_preset(bundle.filaments, "My PETG");
|
||||||
|
petg.config.opt_string("filament_type", 0u) = "PETG";
|
||||||
|
petg.config.opt<ConfigOptionFloatsNullable>("filament_retraction_length", true)->values = { 0.6 };
|
||||||
|
bundle.filament_presets = { "My PETG" };
|
||||||
|
|
||||||
|
auto make_entry = [] {
|
||||||
|
PublishedMaterialEntry entry;
|
||||||
|
entry.slot = 0;
|
||||||
|
entry.full = true;
|
||||||
|
entry.publish_type = true;
|
||||||
|
entry.publish_type_value = "PLA";
|
||||||
|
entry.filament_id = "AFL01";
|
||||||
|
entry.setting_id = "Sid000111222";
|
||||||
|
entry.preset_name = "Author PLA @Vendor";
|
||||||
|
entry.full_keys = { "filament_retraction_length" };
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int round = 0; round < 2; ++round) {
|
||||||
|
PublishedConfig pub;
|
||||||
|
pub.published = true;
|
||||||
|
pub.material_keys = { make_entry() };
|
||||||
|
DynamicPrintConfig config = published_pla_file_config();
|
||||||
|
Preset::normalize(config);
|
||||||
|
bundle.load_config_model("test.3mf", std::move(config), Semver(), &pub);
|
||||||
|
|
||||||
|
if (round == 0) {
|
||||||
|
CHECK(bundle.filament_presets[0] == "Author PLA");
|
||||||
|
CHECK(bundle.filaments.find_preset("Author PLA (Published)", false, true) == nullptr);
|
||||||
|
} else {
|
||||||
|
// The second import uniquifies beside the first instead of touching it.
|
||||||
|
CHECK(bundle.filament_presets[0] == "Author PLA (Published)");
|
||||||
|
CHECK(bundle.filaments.find_preset("Author PLA (Published)", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
CHECK(bundle.filaments.find_preset("Author PLA", false, true)->config.opt<ConfigOptionFloatsNullable>("filament_retraction_length")->values == std::vector<double>{ 0.9 });
|
||||||
|
REQUIRE(pub.material_replacements.size() == 1);
|
||||||
|
CHECK(pub.material_replacements[0] == "slot 0: Author PLA -> Author PLA (Published) (published material imported)");
|
||||||
|
}
|
||||||
CHECK(pub.skipped_keys.empty());
|
CHECK(pub.skipped_keys.empty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user