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