1 #include <catch2/catch.hpp>
2 
3 #include "libslic3r/PrintConfig.hpp"
4 
5 using namespace Slic3r;
6 
7 SCENARIO("Generic config validation performs as expected.", "[Config]") {
8     GIVEN("A config generated from default options") {
9         Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
10         WHEN( "perimeter_extrusion_width is set to 250%, a valid value") {
11             config.set_deserialize_strict("perimeter_extrusion_width", "250%");
12             THEN( "The config is read as valid.") {
13                 REQUIRE(config.validate().empty());
14             }
15         }
16         WHEN( "perimeter_extrusion_width is set to -10, an invalid value") {
17             config.set("perimeter_extrusion_width", -10);
18             THEN( "Validate returns error") {
19                 REQUIRE(! config.validate().empty());
20             }
21         }
22 
23         WHEN( "perimeters is set to -10, an invalid value") {
24             config.set("perimeters", -10);
25             THEN( "Validate returns error") {
26                 REQUIRE(! config.validate().empty());
27             }
28         }
29     }
30 }
31 
32 SCENARIO("Config accessor functions perform as expected.", "[Config]") {
33     GIVEN("A config generated from default options") {
34         Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
35         WHEN("A boolean option is set to a boolean value") {
36             REQUIRE_NOTHROW(config.set("gcode_comments", true));
37             THEN("The underlying value is set correctly.") {
38                 REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
39             }
40         }
41         WHEN("A boolean option is set to a string value representing a 0 or 1") {
42             CHECK_NOTHROW(config.set_deserialize_strict("gcode_comments", "1"));
43             THEN("The underlying value is set correctly.") {
44                 REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
45             }
46         }
47         WHEN("A boolean option is set to a string value representing something other than 0 or 1") {
48             THEN("A BadOptionTypeException exception is thrown.") {
49                 REQUIRE_THROWS_AS(config.set("gcode_comments", "Z"), BadOptionTypeException);
50             }
51             AND_THEN("Value is unchanged.") {
52                 REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == false);
53             }
54         }
55         WHEN("A boolean option is set to an int value") {
56             THEN("A BadOptionTypeException exception is thrown.") {
57                 REQUIRE_THROWS_AS(config.set("gcode_comments", 1), BadOptionTypeException);
58             }
59         }
60         WHEN("A numeric option is set from serialized string") {
61             config.set_deserialize_strict("bed_temperature", "100");
62             THEN("The underlying value is set correctly.") {
63                 REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
64             }
65         }
66 #if 0
67 		//FIXME better design accessors for vector elements.
68 		WHEN("An integer-based option is set through the integer interface") {
69             config.set("bed_temperature", 100);
70             THEN("The underlying value is set correctly.") {
71                 REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
72             }
73         }
74 #endif
75         WHEN("An floating-point option is set through the integer interface") {
76             config.set("perimeter_speed", 10);
77             THEN("The underlying value is set correctly.") {
78                 REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 10.0);
79             }
80         }
81         WHEN("A floating-point option is set through the double interface") {
82             config.set("perimeter_speed", 5.5);
83             THEN("The underlying value is set correctly.") {
84                 REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 5.5);
85             }
86         }
87         WHEN("An integer-based option is set through the double interface") {
88             THEN("A BadOptionTypeException exception is thrown.") {
89                 REQUIRE_THROWS_AS(config.set("bed_temperature", 5.5), BadOptionTypeException);
90             }
91         }
92         WHEN("A numeric option is set to a non-numeric value.") {
93             THEN("A BadOptionTypeException exception is thown.") {
94                 REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionValueException);
95             }
96             THEN("The value does not change.") {
97                 REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);
98             }
99         }
100         WHEN("A string option is set through the string interface") {
101             config.set("end_gcode", "100");
102             THEN("The underlying value is set correctly.") {
103                 REQUIRE(config.opt<ConfigOptionString>("end_gcode")->value == "100");
104             }
105         }
106         WHEN("A string option is set through the integer interface") {
107             config.set("end_gcode", 100);
108             THEN("The underlying value is set correctly.") {
109                 REQUIRE(config.opt<ConfigOptionString>("end_gcode")->value == "100");
110             }
111         }
112         WHEN("A string option is set through the double interface") {
113             config.set("end_gcode", 100.5);
114             THEN("The underlying value is set correctly.") {
115                 REQUIRE(config.opt<ConfigOptionString>("end_gcode")->value == std::to_string(100.5));
116             }
117         }
118         WHEN("A float or percent is set as a percent through the string interface.") {
119             config.set_deserialize_strict("first_layer_extrusion_width", "100%");
120             THEN("Value and percent flag are 100/true") {
121                 auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
122                 REQUIRE(tmp->percent == true);
123                 REQUIRE(tmp->value == 100);
124             }
125         }
126         WHEN("A float or percent is set as a float through the string interface.") {
127             config.set_deserialize_strict("first_layer_extrusion_width", "100");
128             THEN("Value and percent flag are 100/false") {
129                 auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
130                 REQUIRE(tmp->percent == false);
131                 REQUIRE(tmp->value == 100);
132             }
133         }
134         WHEN("A float or percent is set as a float through the int interface.") {
135             config.set("first_layer_extrusion_width", 100);
136             THEN("Value and percent flag are 100/false") {
137                 auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
138                 REQUIRE(tmp->percent == false);
139                 REQUIRE(tmp->value == 100);
140             }
141         }
142         WHEN("A float or percent is set as a float through the double interface.") {
143             config.set("first_layer_extrusion_width", 100.5);
144             THEN("Value and percent flag are 100.5/false") {
145                 auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
146                 REQUIRE(tmp->percent == false);
147                 REQUIRE(tmp->value == 100.5);
148             }
149         }
150         WHEN("An invalid option is requested during set.") {
151             THEN("A BadOptionTypeException exception is thrown.") {
152                 REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", 1), UnknownOptionException);
153                 REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", 1.0), UnknownOptionException);
154                 REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", "1"), UnknownOptionException);
155                 REQUIRE_THROWS_AS(config.set("deadbeef_invalid_option", true), UnknownOptionException);
156             }
157         }
158 
159         WHEN("An invalid option is requested during get.") {
160             THEN("A UnknownOptionException exception is thrown.") {
161                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
162                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
163                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
164                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionBool>("deadbeef_invalid_option", false), UnknownOptionException);
165             }
166         }
167         WHEN("An invalid option is requested during opt.") {
168             THEN("A UnknownOptionException exception is thrown.") {
169                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionString>("deadbeef_invalid_option", false), UnknownOptionException);
170                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionFloat>("deadbeef_invalid_option", false), UnknownOptionException);
171                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionInt>("deadbeef_invalid_option", false), UnknownOptionException);
172                 REQUIRE_THROWS_AS(config.option_throw<ConfigOptionBool>("deadbeef_invalid_option", false), UnknownOptionException);
173             }
174         }
175 
176         WHEN("getX called on an unset option.") {
177             THEN("The default is returned.") {
178                 REQUIRE(config.opt_float("layer_height") == 0.3);
179                 REQUIRE(config.opt_int("raft_layers") == 0);
180                 REQUIRE(config.opt_bool("support_material") == false);
181             }
182         }
183 
184         WHEN("getFloat called on an option that has been set.") {
185             config.set("layer_height", 0.5);
186             THEN("The set value is returned.") {
187                 REQUIRE(config.opt_float("layer_height") == 0.5);
188             }
189         }
190     }
191 }
192 
193 SCENARIO("Config ini load/save interface", "[Config]") {
194     WHEN("new_from_ini is called") {
195 		Slic3r::DynamicPrintConfig config;
196 		std::string path = std::string(TEST_DATA_DIR) + "/test_config/new_from_ini.ini";
197 		config.load_from_ini(path, ForwardCompatibilitySubstitutionRule::Disable);
198         THEN("Config object contains ini file options.") {
199 			REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.size() == 1);
200 			REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.front() == "#ABCD");
201         }
202     }
203 }
204