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