blob: f3302022c04ab904cf66463c9075e150d969f743 [file] [log] [blame]
Patrick Venture566a1512018-06-12 14:51:07 -07001#include "pid/ec/pid.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07002#include "pid/fancontroller.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07003#include "test/sensor_mock.hpp"
4#include "test/zone_mock.hpp"
5
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07006#include <string>
7#include <vector>
8
9#include <gmock/gmock.h>
10#include <gtest/gtest.h>
11
Patrick Venturea0764872020-08-08 07:48:43 -070012namespace pid_control
13{
14namespace
15{
16
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070017using ::testing::_;
Patrick Venture566a1512018-06-12 14:51:07 -070018using ::testing::DoubleEq;
19using ::testing::Invoke;
20using ::testing::Return;
21using ::testing::StrEq;
Patrick Venture566a1512018-06-12 14:51:07 -070022
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070023TEST(FanControllerTest, BoringFactoryTest)
24{
Patrick Venture566a1512018-06-12 14:51:07 -070025 // Verify the factory will properly build the FanPIDController in the
26 // boring (uninteresting) case.
27 ZoneMock z;
28
29 std::vector<std::string> inputs = {"fan0"};
30 ec::pidinfo initial;
31
32 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070033 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070034 // Success
35 EXPECT_FALSE(p == nullptr);
36}
37
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070038TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs)
39{
Patrick Venture566a1512018-06-12 14:51:07 -070040 // A fan controller needs at least one input.
41
42 ZoneMock z;
43
44 std::vector<std::string> inputs = {};
45 ec::pidinfo initial;
46
47 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070048 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070049 EXPECT_TRUE(p == nullptr);
50}
51
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070052TEST(FanControllerTest, InputProc_AllSensorsReturnZero)
53{
Patrick Venture566a1512018-06-12 14:51:07 -070054 // If all your inputs are 0, return 0.
55
56 ZoneMock z;
57
58 std::vector<std::string> inputs = {"fan0", "fan1"};
59 ec::pidinfo initial;
60
61 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070062 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070063 EXPECT_FALSE(p == nullptr);
64
65 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0));
66 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0));
67
Patrick Venture563a3562018-10-30 09:31:26 -070068 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070069}
70
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070071TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored)
72{
Patrick Venture566a1512018-06-12 14:51:07 -070073 // A sensor value returning sub-zero is ignored as an error.
74 ZoneMock z;
75
76 std::vector<std::string> inputs = {"fan0", "fan1"};
77 ec::pidinfo initial;
78
79 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070080 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070081 EXPECT_FALSE(p == nullptr);
82
83 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1));
84 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1));
85
Patrick Venture563a3562018-10-30 09:31:26 -070086 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070087}
88
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070089TEST(FanControllerTest, InputProc_ChoosesMinimumValue)
90{
Patrick Venture566a1512018-06-12 14:51:07 -070091 // Verify it selects the minimum value from its inputs.
92
93 ZoneMock z;
94
95 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"};
96 ec::pidinfo initial;
97
98 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070099 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700100 EXPECT_FALSE(p == nullptr);
101
102 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0));
103 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0));
104 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0));
105
Patrick Venture563a3562018-10-30 09:31:26 -0700106 EXPECT_EQ(5.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700107}
108
109// The direction is unused presently, but these tests validate the logic.
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700110TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection)
111{
Patrick Venture566a1512018-06-12 14:51:07 -0700112 // The fan direction defaults to neutral, because we have no data. Verify
113 // that after this point it appropriately will indicate speeding up or
114 // slowing down based on the RPM values specified.
115
116 ZoneMock z;
117
118 std::vector<std::string> inputs = {"fan0", "fan1"};
119 ec::pidinfo initial;
120
121 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700122 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700123 EXPECT_FALSE(p == nullptr);
124 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700125 FanController* fp = reinterpret_cast<FanController*>(p.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700126
127 // Fanspeed starts are Neutral.
128 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
129
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700130 // getMaxSetPointRequest returns a higher value than 0, so the fans should
131 // be marked as speeding up.
132 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(10.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700133 EXPECT_EQ(10.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700134 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection());
135
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700136 // getMaxSetPointRequest returns a lower value than 10, so the fans should
137 // be marked as slowing down.
138 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700139 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700140 EXPECT_EQ(FanSpeedDirection::DOWN, fp->getFanDirection());
141
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700142 // getMaxSetPointRequest returns the same value, so the fans should be
143 // marked as neutral.
144 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700145 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700146 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
147}
148
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700149TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored)
150{
Patrick Venture566a1512018-06-12 14:51:07 -0700151 // Verify that if failsafe mode is enabled and the input value for the fans
152 // is below the failsafe minimum value, the input is not used and the fans
Brandon Kimbcdeb832022-08-15 23:27:36 +0000153 // are driven at failsafe RPM (this assumes STRICT_FAILSAFE_PWM is not set)
Patrick Venture566a1512018-06-12 14:51:07 -0700154
155 ZoneMock z;
156
157 std::vector<std::string> inputs = {"fan0", "fan1"};
158 ec::pidinfo initial;
159
160 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700161 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700162 EXPECT_FALSE(p == nullptr);
163
164 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000165 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0));
Patrick Venture566a1512018-06-12 14:51:07 -0700166
167 int64_t timeout = 0;
168 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
169 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
170 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700171 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
172 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700173
Josh Lehana4146eb2020-10-01 11:49:09 -0700174 EXPECT_CALL(z, getRedundantWrite())
175 .WillOnce(Return(false))
176 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700177 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700178 EXPECT_CALL(*sm1, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700179 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700180 EXPECT_CALL(*sm2, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700181
Patrick Venture563a3562018-10-30 09:31:26 -0700182 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700183 // to the sensors.
184
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700185 // Setting 50%, will end up being 75% because the sensors are in failsafe
186 // mode.
Patrick Venture563a3562018-10-30 09:31:26 -0700187 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700188}
189
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700190TEST(FanControllerTest, OutputProc_BehavesAsExpected)
191{
Patrick Venture566a1512018-06-12 14:51:07 -0700192 // Verifies that when the system is not in failsafe mode, the input value
Patrick Venture563a3562018-10-30 09:31:26 -0700193 // to outputProc is used to drive the sensors (fans).
Patrick Venture566a1512018-06-12 14:51:07 -0700194
195 ZoneMock z;
196
197 std::vector<std::string> inputs = {"fan0", "fan1"};
198 ec::pidinfo initial;
199
200 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700201 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700202 EXPECT_FALSE(p == nullptr);
203
204 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
205
206 int64_t timeout = 0;
207 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
208 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
209 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700210 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
211 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700212
Josh Lehana4146eb2020-10-01 11:49:09 -0700213 EXPECT_CALL(z, getRedundantWrite())
214 .WillOnce(Return(false))
215 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700216 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700217 EXPECT_CALL(*sm1, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700218 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700219 EXPECT_CALL(*sm2, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700220
Patrick Venture563a3562018-10-30 09:31:26 -0700221 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700222 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700223 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700224}
225
Brandon Kimbcdeb832022-08-15 23:27:36 +0000226TEST(FanControllerTest, OutputProc_VerifyFailSafeWhenInputHigher)
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700227{
Brandon Kimbcdeb832022-08-15 23:27:36 +0000228 // If STRICT_FAILSAFE_PWM flag is NOT defined and the requested output is
229 // higher than the failsafe value, then use the value provided to outputProc
230 //
231 // If STRICT_FAILSAFE_PWM is defined, we expect the FailSafe PWM to be
232 // capped to the failsafe PWM, and not go higher than that.
Patrick Venture566a1512018-06-12 14:51:07 -0700233
234 ZoneMock z;
235
236 std::vector<std::string> inputs = {"fan0"};
237 ec::pidinfo initial;
Brandon Kimbcdeb832022-08-15 23:27:36 +0000238 const double failsafePWM = 75.0;
Patrick Venture566a1512018-06-12 14:51:07 -0700239
240 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700241 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700242 EXPECT_FALSE(p == nullptr);
243
244 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000245 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(failsafePWM));
Patrick Venture566a1512018-06-12 14:51:07 -0700246
247 int64_t timeout = 0;
248 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
249 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700250 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700251
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800252 double percent = 80;
Patrick Venture566a1512018-06-12 14:51:07 -0700253
Josh Lehana4146eb2020-10-01 11:49:09 -0700254 EXPECT_CALL(z, getRedundantWrite()).WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700255 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000256#ifdef STRICT_FAILSAFE_PWM
257 double failsafeValue = failsafePWM / 100;
258 EXPECT_CALL(*sm1, write(failsafeValue, false, _));
259#else
260 // Converting from double to double for expectation.
261 double value = percent / 100;
Josh Lehana4146eb2020-10-01 11:49:09 -0700262 EXPECT_CALL(*sm1, write(value, false, _));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000263#endif
Patrick Venture566a1512018-06-12 14:51:07 -0700264
Patrick Venture563a3562018-10-30 09:31:26 -0700265 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700266 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700267 p->outputProc(percent);
Patrick Venture566a1512018-06-12 14:51:07 -0700268}
Patrick Venturea0764872020-08-08 07:48:43 -0700269
Josh Lehana4146eb2020-10-01 11:49:09 -0700270TEST(FanControllerTest, OutputProc_VerifyRedundantWrites)
271{
272 /* when a zone indicates that redundant writes are enabled
273 * make sure the fan controller honors this by forcing a sensor write
274 */
275 ZoneMock z;
276
277 std::vector<std::string> inputs = {"fan0", "fan1"};
278 ec::pidinfo initial;
279
280 std::unique_ptr<PIDController> p =
281 FanController::createFanPid(&z, "fan1", inputs, initial);
282 EXPECT_FALSE(p == nullptr);
283
284 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
285
286 int64_t timeout = 0;
287 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
288 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
289 // Grab pointers for mocking.
290 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
291 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
292
293 EXPECT_CALL(z, getRedundantWrite())
294 .WillOnce(Return(true))
295 .WillOnce(Return(true));
296 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
297 EXPECT_CALL(*sm1, write(0.5, true, _));
298 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
299 EXPECT_CALL(*sm2, write(0.5, true, _));
300
301 // This is a fan PID, so calling outputProc will try to write this value
302 // to the sensors.
303 p->outputProc(50.0);
304}
305
Patrick Venturea0764872020-08-08 07:48:43 -0700306} // namespace
307} // namespace pid_control