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