1 //Copyright (c) 2019 Ultimaker B.V.
2 //CuraEngine is released under the terms of the AGPLv3 or higher.
3 
4 #include <cmath> //For M_PI.
5 #include <gtest/gtest.h>
6 #include <memory> //For shared_ptr.
7 
8 #include "../src/Application.h" //To test extruder train settings.
9 #include "../src/ExtruderTrain.h"
10 #include "../src/Slice.h"
11 #include "../src/settings/FlowTempGraph.h"
12 #include "../src/settings/Settings.h" //The class under test.
13 #include "../src/settings/types/LayerIndex.h"
14 #include "../src/settings/types/AngleRadians.h"
15 #include "../src/settings/types/AngleDegrees.h"
16 #include "../src/settings/types/Temperature.h"
17 #include "../src/settings/types/Velocity.h"
18 #include "../src/settings/types/Ratio.h"
19 #include "../src/settings/types/Duration.h"
20 #include "../src/utils/floatpoint.h"
21 
22 namespace cura
23 {
24 
25 /*
26  * A test fixture with an empty settings object to test with.
27  */
28 class SettingsTest : public testing::Test
29 {
30 public:
31     Settings settings;
32 };
33 
TEST_F(SettingsTest,AddSettingString)34 TEST_F(SettingsTest, AddSettingString)
35 {
36     const std::string setting_value("The human body contains enough bones to make an entire skeleton.");
37     settings.add("test_setting", setting_value);
38     EXPECT_EQ(setting_value, settings.get<std::string>("test_setting"));
39 }
40 
TEST_F(SettingsTest,AddSettingDouble)41 TEST_F(SettingsTest, AddSettingDouble)
42 {
43     settings.add("test_setting", "1234567.890");
44     EXPECT_DOUBLE_EQ(double(1234567.89), settings.get<double>("test_setting"));
45 }
46 
TEST_F(SettingsTest,AddSettingSizeT)47 TEST_F(SettingsTest, AddSettingSizeT)
48 {
49     settings.add("test_setting", "666");
50     EXPECT_EQ(size_t(666), settings.get<size_t>("test_setting"));
51 }
52 
TEST_F(SettingsTest,AddSettingBool)53 TEST_F(SettingsTest, AddSettingBool)
54 {
55     settings.add("test_setting", "true");
56     EXPECT_EQ(true, settings.get<bool>("test_setting"));
57 
58     settings.add("test_setting", "on");
59     EXPECT_EQ(true, settings.get<bool>("test_setting"));
60 
61     settings.add("test_setting", "yes");
62     EXPECT_EQ(true, settings.get<bool>("test_setting"));
63 
64     settings.add("test_setting", "True");
65     EXPECT_EQ(true, settings.get<bool>("test_setting"));
66 
67     settings.add("test_setting", "50");
68     EXPECT_EQ(true, settings.get<bool>("test_setting"));
69 
70     settings.add("test_setting", "0");
71     EXPECT_EQ(false, settings.get<bool>("test_setting")) << "0 should cast to false.";
72 
73     settings.add("test_setting", "false");
74     EXPECT_EQ(false, settings.get<bool>("test_setting"));
75 
76     settings.add("test_setting", "False");
77     EXPECT_EQ(false, settings.get<bool>("test_setting"));
78 }
79 
80 class Slice; //Forward declaration to save some time compiling.
81 
TEST_F(SettingsTest,AddSettingExtruderTrain)82 TEST_F(SettingsTest, AddSettingExtruderTrain)
83 {
84     //Add a slice with some extruder trains.
85     std::shared_ptr<Slice> current_slice = std::make_shared<Slice>(0);
86     Application::getInstance().current_slice = current_slice.get();
87     current_slice->scene.extruders.emplace_back(0, nullptr);
88     current_slice->scene.extruders.emplace_back(1, nullptr);
89     current_slice->scene.extruders.emplace_back(2, nullptr);
90 
91     settings.add("test_setting", "2");
92     EXPECT_EQ(&current_slice->scene.extruders[2], &settings.get<ExtruderTrain&>("test_setting"));
93 
94     settings.add("extruder_nr", "1");
95     settings.add("test_setting", "-1"); //-1 should let it fall back to the current extruder_nr.
96     EXPECT_EQ(&current_slice->scene.extruders[1], &settings.get<ExtruderTrain&>("test_setting")) << "If the extruder is negative, it uses the extruder_nr setting.";
97 }
98 
TEST_F(SettingsTest,AddSettingLayerIndex)99 TEST_F(SettingsTest, AddSettingLayerIndex)
100 {
101     settings.add("test_setting", "4");
102     EXPECT_EQ(LayerIndex(3), settings.get<LayerIndex>("test_setting")) << "LayerIndex settings start counting from 0, so subtract one.";
103 }
104 
TEST_F(SettingsTest,AddSettingLayerIndexNegative)105 TEST_F(SettingsTest, AddSettingLayerIndexNegative)
106 {
107     settings.add("test_setting", "-10");
108     EXPECT_EQ(LayerIndex(-11), settings.get<LayerIndex>("test_setting")) << "LayerIndex settings still subtract 1 even in negative layers.";
109 }
110 
TEST_F(SettingsTest,AddSettingCoordT)111 TEST_F(SettingsTest, AddSettingCoordT)
112 {
113     settings.add("test_setting", "8589934.592"); //2^33 microns, so this MUST be a 64-bit integer! (Or at least 33-bit, but those don't exist.)
114     EXPECT_EQ(coord_t(8589934592), settings.get<coord_t>("test_setting")) << "Coordinates must be entered in the setting as millimetres, but are converted to micrometres.";
115 }
116 
TEST_F(SettingsTest,AddSettingAngleRadians)117 TEST_F(SettingsTest, AddSettingAngleRadians)
118 {
119     settings.add("test_setting", "180");
120     EXPECT_DOUBLE_EQ(AngleRadians(M_PI), settings.get<AngleRadians>("test_setting")) << "180 degrees is 1 pi radians.";
121 
122     settings.add("test_setting", "810");
123     EXPECT_NEAR(AngleRadians(M_PI / 2.0), settings.get<AngleRadians>("test_setting"), 0.00000001) << "810 degrees in clock arithmetic is 90 degrees, which is 0.5 pi radians.";
124 }
125 
TEST_F(SettingsTest,AddSettingAngleDegrees)126 TEST_F(SettingsTest, AddSettingAngleDegrees)
127 {
128     settings.add("test_setting", "4442.4");
129     EXPECT_NEAR(AngleDegrees(122.4), settings.get<AngleDegrees>("test_setting"), 0.00000001) << "4320 is divisible by 360, so 4442.4 in clock arithmetic is 122.4 degrees.";
130 }
131 
TEST_F(SettingsTest,AddSettingTemperature)132 TEST_F(SettingsTest, AddSettingTemperature)
133 {
134     settings.add("test_setting", "245.5");
135     EXPECT_DOUBLE_EQ(Temperature(245.5), settings.get<Temperature>("test_setting"));
136 }
137 
TEST_F(SettingsTest,AddSettingVelocity)138 TEST_F(SettingsTest, AddSettingVelocity)
139 {
140     settings.add("test_setting", "12.345");
141     EXPECT_DOUBLE_EQ(Velocity(12.345), settings.get<Velocity>("test_setting"));
142 
143     settings.add("test_setting", "-78");
144     EXPECT_DOUBLE_EQ(Velocity(-78), settings.get<Velocity>("test_setting"));
145 }
146 
TEST_F(SettingsTest,AddSettingRatio)147 TEST_F(SettingsTest, AddSettingRatio)
148 {
149     settings.add("test_setting", "1.618");
150     EXPECT_DOUBLE_EQ(Ratio(0.01618), settings.get<Ratio>("test_setting")) << "With ratios, the input is interpreted in percentages.";
151 }
152 
TEST_F(SettingsTest,AddSettingDuration)153 TEST_F(SettingsTest, AddSettingDuration)
154 {
155     settings.add("test_setting", "1234.5678");
156     EXPECT_DOUBLE_EQ(Duration(1234.5678), settings.get<Duration>("test_setting"));
157 
158     settings.add("test_setting", "-1234.5678");
159     EXPECT_DOUBLE_EQ(Duration(0), settings.get<Duration>("test_setting")) << "Negative duration doesn't exist, so it gets rounded to 0.";
160 }
161 
TEST_F(SettingsTest,AddSettingFlowTempGraph)162 TEST_F(SettingsTest, AddSettingFlowTempGraph)
163 {
164     settings.add("test_setting", "[[1.50, 10.1],[ 25.1,40.4 ], [26.5,75], [50 , 100.10]]"); //Try various spacing and radixes.
165     const FlowTempGraph flow_temp_graph = settings.get<FlowTempGraph>("test_setting");
166 
167     double stored_temperature = flow_temp_graph.getTemp(30.5, 200.0, true);
168     EXPECT_DOUBLE_EQ(75.0 + (100.10 - 75.0) * (30.5 - 26.5) / (50.0 - 26.5), stored_temperature) << "Interpolate between low and high value.";
169 
170     stored_temperature = flow_temp_graph.getTemp(1, 200.0, true);
171     EXPECT_DOUBLE_EQ(10.1, stored_temperature) << "Flow too low - Return lower temperature in the graph.";
172 
173     stored_temperature = flow_temp_graph.getTemp(80, 200.0, true);
174     EXPECT_DOUBLE_EQ(100.1, stored_temperature) << "Flow too high - Return higher temperature in the graph.";
175 
176     stored_temperature = flow_temp_graph.getTemp(30.5, 200.0, false);
177     EXPECT_DOUBLE_EQ(200.0, stored_temperature);
178 }
179 
TEST_F(SettingsTest,AddSettingFMatrix3x3)180 TEST_F(SettingsTest, AddSettingFMatrix3x3)
181 {
182     settings.add("test_setting", "[[1.0, 2.0, 3.3],[ 2 , 3.0 , 1.0],[3.0 ,1.0,2.0 ]]"); //Try various spacing and radixes.
183     FMatrix3x3 float_matrix = settings.get<FMatrix3x3>("test_setting");
184 
185     EXPECT_DOUBLE_EQ(1.0, float_matrix.m[0][0]);
186     EXPECT_DOUBLE_EQ(2.0, float_matrix.m[1][0]);
187     EXPECT_DOUBLE_EQ(3.3, float_matrix.m[2][0]);
188     EXPECT_DOUBLE_EQ(2.0, float_matrix.m[0][1]);
189     EXPECT_DOUBLE_EQ(3.0, float_matrix.m[1][1]);
190     EXPECT_DOUBLE_EQ(1.0, float_matrix.m[2][1]);
191     EXPECT_DOUBLE_EQ(3.0, float_matrix.m[0][2]);
192     EXPECT_DOUBLE_EQ(1.0, float_matrix.m[1][2]);
193     EXPECT_DOUBLE_EQ(2.0, float_matrix.m[2][2]);
194 }
195 
TEST_F(SettingsTest,AddSettingVector)196 TEST_F(SettingsTest, AddSettingVector)
197 {
198     settings.add("test_setting", "[0, 1, 1,2, 3 , 5,  8,13]");
199     const std::vector<int> vector_int = settings.get<std::vector<int>>("test_setting");
200     const std::vector<int> ground_truth = {0, 1, 1, 2, 3, 5, 8, 13};
201     ASSERT_EQ(ground_truth.size(), vector_int.size());
202     for (size_t i = 0; i < ground_truth.size(); i++)
203     {
204         EXPECT_EQ(ground_truth[i], vector_int[i]);
205     }
206 }
207 
TEST_F(SettingsTest,OverwriteSetting)208 TEST_F(SettingsTest, OverwriteSetting)
209 {
210     settings.add("test_setting", "P");
211     settings.add("test_setting", "NP");
212     ASSERT_NE(settings.get<std::string>("test_setting"), std::string("P")) << "When overriding a setting, the value must be changed.";
213     ASSERT_EQ(settings.get<std::string>("test_setting"), std::string("NP"));
214 }
215 
TEST_F(SettingsTest,Inheritance)216 TEST_F(SettingsTest, Inheritance)
217 {
218     std::shared_ptr<Slice> current_slice = std::make_shared<Slice>(0);
219     Application::getInstance().current_slice = current_slice.get();
220 
221     const std::string value = "To be frank, I'd have to change my name.";
222     Settings parent;
223     parent.add("test_setting", value);
224     settings.setParent(&parent);
225 
226     EXPECT_EQ(value, settings.get<std::string>("test_setting"));
227 
228     const std::string override_value = "It's quick, it's easy and it's free: Pouring river water in your socks.";
229     settings.add("test_setting", override_value);
230     EXPECT_EQ(override_value, settings.get<std::string>("test_setting")) << "The new value overrides the one from the parent.";
231 }
232 
TEST_F(SettingsTest,LimitToExtruder)233 TEST_F(SettingsTest, LimitToExtruder)
234 {
235     std::shared_ptr<Slice> current_slice = std::make_shared<Slice>(0);
236     Application::getInstance().current_slice = current_slice.get();
237     current_slice->scene.extruders.emplace_back(0, nullptr);
238     current_slice->scene.extruders.emplace_back(1, nullptr);
239     current_slice->scene.extruders.emplace_back(2, nullptr);
240 
241     //Add a setting to the extruder this is limiting to.
242     const std::string limit_extruder_value = "I was gonna tell a time travelling joke but you didn't like it.";
243     current_slice->scene.extruders[2].settings.add("test_setting", limit_extruder_value);
244     current_slice->scene.limit_to_extruder.emplace("test_setting", &current_slice->scene.extruders[2]);
245 
246     //Add a decoy setting to the main scene to make sure that we aren't getting the global setting instead.
247     current_slice->scene.settings.add("test_setting", "Sting has been kidnapped. The Police have no lead.");
248 
249     EXPECT_EQ(limit_extruder_value, settings.get<std::string>("test_setting"));
250 }
251 
252 }
253