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