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