diff --git a/src/test/libslic3r/test_config.cpp b/src/test/libslic3r/test_config.cpp index efb87d70d..b8d7005d4 100644 --- a/src/test/libslic3r/test_config.cpp +++ b/src/test/libslic3r/test_config.cpp @@ -12,7 +12,7 @@ SCENARIO("Generic config validation performs as expected.") { GIVEN("A config generated from default options") { auto config {Slic3r::Config::new_from_defaults()}; WHEN( "perimeter_extrusion_width is set to 250%, a valid value") { - config->set("perimeter_extrusion_width", "250\%"); + config->set("perimeter_extrusion_width", "250%"); THEN( "The config is read as valid.") { REQUIRE(config->validate() == true); } @@ -29,59 +29,99 @@ SCENARIO("Generic config validation performs as expected.") { REQUIRE(except_thrown == true); } } + + WHEN( "perimeters is set to -10, an invalid value") { + config->set("perimeters", -10); + THEN( "An InvalidOptionValue exception is thrown.") { + auto except_thrown {false}; + try { + config->validate(); + } catch (const InvalidOptionValue& e) { + except_thrown = true; + } + REQUIRE(except_thrown == true); + } + } } } -SCENARIO("Config accessor functions perform as expected.", "[!mayfail]") { +SCENARIO("Config accessor functions perform as expected.") { GIVEN("A config generated from default options") { auto config {Slic3r::Config::new_from_defaults()}; WHEN("A numeric option is set through the string interface") { + config->set("bed_temperature", "100"); THEN("The underlying value is set correctly.") { - REQUIRE(false); + REQUIRE(config->get("bed_temperature").getInt() == 100); } } WHEN("An integer-based option is set through the integer interface") { + config->set("bed_temperature", 100); THEN("The underlying value is set correctly.") { - REQUIRE(false); + REQUIRE(config->get("bed_temperature").getInt() == 100); } } WHEN("An floating-point option is set through the integer interface") { + config->set("perimeter_speed", 10); THEN("The underlying value is set correctly.") { - REQUIRE(false); + REQUIRE(config->get("perimeter_speed").getFloat() == 10.0); } } WHEN("A floating-point option is set through the double interface") { + config->set("perimeter_speed", 5.5); THEN("The underlying value is set correctly.") { - REQUIRE(false); + REQUIRE(config->get("perimeter_speed").getFloat() == 5.5); } } WHEN("An integer-based option is set through the double interface") { + config->set("bed_temperature", 5.5); THEN("The underlying value is set, rounded to the nearest integer.") { - REQUIRE(false); + REQUIRE(config->get("bed_temperature").getInt() == 6); } } WHEN("A numeric option is set to a non-numeric value.") { auto except_thrown {false}; try { - config->set("perimeter_extrusion_width", "zzzz"); - } catch (const InvalidOptionType& e) { + config->set("perimeter_speed", "zzzz"); + } catch (const InvalidOptionValue& e) { except_thrown = true; } - THEN("An exception is thown.") { + THEN("An InvalidOptionValue exception is thown.") { REQUIRE(except_thrown == true); } THEN("The value does not change.") { - REQUIRE(false); + REQUIRE(config->get("perimeter_speed").getFloat() == 60.0); } } - WHEN("An invalid option is requested during set.") { + WHEN("An invalid option is requested during set (string).") { auto except_thrown {false}; try { config->set("deadbeef_invalid_option", "1"); } catch (const InvalidOptionType& e) { except_thrown = true; } - THEN("An exception is thrown.") { + THEN("An InvalidOptionType exception is thrown.") { + REQUIRE(except_thrown == true); + } + } + WHEN("An invalid option is requested during set (double).") { + auto except_thrown {false}; + try { + config->set("deadbeef_invalid_option", 1.0); + } catch (const InvalidOptionType& e) { + except_thrown = true; + } + THEN("An InvalidOptionType exception is thrown.") { + REQUIRE(except_thrown == true); + } + } + WHEN("An invalid option is requested during set (int).") { + auto except_thrown {false}; + try { + config->set("deadbeef_invalid_option", 1); + } catch (const InvalidOptionType& e) { + except_thrown = true; + } + THEN("An InvalidOptionType exception is thrown.") { REQUIRE(except_thrown == true); } } @@ -92,7 +132,18 @@ SCENARIO("Config accessor functions perform as expected.", "[!mayfail]") { } catch (const InvalidOptionType& e) { except_thrown = true; } - THEN("An exception is thrown.") { + THEN("An InvalidOptionType exception is thrown.") { + REQUIRE(except_thrown == true); + } + } + WHEN("An invalid option is requested during get_ptr.") { + auto except_thrown {false}; + try { + config->get_ptr("deadbeef_invalid_option", false); + } catch (const InvalidOptionType& e) { + except_thrown = true; + } + THEN("An InvalidOptionType exception is thrown.") { REQUIRE(except_thrown == true); } } diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 1c6cb13ec..3eb6e252a 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -64,7 +64,7 @@ Config::validate() try { tmp_opt = get_ptr(k); } catch (std::bad_cast) { - throw InvalidOptionValue((std::string("Invalid value for ") + std::string(k)).c_str()); + throw InvalidOptionType((std::string("(cast failure) Invalid value for ") + std::string(k)).c_str()); } auto tmp_str {tmp_opt->vserialize()}; values.insert(values.end(), tmp_str.begin(), tmp_str.end()); @@ -75,11 +75,10 @@ Config::validate() } // Verify each value for (auto v : values) { - Slic3r::Log::debug("Config::validate", v); if (type == "i" || type == "f" || opt.type == coPercent || opt.type == coFloatOrPercent) { if (opt.type == coPercent || opt.type == coFloatOrPercent) v = std::regex_replace(v, std::regex("%$"), std::string("")); - if (!is_valid_int(type, opt, v) || !is_valid_float(type, opt, v)) { + if ((type == "i" && !is_valid_int(type, opt, v)) || !is_valid_float(type, opt, v)) { throw InvalidOptionValue((std::string("Invalid value for ") + std::string(k)).c_str()); } } @@ -91,46 +90,130 @@ Config::validate() void Config::set(const t_config_option_key& opt_key, const std::string& value) { + try { + const auto& def {print_config_def.options.at(opt_key)}; + switch (def.type) { + case coInt: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setInt(std::stoi(value)); + } break; + case coInts: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + if (!ptr->deserialize(value, true) ) { + throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); + } + } break; + case coFloat: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setFloat(std::stod(value)); + } break; + case coFloatOrPercent: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + const size_t perc = value.find("%"); + ptr->percent = (perc != std::string::npos); + if (ptr->percent) + ptr->setFloat(std::stod(std::string(value).replace(value.find("%"), std::string("%").length(), ""))); + else + ptr->setFloat(std::stod(value)); + } break; + case coFloats: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + if (!ptr->deserialize(value, true) ) { + throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); + } + } break; + default: + Slic3r::Log::warn("Config::set", "Unknown set type."); + } + } catch (std::invalid_argument &e) { + throw InvalidOptionValue(std::string(opt_key) + std::string(" set with invalid value.")); + } catch (std::out_of_range &e) { + throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); + } } void Config::set(const t_config_option_key& opt_key, const int value) { - const auto& def {print_config_def.options.at(opt_key)}; - switch (def.type) { - case coInt: - { - auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; - ptr->setInt(value); - } break; - case coInts: - { - auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - case coFloat: - { - auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; - ptr->setFloat(value); - } break; - case coFloatOrPercent: - { - auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; - ptr->setFloat(value); - } break; - case coFloats: - { - auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; - ptr->deserialize(std::to_string(value), true); - } break; - default: - Slic3r::Log::warn("Config::set", "Unknown set type."); + try { + const auto& def {print_config_def.options.at(opt_key)}; + switch (def.type) { + case coInt: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setInt(value); + } break; + case coInts: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->deserialize(std::to_string(value), true); + } break; + case coFloat: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setFloat(value); + } break; + case coFloatOrPercent: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setFloat(value); + } break; + case coFloats: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->deserialize(std::to_string(value), true); + } break; + default: + Slic3r::Log::warn("Config::set", "Unknown set type."); + } + + } catch (std::out_of_range &e) { + throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); } } void Config::set(const t_config_option_key& opt_key, const double value) { + try { + const auto& def {print_config_def.options.at(opt_key)}; + switch (def.type) { + case coInt: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setInt(std::round(value)); + } break; + case coInts: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->deserialize(std::to_string(std::round(value)), true); + } break; + case coFloat: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setFloat(value); + } break; + case coFloatOrPercent: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->setFloat(value); + } break; + case coFloats: + { + auto* ptr {dynamic_cast(this->optptr(opt_key, true))}; + ptr->deserialize(std::to_string(value), true); + } break; + default: + Slic3r::Log::warn("Config::set", "Unknown set type."); + } + } catch (std::out_of_range &e) { + throw InvalidOptionType(std::string(opt_key) + std::string(" is an invalid Slic3r option.")); + } } void diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 4706e9305..2f4f24447 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -18,6 +18,7 @@ namespace Slic3r { class InvalidOptionValue : public std::runtime_error { public: InvalidOptionValue(const char* v) : runtime_error(v) {} + InvalidOptionValue(const std::string v) : runtime_error(v.c_str()) {} }; /// Exception class to handle config options that don't exist. @@ -27,6 +28,7 @@ class InvalidConfigOption : public std::runtime_error {}; class InvalidOptionType : public std::runtime_error { public: InvalidOptionType(const char* v) : runtime_error(v) {} + InvalidOptionType(const std::string v) : runtime_error(v.c_str()) {} }; class Config; @@ -60,12 +62,14 @@ public: /// format than longwinded dynamic_cast<> template T& get(const t_config_option_key& opt_key, bool create=false) { + if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); return *(dynamic_cast(this->optptr(opt_key, create))); } /// Template function to dynamic cast and leave it in pointer form. template T* get_ptr(const t_config_option_key& opt_key, bool create=false) { + if (print_config_def.options.count(opt_key) == 0) throw InvalidOptionType(opt_key + std::string(" is an invalid option.")); return dynamic_cast(this->optptr(opt_key, create)); }