mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
Slice-validate every custom G-code and filename_format in system profiles (#15966)
# Description Follow-up to #15950, where a `filename_format` using `initial_no_support_extruder` shipped broken because the profile validator's slice sweep never expands `filename_format`. The sweep now expands every custom G-code and `filename_format` text shipped in any system profile. Each printer's slice also fires the pause, template custom G-code and clumping-detection hooks and names the output file, and the first printer shipping a `printing_by_object_gcode` also slices by object. Beyond each printer's default process and filament, every compatible system process and filament carrying a template text no earlier slice has expanded is sliced once, which takes the sweep from 1,110 to 1,248 slices (about 49 s locally, up from 43 s). While the sweep runs, the placeholder parser also resolves variable names inside `{if}` branches a slice does not take, so one expansion checks every branch. The stricter sweep found two profile bugs, fixed here: the Anycubic Kobra X filament change G-code carried an unreachable block reading variables only Anycubic's own slicer defines, and the Wanhao France D12 template custom G-code had an unterminated `{if}`, so adding a template custom G-code on those printers failed the slice. It also fixes a parser bug where a declaration such as `{local a = layer_height + 1}` failed to parse inside a branch that is not taken. No change to slicing output for templates that already worked: the untaken-branch check is enabled only by the validator's slice mode, and the parser fix only lets previously rejected templates parse. # Screenshots/Recordings/Graphs <!-- > Please attach relevant screenshots to showcase the UI changes. > Please attach images that can help explain the changes. --> ## Tests New placeholder-parser cases cover the parse fix and the untaken-branch check: off by default it changes nothing; on, it rejects undefined names in branches not taken, accepts names declared there, and leaves boolean expressions (compatibility conditions) alone. The full sweep passes on all system profiles, and planting an undefined variable in an untaken branch of a printer's start G-code, of a non-default filament's start G-code or of a non-default process's `filename_format`, or reverting #15950, each fails it and names the preset. `scripts/check_profile.sh`, `libslic3r_tests` and `fff_print_tests` pass. <!-- > A guide for users on how to download the artifacts from this PR. --> [How to Download Pull Requests Artifacts for Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
This commit is contained in:
@@ -356,3 +356,38 @@ SCENARIO("Placeholder parser coFloatsOrPercents vector access", "[PlaceholderPar
|
||||
REQUIRE(std::stod(parser.process("{pressure_advance[2]}")) == Catch::Approx(3.0));
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Placeholder parser names in branches that are not taken", "[PlaceholderParser]") {
|
||||
PlaceholderParser parser;
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
parser.apply_config(config);
|
||||
parser.set("idx", 0);
|
||||
PlaceholderParser::ContextData context;
|
||||
context.global_config = std::make_unique<DynamicConfig>();
|
||||
auto process = [&parser, &context](const std::string &templ) { return parser.process(templ, 0, nullptr, nullptr, &context); };
|
||||
|
||||
SECTION("a declaration continuing after a variable reference parses when not taken") {
|
||||
REQUIRE(process("{if false}{local a = layer_height + 1}{endif}ok") == "ok");
|
||||
}
|
||||
SECTION("names are not checked by default") {
|
||||
REQUIRE(process("{if false}{no_such_var}[no_such_var]{endif}ok") == "ok");
|
||||
}
|
||||
SECTION("names must resolve when check_inactive_branches is set") {
|
||||
struct Restore { ~Restore() { PlaceholderParser::check_inactive_branches = false; } } restore;
|
||||
PlaceholderParser::check_inactive_branches = true;
|
||||
|
||||
CHECK_THROWS_WITH(process("{if false}{no_such_var}{endif}"), Catch::Matchers::ContainsSubstring("Not a variable name (in an inactive branch)"));
|
||||
CHECK_THROWS_WITH(process("{if false}[no_such_var]{endif}"), Catch::Matchers::ContainsSubstring("Variable does not exist (in an inactive branch)"));
|
||||
CHECK_THROWS_WITH(process("{if false}[nozzle_temperature[no_such_var]]{endif}"), Catch::Matchers::ContainsSubstring("Variable does not exist (in an inactive branch)"));
|
||||
CHECK_THROWS(process("{if true}{else}{no_such_var}{endif}"));
|
||||
CHECK_THROWS(process("{if false}{local a = no_such_var + 1}{endif}"));
|
||||
|
||||
CHECK(process("{if false}{layer_height}[layer_height][nozzle_temperature_0][nozzle_temperature[idx]]{endif}ok") == "ok");
|
||||
CHECK(process("{if false}{local a = 1}{a = a + 1}{a}{endif}{if false}{a}{endif}ok") == "ok");
|
||||
// A global declared in a branch that is not taken counts as defined for later expansions sharing the context.
|
||||
CHECK(process("{if false}{global g = 1}{endif}{if false}{g}{endif}ok") == "ok");
|
||||
CHECK(process("{if false}{g}{endif}ok") == "ok");
|
||||
// Boolean expressions are not checked, so compatibility conditions keep their behaviour.
|
||||
CHECK(PlaceholderParser::evaluate_boolean_expression("false ? no_such_var == 1 : true", config));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user