1 #include "conf.hpp"
2 #include "pid/ec/pid.hpp"
3 #include "pid/pidcontroller.hpp"
4 #include "pid/thermalcontroller.hpp"
5 #include "test/zone_mock.hpp"
6
7 #include <exception>
8 #include <memory>
9 #include <string>
10 #include <vector>
11
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14
15 namespace pid_control
16 {
17 namespace
18 {
19
20 using ::testing::_;
21 using ::testing::Return;
22 using ::testing::StrEq;
23
TEST(ThermalControllerTest,BoringFactoryTest)24 TEST(ThermalControllerTest, BoringFactoryTest)
25 {
26 // Verifies building a ThermalPIDController with the factory works as
27 // expected in the boring (uninteresting) case.
28
29 ZoneMock z;
30
31 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
32 double setpoint = 10.0;
33 ec::pidinfo initial;
34
35 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
36 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
37 // Success
38 EXPECT_FALSE(p == nullptr);
39 }
40
TEST(ThermalControllerTest,VerifyFactoryFailsWithZeroInputs)41 TEST(ThermalControllerTest, VerifyFactoryFailsWithZeroInputs)
42 {
43 // A thermal controller needs at least one input.
44
45 ZoneMock z;
46
47 std::vector<pid_control::conf::SensorInput> inputs = {};
48 double setpoint = 10.0;
49 ec::pidinfo initial;
50 std::unique_ptr<PIDController> p;
51 EXPECT_THROW(
52 {
53 p = ThermalController::createThermalPid(
54 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
55 },
56 std::exception);
57 EXPECT_TRUE(p == nullptr);
58 }
59
TEST(ThermalControllerTest,InputProc_BehavesAsExpected)60 TEST(ThermalControllerTest, InputProc_BehavesAsExpected)
61 {
62 // This test just verifies inputProc behaves as expected.
63
64 ZoneMock z;
65
66 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
67 double setpoint = 10.0;
68 ec::pidinfo initial;
69
70 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
71 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
72 EXPECT_FALSE(p == nullptr);
73
74 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
75
76 EXPECT_EQ(5.0, p->inputProc());
77 }
78
TEST(ThermalControllerTest,SetPtProc_BehavesAsExpected)79 TEST(ThermalControllerTest, SetPtProc_BehavesAsExpected)
80 {
81 // This test just verifies inputProc behaves as expected.
82
83 ZoneMock z;
84
85 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
86 double setpoint = 10.0;
87 ec::pidinfo initial;
88
89 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
90 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
91 EXPECT_FALSE(p == nullptr);
92
93 EXPECT_EQ(setpoint, p->setptProc());
94 }
95
TEST(ThermalControllerTest,OutputProc_BehavesAsExpected)96 TEST(ThermalControllerTest, OutputProc_BehavesAsExpected)
97 {
98 // This test just verifies outputProc behaves as expected.
99
100 ZoneMock z;
101
102 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
103 double setpoint = 10.0;
104 ec::pidinfo initial;
105
106 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
107 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
108 EXPECT_FALSE(p == nullptr);
109
110 double value = 90.0;
111 EXPECT_CALL(z, addSetPoint(value, "therm1"));
112
113 p->outputProc(value);
114 }
115
TEST(ThermalControllerTest,InputProc_MultipleInputsAbsolute)116 TEST(ThermalControllerTest, InputProc_MultipleInputsAbsolute)
117 {
118 // This test verifies inputProc behaves as expected with multiple absolute
119 // inputs.
120
121 ZoneMock z;
122
123 std::vector<pid_control::conf::SensorInput> inputs = {
124 {"fleeting0"}, {"fleeting1"}};
125 double setpoint = 10.0;
126 ec::pidinfo initial;
127
128 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
129 &z, "therm1", inputs, setpoint, initial, ThermalType::absolute);
130 EXPECT_FALSE(p == nullptr);
131
132 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
133 EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
134
135 EXPECT_EQ(10.0, p->inputProc());
136 }
137
TEST(ThermalControllerTest,InputProc_MultipleInputsMargin)138 TEST(ThermalControllerTest, InputProc_MultipleInputsMargin)
139 {
140 // This test verifies inputProc behaves as expected with multiple margin
141 // inputs.
142
143 ZoneMock z;
144
145 std::vector<pid_control::conf::SensorInput> inputs = {
146 {"fleeting0"}, {"fleeting1"}};
147 double setpoint = 10.0;
148 ec::pidinfo initial;
149
150 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
151 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
152 EXPECT_FALSE(p == nullptr);
153
154 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
155 EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
156
157 EXPECT_EQ(5.0, p->inputProc());
158 }
159
TEST(ThermalControllerTest,InputProc_MultipleInputsSummation)160 TEST(ThermalControllerTest, InputProc_MultipleInputsSummation)
161 {
162 // This test verifies inputProc behaves as expected with multiple summation
163 // inputs.
164
165 ZoneMock z;
166
167 std::vector<pid_control::conf::SensorInput> inputs = {
168 {"fleeting0"}, {"fleeting1"}};
169 double setpoint = 10.0;
170 ec::pidinfo initial;
171
172 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
173 &z, "therm1", inputs, setpoint, initial, ThermalType::summation);
174 EXPECT_FALSE(p == nullptr);
175
176 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
177 EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
178
179 EXPECT_EQ(15.0, p->inputProc());
180 }
181
TEST(ThermalControllerTest,InputProc_MultipleInputsTempToMargin)182 TEST(ThermalControllerTest, InputProc_MultipleInputsTempToMargin)
183 {
184 // This test verifies inputProc behaves as expected with multiple margin
185 // inputs and TempToMargin in use.
186
187 ZoneMock z;
188
189 std::vector<pid_control::conf::SensorInput> inputs = {
190 {"absolute0", 85.0, true}, {"margin1"}};
191 double setpoint = 10.0;
192 ec::pidinfo initial;
193
194 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
195 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
196 EXPECT_FALSE(p == nullptr);
197
198 EXPECT_CALL(z, getCachedValue(StrEq("absolute0"))).WillOnce(Return(82.0));
199 EXPECT_CALL(z, getCachedValue(StrEq("margin1"))).WillOnce(Return(5.0));
200
201 // 82 degrees temp, 85 degrees Tjmax => 3 degrees of safety margin
202 EXPECT_EQ(3.0, p->inputProc());
203 }
204
TEST(ThermalControllerTest,NegHysteresis_BehavesAsExpected)205 TEST(ThermalControllerTest, NegHysteresis_BehavesAsExpected)
206 {
207 // This test verifies Negative hysteresis behaves as expected by
208 // crossing the setpoint and noticing readings don't change until past the
209 // hysteresis value
210
211 ZoneMock z;
212
213 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
214 double setpoint = 10.0;
215 ec::pidinfo initial;
216 initial.checkHysterWithSetpt = false;
217 initial.negativeHysteresis = 4.0;
218
219 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
220 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
221 EXPECT_FALSE(p == nullptr);
222
223 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
224 .Times(3)
225 .WillOnce(Return(12.0))
226 .WillOnce(Return(9.0))
227 .WillOnce(Return(7.0));
228
229 EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
230
231 std::vector<double> lastReadings = {12.0, 12.0, 7.0};
232 for (auto& reading : lastReadings)
233 {
234 p->process();
235 EXPECT_EQ(p->getLastInput(), reading);
236 }
237 }
238
TEST(ThermalControllerTest,PosHysteresis_BehavesAsExpected)239 TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
240 {
241 // This test verifies Positive hysteresis behaves as expected by
242 // crossing the setpoint and noticing readings don't change until past the
243 // hysteresis value
244
245 ZoneMock z;
246
247 std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
248 double setpoint = 10.0;
249 ec::pidinfo initial;
250 initial.checkHysterWithSetpt = false;
251 initial.positiveHysteresis = 5.0;
252
253 std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
254 &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
255 EXPECT_FALSE(p == nullptr);
256
257 EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
258 .Times(3)
259 .WillOnce(Return(8.0))
260 .WillOnce(Return(13.0))
261 .WillOnce(Return(14.0));
262
263 EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
264
265 std::vector<double> lastReadings = {8.0, 8.0, 14.0};
266 for (auto& reading : lastReadings)
267 {
268 p->process();
269 EXPECT_EQ(p->getLastInput(), reading);
270 }
271 }
272
273 } // namespace
274 } // namespace pid_control
275