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 
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 
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 
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 
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 
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 
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 = {{"fleeting0"},
122                                                           {"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 
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 = {{"fleeting0"},
144                                                           {"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 
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 = {{"fleeting0"},
166                                                           {"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 
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 
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.negativeHysteresis = 4.0;
215 
216     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
217         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
218     EXPECT_FALSE(p == nullptr);
219 
220     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
221         .Times(3)
222         .WillOnce(Return(12.0))
223         .WillOnce(Return(9.0))
224         .WillOnce(Return(7.0));
225 
226     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
227 
228     std::vector<double> lastReadings = {12.0, 12.0, 7.0};
229     for (auto& reading : lastReadings)
230     {
231         p->process();
232         EXPECT_EQ(p->getLastInput(), reading);
233     }
234 }
235 
236 TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
237 {
238     // This test verifies Positive hysteresis behaves as expected by
239     // crossing the setpoint and noticing readings don't change until past the
240     // hysteresis value
241 
242     ZoneMock z;
243 
244     std::vector<pid_control::conf::SensorInput> inputs = {{"fleeting0"}};
245     double setpoint = 10.0;
246     ec::pidinfo initial;
247     initial.positiveHysteresis = 5.0;
248 
249     std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
250         &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
251     EXPECT_FALSE(p == nullptr);
252 
253     EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
254         .Times(3)
255         .WillOnce(Return(8.0))
256         .WillOnce(Return(13.0))
257         .WillOnce(Return(14.0));
258 
259     EXPECT_CALL(z, addSetPoint(_, "therm1")).Times(3);
260 
261     std::vector<double> lastReadings = {8.0, 8.0, 14.0};
262     for (auto& reading : lastReadings)
263     {
264         p->process();
265         EXPECT_EQ(p->getLastInput(), reading);
266     }
267 }
268 
269 } // namespace
270 } // namespace pid_control
271