Compare commits

..

3 Commits

Author SHA1 Message Date
Hanif Koh
b1d57df8d4 Update Option Type for LogFile argument 2026-09-01 12:42:47 +08:00
Hanif Koh
57f952df55 Add read_cli accept/reject tests 2026-09-01 12:42:41 +08:00
Hanif Koh
b8914f4ca4 Reject invalid CLI argument values instead of silently accepting them 2026-09-01 12:12:24 +08:00
4 changed files with 52 additions and 12 deletions

View File

@@ -7384,7 +7384,7 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
<< std::endl << std::endl
<< "Print setting priorities:" << std::endl << "Print setting priorities:" << std::endl
<< "\t1) setting values from the command line (highest priority)"<< std::endl << "\t1) setting values from the command line (highest priority)"<< std::endl
<< "\t2) setting values loaded with --load_settings and --load_filaments" << std::endl << "\t2) setting values loaded with --load-settings and --load-filaments" << std::endl
<< "\t3) setting values loaded from 3mf(lowest priority)" << std::endl; << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
/*if (include_print_options) { /*if (include_print_options) {

View File

@@ -1812,17 +1812,31 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
// to the end of the value. // to the end of the value.
if (opt_base->type() == coBools && value.empty()) if (opt_base->type() == coBools && value.empty())
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no); static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else else {
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way // Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape // they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
// unescaped by the calling shell. // unescaped by the calling shell.
opt_vector->deserialize(value, true); bool deserialized = false;
try {
deserialized = opt_vector->deserialize(value, true);
} catch (const std::exception &ex) {
// e.g. "nil" deserialized into a non-nullable vector option throws instead of
// returning false - treat that the same as any other invalid value here.
deserialized = false;
}
if (! deserialized) {
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
}
} else if (opt_base->type() == coBool) { } else if (opt_base->type() == coBool) {
if (value.empty()) if (value.empty())
static_cast<ConfigOptionBool*>(opt_base)->value = !no; static_cast<ConfigOptionBool*>(opt_base)->value = !no;
else else if (! opt_base->deserialize(value)) {
opt_base->deserialize(value); boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
} else if (opt_base->type() == coString) { } else if (opt_base->type() == coString) {
// Do not unescape single string values, the unescaping is left to the calling shell. // Do not unescape single string values, the unescaping is left to the calling shell.
static_cast<ConfigOptionString*>(opt_base)->value = value; static_cast<ConfigOptionString*>(opt_base)->value = value;

View File

@@ -11948,13 +11948,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("load_defaultfila", coBool); def = this->add("load_defaultfila", coBool);
def->label = L("Load default filaments"); def->label = L("Load default filaments");
def->tooltip = L("Load first filament as default for those not loaded."); def->tooltip = L("Load first filament as default for those not loaded.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("min_save", coBool); def = this->add("min_save", coBool);
def->label = L("Minimum save"); def->label = L("Minimum save");
def->tooltip = L("Export 3MF with minimum size."); def->tooltip = L("Export 3MF with minimum size.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("mtcpp", coInt); def = this->add("mtcpp", coInt);
@@ -11980,7 +11978,6 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("normative_check", coBool); def = this->add("normative_check", coBool);
def->label = L("Normative check"); def->label = L("Normative check");
def->tooltip = L("Check the normative items."); def->tooltip = L("Check the normative items.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
/*def = this->add("help_fff", coBool); /*def = this->add("help_fff", coBool);
@@ -12247,7 +12244,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->cli_params = "level"; def->cli_params = "level";
def->set_default_value(new ConfigOptionInt(1)); def->set_default_value(new ConfigOptionInt(1));
def = this->add("logfile", coInt); def = this->add("logfile", coString);
def->label = L("Log file"); def->label = L("Log file");
def->tooltip = L("Redirects debug logging to file.\n"); def->tooltip = L("Redirects debug logging to file.\n");
def->cli_params = "file"; def->cli_params = "file";
@@ -12295,7 +12292,6 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("skip_modified_gcodes", coBool); def = this->add("skip_modified_gcodes", coBool);
def->label = L("Skip modified G-code in 3MF"); def->label = L("Skip modified G-code in 3MF");
def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets."); def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("makerlab_name", coString); def = this->add("makerlab_name", coString);
@@ -12325,14 +12321,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("allow_newer_file", coBool); def = this->add("allow_newer_file", coBool);
def->label = L("Allow 3MF with newer version to be sliced"); def->label = L("Allow 3MF with newer version to be sliced");
def->tooltip = L("Allow 3MF with newer version to be sliced."); def->tooltip = L("Allow 3MF with newer version to be sliced.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("allow_mix_temp", coBool); def = this->add("allow_mix_temp", coBool);
// internal use only, don't need translation // internal use only, don't need translation
def->label = "Allow filaments with high/low temperature to be printed together"; def->label = "Allow filaments with high/low temperature to be printed together";
def->tooltip = "Allow filaments with high/low temperature to be printed together."; def->tooltip = "Allow filaments with high/low temperature to be printed together.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
} }

View File

@@ -828,3 +828,35 @@ SCENARIO("ConfigOptionVector::set_to_index throws on incompatible type", "[Confi
} }
} }
} }
TEST_CASE("read_cli applies valid values and collects non-option arguments", "[Config]") {
Slic3r::DynamicPrintConfig config;
t_config_option_keys extra, keys;
const char* argv[] = {"orca-slicer", "--nozzle-temperature", "210,190", "--reduce-crossing-wall=1", "model.3mf"};
REQUIRE(config.read_cli(5, argv, &extra, &keys));
REQUIRE(config.opt<ConfigOptionInts>("nozzle_temperature")->values == std::vector<int>{210, 190});
REQUIRE(config.opt<ConfigOptionBool>("reduce_crossing_wall")->value);
REQUIRE(extra == t_config_option_keys{"model.3mf"});
REQUIRE(keys == t_config_option_keys{"nozzle_temperature", "reduce_crossing_wall"});
}
TEST_CASE("read_cli rejects nil for a non-nullable vector option", "[Config]") {
Slic3r::DynamicPrintConfig config;
t_config_option_keys extra, keys;
const char* argv[] = {"orca-slicer", "--nozzle-temperature", "nil"};
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
}
TEST_CASE("read_cli rejects an invalid boolean value", "[Config]") {
Slic3r::DynamicPrintConfig config;
t_config_option_keys extra, keys;
const char* argv[] = {"orca-slicer", "--reduce-crossing-wall=maybe"};
REQUIRE_FALSE(config.read_cli(2, argv, &extra, &keys));
}
TEST_CASE("read_cli rejects an invalid scalar numeric value", "[Config]") {
Slic3r::DynamicPrintConfig config;
t_config_option_keys extra, keys;
const char* argv[] = {"orca-slicer", "--top-shell-layers", "several"};
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
}