1 #include "pid/ec/pid.hpp" 2 #include "pid/fancontroller.hpp" 3 #include "test/sensor_mock.hpp" 4 #include "test/zone_mock.hpp" 5 6 #include <string> 7 #include <vector> 8 9 #include <gmock/gmock.h> 10 #include <gtest/gtest.h> 11 12 using ::testing::_; 13 using ::testing::DoubleEq; 14 using ::testing::Invoke; 15 using ::testing::Return; 16 using ::testing::StrEq; 17 18 TEST(FanControllerTest, BoringFactoryTest) 19 { 20 // Verify the factory will properly build the FanPIDController in the 21 // boring (uninteresting) case. 22 ZoneMock z; 23 24 std::vector<std::string> inputs = {"fan0"}; 25 ec::pidinfo initial; 26 27 std::unique_ptr<PIDController> p = 28 FanController::createFanPid(&z, "fan1", inputs, initial); 29 // Success 30 EXPECT_FALSE(p == nullptr); 31 } 32 33 TEST(FanControllerTest, VerifyFactoryFailsWithZeroInputs) 34 { 35 // A fan controller needs at least one input. 36 37 ZoneMock z; 38 39 std::vector<std::string> inputs = {}; 40 ec::pidinfo initial; 41 42 std::unique_ptr<PIDController> p = 43 FanController::createFanPid(&z, "fan1", inputs, initial); 44 EXPECT_TRUE(p == nullptr); 45 } 46 47 TEST(FanControllerTest, InputProc_AllSensorsReturnZero) 48 { 49 // If all your inputs are 0, return 0. 50 51 ZoneMock z; 52 53 std::vector<std::string> inputs = {"fan0", "fan1"}; 54 ec::pidinfo initial; 55 56 std::unique_ptr<PIDController> p = 57 FanController::createFanPid(&z, "fan1", inputs, initial); 58 EXPECT_FALSE(p == nullptr); 59 60 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(0)); 61 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(0)); 62 63 EXPECT_EQ(0.0, p->inputProc()); 64 } 65 66 TEST(FanControllerTest, InputProc_IfSensorNegativeIsIgnored) 67 { 68 // A sensor value returning sub-zero is ignored as an error. 69 ZoneMock z; 70 71 std::vector<std::string> inputs = {"fan0", "fan1"}; 72 ec::pidinfo initial; 73 74 std::unique_ptr<PIDController> p = 75 FanController::createFanPid(&z, "fan1", inputs, initial); 76 EXPECT_FALSE(p == nullptr); 77 78 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(-1)); 79 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(-1)); 80 81 EXPECT_EQ(0.0, p->inputProc()); 82 } 83 84 TEST(FanControllerTest, InputProc_ChoosesMinimumValue) 85 { 86 // Verify it selects the minimum value from its inputs. 87 88 ZoneMock z; 89 90 std::vector<std::string> inputs = {"fan0", "fan1", "fan2"}; 91 ec::pidinfo initial; 92 93 std::unique_ptr<PIDController> p = 94 FanController::createFanPid(&z, "fan1", inputs, initial); 95 EXPECT_FALSE(p == nullptr); 96 97 EXPECT_CALL(z, getCachedValue(StrEq("fan0"))).WillOnce(Return(10.0)); 98 EXPECT_CALL(z, getCachedValue(StrEq("fan1"))).WillOnce(Return(30.0)); 99 EXPECT_CALL(z, getCachedValue(StrEq("fan2"))).WillOnce(Return(5.0)); 100 101 EXPECT_EQ(5.0, p->inputProc()); 102 } 103 104 // The direction is unused presently, but these tests validate the logic. 105 TEST(FanControllerTest, SetPtProc_SpeedChanges_VerifyDirection) 106 { 107 // The fan direction defaults to neutral, because we have no data. Verify 108 // that after this point it appropriately will indicate speeding up or 109 // slowing down based on the RPM values specified. 110 111 ZoneMock z; 112 113 std::vector<std::string> inputs = {"fan0", "fan1"}; 114 ec::pidinfo initial; 115 116 std::unique_ptr<PIDController> p = 117 FanController::createFanPid(&z, "fan1", inputs, initial); 118 EXPECT_FALSE(p == nullptr); 119 // Grab pointer for mocking. 120 FanController* fp = reinterpret_cast<FanController*>(p.get()); 121 122 // Fanspeed starts are Neutral. 123 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection()); 124 125 // getMaxSetPointRequest returns a higher value than 0, so the fans should 126 // be marked as speeding up. 127 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(10.0)); 128 EXPECT_EQ(10.0, p->setptProc()); 129 EXPECT_EQ(FanSpeedDirection::UP, fp->getFanDirection()); 130 131 // getMaxSetPointRequest returns a lower value than 10, so the fans should 132 // be marked as slowing down. 133 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0)); 134 EXPECT_EQ(5.0, p->setptProc()); 135 EXPECT_EQ(FanSpeedDirection::DOWN, fp->getFanDirection()); 136 137 // getMaxSetPointRequest returns the same value, so the fans should be 138 // marked as neutral. 139 EXPECT_CALL(z, getMaxSetPointRequest()).WillOnce(Return(5.0)); 140 EXPECT_EQ(5.0, p->setptProc()); 141 EXPECT_EQ(FanSpeedDirection::NEUTRAL, fp->getFanDirection()); 142 } 143 144 TEST(FanControllerTest, OutputProc_VerifiesIfFailsafeEnabledInputIsIgnored) 145 { 146 // Verify that if failsafe mode is enabled and the input value for the fans 147 // is below the failsafe minimum value, the input is not used and the fans 148 // are driven at failsafe RPM. 149 150 ZoneMock z; 151 152 std::vector<std::string> inputs = {"fan0", "fan1"}; 153 ec::pidinfo initial; 154 155 std::unique_ptr<PIDController> p = 156 FanController::createFanPid(&z, "fan1", inputs, initial); 157 EXPECT_FALSE(p == nullptr); 158 159 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true)); 160 EXPECT_CALL(z, getFailSafePercent()).Times(2).WillRepeatedly(Return(75.0)); 161 162 int64_t timeout = 0; 163 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout); 164 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout); 165 // Grab pointers for mocking. 166 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get()); 167 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get()); 168 169 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get())); 170 EXPECT_CALL(*sm1, write(0.75)); 171 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get())); 172 EXPECT_CALL(*sm2, write(0.75)); 173 174 // This is a fan PID, so calling outputProc will try to write this value 175 // to the sensors. 176 177 // Setting 50%, will end up being 75% because the sensors are in failsafe 178 // mode. 179 p->outputProc(50.0); 180 } 181 182 TEST(FanControllerTest, OutputProc_BehavesAsExpected) 183 { 184 // Verifies that when the system is not in failsafe mode, the input value 185 // to outputProc is used to drive the sensors (fans). 186 187 ZoneMock z; 188 189 std::vector<std::string> inputs = {"fan0", "fan1"}; 190 ec::pidinfo initial; 191 192 std::unique_ptr<PIDController> p = 193 FanController::createFanPid(&z, "fan1", inputs, initial); 194 EXPECT_FALSE(p == nullptr); 195 196 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(false)); 197 198 int64_t timeout = 0; 199 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout); 200 std::unique_ptr<Sensor> s2 = std::make_unique<SensorMock>("fan1", timeout); 201 // Grab pointers for mocking. 202 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get()); 203 SensorMock* sm2 = reinterpret_cast<SensorMock*>(s2.get()); 204 205 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get())); 206 EXPECT_CALL(*sm1, write(0.5)); 207 EXPECT_CALL(z, getSensor(StrEq("fan1"))).WillOnce(Return(s2.get())); 208 EXPECT_CALL(*sm2, write(0.5)); 209 210 // This is a fan PID, so calling outputProc will try to write this value 211 // to the sensors. 212 p->outputProc(50.0); 213 } 214 215 TEST(FanControllerTest, OutputProc_VerifyFailSafeIgnoredIfInputHigher) 216 { 217 // If the requested output is higher than the failsafe value, then use the 218 // value provided to outputProc. 219 220 ZoneMock z; 221 222 std::vector<std::string> inputs = {"fan0"}; 223 ec::pidinfo initial; 224 225 std::unique_ptr<PIDController> p = 226 FanController::createFanPid(&z, "fan1", inputs, initial); 227 EXPECT_FALSE(p == nullptr); 228 229 EXPECT_CALL(z, getFailSafeMode()).WillOnce(Return(true)); 230 EXPECT_CALL(z, getFailSafePercent()).WillOnce(Return(75.0)); 231 232 int64_t timeout = 0; 233 std::unique_ptr<Sensor> s1 = std::make_unique<SensorMock>("fan0", timeout); 234 // Grab pointer for mocking. 235 SensorMock* sm1 = reinterpret_cast<SensorMock*>(s1.get()); 236 237 // Converting from double to double for expectation. 238 double percent = 80; 239 double value = percent / 100; 240 241 EXPECT_CALL(z, getSensor(StrEq("fan0"))).WillOnce(Return(s1.get())); 242 EXPECT_CALL(*sm1, write(value)); 243 244 // This is a fan PID, so calling outputProc will try to write this value 245 // to the sensors. 246 p->outputProc(percent); 247 } 248