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