xref: /openbmc/phosphor-pid-control/test/pid_thermalcontroller_unittest.cpp (revision f8b6e55147148c3cfb42327ff267197a460b411c)
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