xref: /openbmc/phosphor-pid-control/test/pid_thermalcontroller_unittest.cpp (revision 572c43dab62f9e207781b96e7e48b7b38ca024e5)
1  #include "pid/ec/pid.hpp"
2  #include "pid/thermalcontroller.hpp"
3  #include "test/zone_mock.hpp"
4  
5  #include <string>
6  #include <vector>
7  
8  #include <gmock/gmock.h>
9  #include <gtest/gtest.h>
10  
11  using ::testing::_;
12  using ::testing::Return;
13  using ::testing::StrEq;
14  
15  TEST(ThermalControllerTest, BoringFactoryTest)
16  {
17      // Verifies building a ThermalPIDController with the factory works as
18      // expected in the boring (uninteresting) case.
19  
20      ZoneMock z;
21  
22      std::vector<std::string> inputs = {"fleeting0"};
23      double setpoint = 10.0;
24      ec::pidinfo initial;
25  
26      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
27          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
28      // Success
29      EXPECT_FALSE(p == nullptr);
30  }
31  
32  TEST(ThermalControllerTest, VerifyFactoryFailsWithZeroInputs)
33  {
34      // A thermal controller needs at least one input.
35  
36      ZoneMock z;
37  
38      std::vector<std::string> inputs = {};
39      double setpoint = 10.0;
40      ec::pidinfo initial;
41      std::unique_ptr<PIDController> p;
42      EXPECT_THROW(
43          {
44              p = ThermalController::createThermalPid(
45                  &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
46          },
47          std::exception);
48      EXPECT_TRUE(p == nullptr);
49  }
50  
51  TEST(ThermalControllerTest, InputProc_BehavesAsExpected)
52  {
53      // This test just verifies inputProc behaves as expected.
54  
55      ZoneMock z;
56  
57      std::vector<std::string> inputs = {"fleeting0"};
58      double setpoint = 10.0;
59      ec::pidinfo initial;
60  
61      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
62          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
63      EXPECT_FALSE(p == nullptr);
64  
65      EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
66  
67      EXPECT_EQ(5.0, p->inputProc());
68  }
69  
70  TEST(ThermalControllerTest, SetPtProc_BehavesAsExpected)
71  {
72      // This test just verifies inputProc behaves as expected.
73  
74      ZoneMock z;
75  
76      std::vector<std::string> inputs = {"fleeting0"};
77      double setpoint = 10.0;
78      ec::pidinfo initial;
79  
80      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
81          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
82      EXPECT_FALSE(p == nullptr);
83  
84      EXPECT_EQ(setpoint, p->setptProc());
85  }
86  
87  TEST(ThermalControllerTest, OutputProc_BehavesAsExpected)
88  {
89      // This test just verifies outputProc behaves as expected.
90  
91      ZoneMock z;
92  
93      std::vector<std::string> inputs = {"fleeting0"};
94      double setpoint = 10.0;
95      ec::pidinfo initial;
96  
97      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
98          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
99      EXPECT_FALSE(p == nullptr);
100  
101      double value = 90.0;
102      EXPECT_CALL(z, addRPMSetPoint(value));
103  
104      p->outputProc(value);
105  }
106  
107  TEST(ThermalControllerTest, InputProc_MultipleInputsAbsolute)
108  {
109      // This test verifies inputProc behaves as expected with multiple absolute
110      // inputs.
111  
112      ZoneMock z;
113  
114      std::vector<std::string> inputs = {"fleeting0", "fleeting1"};
115      double setpoint = 10.0;
116      ec::pidinfo initial;
117  
118      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
119          &z, "therm1", inputs, setpoint, initial, ThermalType::absolute);
120      EXPECT_FALSE(p == nullptr);
121  
122      EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
123      EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
124  
125      EXPECT_EQ(10.0, p->inputProc());
126  }
127  
128  TEST(ThermalControllerTest, InputProc_MultipleInputsMargin)
129  {
130      // This test verifies inputProc behaves as expected with multiple margin
131      // inputs.
132  
133      ZoneMock z;
134  
135      std::vector<std::string> inputs = {"fleeting0", "fleeting1"};
136      double setpoint = 10.0;
137      ec::pidinfo initial;
138  
139      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
140          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
141      EXPECT_FALSE(p == nullptr);
142  
143      EXPECT_CALL(z, getCachedValue(StrEq("fleeting0"))).WillOnce(Return(5.0));
144      EXPECT_CALL(z, getCachedValue(StrEq("fleeting1"))).WillOnce(Return(10.0));
145  
146      EXPECT_EQ(5.0, p->inputProc());
147  }
148  
149  TEST(ThermalControllerTest, NegHysteresis_BehavesAsExpected)
150  {
151  
152      // This test verifies Negative hysteresis behaves as expected by
153      // crossing the setpoint and noticing readings don't change until past the
154      // hysteresis value
155  
156      ZoneMock z;
157  
158      std::vector<std::string> inputs = {"fleeting0"};
159      double setpoint = 10.0;
160      ec::pidinfo initial;
161      initial.negativeHysteresis = 4.0;
162  
163      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
164          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
165      EXPECT_FALSE(p == nullptr);
166  
167      EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
168          .Times(3)
169          .WillOnce(Return(12.0))
170          .WillOnce(Return(9.0))
171          .WillOnce(Return(7.0));
172  
173      EXPECT_CALL(z, addRPMSetPoint(_)).Times(3);
174  
175      std::vector<double> lastReadings = {12.0, 12.0, 7.0};
176      for (auto& reading : lastReadings)
177      {
178          p->process();
179          EXPECT_EQ(p->getLastInput(), reading);
180      }
181  }
182  
183  TEST(ThermalControllerTest, PosHysteresis_BehavesAsExpected)
184  {
185      // This test verifies Positive hysteresis behaves as expected by
186      // crossing the setpoint and noticing readings don't change until past the
187      // hysteresis value
188  
189      ZoneMock z;
190  
191      std::vector<std::string> inputs = {"fleeting0"};
192      double setpoint = 10.0;
193      ec::pidinfo initial;
194      initial.positiveHysteresis = 5.0;
195  
196      std::unique_ptr<PIDController> p = ThermalController::createThermalPid(
197          &z, "therm1", inputs, setpoint, initial, ThermalType::margin);
198      EXPECT_FALSE(p == nullptr);
199  
200      EXPECT_CALL(z, getCachedValue(StrEq("fleeting0")))
201          .Times(3)
202          .WillOnce(Return(8.0))
203          .WillOnce(Return(13.0))
204          .WillOnce(Return(14.0));
205  
206      EXPECT_CALL(z, addRPMSetPoint(_)).Times(3);
207  
208      std::vector<double> lastReadings = {8.0, 8.0, 14.0};
209      for (auto& reading : lastReadings)
210      {
211          p->process();
212          EXPECT_EQ(p->getLastInput(), reading);
213      }
214  }