From cfc520c97ab9aa692f843a72f0dad194901b489f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 8 Sep 2023 18:32:39 +0200 Subject: [PATCH] Thumbnails refactoring: Next round * Check errors state on all places of its using (throw exceptions or show a warnings message) + To backward compatibility, save "thumbnails_format" value to the config + deep_diff() function is extended for the case of "thumbnails" comparison + Added unit tests to: * check a load of configuration for "thumbnails" and "thumbnails_format" options * check return values for make_and_check_thumbnail_list() function --- src/libslic3r/GCode.cpp | 22 +- src/libslic3r/GCode/Thumbnails.cpp | 56 +++-- src/libslic3r/GCode/Thumbnails.hpp | 9 +- src/libslic3r/Preset.cpp | 20 +- src/libslic3r/PrintConfig.cpp | 14 +- src/slic3r/GUI/Field.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 27 ++ tests/CMakeLists.txt | 1 + tests/thumbnails/CMakeLists.txt | 13 + .../thumbnails/test_thumbnails_ini_string.cpp | 235 ++++++++++++++++++ .../test_thumbnails_input_string.cpp | 152 +++++++++++ tests/thumbnails/thumbnails_tests_main.cpp | 1 + 12 files changed, 515 insertions(+), 38 deletions(-) create mode 100644 tests/thumbnails/CMakeLists.txt create mode 100644 tests/thumbnails/test_thumbnails_ini_string.cpp create mode 100644 tests/thumbnails/test_thumbnails_input_string.cpp create mode 100644 tests/thumbnails/thumbnails_tests_main.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4262a4d8ec..0a2c1066c4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -874,8 +874,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. - if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); - ! thumbnails.empty()) + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) GCodeThumbnails::generate_binary_thumbnails( thumbnail_cb, binary_data.thumbnails, thumbnails, [&print]() { print.throw_if_canceled(); }); @@ -1009,8 +1016,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail if (! export_to_binary_gcode) { // if exporting gcode in ascii format, generate the thumbnails here - if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); - ! thumbnails.empty()) + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index 9095f4e20d..edafccbea8 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -123,27 +123,27 @@ std::unique_ptr compress_thumbnail(const ThumbnailData &d } } -std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext /*= "PNG"*/) +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext /*= "PNG"sv*/) { if (thumbnails_string.empty()) return {}; - const auto& extentions = ConfigOptionEnum::get_enum_names(); - std::istringstream is(thumbnails_string); std::string point_str; + ThumbnailErrors errors; + // generate thumbnails data to process it - std::vector> thumbnails_list; + GCodeThumbnailDefinitionsList thumbnails_list; while (std::getline(is, point_str, ',')) { Vec2d point(Vec2d::Zero()); GCodeThumbnailsFormat format; std::istringstream iss(point_str); std::string coord_str; - if (std::getline(iss, coord_str, 'x')) { + if (std::getline(iss, coord_str, 'x') && !coord_str.empty()) { std::istringstream(coord_str) >> point(0); - if (std::getline(iss, coord_str, '/')) { + if (std::getline(iss, coord_str, '/') && !coord_str.empty()) { std::istringstream(coord_str) >> point(1); if (0 < point(0) && point(0) < 1000 && 0 < point(1) && point(1) < 1000) { @@ -151,15 +151,15 @@ std::vector> make_and_check_thumbnail_li std::getline(iss, ext_str, '/'); if (ext_str.empty()) - ext_str = def_ext; - else { - // check validity of extention - boost::to_upper(ext_str); - if (std::find(extentions.begin(), extentions.end(), ext_str) == extentions.end()) - errors = enum_bitmask(errors | ThumbnailError::InvalidExt); + ext_str = def_ext.empty() ? "PNG"sv : def_ext; + + // check validity of extention + boost::to_upper(ext_str); + if (!ConfigOptionEnum::from_string(ext_str, format)) { + format = GCodeThumbnailsFormat::PNG; + errors = enum_bitmask(errors | ThumbnailError::InvalidExt); } - format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG : - ext_str == "QOI" ? GCodeThumbnailsFormat::QOI : GCodeThumbnailsFormat::PNG; + thumbnails_list.emplace_back(std::make_pair(format, point)); } else @@ -170,24 +170,34 @@ std::vector> make_and_check_thumbnail_li errors = enum_bitmask(errors | ThumbnailError::InvalidVal); } - return thumbnails_list; + return std::make_pair(std::move(thumbnails_list), errors); } -std::vector> make_thumbnail_list(const DynamicPrintConfig& config) +std::pair make_and_check_thumbnail_list(const ConfigBase& config) { // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // ??? If "thumbnails_format" is not defined, export to PNG. // generate thumbnails data to process it - std::vector> thumbnails_list; - if (const auto thumbnails_value = config.option("thumbnails")) { - ThumbnailErrors errors; - thumbnails_list = make_and_check_thumbnail_list(thumbnails_value->value, errors); - assert(errors == enum_bitmask()); - } + if (const auto thumbnails_value = config.option("thumbnails")) + return make_and_check_thumbnail_list(thumbnails_value->value); - return thumbnails_list; + return {}; +} + +std::string get_error_string(const ThumbnailErrors& errors) +{ + std::string error_str; + + if (errors.has(ThumbnailError::InvalidVal)) + error_str += "\n - " + format("Invalid input format. Expected vector of dimensions in the following format: \"%1%\"", "XxYxEXT, XxYxEXT, ..."); + if (errors.has(ThumbnailError::OutOfRange)) + error_str += "\n - Input value is out of range"; + if (errors.has(ThumbnailError::InvalidExt)) + error_str += "\n - Some input extention is invalid"; + + return error_str; } } // namespace Slic3r::GCodeThumbnails diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index b7b45850ff..a5f9803363 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -37,8 +37,13 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); -std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext = "PNG"); -std::vector> make_thumbnail_list(const DynamicPrintConfig &config); +typedef std::vector> GCodeThumbnailDefinitionsList; + +using namespace std::literals; +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv); +std::pair make_and_check_thumbnail_list(const ConfigBase &config); + +std::string get_error_string(const ThumbnailErrors& errors); template inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 1e9764f815..0ff2f103c3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -44,6 +44,7 @@ #include "libslic3r.h" #include "Utils.hpp" #include "PlaceholderParser.hpp" +#include "GCode/Thumbnails.hpp" using boost::property_tree::ptree; @@ -1299,7 +1300,6 @@ static const std::set independent_from_extruder_number_options = { "filament_ramming_parameters", "gcode_substitutions", "post_process", - "thumbnails", }; bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key) @@ -1323,6 +1323,15 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi } else if (opt_key == "default_filament_profile") { // Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help. // Also the length of this field may differ, which may lead to a crash if the block below is used. + } else if (opt_key == "thumbnails") { + // "thumbnails" can not containes a extentions in old config but are valid and use PNG extention by default + // So, check if "thumbnails" is really changed + // We will compare full thumnails instead of exactly config values + auto [thumbnails, er] = GCodeThumbnails::make_and_check_thumbnail_list(config_this); + auto [thumbnails_new, er_new] = GCodeThumbnails::make_and_check_thumbnail_list(config_other); + if (thumbnails != thumbnails_new || er != er_new) + // if those strings are actually the same, erase them from the list of dirty oprions + diff.emplace_back(opt_key); } else { switch (other_opt->type()) { case coInts: add_correct_opts_to_diff(opt_key, diff, config_other, config_this); break; @@ -1346,7 +1355,14 @@ bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference) { if (edited != nullptr && reference != nullptr) { // Only compares options existing in both configs. - if (! reference->config.equals(edited->config)) + bool is_dirty = !reference->config.equals(edited->config); + if (is_dirty && edited->type != Preset::TYPE_FILAMENT) { + // for non-filaments preset check deep difference for compared configs + // there can be cases (as for thumbnails), when configs can logically equal + // even when their values are not equal. + is_dirty = !deep_diff(edited->config, reference->config).empty(); + } + if (is_dirty) return true; // The "compatible_printers" option key is handled differently from the others: // It is not mandatory. If the key is missing, it means it is compatible with any printer. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0af7868222..c712639660 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4373,7 +4373,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) { if (config.has("thumbnails")) { - std::string extention = "PNG"; + std::string extention; if (config.has("thumbnails_format")) { if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) { auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); @@ -4381,11 +4381,15 @@ void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) extention = *label; } } - std::string thumbnails_str = config.opt_string("thumbnails"); - ThumbnailErrors errors; - auto thumbnails_list = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, errors, extention); - assert(errors == enum_bitmask()); + std::string thumbnails_str = config.opt_string("thumbnails"); + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, extention); + + if (errors != enum_bitmask()) { + std::string error_str = "\n" + format("Invalid value provided for parameter %1%: %2%", "thumbnails", thumbnails_str); + error_str += GCodeThumbnails::get_error_string(errors); + throw BadOptionValueException(error_str); + } if (!thumbnails_list.empty()) { const auto& extentions = ConfigOptionEnum::get_enum_names(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index a95b2e7b66..e82f835587 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -71,9 +71,8 @@ ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ex std::string input_string = into_u8(str); str.Clear(); - ThumbnailErrors errors; - auto thumbnails_list = Slic3r::GCodeThumbnails::make_and_check_thumbnail_list(input_string, errors); + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(input_string); if (!thumbnails_list.empty()) { const auto& extentions = ConfigOptionEnum::get_enum_names(); for (const auto& [format, size] : thumbnails_list) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0aee803397..305346b0b6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -27,6 +27,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "libslic3r/GCode/GCodeWriter.hpp" +#include "libslic3r/GCode/Thumbnails.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" @@ -2658,6 +2659,32 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { wxTheApp->CallAfter([this, opt_key, value]() { + if (opt_key == "thumbnails" && m_config->has("thumbnails_format")) { + // to backward compatibility we need to update "thumbnails_format" from new "thumbnails" + if (const std::string val = boost::any_cast(value); !value.empty()) { + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(val); + + if (errors != enum_bitmask()) { + std::string error_str = format(_u8L("Invalid value provided for parameter %1%: %2%"), "thumbnails", val); + error_str += GCodeThumbnails::get_error_string(errors); + InfoDialog(parent(), _L("G-code flavor is switched"), from_u8(error_str)).ShowModal(); + } + + if (!thumbnails_list.empty()) { + GCodeThumbnailsFormat old_format = GCodeThumbnailsFormat(m_config->option("thumbnails_format")->getInt()); + GCodeThumbnailsFormat new_format = thumbnails_list.begin()->first; + if (old_format != new_format) { + DynamicPrintConfig new_conf = *m_config; + + auto* opt = m_config->option("thumbnails_format")->clone(); + opt->setInt(int(new_format)); + new_conf.set_key_value("thumbnails_format", opt); + + load_config(new_conf); + } + } + } + } if (opt_key == "silent_mode") { bool val = boost::any_cast(value); if (m_use_silent_mode != val) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d6d16db1d2..af2a4e033d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) add_subdirectory(arrange) +add_subdirectory(thumbnails) add_subdirectory(libslic3r) add_subdirectory(slic3rutils) add_subdirectory(fff_print) diff --git a/tests/thumbnails/CMakeLists.txt b/tests/thumbnails/CMakeLists.txt new file mode 100644 index 0000000000..e07ef0421f --- /dev/null +++ b/tests/thumbnails/CMakeLists.txt @@ -0,0 +1,13 @@ +get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + +add_executable(${_TEST_NAME}_tests + ${_TEST_NAME}_tests_main.cpp + test_thumbnails_input_string.cpp + test_thumbnails_ini_string.cpp +) + +target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) +set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") + +# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) \ No newline at end of file diff --git a/tests/thumbnails/test_thumbnails_ini_string.cpp b/tests/thumbnails/test_thumbnails_ini_string.cpp new file mode 100644 index 0000000000..32ec5cbd13 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_ini_string.cpp @@ -0,0 +1,235 @@ +#include + +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +static std::string empty_thumbnails() +{ + return "thumbnails = \n" + "thumbnails_format = "; +} + +static std::string valid_thumbnails() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string valid_thumbnails2() +{ + return "thumbnails = 160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG\n" + "thumbnails_format = pnG"; +} + +static std::string valid_thumbnails3() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG"; +} + +static std::string old_valid_thumbnails() +{ + return "thumbnails = 160x120\n" + "thumbnails_format = JPG"; +} + +static std::string old_valid_thumbnails2() +{ + return "thumbnails = 160x120, 23x78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string old_invalid_thumbnails() +{ + return "thumbnails = 160x\n" + "thumbnails_format = JPG"; +} + +static std::string old_invalid_thumbnails2() +{ + return "thumbnails = 160x120, 23*78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails() +{ + return "thumbnails = 1160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_ext_thumbnails() +{ + return "thumbnails = 1160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = QOI"; +} + +static std::string invalid_ext_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails() +{ + return "thumbnails = 1160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string invalid_val_thumbnails2() +{ + return "thumbnails = x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails3() +{ + return "thumbnails = 1x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = qoi"; +} + +static std::string invalid_val_thumbnails4() +{ + return "thumbnails = 123*78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = jpG"; +} + +static DynamicPrintConfig thumbnails_config() +{ + DynamicPrintConfig config; + config.apply_only(FullPrintConfig::defaults() , { "thumbnails", "thumbnails_format" }); + + return config; +} + +TEST_CASE("Validate Empty Thumbnails", "[Thumbnails in Config]") { + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config) { + REQUIRE(config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)GCodeThumbnailsFormat::PNG); + }; + + SECTION("Load empty init_data") { + REQUIRE_NOTHROW(config.load_from_ini_string("", Enable)); + test_loaded_config(config); + } + + SECTION("Load empty format and empty thumbnails") { + REQUIRE_THROWS_AS(config.load_from_ini_string(empty_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config); + } +} + +TEST_CASE("Validate New Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails3(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 2 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::QOI); + } + + SECTION("Test 2 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails3(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 4 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails4(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + +TEST_CASE("Validate Old Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 1 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + + + + + diff --git a/tests/thumbnails/test_thumbnails_input_string.cpp b/tests/thumbnails/test_thumbnails_input_string.cpp new file mode 100644 index 0000000000..4c623b2736 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_input_string.cpp @@ -0,0 +1,152 @@ +#include +#include + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +// Test Thumbnails lines + +static std::string empty_thumbnails() +{ + return ""; +} + +static std::string valid_thumbnails() +{ + return "160x120/PNG, 23x78/QOI, 230x780/JPG"; +} + +static std::string valid_thumbnails2() +{ + return "160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail() +{ + return "160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail2() +{ + return "160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail() +{ + return "160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail2() +{ + return "160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail() +{ + return "160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail2() +{ + return "x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail3() +{ + return "x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail4() +{ + return "23*78/QOI, 320x240/PNG, 230x780/JPG"; +} + + +TEST_CASE("Empty Thumbnails", "[Thumbnails]") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(empty_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.empty()); +} + +TEST_CASE("Valid Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails2()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Out of range Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } +} + +TEST_CASE("Invalid extention Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Invalid value Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 3") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail3()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 4") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail4()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 2); + } +} \ No newline at end of file diff --git a/tests/thumbnails/thumbnails_tests_main.cpp b/tests/thumbnails/thumbnails_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/thumbnails/thumbnails_tests_main.cpp @@ -0,0 +1 @@ +#include