From 40cedc350c848f5f989649be249b463f33bcec0a Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 12 Jul 2018 20:21:07 -0500 Subject: [PATCH] Basic validation for generic numeric types and its test. --- src/test/libslic3r/test_config.cpp | 4 +- xs/src/libslic3r/Config.cpp | 108 ++++++++++++++++++++++++++++- xs/src/libslic3r/Config.hpp | 24 ++++++- 3 files changed, 131 insertions(+), 5 deletions(-) diff --git a/src/test/libslic3r/test_config.cpp b/src/test/libslic3r/test_config.cpp index 730f3119c..efb87d70d 100644 --- a/src/test/libslic3r/test_config.cpp +++ b/src/test/libslic3r/test_config.cpp @@ -8,7 +8,7 @@ using namespace Slic3r; using namespace std::literals::string_literals; -SCENARIO("Generic config validation performs as expected.", "[!mayfail]") { +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") { @@ -19,7 +19,7 @@ SCENARIO("Generic config validation performs as expected.", "[!mayfail]") { } WHEN( "perimeter_extrusion_width is set to -10, an invalid value") { config->set("perimeter_extrusion_width", -10); - THEN( "An exception is thrown.") { + THEN( "An InvalidOptionValue exception is thrown.") { auto except_thrown {false}; try { config->validate(); diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 4bf60e5c7..1c6cb13ec 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -2,8 +2,12 @@ #include "Config.hpp" #include "Log.hpp" +#include + namespace Slic3r { +extern const PrintConfigDef print_config_def; + std::shared_ptr Config::new_from_defaults() { @@ -47,7 +51,41 @@ Config::new_from_ini(const std::string& inifile) bool Config::validate() { - return false; + // general validation + for (auto k : this->keys()) { + if (print_config_def.options.count(k) == 0) continue; // skip over keys that aren't in the master list + const auto& opt {print_config_def.options.at(k)}; + if (opt.cli == "" || std::regex_search(opt.cli, _match_info, _cli_pattern) == false) continue; + auto type {_match_info.str(1)}; + std::vector values; + if (std::regex_search(type, _match_info, std::regex("@$"))) { + type = std::regex_replace(type, std::regex("@$"), std::string("")); // strip off the @ for later; + ConfigOptionVectorBase* tmp_opt; + try { + tmp_opt = get_ptr(k); + } catch (std::bad_cast) { + throw InvalidOptionValue((std::string("Invalid value for ") + std::string(k)).c_str()); + } + auto tmp_str {tmp_opt->vserialize()}; + values.insert(values.end(), tmp_str.begin(), tmp_str.end()); + } else { + Slic3r::Log::debug("Config::validate", std::string("Not an array")); + Slic3r::Log::debug("Config::validate", type); + values.emplace_back(get_ptr(k)->serialize()); + } + // 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)) { + throw InvalidOptionValue((std::string("Invalid value for ") + std::string(k)).c_str()); + } + } + } + } + return true; } void @@ -58,6 +96,36 @@ Config::set(const t_config_option_key& opt_key, const std::string& value) 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."); + } } void @@ -75,6 +143,44 @@ Config::write_ini(const std::string& file) const { } +bool +is_valid_int(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value) +{ + std::regex _valid_int {"^-?\\d+$"}; + std::smatch match; + ConfigOptionInt tmp; + + bool result {type == "i"}; + if (result) + result = result && std::regex_search(ser_value, match, _valid_int); + if (result) { + tmp.deserialize(ser_value); + result = result & (tmp.getInt() <= opt.max && tmp.getInt() >= opt.min); + } + + return result; +} + +bool +is_valid_float(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value) +{ + std::regex _valid_float {"^-?(?:\\d+|\\d*\\.\\d+)$"}; + std::smatch match; + ConfigOptionFloat tmp; + Slic3r::Log::debug("is_valid_float", ser_value); + + bool result {type == "f" || opt.type == coPercent || opt.type == coFloatOrPercent}; + if (result) + result = result && std::regex_search(ser_value, match, _valid_float); + if (result) { + tmp.deserialize(ser_value); + result = result & (tmp.getFloat() <= opt.max && tmp.getFloat() >= opt.min); + } + return result; +} + + + } // namespace Slic3r diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 231b635ae..4706e9305 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -6,6 +6,7 @@ #include #include +#include #include "PrintConfig.hpp" #include "ConfigBase.hpp" @@ -14,13 +15,19 @@ namespace Slic3r { /// Exception class for invalid (but correct type) option values. /// Thrown by validate() -class InvalidOptionValue : public std::runtime_error {}; +class InvalidOptionValue : public std::runtime_error { +public: + InvalidOptionValue(const char* v) : runtime_error(v) {} +}; /// Exception class to handle config options that don't exist. class InvalidConfigOption : public std::runtime_error {}; /// Exception class for type mismatches -class InvalidOptionType : public std::runtime_error {}; +class InvalidOptionType : public std::runtime_error { +public: + InvalidOptionType(const char* v) : runtime_error(v) {} +}; class Config; using config_ptr = std::shared_ptr; @@ -56,6 +63,12 @@ public: 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) { + return dynamic_cast(this->optptr(opt_key, create)); + } + /// Function to parse value from a string to whatever opt_key is. void set(const t_config_option_key& opt_key, const std::string& value); @@ -72,8 +85,15 @@ public: /// Method to validate the different configuration options. /// It will throw InvalidConfigOption exceptions on failure. bool validate(); + +private: + std::regex _cli_pattern {"=(.+)$"}; + std::smatch _match_info {}; }; +bool is_valid_int(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value); +bool is_valid_float(const std::string& type, const ConfigOptionDef& opt, const std::string& ser_value); + } // namespace Slic3r #endif // CONFIG_HPP