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