blob: 97dda0a4555a5e8ddb2b6ceb5fe5b89c4bb1dfd6 [file] [log] [blame]
Patrick Rudolph93660892023-10-13 14:18:46 +02001#include "config.h"
2
James Zheng6df8bb52024-11-27 23:38:47 +00003#include "failsafeloggers/builder.hpp"
4#include "failsafeloggers/failsafe_logger.hpp"
5#include "failsafeloggers/failsafe_logger_utility.hpp"
Josh Lehande745422020-11-07 02:14:09 -08006#include "pid/ec/logging.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07007#include "pid/ec/pid.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07008#include "pid/fancontroller.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07009#include "test/sensor_mock.hpp"
10#include "test/zone_mock.hpp"
11
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070012#include <string>
13#include <vector>
14
15#include <gmock/gmock.h>
16#include <gtest/gtest.h>
17
Patrick Venturea0764872020-08-08 07:48:43 -070018namespace pid_control
19{
20namespace
21{
22
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070023using ::testing::_;
Patrick Venture566a1512018-06-12 14:51:07 -070024using ::testing::DoubleEq;
25using ::testing::Invoke;
26using ::testing::Return;
27using ::testing::StrEq;
Patrick Venture566a1512018-06-12 14:51:07 -070028
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070029TEST(FanControllerTest, BoringFactoryTest)
30{
Patrick Venture566a1512018-06-12 14:51:07 -070031 // Verify the factory will properly build the FanPIDController in the
32 // boring (uninteresting) case.
33 ZoneMock z;
34
35 std::vector<std::string> inputs = {"fan0"};
36 ec::pidinfo initial;
37
38 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070039 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070040 // Success
41 EXPECT_FALSE(p == nullptr);
42}
43
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070044TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs)
45{
Patrick Venture566a1512018-06-12 14:51:07 -070046 // A fan controller needs at least one input.
47
48 ZoneMock z;
49
50 std::vector<std::string> inputs = {};
51 ec::pidinfo initial;
52
53 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070054 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070055 EXPECT_TRUE(p == nullptr);
56}
57
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070058TEST(FanControllerTest, InputProc_AllSensorsReturnZero)
59{
Patrick Venture566a1512018-06-12 14:51:07 -070060 // If all your inputs are 0, return 0.
61
62 ZoneMock z;
63
64 std::vector<std::string> inputs = {"fan0", "fan1"};
65 ec::pidinfo initial;
66
67 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070068 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070069 EXPECT_FALSE(p == nullptr);
70
71 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0));
72 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0));
73
Patrick Venture563a3562018-10-30 09:31:26 -070074 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070075}
76
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070077TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored)
78{
Patrick Venture566a1512018-06-12 14:51:07 -070079 // A sensor value returning sub-zero is ignored as an error.
80 ZoneMock z;
81
82 std::vector<std::string> inputs = {"fan0", "fan1"};
83 ec::pidinfo initial;
84
85 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070086 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070087 EXPECT_FALSE(p == nullptr);
88
89 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1));
90 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1));
91
Patrick Venture563a3562018-10-30 09:31:26 -070092 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070093}
94
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070095TEST(FanControllerTest, InputProc_ChoosesMinimumValue)
96{
Patrick Venture566a1512018-06-12 14:51:07 -070097 // Verify it selects the minimum value from its inputs.
98
99 ZoneMock z;
100
101 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"};
102 ec::pidinfo initial;
103
104 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700105 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700106 EXPECT_FALSE(p == nullptr);
107
108 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0));
109 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0));
110 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0));
111
Patrick Venture563a3562018-10-30 09:31:26 -0700112 EXPECT_EQ(5.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700113}
114
115// The direction is unused presently, but these tests validate the logic.
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700116TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection)
117{
Patrick Venture566a1512018-06-12 14:51:07 -0700118 // The fan direction defaults to neutral, because we have no data. Verify
119 // that after this point it appropriately will indicate speeding up or
120 // slowing down based on the RPM values specified.
121
122 ZoneMock z;
123
124 std::vector<std::string> inputs = {"fan0", "fan1"};
125 ec::pidinfo initial;
126
127 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700128 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700129 EXPECT_FALSE(p == nullptr);
130 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700131 FanController* fp = reinterpret_cast<FanController*>(p.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700132
133 // Fanspeed starts are Neutral.
134 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
135
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700136 // getMaxSetPointRequest returns a higher value than 0, so the fans should
137 // be marked as speeding up.
138 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(10.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700139 EXPECT_EQ(10.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700140 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection());
141
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700142 // getMaxSetPointRequest returns a lower value than 10, so the fans should
143 // be marked as slowing down.
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::DOWN, fp->getFanDirection());
147
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700148 // getMaxSetPointRequest returns the same value, so the fans should be
149 // marked as neutral.
150 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700151 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700152 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
153}
154
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700155TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored)
156{
Patrick Venture566a1512018-06-12 14:51:07 -0700157 // Verify that if failsafe mode is enabled and the input value for the fans
158 // is below the failsafe minimum value, the input is not used and the fans
Brandon Kimbcdeb832022-08-15 23:27:36 +0000159 // are driven at failsafe RPM (this assumes STRICT_FAILSAFE_PWM is not set)
Patrick Venture566a1512018-06-12 14:51:07 -0700160
161 ZoneMock z;
162
163 std::vector<std::string> inputs = {"fan0", "fan1"};
164 ec::pidinfo initial;
165
166 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700167 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700168 EXPECT_FALSE(p == nullptr);
169
170 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000171 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0));
Patrick Venture566a1512018-06-12 14:51:07 -0700172
173 int64_t timeout = 0;
174 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
175 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
176 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700177 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
178 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700179
Josh Lehana4146eb2020-10-01 11:49:09 -0700180 EXPECT_CALL(z, getRedundantWrite())
181 .WillOnce(Return(false))
182 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700183 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700184 EXPECT_CALL(*sm1, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700185 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700186 EXPECT_CALL(*sm2, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700187
Patrick Venture563a3562018-10-30 09:31:26 -0700188 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700189 // to the sensors.
190
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700191 // Setting 50%, will end up being 75% because the sensors are in failsafe
192 // mode.
Patrick Venture563a3562018-10-30 09:31:26 -0700193 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700194}
195
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700196TEST(FanControllerTest, OutputProc_BehavesAsExpected)
197{
Patrick Venture566a1512018-06-12 14:51:07 -0700198 // Verifies that when the system is not in failsafe mode, the input value
Patrick Venture563a3562018-10-30 09:31:26 -0700199 // to outputProc is used to drive the sensors (fans).
Patrick Venture566a1512018-06-12 14:51:07 -0700200
201 ZoneMock z;
202
203 std::vector<std::string> inputs = {"fan0", "fan1"};
204 ec::pidinfo initial;
205
206 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700207 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700208 EXPECT_FALSE(p == nullptr);
209
210 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
211
212 int64_t timeout = 0;
213 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
214 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
215 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700216 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
217 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700218
Josh Lehana4146eb2020-10-01 11:49:09 -0700219 EXPECT_CALL(z, getRedundantWrite())
220 .WillOnce(Return(false))
221 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700222 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700223 EXPECT_CALL(*sm1, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700224 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700225 EXPECT_CALL(*sm2, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700226
Patrick Venture563a3562018-10-30 09:31:26 -0700227 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700228 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700229 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700230}
231
Brandon Kimbcdeb832022-08-15 23:27:36 +0000232TEST(FanControllerTest, OutputProc_VerifyFailSafeWhenInputHigher)
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700233{
Brandon Kimbcdeb832022-08-15 23:27:36 +0000234 // If STRICT_FAILSAFE_PWM flag is NOT defined and the requested output is
235 // higher than the failsafe value, then use the value provided to outputProc
236 //
237 // If STRICT_FAILSAFE_PWM is defined, we expect the FailSafe PWM to be
238 // capped to the failsafe PWM, and not go higher than that.
Patrick Venture566a1512018-06-12 14:51:07 -0700239
240 ZoneMock z;
241
242 std::vector<std::string> inputs = {"fan0"};
243 ec::pidinfo initial;
Brandon Kimbcdeb832022-08-15 23:27:36 +0000244 const double failsafePWM = 75.0;
Patrick Venture566a1512018-06-12 14:51:07 -0700245
246 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700247 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700248 EXPECT_FALSE(p == nullptr);
249
250 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000251 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(failsafePWM));
Patrick Venture566a1512018-06-12 14:51:07 -0700252
253 int64_t timeout = 0;
254 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
255 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700256 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700257
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800258 double percent = 80;
Patrick Venture566a1512018-06-12 14:51:07 -0700259
Josh Lehana4146eb2020-10-01 11:49:09 -0700260 EXPECT_CALL(z, getRedundantWrite()).WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700261 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000262#ifdef STRICT_FAILSAFE_PWM
263 double failsafeValue = failsafePWM / 100;
264 EXPECT_CALL(*sm1, write(failsafeValue, false, _));
265#else
266 // Converting from double to double for expectation.
267 double value = percent / 100;
Josh Lehana4146eb2020-10-01 11:49:09 -0700268 EXPECT_CALL(*sm1, write(value, false, _));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000269#endif
Patrick Venture566a1512018-06-12 14:51:07 -0700270
Patrick Venture563a3562018-10-30 09:31:26 -0700271 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700272 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700273 p->outputProc(percent);
Patrick Venture566a1512018-06-12 14:51:07 -0700274}
Patrick Venturea0764872020-08-08 07:48:43 -0700275
Josh Lehana4146eb2020-10-01 11:49:09 -0700276TEST(FanControllerTest, OutputProc_VerifyRedundantWrites)
277{
278 /* when a zone indicates that redundant writes are enabled
279 * make sure the fan controller honors this by forcing a sensor write
280 */
281 ZoneMock z;
282
283 std::vector<std::string> inputs = {"fan0", "fan1"};
284 ec::pidinfo initial;
285
286 std::unique_ptr<PIDController> p =
287 FanController::createFanPid(&z, "fan1", inputs, initial);
288 EXPECT_FALSE(p == nullptr);
289
290 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
291
292 int64_t timeout = 0;
293 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
294 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
295 // Grab pointers for mocking.
296 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
297 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
298
299 EXPECT_CALL(z, getRedundantWrite())
300 .WillOnce(Return(true))
301 .WillOnce(Return(true));
302 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
303 EXPECT_CALL(*sm1, write(0.5, true, _));
304 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
305 EXPECT_CALL(*sm2, write(0.5, true, _));
306
307 // This is a fan PID, so calling outputProc will try to write this value
308 // to the sensors.
309 p->outputProc(50.0);
310}
311
Patrick Venturea0764872020-08-08 07:48:43 -0700312} // namespace
313} // namespace pid_control