1 #include "pid/ec/pid.hpp" 2 #include "pid/zone.hpp" 3 #include "sensors/manager.hpp" 4 #include "test/controller_mock.hpp" 5 #include "test/helpers.hpp" 6 #include "test/sensor_mock.hpp" 7 8 #include <chrono> 9 #include <cstring> 10 #include <sdbusplus/test/sdbus_mock.hpp> 11 #include <vector> 12 13 #include <gmock/gmock.h> 14 #include <gtest/gtest.h> 15 16 using ::testing::_; 17 using ::testing::IsNull; 18 using ::testing::Return; 19 using ::testing::StrEq; 20 21 static std::string modeInterface = "xyz.openbmc_project.Control.Mode"; 22 23 namespace 24 { 25 26 TEST(PidZoneConstructorTest, BoringConstructorTest) 27 { 28 // Build a PID Zone. 29 30 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode; 31 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 32 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 33 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 34 35 EXPECT_CALL(sdbus_mock_host, 36 sd_bus_add_object_manager( 37 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 38 .WillOnce(Return(0)); 39 40 SensorManager m(std::move(bus_mock_passive), std::move(bus_mock_host)); 41 42 bool defer = true; 43 const char* objPath = "/path/"; 44 int64_t zone = 1; 45 double minThermalRpm = 1000.0; 46 double failSafePercent = 0.75; 47 48 int i; 49 std::vector<std::string> properties; 50 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties, 51 &i); 52 53 PIDZone p(zone, minThermalRpm, failSafePercent, m, bus_mock_mode, objPath, 54 defer); 55 // Success. 56 } 57 58 } // namespace 59 60 class PidZoneTest : public ::testing::Test 61 { 62 protected: 63 PidZoneTest() : 64 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(), 65 sdbus_mock_mode() 66 { 67 EXPECT_CALL(sdbus_mock_host, 68 sd_bus_add_object_manager( 69 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 70 .WillOnce(Return(0)); 71 72 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 73 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 74 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 75 76 // Compiler weirdly not happy about just instantiating mgr(...); 77 SensorManager m(std::move(bus_mock_passive), std::move(bus_mock_host)); 78 mgr = std::move(m); 79 80 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 81 properties, &property_index); 82 83 zone = std::make_unique<PIDZone>(zoneId, minThermalRpm, failSafePercent, 84 mgr, bus_mock_mode, objPath, defer); 85 } 86 87 // unused 88 int property_index; 89 std::vector<std::string> properties; 90 91 sdbusplus::SdBusMock sdbus_mock_passive; 92 sdbusplus::SdBusMock sdbus_mock_host; 93 sdbusplus::SdBusMock sdbus_mock_mode; 94 int64_t zoneId = 1; 95 double minThermalRpm = 1000.0; 96 double failSafePercent = 0.75; 97 bool defer = true; 98 const char* objPath = "/path/"; 99 SensorManager mgr; 100 101 std::unique_ptr<PIDZone> zone; 102 }; 103 104 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected) 105 { 106 // Verifies the zoneId returned is what we expect. 107 108 EXPECT_EQ(zoneId, zone->getZoneID()); 109 } 110 111 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected) 112 { 113 // Verifies that the zone starts in manual mode. Verifies that one can set 114 // the mode. 115 EXPECT_FALSE(zone->getManualMode()); 116 117 zone->setManualMode(true); 118 EXPECT_TRUE(zone->getManualMode()); 119 } 120 121 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) 122 { 123 // Tests addRPMSetPoint, clearRPMSetPoints, determineMaxRPMRequest 124 // and getMinThermalRPMSetpoint. 125 126 // At least one value must be above the minimum thermal setpoint used in 127 // the constructor otherwise it'll choose that value 128 std::vector<double> values = {100, 200, 300, 400, 500, 5000}; 129 for (auto v : values) 130 { 131 zone->addRPMSetPoint(v); 132 } 133 134 // This will pull the maximum RPM setpoint request. 135 zone->determineMaxRPMRequest(); 136 EXPECT_EQ(5000, zone->getMaxRPMRequest()); 137 138 // Clear the values, so it'll choose the minimum thermal setpoint. 139 zone->clearRPMSetPoints(); 140 141 // This will go through the RPM set point values and grab the maximum. 142 zone->determineMaxRPMRequest(); 143 EXPECT_EQ(zone->getMinThermalRPMSetpoint(), zone->getMaxRPMRequest()); 144 } 145 146 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) 147 { 148 // Tests adding several RPM setpoints, however, they're all lower than the 149 // configured minimal thermal setpoint RPM value. 150 151 std::vector<double> values = {100, 200, 300, 400, 500}; 152 for (auto v : values) 153 { 154 zone->addRPMSetPoint(v); 155 } 156 157 // This will pull the maximum RPM setpoint request. 158 zone->determineMaxRPMRequest(); 159 160 // Verifies the value returned in the minimal thermal rpm set point. 161 EXPECT_EQ(zone->getMinThermalRPMSetpoint(), zone->getMaxRPMRequest()); 162 } 163 164 TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected) 165 { 166 // Verify the value used to create the object is stored. 167 EXPECT_EQ(failSafePercent, zone->getFailSafePercent()); 168 } 169 170 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) 171 { 172 // This test will add a couple thermal inputs, and verify that the zone 173 // initializes into failsafe mode, and will read each sensor. 174 175 std::string name1 = "temp1"; 176 int64_t timeout = 1; 177 178 std::unique_ptr<Sensor> sensor1 = 179 std::make_unique<SensorMock>(name1, timeout); 180 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 181 182 std::string name2 = "temp2"; 183 std::unique_ptr<Sensor> sensor2 = 184 std::make_unique<SensorMock>(name2, timeout); 185 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 186 187 std::string type = "unchecked"; 188 mgr.addSensor(type, name1, std::move(sensor1)); 189 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 190 mgr.addSensor(type, name2, std::move(sensor2)); 191 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 192 193 // Now that the sensors exist, add them to the zone. 194 zone->addThermalInput(name1); 195 zone->addThermalInput(name2); 196 197 // Initialize Zone 198 zone->initializeCache(); 199 200 // Verify now in failsafe mode. 201 EXPECT_TRUE(zone->getFailSafeMode()); 202 203 ReadReturn r1; 204 r1.value = 10.0; 205 r1.updated = std::chrono::high_resolution_clock::now(); 206 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 207 208 ReadReturn r2; 209 r2.value = 11.0; 210 r2.updated = std::chrono::high_resolution_clock::now(); 211 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 212 213 // Read the sensors, this will put the values into the cache. 214 zone->updateSensors(); 215 216 // We should no longer be in failsafe mode. 217 EXPECT_FALSE(zone->getFailSafeMode()); 218 219 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 220 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 221 } 222 223 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached) 224 { 225 // This will add a couple fan inputs, and verify the values are cached. 226 227 std::string name1 = "fan1"; 228 int64_t timeout = 2; 229 230 std::unique_ptr<Sensor> sensor1 = 231 std::make_unique<SensorMock>(name1, timeout); 232 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 233 234 std::string name2 = "fan2"; 235 std::unique_ptr<Sensor> sensor2 = 236 std::make_unique<SensorMock>(name2, timeout); 237 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 238 239 std::string type = "unchecked"; 240 mgr.addSensor(type, name1, std::move(sensor1)); 241 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 242 mgr.addSensor(type, name2, std::move(sensor2)); 243 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 244 245 // Now that the sensors exist, add them to the zone. 246 zone->addFanInput(name1); 247 zone->addFanInput(name2); 248 249 // Initialize Zone 250 zone->initializeCache(); 251 252 ReadReturn r1; 253 r1.value = 10.0; 254 r1.updated = std::chrono::high_resolution_clock::now(); 255 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 256 257 ReadReturn r2; 258 r2.value = 11.0; 259 r2.updated = std::chrono::high_resolution_clock::now(); 260 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 261 262 // Method under test will read through each fan sensor for the zone and 263 // cache the values. 264 zone->updateFanTelemetry(); 265 266 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 267 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 268 } 269 270 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode) 271 { 272 // On the second updateSensors call, the updated timestamp will be beyond 273 // the timeout limit. 274 275 int64_t timeout = 1; 276 277 std::string name1 = "temp1"; 278 std::unique_ptr<Sensor> sensor1 = 279 std::make_unique<SensorMock>(name1, timeout); 280 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 281 282 std::string name2 = "temp2"; 283 std::unique_ptr<Sensor> sensor2 = 284 std::make_unique<SensorMock>(name2, timeout); 285 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 286 287 std::string type = "unchecked"; 288 mgr.addSensor(type, name1, std::move(sensor1)); 289 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 290 mgr.addSensor(type, name2, std::move(sensor2)); 291 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 292 293 zone->addThermalInput(name1); 294 zone->addThermalInput(name2); 295 296 // Initialize Zone 297 zone->initializeCache(); 298 299 // Verify now in failsafe mode. 300 EXPECT_TRUE(zone->getFailSafeMode()); 301 302 ReadReturn r1; 303 r1.value = 10.0; 304 r1.updated = std::chrono::high_resolution_clock::now(); 305 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 306 307 ReadReturn r2; 308 r2.value = 11.0; 309 r2.updated = std::chrono::high_resolution_clock::now(); 310 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 311 312 zone->updateSensors(); 313 EXPECT_FALSE(zone->getFailSafeMode()); 314 315 // Ok, so we're not in failsafe mode, so let's set updated to the past. 316 // sensor1 will have an updated field older than its timeout value, but 317 // sensor2 will be fine. :D 318 r1.updated -= std::chrono::seconds(3); 319 r2.updated = std::chrono::high_resolution_clock::now(); 320 321 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 322 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 323 324 // Method under test will read each sensor. One sensor's value is older 325 // than the timeout for that sensor and this triggers failsafe mode. 326 zone->updateSensors(); 327 EXPECT_TRUE(zone->getFailSafeMode()); 328 } 329 330 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) 331 { 332 // One can grab a sensor from the manager through the zone. 333 334 int64_t timeout = 1; 335 336 std::string name1 = "temp1"; 337 std::unique_ptr<Sensor> sensor1 = 338 std::make_unique<SensorMock>(name1, timeout); 339 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 340 341 std::string type = "unchecked"; 342 mgr.addSensor(type, name1, std::move(sensor1)); 343 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 344 345 zone->addThermalInput(name1); 346 347 // Verify method under test returns the pointer we expect. 348 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 349 } 350 351 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) 352 { 353 // Tests adding a thermal PID controller to the zone, and verifies it's 354 // touched during processing. 355 356 std::unique_ptr<PIDController> tpid = 357 std::make_unique<ControllerMock>("thermal1", zone.get()); 358 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 359 360 // Access the internal pid configuration to clear it out (unrelated to the 361 // test). 362 ec::pid_info_t* info = tpid->getPIDInfo(); 363 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 364 365 zone->addThermalPID(std::move(tpid)); 366 367 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 368 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 369 EXPECT_CALL(*tmock, outputProc(_)); 370 371 // Method under test will, for each thermal PID, call setpt, input, and 372 // output. 373 zone->processThermals(); 374 } 375 376 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) 377 { 378 // Tests adding a fan PID controller to the zone, and verifies it's 379 // touched during processing. 380 381 std::unique_ptr<PIDController> tpid = 382 std::make_unique<ControllerMock>("fan1", zone.get()); 383 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 384 385 // Access the internal pid configuration to clear it out (unrelated to the 386 // test). 387 ec::pid_info_t* info = tpid->getPIDInfo(); 388 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 389 390 zone->addFanPID(std::move(tpid)); 391 392 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 393 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 394 EXPECT_CALL(*tmock, outputProc(_)); 395 396 // Method under test will, for each fan PID, call setpt, input, and output. 397 zone->processFans(); 398 } 399 400 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) 401 { 402 // The manual(bool) method is inherited from the dbus mode interface. 403 404 // Verifies that someone doesn't remove the internal call to the dbus 405 // object from which we're inheriting. 406 EXPECT_CALL(sdbus_mock_mode, 407 sd_bus_emit_properties_changed_strv( 408 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull())) 409 .WillOnce(Invoke([&](sd_bus* bus, const char* path, 410 const char* interface, char** names) { 411 EXPECT_STREQ("Manual", names[0]); 412 return 0; 413 })); 414 415 // Method under test will set the manual mode to true and broadcast this 416 // change on dbus. 417 zone->manual(true); 418 EXPECT_TRUE(zone->getManualMode()); 419 } 420 421 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) 422 { 423 // This property is implemented by us as read-only, such that trying to 424 // write to it will have no effect. 425 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 426 } 427