1 #include "pid/ec/logging.hpp" 2 #include "pid/ec/pid.hpp" 3 #include "pid/zone.hpp" 4 #include "sensors/manager.hpp" 5 #include "test/controller_mock.hpp" 6 #include "test/helpers.hpp" 7 #include "test/sensor_mock.hpp" 8 9 #include <sdbusplus/test/sdbus_mock.hpp> 10 11 #include <chrono> 12 #include <cstring> 13 #include <vector> 14 15 #include <gmock/gmock.h> 16 #include <gtest/gtest.h> 17 18 namespace pid_control 19 { 20 namespace 21 { 22 23 using ::testing::_; 24 using ::testing::IsNull; 25 using ::testing::Return; 26 using ::testing::StrEq; 27 28 static std::string modeInterface = "xyz.openbmc_project.Control.Mode"; 29 static std::string debugZoneInterface = "xyz.openbmc_project.Debug.Pid.Zone"; 30 static std::string enableInterface = "xyz.openbmc_project.Object.Enable"; 31 static std::string debugThermalPowerInterface = 32 "xyz.openbmc_project.Debug.Pid.ThermalPower"; 33 34 namespace 35 { 36 37 TEST(PidZoneConstructorTest, BoringConstructorTest) 38 { 39 // Build a PID Zone. 40 41 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode, 42 sdbus_mock_enable; 43 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 44 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 45 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 46 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 47 48 EXPECT_CALL(sdbus_mock_host, 49 sd_bus_add_object_manager( 50 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 51 .WillOnce(Return(0)); 52 53 SensorManager m(bus_mock_passive, bus_mock_host); 54 55 bool defer = true; 56 bool accSetPoint = false; 57 const char* objPath = "/path/"; 58 int64_t zone = 1; 59 double minThermalOutput = 1000.0; 60 double failSafePercent = 100; 61 conf::CycleTime cycleTime; 62 63 double d; 64 std::vector<std::string> properties; 65 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties, 66 &d); 67 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface, 68 properties, &d); 69 70 std::string sensorname = "temp1"; 71 std::string pidsensorpath = 72 "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname; 73 74 double de; 75 std::vector<std::string> propertiesenable; 76 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(), 77 enableInterface, propertiesenable, &de); 78 79 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m, 80 bus_mock_mode, objPath, defer, accSetPoint); 81 // Success. 82 } 83 84 } // namespace 85 86 class PidZoneTest : public ::testing::Test 87 { 88 protected: 89 PidZoneTest() : 90 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(), 91 sdbus_mock_mode(), sdbus_mock_enable() 92 { 93 EXPECT_CALL(sdbus_mock_host, 94 sd_bus_add_object_manager( 95 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 96 .WillOnce(Return(0)); 97 98 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 99 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 100 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 101 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 102 103 // Compiler weirdly not happy about just instantiating mgr(...); 104 SensorManager m(bus_mock_passive, bus_mock_host); 105 mgr = std::move(m); 106 107 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 108 properties, &property_index); 109 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface, 110 properties, &property_index); 111 112 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(), 113 enableInterface, propertiesenable, 114 &propertyenable_index); 115 116 zone = std::make_unique<DbusPidZone>( 117 zoneId, minThermalOutput, failSafePercent, cycleTime, mgr, 118 bus_mock_mode, objPath, defer, accSetPoint); 119 } 120 121 // unused 122 double property_index; 123 std::vector<std::string> properties; 124 double propertyenable_index; 125 std::vector<std::string> propertiesenable; 126 127 sdbusplus::SdBusMock sdbus_mock_passive; 128 sdbusplus::SdBusMock sdbus_mock_host; 129 sdbusplus::SdBusMock sdbus_mock_mode; 130 sdbusplus::SdBusMock sdbus_mock_enable; 131 int64_t zoneId = 1; 132 double minThermalOutput = 1000.0; 133 double failSafePercent = 100; 134 double setpoint = 50.0; 135 bool defer = true; 136 bool accSetPoint = false; 137 const char* objPath = "/path/"; 138 SensorManager mgr; 139 conf::CycleTime cycleTime; 140 141 std::string sensorname = "temp1"; 142 std::string sensorType = "temp"; 143 std::string pidsensorpath = 144 "/xyz/openbmc_project/settings/fanctrl/zone1/" + sensorname; 145 146 std::unique_ptr<DbusPidZone> zone; 147 }; 148 149 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected) 150 { 151 // Verifies the zoneId returned is what we expect. 152 153 EXPECT_EQ(zoneId, zone->getZoneID()); 154 } 155 156 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected) 157 { 158 // Verifies that the zone starts in manual mode. Verifies that one can set 159 // the mode. 160 EXPECT_FALSE(zone->getManualMode()); 161 162 zone->setManualMode(true); 163 EXPECT_TRUE(zone->getManualMode()); 164 } 165 166 TEST_F(PidZoneTest, AddPidControlProcessGetAndSetEnableTest_BehavesAsExpected) 167 { 168 // Verifies that the zone starts in enable mode. Verifies that one can set 169 // enable the mode. 170 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 171 172 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv( 173 IsNull(), StrEq(pidsensorpath.c_str()), 174 StrEq(enableInterface), NotNull())) 175 .Times(::testing::AnyNumber()) 176 .WillOnce(Invoke( 177 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 178 [[maybe_unused]] const char* interface, const char** names) { 179 EXPECT_STREQ("Enable", names[0]); 180 return 0; 181 })); 182 183 zone->addPidControlProcess(sensorname, sensorType, setpoint, 184 bus_mock_enable, pidsensorpath.c_str(), defer); 185 EXPECT_TRUE(zone->isPidProcessEnabled(sensorname)); 186 } 187 188 TEST_F(PidZoneTest, SetManualMode_RedundantWritesEnabledOnceAfterManualMode) 189 { 190 // Tests adding a fan PID controller to the zone, and verifies it's 191 // touched during processing. 192 193 std::unique_ptr<PIDController> tpid = 194 std::make_unique<ControllerMock>("fan1", zone.get()); 195 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 196 197 // Access the internal pid configuration to clear it out (unrelated to the 198 // test). 199 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo(); 200 201 zone->addFanPID(std::move(tpid)); 202 203 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 204 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 205 EXPECT_CALL(*tmock, outputProc(_)); 206 207 // while zone is in auto mode redundant writes should be disabled 208 EXPECT_FALSE(zone->getRedundantWrite()); 209 210 // but switching from manual to auto enables a single redundant write 211 zone->setManualMode(true); 212 zone->setManualMode(false); 213 EXPECT_TRUE(zone->getRedundantWrite()); 214 215 // after one iteration of a pid loop redundant write should be cleared 216 zone->processFans(); 217 EXPECT_FALSE(zone->getRedundantWrite()); 218 } 219 220 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) 221 { 222 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest 223 // and getMinThermalSetPoint. 224 225 // Need to add pid control process for the zone that can enable 226 // the process and add the set point. 227 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 228 229 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv( 230 IsNull(), StrEq(pidsensorpath.c_str()), 231 StrEq(enableInterface), NotNull())) 232 .Times(::testing::AnyNumber()) 233 .WillOnce(Invoke( 234 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 235 [[maybe_unused]] const char* interface, const char** names) { 236 EXPECT_STREQ("Enable", names[0]); 237 return 0; 238 })); 239 240 zone->addPidControlProcess(sensorname, sensorType, setpoint, 241 bus_mock_enable, pidsensorpath.c_str(), defer); 242 243 // At least one value must be above the minimum thermal setpoint used in 244 // the constructor otherwise it'll choose that value 245 std::vector<double> values = {100, 200, 300, 400, 500, 5000}; 246 247 for (auto v : values) 248 { 249 zone->addSetPoint(v, sensorname); 250 } 251 252 // This will pull the maximum RPM setpoint request. 253 zone->determineMaxSetPointRequest(); 254 EXPECT_EQ(5000, zone->getMaxSetPointRequest()); 255 256 // Clear the values, so it'll choose the minimum thermal setpoint. 257 zone->clearSetPoints(); 258 259 // This will go through the RPM set point values and grab the maximum. 260 zone->determineMaxSetPointRequest(); 261 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest()); 262 } 263 264 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) 265 { 266 // Tests adding several RPM setpoints, however, they're all lower than the 267 // configured minimal thermal setpoint RPM value. 268 269 // Need to add pid control process for the zone that can enable 270 // the process and add the set point. 271 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 272 273 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv( 274 IsNull(), StrEq(pidsensorpath.c_str()), 275 StrEq(enableInterface), NotNull())) 276 .Times(::testing::AnyNumber()) 277 .WillOnce(Invoke( 278 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 279 [[maybe_unused]] const char* interface, const char** names) { 280 EXPECT_STREQ("Enable", names[0]); 281 return 0; 282 })); 283 284 zone->addPidControlProcess(sensorname, sensorType, setpoint, 285 bus_mock_enable, pidsensorpath.c_str(), defer); 286 287 std::vector<double> values = {100, 200, 300, 400, 500}; 288 289 for (auto v : values) 290 { 291 zone->addSetPoint(v, sensorname); 292 } 293 294 // This will pull the maximum RPM setpoint request. 295 zone->determineMaxSetPointRequest(); 296 297 // Verifies the value returned in the minimal thermal rpm set point. 298 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest()); 299 } 300 301 TEST_F(PidZoneTest, GetFailSafePercent_SingleFailedReturnsExpected) 302 { 303 // Tests when only one sensor failed and the sensor's failsafe duty is zero, 304 // and verify that the sensor name is empty and failsafe duty is PID zone's 305 // failsafe duty. 306 307 std::vector<std::string> input1 = {"temp1"}; 308 std::vector<std::string> input2 = {"temp2"}; 309 std::vector<std::string> input3 = {"temp3"}; 310 std::vector<double> values = {0, 0, 0}; 311 312 zone->addPidFailSafePercent(input1, values[0]); 313 zone->addPidFailSafePercent(input2, values[1]); 314 zone->addPidFailSafePercent(input3, values[2]); 315 316 zone->markSensorMissing("temp1"); 317 318 EXPECT_EQ(failSafePercent, zone->getFailSafePercent()); 319 } 320 321 TEST_F(PidZoneTest, GetFailSafePercent_MultiFailedReturnsExpected) 322 { 323 // Tests when multi sensor failed, and verify the final failsafe's sensor 324 // name and duty as expected. 325 326 std::vector<std::string> input1 = {"temp1"}; 327 std::vector<std::string> input2 = {"temp2"}; 328 std::vector<std::string> input3 = {"temp3"}; 329 std::vector<double> values = {60, 80, 70}; 330 331 zone->addPidFailSafePercent(input1, values[0]); 332 zone->addPidFailSafePercent(input2, values[1]); 333 zone->addPidFailSafePercent(input3, values[2]); 334 335 zone->markSensorMissing("temp1"); 336 zone->markSensorMissing("temp2"); 337 zone->markSensorMissing("temp3"); 338 339 EXPECT_EQ(80, zone->getFailSafePercent()); 340 } 341 342 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) 343 { 344 // This test will add a couple thermal inputs, and verify that the zone 345 // initializes into failsafe mode, and will read each sensor. 346 347 std::string name1 = "temp1"; 348 int64_t timeout = 1; 349 350 std::unique_ptr<Sensor> sensor1 = 351 std::make_unique<SensorMock>(name1, timeout); 352 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 353 354 std::string name2 = "temp2"; 355 std::unique_ptr<Sensor> sensor2 = 356 std::make_unique<SensorMock>(name2, timeout); 357 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 358 359 std::string type = "unchecked"; 360 mgr.addSensor(type, name1, std::move(sensor1)); 361 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 362 mgr.addSensor(type, name2, std::move(sensor2)); 363 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 364 365 // Now that the sensors exist, add them to the zone. 366 zone->addThermalInput(name1, false); 367 zone->addThermalInput(name2, false); 368 369 // Initialize Zone 370 zone->initializeCache(); 371 372 // Verify now in failsafe mode. 373 EXPECT_TRUE(zone->getFailSafeMode()); 374 375 ReadReturn r1; 376 r1.value = 10.0; 377 r1.updated = std::chrono::high_resolution_clock::now(); 378 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 379 380 ReadReturn r2; 381 r2.value = 11.0; 382 r2.updated = std::chrono::high_resolution_clock::now(); 383 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 384 385 // Read the sensors, this will put the values into the cache. 386 zone->updateSensors(); 387 388 // We should no longer be in failsafe mode. 389 EXPECT_FALSE(zone->getFailSafeMode()); 390 391 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 392 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 393 } 394 395 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached) 396 { 397 // This will add a couple fan inputs, and verify the values are cached. 398 399 std::string name1 = "fan1"; 400 int64_t timeout = 2; 401 402 std::unique_ptr<Sensor> sensor1 = 403 std::make_unique<SensorMock>(name1, timeout); 404 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 405 406 std::string name2 = "fan2"; 407 std::unique_ptr<Sensor> sensor2 = 408 std::make_unique<SensorMock>(name2, timeout); 409 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 410 411 std::string type = "unchecked"; 412 mgr.addSensor(type, name1, std::move(sensor1)); 413 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 414 mgr.addSensor(type, name2, std::move(sensor2)); 415 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 416 417 // Now that the sensors exist, add them to the zone. 418 zone->addFanInput(name1, false); 419 zone->addFanInput(name2, false); 420 421 // Initialize Zone 422 zone->initializeCache(); 423 424 ReadReturn r1; 425 r1.value = 10.0; 426 r1.updated = std::chrono::high_resolution_clock::now(); 427 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 428 429 ReadReturn r2; 430 r2.value = 11.0; 431 r2.updated = std::chrono::high_resolution_clock::now(); 432 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 433 434 // Method under test will read through each fan sensor for the zone and 435 // cache the values. 436 zone->updateFanTelemetry(); 437 438 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 439 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 440 } 441 442 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode) 443 { 444 // On the second updateSensors call, the updated timestamp will be beyond 445 // the timeout limit. 446 447 int64_t timeout = 1; 448 449 std::string name1 = "temp1"; 450 std::unique_ptr<Sensor> sensor1 = 451 std::make_unique<SensorMock>(name1, timeout); 452 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 453 454 std::string name2 = "temp2"; 455 std::unique_ptr<Sensor> sensor2 = 456 std::make_unique<SensorMock>(name2, timeout); 457 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 458 459 std::string type = "unchecked"; 460 mgr.addSensor(type, name1, std::move(sensor1)); 461 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 462 mgr.addSensor(type, name2, std::move(sensor2)); 463 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 464 465 zone->addThermalInput(name1, false); 466 zone->addThermalInput(name2, false); 467 468 // Initialize Zone 469 zone->initializeCache(); 470 471 // Verify now in failsafe mode. 472 EXPECT_TRUE(zone->getFailSafeMode()); 473 474 ReadReturn r1; 475 r1.value = 10.0; 476 r1.updated = std::chrono::high_resolution_clock::now(); 477 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 478 479 ReadReturn r2; 480 r2.value = 11.0; 481 r2.updated = std::chrono::high_resolution_clock::now(); 482 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 483 484 zone->updateSensors(); 485 EXPECT_FALSE(zone->getFailSafeMode()); 486 487 // Ok, so we're not in failsafe mode, so let's set updated to the past. 488 // sensor1 will have an updated field older than its timeout value, but 489 // sensor2 will be fine. :D 490 r1.updated -= std::chrono::seconds(3); 491 r2.updated = std::chrono::high_resolution_clock::now(); 492 493 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 494 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 495 496 // Method under test will read each sensor. One sensor's value is older 497 // than the timeout for that sensor and this triggers failsafe mode. 498 zone->updateSensors(); 499 EXPECT_TRUE(zone->getFailSafeMode()); 500 } 501 502 TEST_F(PidZoneTest, ThermalInput_MissingIsAcceptableNoFailSafe) 503 { 504 // This is similar to the above test, but because missingIsAcceptable 505 // is set for sensor1, the zone should not enter failsafe mode when 506 // only sensor1 goes missing. 507 // However, sensor2 going missing should still trigger failsafe mode. 508 509 int64_t timeout = 1; 510 511 std::string name1 = "temp1"; 512 std::unique_ptr<Sensor> sensor1 = 513 std::make_unique<SensorMock>(name1, timeout); 514 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 515 516 std::string name2 = "temp2"; 517 std::unique_ptr<Sensor> sensor2 = 518 std::make_unique<SensorMock>(name2, timeout); 519 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 520 521 std::string type = "unchecked"; 522 mgr.addSensor(type, name1, std::move(sensor1)); 523 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 524 mgr.addSensor(type, name2, std::move(sensor2)); 525 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 526 527 // Only sensor1 has MissingIsAcceptable enabled for it 528 zone->addThermalInput(name1, true); 529 zone->addThermalInput(name2, false); 530 531 // Initialize Zone 532 zone->initializeCache(); 533 534 // As sensors are not initialized, zone should be in failsafe mode 535 EXPECT_TRUE(zone->getFailSafeMode()); 536 537 // r1 not populated here, intentionally, to simulate a sensor that 538 // is not available yet, perhaps takes a long time to start up. 539 ReadReturn r1; 540 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 541 542 ReadReturn r2; 543 r2.value = 11.0; 544 r2.updated = std::chrono::high_resolution_clock::now(); 545 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 546 547 zone->updateSensors(); 548 549 // Only sensor2 has been initialized here. Failsafe should be false, 550 // because sensor1 MissingIsAcceptable so it is OK for it to go missing. 551 EXPECT_FALSE(zone->getFailSafeMode()); 552 553 r1.value = 10.0; 554 r1.updated = std::chrono::high_resolution_clock::now(); 555 556 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 557 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 558 zone->updateSensors(); 559 560 // Both sensors are now properly initialized 561 EXPECT_FALSE(zone->getFailSafeMode()); 562 563 // Ok, so we're not in failsafe mode, so let's set updated to the past. 564 // sensor1 will have an updated field older than its timeout value, but 565 // sensor2 will be fine. :D 566 r1.updated -= std::chrono::seconds(3); 567 r2.updated = std::chrono::high_resolution_clock::now(); 568 569 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 570 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 571 zone->updateSensors(); 572 573 // MissingIsAcceptable is true for sensor1, so the zone should not be 574 // thrown into failsafe mode. 575 EXPECT_FALSE(zone->getFailSafeMode()); 576 577 // Do the same thing, but for the opposite sensors: r1 is good, 578 // but r2 is set to some time in the past. 579 r1.updated = std::chrono::high_resolution_clock::now(); 580 r2.updated -= std::chrono::seconds(3); 581 582 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 583 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 584 zone->updateSensors(); 585 586 // Now, the zone should be in failsafe mode, because sensor2 does not 587 // have MissingIsAcceptable set true, it is still subject to failsafe. 588 EXPECT_TRUE(zone->getFailSafeMode()); 589 590 r1.updated = std::chrono::high_resolution_clock::now(); 591 r2.updated = std::chrono::high_resolution_clock::now(); 592 593 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 594 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 595 zone->updateSensors(); 596 597 // The failsafe mode should cease, as both sensors are good again. 598 EXPECT_FALSE(zone->getFailSafeMode()); 599 } 600 601 TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors) 602 { 603 // This will add a couple fan inputs, and verify the values are cached. 604 605 std::string name1 = "fan1"; 606 int64_t timeout = 2; 607 608 std::unique_ptr<Sensor> sensor1 = 609 std::make_unique<SensorMock>(name1, timeout); 610 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 611 612 std::string name2 = "fan2"; 613 std::unique_ptr<Sensor> sensor2 = 614 std::make_unique<SensorMock>(name2, timeout); 615 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 616 617 std::string type = "unchecked"; 618 mgr.addSensor(type, name1, std::move(sensor1)); 619 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 620 mgr.addSensor(type, name2, std::move(sensor2)); 621 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 622 623 // Now that the sensors exist, add them to the zone. 624 zone->addFanInput(name1, false); 625 zone->addFanInput(name2, false); 626 627 // Initialize Zone 628 zone->initializeCache(); 629 630 // Verify now in failsafe mode. 631 EXPECT_TRUE(zone->getFailSafeMode()); 632 633 ReadReturn r1; 634 r1.value = 10.0; 635 r1.updated = std::chrono::high_resolution_clock::now(); 636 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 637 638 ReadReturn r2; 639 r2.value = 11.0; 640 r2.updated = std::chrono::high_resolution_clock::now(); 641 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 642 643 // Method under test will read through each fan sensor for the zone and 644 // cache the values. 645 zone->updateFanTelemetry(); 646 647 // We should no longer be in failsafe mode. 648 EXPECT_FALSE(zone->getFailSafeMode()); 649 650 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 651 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 652 } 653 654 TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode) 655 { 656 // This will add a couple fan inputs, and verify the values are cached. 657 658 std::string name1 = "fan1"; 659 int64_t timeout = 2; 660 661 std::unique_ptr<Sensor> sensor1 = 662 std::make_unique<SensorMock>(name1, timeout); 663 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 664 665 std::string name2 = "fan2"; 666 std::unique_ptr<Sensor> sensor2 = 667 std::make_unique<SensorMock>(name2, timeout); 668 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 669 670 std::string type = "unchecked"; 671 mgr.addSensor(type, name1, std::move(sensor1)); 672 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 673 mgr.addSensor(type, name2, std::move(sensor2)); 674 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 675 676 // Now that the sensors exist, add them to the zone. 677 zone->addFanInput(name1, false); 678 zone->addFanInput(name2, false); 679 680 // Initialize Zone 681 zone->initializeCache(); 682 683 // Verify now in failsafe mode. 684 EXPECT_TRUE(zone->getFailSafeMode()); 685 686 ReadReturn r1; 687 r1.value = 10.0; 688 r1.updated = std::chrono::high_resolution_clock::now(); 689 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 690 691 ReadReturn r2; 692 r2.value = 11.0; 693 r2.updated = std::chrono::high_resolution_clock::now(); 694 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 695 696 // Method under test will read through each fan sensor for the zone and 697 // cache the values. 698 zone->updateFanTelemetry(); 699 700 // We should no longer be in failsafe mode. 701 EXPECT_FALSE(zone->getFailSafeMode()); 702 703 r1.updated -= std::chrono::seconds(3); 704 r2.updated = std::chrono::high_resolution_clock::now(); 705 706 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 707 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 708 709 zone->updateFanTelemetry(); 710 EXPECT_TRUE(zone->getFailSafeMode()); 711 } 712 713 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) 714 { 715 // One can grab a sensor from the manager through the zone. 716 717 int64_t timeout = 1; 718 719 std::string name1 = "temp1"; 720 std::unique_ptr<Sensor> sensor1 = 721 std::make_unique<SensorMock>(name1, timeout); 722 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 723 724 std::string type = "unchecked"; 725 mgr.addSensor(type, name1, std::move(sensor1)); 726 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 727 728 zone->addThermalInput(name1, false); 729 730 // Verify method under test returns the pointer we expect. 731 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 732 } 733 734 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) 735 { 736 // Tests adding a thermal PID controller to the zone, and verifies it's 737 // touched during processing. 738 739 std::unique_ptr<PIDController> tpid = 740 std::make_unique<ControllerMock>("thermal1", zone.get()); 741 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 742 743 // Access the internal pid configuration to clear it out (unrelated to the 744 // test). 745 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo(); 746 747 zone->addThermalPID(std::move(tpid)); 748 749 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 750 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 751 EXPECT_CALL(*tmock, outputProc(_)); 752 753 // Method under test will, for each thermal PID, call setpt, input, and 754 // output. 755 zone->processThermals(); 756 } 757 758 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) 759 { 760 // Tests adding a fan PID controller to the zone, and verifies it's 761 // touched during processing. 762 763 std::unique_ptr<PIDController> tpid = 764 std::make_unique<ControllerMock>("fan1", zone.get()); 765 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 766 767 // Access the internal pid configuration to clear it out (unrelated to the 768 // test). 769 [[maybe_unused]] ec::pid_info_t* info = tpid->getPIDInfo(); 770 771 zone->addFanPID(std::move(tpid)); 772 773 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 774 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 775 EXPECT_CALL(*tmock, outputProc(_)); 776 777 // Method under test will, for each fan PID, call setpt, input, and output. 778 zone->processFans(); 779 } 780 781 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) 782 { 783 // The manual(bool) method is inherited from the dbus mode interface. 784 785 // Verifies that someone doesn't remove the internal call to the dbus 786 // object from which we're inheriting. 787 EXPECT_CALL(sdbus_mock_mode, 788 sd_bus_emit_properties_changed_strv( 789 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull())) 790 .WillOnce(Invoke( 791 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 792 [[maybe_unused]] const char* interface, const char** names) { 793 EXPECT_STREQ("Manual", names[0]); 794 return 0; 795 })); 796 797 // Method under test will set the manual mode to true and broadcast this 798 // change on dbus. 799 zone->manual(true); 800 EXPECT_TRUE(zone->getManualMode()); 801 } 802 803 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) 804 { 805 // This property is implemented by us as read-only, such that trying to 806 // write to it will have no effect. 807 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 808 } 809 810 } // namespace 811 } // namespace pid_control 812