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