blob: 8379113949b32fd8bd6e22648e70e2da57593183 [file] [log] [blame]
Josh Lehande745422020-11-07 02:14:09 -08001#include "pid/ec/logging.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07002#include "pid/ec/pid.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07003#include "pid/fancontroller.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07004#include "test/sensor_mock.hpp"
5#include "test/zone_mock.hpp"
6
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07007#include <string>
8#include <vector>
9
10#include <gmock/gmock.h>
11#include <gtest/gtest.h>
12
Patrick Venturea0764872020-08-08 07:48:43 -070013namespace pid_control
14{
15namespace
16{
17
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070018using ::testing::_;
Patrick Venture566a1512018-06-12 14:51:07 -070019using ::testing::DoubleEq;
20using ::testing::Invoke;
21using ::testing::Return;
22using ::testing::StrEq;
Patrick Venture566a1512018-06-12 14:51:07 -070023
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070024TEST(FanControllerTest, BoringFactoryTest)
25{
Patrick Venture566a1512018-06-12 14:51:07 -070026 // Verify the factory will properly build the FanPIDController in the
27 // boring (uninteresting) case.
28 ZoneMock z;
29
30 std::vector<std::string> inputs = {"fan0"};
31 ec::pidinfo initial;
32
33 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070034 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070035 // Success
36 EXPECT_FALSE(p == nullptr);
37}
38
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070039TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs)
40{
Patrick Venture566a1512018-06-12 14:51:07 -070041 // A fan controller needs at least one input.
42
43 ZoneMock z;
44
45 std::vector<std::string> inputs = {};
46 ec::pidinfo initial;
47
48 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070049 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070050 EXPECT_TRUE(p == nullptr);
51}
52
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070053TEST(FanControllerTest, InputProc_AllSensorsReturnZero)
54{
Patrick Venture566a1512018-06-12 14:51:07 -070055 // If all your inputs are 0, return 0.
56
57 ZoneMock z;
58
59 std::vector<std::string> inputs = {"fan0", "fan1"};
60 ec::pidinfo initial;
61
62 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070063 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070064 EXPECT_FALSE(p == nullptr);
65
66 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0));
67 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0));
68
Patrick Venture563a3562018-10-30 09:31:26 -070069 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070070}
71
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070072TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored)
73{
Patrick Venture566a1512018-06-12 14:51:07 -070074 // A sensor value returning sub-zero is ignored as an error.
75 ZoneMock z;
76
77 std::vector<std::string> inputs = {"fan0", "fan1"};
78 ec::pidinfo initial;
79
80 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070081 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070082 EXPECT_FALSE(p == nullptr);
83
84 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1));
85 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1));
86
Patrick Venture563a3562018-10-30 09:31:26 -070087 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070088}
89
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070090TEST(FanControllerTest, InputProc_ChoosesMinimumValue)
91{
Patrick Venture566a1512018-06-12 14:51:07 -070092 // Verify it selects the minimum value from its inputs.
93
94 ZoneMock z;
95
96 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"};
97 ec::pidinfo initial;
98
99 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700100 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700101 EXPECT_FALSE(p == nullptr);
102
103 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0));
104 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0));
105 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0));
106
Patrick Venture563a3562018-10-30 09:31:26 -0700107 EXPECT_EQ(5.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700108}
109
110// The direction is unused presently, but these tests validate the logic.
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700111TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection)
112{
Patrick Venture566a1512018-06-12 14:51:07 -0700113 // The fan direction defaults to neutral, because we have no data. Verify
114 // that after this point it appropriately will indicate speeding up or
115 // slowing down based on the RPM values specified.
116
117 ZoneMock z;
118
119 std::vector<std::string> inputs = {"fan0", "fan1"};
120 ec::pidinfo initial;
121
122 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700123 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700124 EXPECT_FALSE(p == nullptr);
125 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700126 FanController* fp = reinterpret_cast<FanController*>(p.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700127
128 // Fanspeed starts are Neutral.
129 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
130
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700131 // getMaxSetPointRequest returns a higher value than 0, so the fans should
132 // be marked as speeding up.
133 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(10.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700134 EXPECT_EQ(10.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700135 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection());
136
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700137 // getMaxSetPointRequest returns a lower value than 10, so the fans should
138 // be marked as slowing down.
139 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700140 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700141 EXPECT_EQ(FanSpeedDirection::DOWN, fp->getFanDirection());
142
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700143 // getMaxSetPointRequest returns the same value, so the fans should be
144 // marked as neutral.
145 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700146 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700147 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
148}
149
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700150TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored)
151{
Patrick Venture566a1512018-06-12 14:51:07 -0700152 // Verify that if failsafe mode is enabled and the input value for the fans
153 // is below the failsafe minimum value, the input is not used and the fans
Brandon Kimbcdeb832022-08-15 23:27:36 +0000154 // are driven at failsafe RPM (this assumes STRICT_FAILSAFE_PWM is not set)
Patrick Venture566a1512018-06-12 14:51:07 -0700155
156 ZoneMock z;
157
158 std::vector<std::string> inputs = {"fan0", "fan1"};
159 ec::pidinfo initial;
160
161 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700162 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700163 EXPECT_FALSE(p == nullptr);
164
165 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000166 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0));
Patrick Venture566a1512018-06-12 14:51:07 -0700167
168 int64_t timeout = 0;
169 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
170 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
171 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700172 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
173 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700174
Josh Lehana4146eb2020-10-01 11:49:09 -0700175 EXPECT_CALL(z, getRedundantWrite())
176 .WillOnce(Return(false))
177 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700178 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700179 EXPECT_CALL(*sm1, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700180 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700181 EXPECT_CALL(*sm2, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700182
Patrick Venture563a3562018-10-30 09:31:26 -0700183 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700184 // to the sensors.
185
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700186 // Setting 50%, will end up being 75% because the sensors are in failsafe
187 // mode.
Patrick Venture563a3562018-10-30 09:31:26 -0700188 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700189}
190
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700191TEST(FanControllerTest, OutputProc_BehavesAsExpected)
192{
Patrick Venture566a1512018-06-12 14:51:07 -0700193 // Verifies that when the system is not in failsafe mode, the input value
Patrick Venture563a3562018-10-30 09:31:26 -0700194 // to outputProc is used to drive the sensors (fans).
Patrick Venture566a1512018-06-12 14:51:07 -0700195
196 ZoneMock z;
197
198 std::vector<std::string> inputs = {"fan0", "fan1"};
199 ec::pidinfo initial;
200
201 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700202 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700203 EXPECT_FALSE(p == nullptr);
204
205 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
206
207 int64_t timeout = 0;
208 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
209 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
210 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700211 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
212 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700213
Josh Lehana4146eb2020-10-01 11:49:09 -0700214 EXPECT_CALL(z, getRedundantWrite())
215 .WillOnce(Return(false))
216 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700217 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700218 EXPECT_CALL(*sm1, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700219 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700220 EXPECT_CALL(*sm2, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700221
Patrick Venture563a3562018-10-30 09:31:26 -0700222 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700223 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700224 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700225}
226
Brandon Kimbcdeb832022-08-15 23:27:36 +0000227TEST(FanControllerTest, OutputProc_VerifyFailSafeWhenInputHigher)
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700228{
Brandon Kimbcdeb832022-08-15 23:27:36 +0000229 // If STRICT_FAILSAFE_PWM flag is NOT defined and the requested output is
230 // higher than the failsafe value, then use the value provided to outputProc
231 //
232 // If STRICT_FAILSAFE_PWM is defined, we expect the FailSafe PWM to be
233 // capped to the failsafe PWM, and not go higher than that.
Patrick Venture566a1512018-06-12 14:51:07 -0700234
235 ZoneMock z;
236
237 std::vector<std::string> inputs = {"fan0"};
238 ec::pidinfo initial;
Brandon Kimbcdeb832022-08-15 23:27:36 +0000239 const double failsafePWM = 75.0;
Patrick Venture566a1512018-06-12 14:51:07 -0700240
241 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700242 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700243 EXPECT_FALSE(p == nullptr);
244
245 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000246 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(failsafePWM));
Patrick Venture566a1512018-06-12 14:51:07 -0700247
248 int64_t timeout = 0;
249 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
250 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700251 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700252
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800253 double percent = 80;
Patrick Venture566a1512018-06-12 14:51:07 -0700254
Josh Lehana4146eb2020-10-01 11:49:09 -0700255 EXPECT_CALL(z, getRedundantWrite()).WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700256 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000257#ifdef STRICT_FAILSAFE_PWM
258 double failsafeValue = failsafePWM / 100;
259 EXPECT_CALL(*sm1, write(failsafeValue, false, _));
260#else
261 // Converting from double to double for expectation.
262 double value = percent / 100;
Josh Lehana4146eb2020-10-01 11:49:09 -0700263 EXPECT_CALL(*sm1, write(value, false, _));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000264#endif
Patrick Venture566a1512018-06-12 14:51:07 -0700265
Patrick Venture563a3562018-10-30 09:31:26 -0700266 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700267 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700268 p->outputProc(percent);
Patrick Venture566a1512018-06-12 14:51:07 -0700269}
Patrick Venturea0764872020-08-08 07:48:43 -0700270
Josh Lehana4146eb2020-10-01 11:49:09 -0700271TEST(FanControllerTest, OutputProc_VerifyRedundantWrites)
272{
273 /* when a zone indicates that redundant writes are enabled
274 * make sure the fan controller honors this by forcing a sensor write
275 */
276 ZoneMock z;
277
278 std::vector<std::string> inputs = {"fan0", "fan1"};
279 ec::pidinfo initial;
280
281 std::unique_ptr<PIDController> p =
282 FanController::createFanPid(&z, "fan1", inputs, initial);
283 EXPECT_FALSE(p == nullptr);
284
285 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
286
287 int64_t timeout = 0;
288 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
289 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
290 // Grab pointers for mocking.
291 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
292 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
293
294 EXPECT_CALL(z, getRedundantWrite())
295 .WillOnce(Return(true))
296 .WillOnce(Return(true));
297 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
298 EXPECT_CALL(*sm1, write(0.5, true, _));
299 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
300 EXPECT_CALL(*sm2, write(0.5, true, _));
301
302 // This is a fan PID, so calling outputProc will try to write this value
303 // to the sensors.
304 p->outputProc(50.0);
305}
306
Patrick Venturea0764872020-08-08 07:48:43 -0700307} // namespace
308} // namespace pid_control