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 const char* objPath = "/path/"; 57 int64_t zone = 1; 58 double minThermalOutput = 1000.0; 59 double failSafePercent = 0; 60 conf::CycleTime cycleTime; 61 62 double d; 63 std::vector<std::string> properties; 64 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties, 65 &d); 66 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface, 67 properties, &d); 68 69 std::string sensorname = "temp1"; 70 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" + 71 sensorname; 72 73 double de; 74 std::vector<std::string> propertiesenable; 75 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(), 76 enableInterface, propertiesenable, &de); 77 78 EXPECT_CALL(sdbus_mock_enable, 79 sd_bus_add_object_vtable( 80 IsNull(), NotNull(), StrEq(pidsensorpath.c_str()), 81 StrEq(debugThermalPowerInterface), NotNull(), NotNull())) 82 .Times(::testing::AnyNumber()) 83 .WillOnce(Return(0)); 84 85 DbusPidZone p(zone, minThermalOutput, failSafePercent, cycleTime, m, 86 bus_mock_mode, objPath, defer); 87 // Success. 88 } 89 90 } // namespace 91 92 class PidZoneTest : public ::testing::Test 93 { 94 protected: 95 PidZoneTest() : 96 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(), 97 sdbus_mock_mode(), sdbus_mock_enable() 98 { 99 EXPECT_CALL(sdbus_mock_host, 100 sd_bus_add_object_manager( 101 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 102 .WillOnce(Return(0)); 103 104 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 105 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 106 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 107 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 108 109 // Compiler weirdly not happy about just instantiating mgr(...); 110 SensorManager m(bus_mock_passive, bus_mock_host); 111 mgr = std::move(m); 112 113 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 114 properties, &property_index); 115 SetupDbusObject(&sdbus_mock_mode, defer, objPath, debugZoneInterface, 116 properties, &property_index); 117 118 SetupDbusObject(&sdbus_mock_enable, defer, pidsensorpath.c_str(), 119 enableInterface, propertiesenable, 120 &propertyenable_index); 121 EXPECT_CALL(sdbus_mock_enable, 122 sd_bus_add_object_vtable(IsNull(), NotNull(), 123 StrEq(pidsensorpath.c_str()), 124 StrEq(debugThermalPowerInterface), 125 NotNull(), NotNull())) 126 .Times(::testing::AnyNumber()) 127 .WillOnce(Return(0)); 128 129 zone = std::make_unique<DbusPidZone>(zoneId, minThermalOutput, 130 failSafePercent, cycleTime, mgr, 131 bus_mock_mode, objPath, defer); 132 } 133 134 // unused 135 double property_index; 136 std::vector<std::string> properties; 137 double propertyenable_index; 138 std::vector<std::string> propertiesenable; 139 140 sdbusplus::SdBusMock sdbus_mock_passive; 141 sdbusplus::SdBusMock sdbus_mock_host; 142 sdbusplus::SdBusMock sdbus_mock_mode; 143 sdbusplus::SdBusMock sdbus_mock_enable; 144 int64_t zoneId = 1; 145 double minThermalOutput = 1000.0; 146 double failSafePercent = 0; 147 double setpoint = 50.0; 148 bool defer = true; 149 const char* objPath = "/path/"; 150 SensorManager mgr; 151 conf::CycleTime cycleTime; 152 153 std::string sensorname = "temp1"; 154 std::string sensorType = "temp"; 155 std::string pidsensorpath = "/xyz/openbmc_project/settings/fanctrl/zone1/" + 156 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 ec::pid_info_t* info = tpid->getPIDInfo(); 212 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 213 214 zone->addFanPID(std::move(tpid)); 215 216 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 217 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 218 EXPECT_CALL(*tmock, outputProc(_)); 219 220 // while zone is in auto mode redundant writes should be disabled 221 EXPECT_FALSE(zone->getRedundantWrite()); 222 223 // but switching from manual to auto enables a single redundant write 224 zone->setManualMode(true); 225 zone->setManualMode(false); 226 EXPECT_TRUE(zone->getRedundantWrite()); 227 228 // after one iteration of a pid loop redundant write should be cleared 229 zone->processFans(); 230 EXPECT_FALSE(zone->getRedundantWrite()); 231 } 232 233 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) 234 { 235 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest 236 // and getMinThermalSetPoint. 237 238 // Need to add pid control process for the zone that can enable 239 // the process and add the set point. 240 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 241 242 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv( 243 IsNull(), StrEq(pidsensorpath.c_str()), 244 StrEq(enableInterface), NotNull())) 245 .Times(::testing::AnyNumber()) 246 .WillOnce(Invoke( 247 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 248 [[maybe_unused]] const char* interface, const char** names) { 249 EXPECT_STREQ("Enable", names[0]); 250 return 0; 251 })); 252 253 zone->addPidControlProcess(sensorname, sensorType, setpoint, 254 bus_mock_enable, pidsensorpath.c_str(), defer); 255 256 // At least one value must be above the minimum thermal setpoint used in 257 // the constructor otherwise it'll choose that value 258 std::vector<double> values = {100, 200, 300, 400, 500, 5000}; 259 260 for (auto v : values) 261 { 262 zone->addSetPoint(v, sensorname); 263 } 264 265 // This will pull the maximum RPM setpoint request. 266 zone->determineMaxSetPointRequest(); 267 EXPECT_EQ(5000, zone->getMaxSetPointRequest()); 268 269 // Clear the values, so it'll choose the minimum thermal setpoint. 270 zone->clearSetPoints(); 271 272 // This will go through the RPM set point values and grab the maximum. 273 zone->determineMaxSetPointRequest(); 274 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest()); 275 } 276 277 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) 278 { 279 // Tests adding several RPM setpoints, however, they're all lower than the 280 // configured minimal thermal setpoint RPM value. 281 282 // Need to add pid control process for the zone that can enable 283 // the process and add the set point. 284 auto bus_mock_enable = sdbusplus::get_mocked_new(&sdbus_mock_enable); 285 286 EXPECT_CALL(sdbus_mock_mode, sd_bus_emit_properties_changed_strv( 287 IsNull(), StrEq(pidsensorpath.c_str()), 288 StrEq(enableInterface), NotNull())) 289 .Times(::testing::AnyNumber()) 290 .WillOnce(Invoke( 291 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 292 [[maybe_unused]] const char* interface, const char** names) { 293 EXPECT_STREQ("Enable", names[0]); 294 return 0; 295 })); 296 297 zone->addPidControlProcess(sensorname, sensorType, setpoint, 298 bus_mock_enable, pidsensorpath.c_str(), defer); 299 300 std::vector<double> values = {100, 200, 300, 400, 500}; 301 302 for (auto v : values) 303 { 304 zone->addSetPoint(v, sensorname); 305 } 306 307 // This will pull the maximum RPM setpoint request. 308 zone->determineMaxSetPointRequest(); 309 310 // Verifies the value returned in the minimal thermal rpm set point. 311 EXPECT_EQ(zone->getMinThermalSetPoint(), zone->getMaxSetPointRequest()); 312 } 313 314 TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected) 315 { 316 // Verify the value used to create the object is stored. 317 // when the final failsafe percent is zero , it indicate 318 // no failsafe percent is configured , set it to 100% as 319 // the default setting. 320 321 std::vector<double> values = {0, 0, 0}; 322 int64_t defaultPercent = 100; 323 324 zone->addPidFailSafePercent("temp1", values[0]); 325 zone->addPidFailSafePercent("temp2", values[1]); 326 zone->addPidFailSafePercent("temp3", values[2]); 327 328 zone->initPidFailSafePercent(); 329 330 EXPECT_EQ(defaultPercent, zone->getFailSafePercent()); 331 } 332 333 TEST_F(PidZoneTest, GetFailSafePercent_VerifyReturnsExpected) 334 { 335 // Tests adding PID controller with FailSafePercent to the zone, 336 // and verifies it's returned as expected. 337 338 std::vector<double> values = {60, 80, 70}; 339 double max_value = 0; 340 341 for (const auto& value : values) 342 { 343 max_value = std::max(max_value, value); 344 } 345 346 zone->addPidFailSafePercent("temp1", values[0]); 347 zone->addPidFailSafePercent("temp2", values[1]); 348 zone->addPidFailSafePercent("temp3", values[2]); 349 350 zone->initPidFailSafePercent(); 351 352 EXPECT_EQ(max_value, zone->getFailSafePercent()); 353 } 354 355 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) 356 { 357 // This test will add a couple thermal inputs, and verify that the zone 358 // initializes into failsafe mode, and will read each sensor. 359 360 std::string name1 = "temp1"; 361 int64_t timeout = 1; 362 363 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 364 timeout); 365 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 366 367 std::string name2 = "temp2"; 368 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2, 369 timeout); 370 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 371 372 std::string type = "unchecked"; 373 mgr.addSensor(type, name1, std::move(sensor1)); 374 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 375 mgr.addSensor(type, name2, std::move(sensor2)); 376 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 377 378 // Now that the sensors exist, add them to the zone. 379 zone->addThermalInput(name1); 380 zone->addThermalInput(name2); 381 382 // Initialize Zone 383 zone->initializeCache(); 384 385 // Verify now in failsafe mode. 386 EXPECT_TRUE(zone->getFailSafeMode()); 387 388 ReadReturn r1; 389 r1.value = 10.0; 390 r1.updated = std::chrono::high_resolution_clock::now(); 391 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 392 393 ReadReturn r2; 394 r2.value = 11.0; 395 r2.updated = std::chrono::high_resolution_clock::now(); 396 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 397 398 // Read the sensors, this will put the values into the cache. 399 zone->updateSensors(); 400 401 // We should no longer be in failsafe mode. 402 EXPECT_FALSE(zone->getFailSafeMode()); 403 404 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 405 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 406 } 407 408 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached) 409 { 410 // This will add a couple fan inputs, and verify the values are cached. 411 412 std::string name1 = "fan1"; 413 int64_t timeout = 2; 414 415 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 416 timeout); 417 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 418 419 std::string name2 = "fan2"; 420 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2, 421 timeout); 422 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 423 424 std::string type = "unchecked"; 425 mgr.addSensor(type, name1, std::move(sensor1)); 426 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 427 mgr.addSensor(type, name2, std::move(sensor2)); 428 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 429 430 // Now that the sensors exist, add them to the zone. 431 zone->addFanInput(name1); 432 zone->addFanInput(name2); 433 434 // Initialize Zone 435 zone->initializeCache(); 436 437 ReadReturn r1; 438 r1.value = 10.0; 439 r1.updated = std::chrono::high_resolution_clock::now(); 440 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 441 442 ReadReturn r2; 443 r2.value = 11.0; 444 r2.updated = std::chrono::high_resolution_clock::now(); 445 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 446 447 // Method under test will read through each fan sensor for the zone and 448 // cache the values. 449 zone->updateFanTelemetry(); 450 451 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 452 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 453 } 454 455 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode) 456 { 457 // On the second updateSensors call, the updated timestamp will be beyond 458 // the timeout limit. 459 460 int64_t timeout = 1; 461 462 std::string name1 = "temp1"; 463 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 464 timeout); 465 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 466 467 std::string name2 = "temp2"; 468 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2, 469 timeout); 470 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 471 472 std::string type = "unchecked"; 473 mgr.addSensor(type, name1, std::move(sensor1)); 474 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 475 mgr.addSensor(type, name2, std::move(sensor2)); 476 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 477 478 zone->addThermalInput(name1); 479 zone->addThermalInput(name2); 480 481 // Initialize Zone 482 zone->initializeCache(); 483 484 // Verify now in failsafe mode. 485 EXPECT_TRUE(zone->getFailSafeMode()); 486 487 ReadReturn r1; 488 r1.value = 10.0; 489 r1.updated = std::chrono::high_resolution_clock::now(); 490 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 491 492 ReadReturn r2; 493 r2.value = 11.0; 494 r2.updated = std::chrono::high_resolution_clock::now(); 495 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 496 497 zone->updateSensors(); 498 EXPECT_FALSE(zone->getFailSafeMode()); 499 500 // Ok, so we're not in failsafe mode, so let's set updated to the past. 501 // sensor1 will have an updated field older than its timeout value, but 502 // sensor2 will be fine. :D 503 r1.updated -= std::chrono::seconds(3); 504 r2.updated = std::chrono::high_resolution_clock::now(); 505 506 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 507 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 508 509 // Method under test will read each sensor. One sensor's value is older 510 // than the timeout for that sensor and this triggers failsafe mode. 511 zone->updateSensors(); 512 EXPECT_TRUE(zone->getFailSafeMode()); 513 } 514 515 TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors) 516 { 517 // This will add a couple fan inputs, and verify the values are cached. 518 519 std::string name1 = "fan1"; 520 int64_t timeout = 2; 521 522 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 523 timeout); 524 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 525 526 std::string name2 = "fan2"; 527 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2, 528 timeout); 529 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 530 531 std::string type = "unchecked"; 532 mgr.addSensor(type, name1, std::move(sensor1)); 533 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 534 mgr.addSensor(type, name2, std::move(sensor2)); 535 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 536 537 // Now that the sensors exist, add them to the zone. 538 zone->addFanInput(name1); 539 zone->addFanInput(name2); 540 541 // Initialize Zone 542 zone->initializeCache(); 543 544 // Verify now in failsafe mode. 545 EXPECT_TRUE(zone->getFailSafeMode()); 546 547 ReadReturn r1; 548 r1.value = 10.0; 549 r1.updated = std::chrono::high_resolution_clock::now(); 550 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 551 552 ReadReturn r2; 553 r2.value = 11.0; 554 r2.updated = std::chrono::high_resolution_clock::now(); 555 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 556 557 // Method under test will read through each fan sensor for the zone and 558 // cache the values. 559 zone->updateFanTelemetry(); 560 561 // We should no longer be in failsafe mode. 562 EXPECT_FALSE(zone->getFailSafeMode()); 563 564 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 565 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 566 } 567 568 TEST_F(PidZoneTest, FanInputTest_ValueTimeoutEntersFailSafeMode) 569 { 570 // This will add a couple fan inputs, and verify the values are cached. 571 572 std::string name1 = "fan1"; 573 int64_t timeout = 2; 574 575 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 576 timeout); 577 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 578 579 std::string name2 = "fan2"; 580 std::unique_ptr<Sensor> sensor2 = std::make_unique<SensorMock>(name2, 581 timeout); 582 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 583 584 std::string type = "unchecked"; 585 mgr.addSensor(type, name1, std::move(sensor1)); 586 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 587 mgr.addSensor(type, name2, std::move(sensor2)); 588 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 589 590 // Now that the sensors exist, add them to the zone. 591 zone->addFanInput(name1); 592 zone->addFanInput(name2); 593 594 // Initialize Zone 595 zone->initializeCache(); 596 597 // Verify now in failsafe mode. 598 EXPECT_TRUE(zone->getFailSafeMode()); 599 600 ReadReturn r1; 601 r1.value = 10.0; 602 r1.updated = std::chrono::high_resolution_clock::now(); 603 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 604 605 ReadReturn r2; 606 r2.value = 11.0; 607 r2.updated = std::chrono::high_resolution_clock::now(); 608 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 609 610 // Method under test will read through each fan sensor for the zone and 611 // cache the values. 612 zone->updateFanTelemetry(); 613 614 // We should no longer be in failsafe mode. 615 EXPECT_FALSE(zone->getFailSafeMode()); 616 617 r1.updated -= std::chrono::seconds(3); 618 r2.updated = std::chrono::high_resolution_clock::now(); 619 620 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 621 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 622 623 zone->updateFanTelemetry(); 624 EXPECT_TRUE(zone->getFailSafeMode()); 625 } 626 627 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) 628 { 629 // One can grab a sensor from the manager through the zone. 630 631 int64_t timeout = 1; 632 633 std::string name1 = "temp1"; 634 std::unique_ptr<Sensor> sensor1 = std::make_unique<SensorMock>(name1, 635 timeout); 636 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 637 638 std::string type = "unchecked"; 639 mgr.addSensor(type, name1, std::move(sensor1)); 640 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 641 642 zone->addThermalInput(name1); 643 644 // Verify method under test returns the pointer we expect. 645 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 646 } 647 648 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) 649 { 650 // Tests adding a thermal PID controller to the zone, and verifies it's 651 // touched during processing. 652 653 std::unique_ptr<PIDController> tpid = 654 std::make_unique<ControllerMock>("thermal1", zone.get()); 655 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 656 657 // Access the internal pid configuration to clear it out (unrelated to the 658 // test). 659 ec::pid_info_t* info = tpid->getPIDInfo(); 660 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 661 662 zone->addThermalPID(std::move(tpid)); 663 664 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 665 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 666 EXPECT_CALL(*tmock, outputProc(_)); 667 668 // Method under test will, for each thermal PID, call setpt, input, and 669 // output. 670 zone->processThermals(); 671 } 672 673 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) 674 { 675 // Tests adding a fan PID controller to the zone, and verifies it's 676 // touched during processing. 677 678 std::unique_ptr<PIDController> tpid = 679 std::make_unique<ControllerMock>("fan1", zone.get()); 680 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 681 682 // Access the internal pid configuration to clear it out (unrelated to the 683 // test). 684 ec::pid_info_t* info = tpid->getPIDInfo(); 685 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 686 687 zone->addFanPID(std::move(tpid)); 688 689 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 690 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 691 EXPECT_CALL(*tmock, outputProc(_)); 692 693 // Method under test will, for each fan PID, call setpt, input, and output. 694 zone->processFans(); 695 } 696 697 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) 698 { 699 // The manual(bool) method is inherited from the dbus mode interface. 700 701 // Verifies that someone doesn't remove the internal call to the dbus 702 // object from which we're inheriting. 703 EXPECT_CALL(sdbus_mock_mode, 704 sd_bus_emit_properties_changed_strv( 705 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull())) 706 .WillOnce(Invoke( 707 [&]([[maybe_unused]] sd_bus* bus, [[maybe_unused]] const char* path, 708 [[maybe_unused]] const char* interface, const char** names) { 709 EXPECT_STREQ("Manual", names[0]); 710 return 0; 711 })); 712 713 // Method under test will set the manual mode to true and broadcast this 714 // change on dbus. 715 zone->manual(true); 716 EXPECT_TRUE(zone->getManualMode()); 717 } 718 719 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) 720 { 721 // This property is implemented by us as read-only, such that trying to 722 // write to it will have no effect. 723 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 724 } 725 726 } // namespace 727 } // namespace pid_control 728