blob: 6075a953089fd68f4e99938603af511446d972a6 [file] [log] [blame]
Patrick Rudolph93660892023-10-13 14:18:46 +02001#include "config.h"
2
Josh Lehande745422020-11-07 02:14:09 -08003#include "pid/ec/logging.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07004#include "pid/ec/pid.hpp"
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07005#include "pid/fancontroller.hpp"
Patrick Venture566a1512018-06-12 14:51:07 -07006#include "test/sensor_mock.hpp"
7#include "test/zone_mock.hpp"
8
Patrick Ventureda4a5dd2018-08-31 09:42:48 -07009#include <string>
10#include <vector>
11
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
Patrick Venturea0764872020-08-08 07:48:43 -070015namespace pid_control
16{
17namespace
18{
19
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070020using ::testing::_;
Patrick Venture566a1512018-06-12 14:51:07 -070021using ::testing::DoubleEq;
22using ::testing::Invoke;
23using ::testing::Return;
24using ::testing::StrEq;
Patrick Venture566a1512018-06-12 14:51:07 -070025
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070026TEST(FanControllerTest, BoringFactoryTest)
27{
Patrick Venture566a1512018-06-12 14:51:07 -070028 // Verify the factory will properly build the FanPIDController in the
29 // boring (uninteresting) case.
30 ZoneMock z;
31
32 std::vector<std::string> inputs = {"fan0"};
33 ec::pidinfo initial;
34
35 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070036 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070037 // Success
38 EXPECT_FALSE(p == nullptr);
39}
40
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070041TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs)
42{
Patrick Venture566a1512018-06-12 14:51:07 -070043 // A fan controller needs at least one input.
44
45 ZoneMock z;
46
47 std::vector<std::string> inputs = {};
48 ec::pidinfo initial;
49
50 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070051 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070052 EXPECT_TRUE(p == nullptr);
53}
54
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070055TEST(FanControllerTest, InputProc_AllSensorsReturnZero)
56{
Patrick Venture566a1512018-06-12 14:51:07 -070057 // If all your inputs are 0, return 0.
58
59 ZoneMock z;
60
61 std::vector<std::string> inputs = {"fan0", "fan1"};
62 ec::pidinfo initial;
63
64 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070065 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070066 EXPECT_FALSE(p == nullptr);
67
68 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0));
69 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0));
70
Patrick Venture563a3562018-10-30 09:31:26 -070071 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070072}
73
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070074TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored)
75{
Patrick Venture566a1512018-06-12 14:51:07 -070076 // A sensor value returning sub-zero is ignored as an error.
77 ZoneMock z;
78
79 std::vector<std::string> inputs = {"fan0", "fan1"};
80 ec::pidinfo initial;
81
82 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -070083 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -070084 EXPECT_FALSE(p == nullptr);
85
86 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1));
87 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1));
88
Patrick Venture563a3562018-10-30 09:31:26 -070089 EXPECT_EQ(0.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -070090}
91
Patrick Ventureda4a5dd2018-08-31 09:42:48 -070092TEST(FanControllerTest, InputProc_ChoosesMinimumValue)
93{
Patrick Venture566a1512018-06-12 14:51:07 -070094 // Verify it selects the minimum value from its inputs.
95
96 ZoneMock z;
97
98 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"};
99 ec::pidinfo initial;
100
101 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700102 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700103 EXPECT_FALSE(p == nullptr);
104
105 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0));
106 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0));
107 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0));
108
Patrick Venture563a3562018-10-30 09:31:26 -0700109 EXPECT_EQ(5.0, p->inputProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700110}
111
112// The direction is unused presently, but these tests validate the logic.
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700113TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection)
114{
Patrick Venture566a1512018-06-12 14:51:07 -0700115 // The fan direction defaults to neutral, because we have no data. Verify
116 // that after this point it appropriately will indicate speeding up or
117 // slowing down based on the RPM values specified.
118
119 ZoneMock z;
120
121 std::vector<std::string> inputs = {"fan0", "fan1"};
122 ec::pidinfo initial;
123
124 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700125 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700126 EXPECT_FALSE(p == nullptr);
127 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700128 FanController* fp = reinterpret_cast<FanController*>(p.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700129
130 // Fanspeed starts are Neutral.
131 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
132
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700133 // getMaxSetPointRequest returns a higher value than 0, so the fans should
134 // be marked as speeding up.
135 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(10.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700136 EXPECT_EQ(10.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700137 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection());
138
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700139 // getMaxSetPointRequest returns a lower value than 10, so the fans should
140 // be marked as slowing down.
141 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700142 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700143 EXPECT_EQ(FanSpeedDirection::DOWN, fp->getFanDirection());
144
Patrick Venturef7a2dd52019-07-16 14:31:13 -0700145 // getMaxSetPointRequest returns the same value, so the fans should be
146 // marked as neutral.
147 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0));
Patrick Venture563a3562018-10-30 09:31:26 -0700148 EXPECT_EQ(5.0, p->setptProc());
Patrick Venture566a1512018-06-12 14:51:07 -0700149 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection());
150}
151
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700152TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored)
153{
Patrick Venture566a1512018-06-12 14:51:07 -0700154 // Verify that if failsafe mode is enabled and the input value for the fans
155 // is below the failsafe minimum value, the input is not used and the fans
Brandon Kimbcdeb832022-08-15 23:27:36 +0000156 // are driven at failsafe RPM (this assumes STRICT_FAILSAFE_PWM is not set)
Patrick Venture566a1512018-06-12 14:51:07 -0700157
158 ZoneMock z;
159
160 std::vector<std::string> inputs = {"fan0", "fan1"};
161 ec::pidinfo initial;
162
163 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700164 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700165 EXPECT_FALSE(p == nullptr);
166
167 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000168 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0));
Patrick Venture566a1512018-06-12 14:51:07 -0700169
170 int64_t timeout = 0;
171 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
172 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
173 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700174 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
175 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700176
Josh Lehana4146eb2020-10-01 11:49:09 -0700177 EXPECT_CALL(z, getRedundantWrite())
178 .WillOnce(Return(false))
179 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700180 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700181 EXPECT_CALL(*sm1, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700182 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700183 EXPECT_CALL(*sm2, write(0.75, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700184
Patrick Venture563a3562018-10-30 09:31:26 -0700185 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700186 // to the sensors.
187
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700188 // Setting 50%, will end up being 75% because the sensors are in failsafe
189 // mode.
Patrick Venture563a3562018-10-30 09:31:26 -0700190 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700191}
192
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700193TEST(FanControllerTest, OutputProc_BehavesAsExpected)
194{
Patrick Venture566a1512018-06-12 14:51:07 -0700195 // Verifies that when the system is not in failsafe mode, the input value
Patrick Venture563a3562018-10-30 09:31:26 -0700196 // to outputProc is used to drive the sensors (fans).
Patrick Venture566a1512018-06-12 14:51:07 -0700197
198 ZoneMock z;
199
200 std::vector<std::string> inputs = {"fan0", "fan1"};
201 ec::pidinfo initial;
202
203 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700204 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700205 EXPECT_FALSE(p == nullptr);
206
207 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
208
209 int64_t timeout = 0;
210 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
211 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
212 // Grab pointers for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700213 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
214 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700215
Josh Lehana4146eb2020-10-01 11:49:09 -0700216 EXPECT_CALL(z, getRedundantWrite())
217 .WillOnce(Return(false))
218 .WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700219 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700220 EXPECT_CALL(*sm1, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700221 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
Josh Lehana4146eb2020-10-01 11:49:09 -0700222 EXPECT_CALL(*sm2, write(0.5, false, _));
Patrick Venture566a1512018-06-12 14:51:07 -0700223
Patrick Venture563a3562018-10-30 09:31:26 -0700224 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700225 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700226 p->outputProc(50.0);
Patrick Venture566a1512018-06-12 14:51:07 -0700227}
228
Brandon Kimbcdeb832022-08-15 23:27:36 +0000229TEST(FanControllerTest, OutputProc_VerifyFailSafeWhenInputHigher)
Patrick Ventureda4a5dd2018-08-31 09:42:48 -0700230{
Brandon Kimbcdeb832022-08-15 23:27:36 +0000231 // If STRICT_FAILSAFE_PWM flag is NOT defined and the requested output is
232 // higher than the failsafe value, then use the value provided to outputProc
233 //
234 // If STRICT_FAILSAFE_PWM is defined, we expect the FailSafe PWM to be
235 // capped to the failsafe PWM, and not go higher than that.
Patrick Venture566a1512018-06-12 14:51:07 -0700236
237 ZoneMock z;
238
239 std::vector<std::string> inputs = {"fan0"};
240 ec::pidinfo initial;
Brandon Kimbcdeb832022-08-15 23:27:36 +0000241 const double failsafePWM = 75.0;
Patrick Venture566a1512018-06-12 14:51:07 -0700242
243 std::unique_ptr<PIDController> p =
Patrick Venture563a3562018-10-30 09:31:26 -0700244 FanController::createFanPid(&z, "fan1", inputs, initial);
Patrick Venture566a1512018-06-12 14:51:07 -0700245 EXPECT_FALSE(p == nullptr);
246
247 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000248 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(failsafePWM));
Patrick Venture566a1512018-06-12 14:51:07 -0700249
250 int64_t timeout = 0;
251 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
252 // Grab pointer for mocking.
Patrick Venturee2ec0f62018-09-04 12:30:27 -0700253 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
Patrick Venture566a1512018-06-12 14:51:07 -0700254
Patrick Venture5f59c0f2018-11-11 12:55:14 -0800255 double percent = 80;
Patrick Venture566a1512018-06-12 14:51:07 -0700256
Josh Lehana4146eb2020-10-01 11:49:09 -0700257 EXPECT_CALL(z, getRedundantWrite()).WillOnce(Return(false));
Patrick Venture566a1512018-06-12 14:51:07 -0700258 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000259#ifdef STRICT_FAILSAFE_PWM
260 double failsafeValue = failsafePWM / 100;
261 EXPECT_CALL(*sm1, write(failsafeValue, false, _));
262#else
263 // Converting from double to double for expectation.
264 double value = percent / 100;
Josh Lehana4146eb2020-10-01 11:49:09 -0700265 EXPECT_CALL(*sm1, write(value, false, _));
Brandon Kimbcdeb832022-08-15 23:27:36 +0000266#endif
Patrick Venture566a1512018-06-12 14:51:07 -0700267
Patrick Venture563a3562018-10-30 09:31:26 -0700268 // This is a fan PID, so calling outputProc will try to write this value
Patrick Venture566a1512018-06-12 14:51:07 -0700269 // to the sensors.
Patrick Venture563a3562018-10-30 09:31:26 -0700270 p->outputProc(percent);
Patrick Venture566a1512018-06-12 14:51:07 -0700271}
Patrick Venturea0764872020-08-08 07:48:43 -0700272
Josh Lehana4146eb2020-10-01 11:49:09 -0700273TEST(FanControllerTest, OutputProc_VerifyRedundantWrites)
274{
275 /* when a zone indicates that redundant writes are enabled
276 * make sure the fan controller honors this by forcing a sensor write
277 */
278 ZoneMock z;
279
280 std::vector<std::string> inputs = {"fan0", "fan1"};
281 ec::pidinfo initial;
282
283 std::unique_ptr<PIDController> p =
284 FanController::createFanPid(&z, "fan1", inputs, initial);
285 EXPECT_FALSE(p == nullptr);
286
287 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false));
288
289 int64_t timeout = 0;
290 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout);
291 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout);
292 // Grab pointers for mocking.
293 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get());
294 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get());
295
296 EXPECT_CALL(z, getRedundantWrite())
297 .WillOnce(Return(true))
298 .WillOnce(Return(true));
299 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get()));
300 EXPECT_CALL(*sm1, write(0.5, true, _));
301 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get()));
302 EXPECT_CALL(*sm2, write(0.5, true, _));
303
304 // This is a fan PID, so calling outputProc will try to write this value
305 // to the sensors.
306 p->outputProc(50.0);
307}
308
Patrick Venturea0764872020-08-08 07:48:43 -0700309} // namespace
310} // namespace pid_control