1 #include "pid/ec/pid.hpp" 2 #include "pid/zone.hpp" 3 #include "sensors/manager.hpp" 4 #include "test/controller_mock.hpp" 5 #include "test/helpers.hpp" 6 #include "test/sensor_mock.hpp" 7 8 #include <sdbusplus/test/sdbus_mock.hpp> 9 10 #include <chrono> 11 #include <cstring> 12 #include <vector> 13 14 #include <gmock/gmock.h> 15 #include <gtest/gtest.h> 16 17 using ::testing::_; 18 using ::testing::IsNull; 19 using ::testing::Return; 20 using ::testing::StrEq; 21 22 static std::string modeInterface = "xyz.openbmc_project.Control.Mode"; 23 24 namespace 25 { 26 27 TEST(PidZoneConstructorTest, BoringConstructorTest) 28 { 29 // Build a PID Zone. 30 31 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode; 32 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 33 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 34 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 35 36 EXPECT_CALL(sdbus_mock_host, 37 sd_bus_add_object_manager( 38 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 39 .WillOnce(Return(0)); 40 41 SensorManager m(bus_mock_passive, bus_mock_host); 42 43 bool defer = true; 44 const char* objPath = "/path/"; 45 int64_t zone = 1; 46 double minThermalOutput = 1000.0; 47 double failSafePercent = 0.75; 48 49 double d; 50 std::vector<std::string> properties; 51 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, properties, 52 &d); 53 54 PIDZone p(zone, minThermalOutput, failSafePercent, m, bus_mock_mode, 55 objPath, defer); 56 // Success. 57 } 58 59 } // namespace 60 61 class PidZoneTest : public ::testing::Test 62 { 63 protected: 64 PidZoneTest() : 65 property_index(), properties(), sdbus_mock_passive(), sdbus_mock_host(), 66 sdbus_mock_mode() 67 { 68 EXPECT_CALL(sdbus_mock_host, 69 sd_bus_add_object_manager( 70 IsNull(), _, StrEq("/xyz/openbmc_project/extsensors"))) 71 .WillOnce(Return(0)); 72 73 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 74 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 75 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 76 77 // Compiler weirdly not happy about just instantiating mgr(...); 78 SensorManager m(bus_mock_passive, bus_mock_host); 79 mgr = std::move(m); 80 81 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 82 properties, &property_index); 83 84 zone = 85 std::make_unique<PIDZone>(zoneId, minThermalOutput, failSafePercent, 86 mgr, bus_mock_mode, objPath, defer); 87 } 88 89 // unused 90 double property_index; 91 std::vector<std::string> properties; 92 93 sdbusplus::SdBusMock sdbus_mock_passive; 94 sdbusplus::SdBusMock sdbus_mock_host; 95 sdbusplus::SdBusMock sdbus_mock_mode; 96 int64_t zoneId = 1; 97 double minThermalOutput = 1000.0; 98 double failSafePercent = 0.75; 99 bool defer = true; 100 const char* objPath = "/path/"; 101 SensorManager mgr; 102 103 std::unique_ptr<PIDZone> zone; 104 }; 105 106 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected) 107 { 108 // Verifies the zoneId returned is what we expect. 109 110 EXPECT_EQ(zoneId, zone->getZoneID()); 111 } 112 113 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected) 114 { 115 // Verifies that the zone starts in manual mode. Verifies that one can set 116 // the mode. 117 EXPECT_FALSE(zone->getManualMode()); 118 119 zone->setManualMode(true); 120 EXPECT_TRUE(zone->getManualMode()); 121 } 122 123 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) 124 { 125 // Tests addSetPoint, clearSetPoints, determineMaxSetPointRequest 126 // and getMinThermalSetpoint. 127 128 // At least one value must be above the minimum thermal setpoint used in 129 // the constructor otherwise it'll choose that value 130 std::vector<double> values = {100, 200, 300, 400, 500, 5000}; 131 for (auto v : values) 132 { 133 zone->addSetPoint(v); 134 } 135 136 // This will pull the maximum RPM setpoint request. 137 zone->determineMaxSetPointRequest(); 138 EXPECT_EQ(5000, zone->getMaxSetPointRequest()); 139 140 // Clear the values, so it'll choose the minimum thermal setpoint. 141 zone->clearSetPoints(); 142 143 // This will go through the RPM set point values and grab the maximum. 144 zone->determineMaxSetPointRequest(); 145 EXPECT_EQ(zone->getMinThermalSetpoint(), zone->getMaxSetPointRequest()); 146 } 147 148 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) 149 { 150 // Tests adding several RPM setpoints, however, they're all lower than the 151 // configured minimal thermal setpoint RPM value. 152 153 std::vector<double> values = {100, 200, 300, 400, 500}; 154 for (auto v : values) 155 { 156 zone->addSetPoint(v); 157 } 158 159 // This will pull the maximum RPM setpoint request. 160 zone->determineMaxSetPointRequest(); 161 162 // Verifies the value returned in the minimal thermal rpm set point. 163 EXPECT_EQ(zone->getMinThermalSetpoint(), zone->getMaxSetPointRequest()); 164 } 165 166 TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected) 167 { 168 // Verify the value used to create the object is stored. 169 EXPECT_EQ(failSafePercent, zone->getFailSafePercent()); 170 } 171 172 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) 173 { 174 // This test will add a couple thermal inputs, and verify that the zone 175 // initializes into failsafe mode, and will read each sensor. 176 177 std::string name1 = "temp1"; 178 int64_t timeout = 1; 179 180 std::unique_ptr<Sensor> sensor1 = 181 std::make_unique<SensorMock>(name1, timeout); 182 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 183 184 std::string name2 = "temp2"; 185 std::unique_ptr<Sensor> sensor2 = 186 std::make_unique<SensorMock>(name2, timeout); 187 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 188 189 std::string type = "unchecked"; 190 mgr.addSensor(type, name1, std::move(sensor1)); 191 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 192 mgr.addSensor(type, name2, std::move(sensor2)); 193 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 194 195 // Now that the sensors exist, add them to the zone. 196 zone->addThermalInput(name1); 197 zone->addThermalInput(name2); 198 199 // Initialize Zone 200 zone->initializeCache(); 201 202 // Verify now in failsafe mode. 203 EXPECT_TRUE(zone->getFailSafeMode()); 204 205 ReadReturn r1; 206 r1.value = 10.0; 207 r1.updated = std::chrono::high_resolution_clock::now(); 208 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 209 210 ReadReturn r2; 211 r2.value = 11.0; 212 r2.updated = std::chrono::high_resolution_clock::now(); 213 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 214 215 // Read the sensors, this will put the values into the cache. 216 zone->updateSensors(); 217 218 // We should no longer be in failsafe mode. 219 EXPECT_FALSE(zone->getFailSafeMode()); 220 221 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 222 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 223 } 224 225 TEST_F(PidZoneTest, FanInputTest_VerifiesFanValuesCached) 226 { 227 // This will add a couple fan inputs, and verify the values are cached. 228 229 std::string name1 = "fan1"; 230 int64_t timeout = 2; 231 232 std::unique_ptr<Sensor> sensor1 = 233 std::make_unique<SensorMock>(name1, timeout); 234 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 235 236 std::string name2 = "fan2"; 237 std::unique_ptr<Sensor> sensor2 = 238 std::make_unique<SensorMock>(name2, timeout); 239 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 240 241 std::string type = "unchecked"; 242 mgr.addSensor(type, name1, std::move(sensor1)); 243 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 244 mgr.addSensor(type, name2, std::move(sensor2)); 245 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 246 247 // Now that the sensors exist, add them to the zone. 248 zone->addFanInput(name1); 249 zone->addFanInput(name2); 250 251 // Initialize Zone 252 zone->initializeCache(); 253 254 ReadReturn r1; 255 r1.value = 10.0; 256 r1.updated = std::chrono::high_resolution_clock::now(); 257 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 258 259 ReadReturn r2; 260 r2.value = 11.0; 261 r2.updated = std::chrono::high_resolution_clock::now(); 262 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 263 264 // Method under test will read through each fan sensor for the zone and 265 // cache the values. 266 zone->updateFanTelemetry(); 267 268 EXPECT_EQ(r1.value, zone->getCachedValue(name1)); 269 EXPECT_EQ(r2.value, zone->getCachedValue(name2)); 270 } 271 272 TEST_F(PidZoneTest, ThermalInput_ValueTimeoutEntersFailSafeMode) 273 { 274 // On the second updateSensors call, the updated timestamp will be beyond 275 // the timeout limit. 276 277 int64_t timeout = 1; 278 279 std::string name1 = "temp1"; 280 std::unique_ptr<Sensor> sensor1 = 281 std::make_unique<SensorMock>(name1, timeout); 282 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 283 284 std::string name2 = "temp2"; 285 std::unique_ptr<Sensor> sensor2 = 286 std::make_unique<SensorMock>(name2, timeout); 287 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 288 289 std::string type = "unchecked"; 290 mgr.addSensor(type, name1, std::move(sensor1)); 291 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 292 mgr.addSensor(type, name2, std::move(sensor2)); 293 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 294 295 zone->addThermalInput(name1); 296 zone->addThermalInput(name2); 297 298 // Initialize Zone 299 zone->initializeCache(); 300 301 // Verify now in failsafe mode. 302 EXPECT_TRUE(zone->getFailSafeMode()); 303 304 ReadReturn r1; 305 r1.value = 10.0; 306 r1.updated = std::chrono::high_resolution_clock::now(); 307 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 308 309 ReadReturn r2; 310 r2.value = 11.0; 311 r2.updated = std::chrono::high_resolution_clock::now(); 312 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 313 314 zone->updateSensors(); 315 EXPECT_FALSE(zone->getFailSafeMode()); 316 317 // Ok, so we're not in failsafe mode, so let's set updated to the past. 318 // sensor1 will have an updated field older than its timeout value, but 319 // sensor2 will be fine. :D 320 r1.updated -= std::chrono::seconds(3); 321 r2.updated = std::chrono::high_resolution_clock::now(); 322 323 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 324 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 325 326 // Method under test will read each sensor. One sensor's value is older 327 // than the timeout for that sensor and this triggers failsafe mode. 328 zone->updateSensors(); 329 EXPECT_TRUE(zone->getFailSafeMode()); 330 } 331 332 TEST_F(PidZoneTest, FanInputTest_FailsafeToValid_ReadsSensors) 333 { 334 // This will add a couple fan inputs, and verify the values are cached. 335 336 std::string name1 = "fan1"; 337 int64_t timeout = 2; 338 339 std::unique_ptr<Sensor> sensor1 = 340 std::make_unique<SensorMock>(name1, timeout); 341 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 342 343 std::string name2 = "fan2"; 344 std::unique_ptr<Sensor> sensor2 = 345 std::make_unique<SensorMock>(name2, timeout); 346 SensorMock* sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 347 348 std::string type = "unchecked"; 349 mgr.addSensor(type, name1, std::move(sensor1)); 350 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 351 mgr.addSensor(type, name2, std::move(sensor2)); 352 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 353 354 // Now that the sensors exist, add them to the zone. 355 zone->addFanInput(name1); 356 zone->addFanInput(name2); 357 358 // Initialize Zone 359 zone->initializeCache(); 360 361 // Verify now in failsafe mode. 362 EXPECT_TRUE(zone->getFailSafeMode()); 363 364 ReadReturn r1; 365 r1.value = 10.0; 366 r1.updated = std::chrono::high_resolution_clock::now(); 367 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 368 369 ReadReturn r2; 370 r2.value = 11.0; 371 r2.updated = std::chrono::high_resolution_clock::now(); 372 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 373 374 // Method under test will read through each fan sensor for the zone and 375 // cache the values. 376 zone->updateFanTelemetry(); 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_ValueTimeoutEntersFailSafeMode) 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 = 393 std::make_unique<SensorMock>(name1, timeout); 394 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 395 396 std::string name2 = "fan2"; 397 std::unique_ptr<Sensor> sensor2 = 398 std::make_unique<SensorMock>(name2, 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 // Verify now in failsafe mode. 415 EXPECT_TRUE(zone->getFailSafeMode()); 416 417 ReadReturn r1; 418 r1.value = 10.0; 419 r1.updated = std::chrono::high_resolution_clock::now(); 420 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 421 422 ReadReturn r2; 423 r2.value = 11.0; 424 r2.updated = std::chrono::high_resolution_clock::now(); 425 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 426 427 // Method under test will read through each fan sensor for the zone and 428 // cache the values. 429 zone->updateFanTelemetry(); 430 431 // We should no longer be in failsafe mode. 432 EXPECT_FALSE(zone->getFailSafeMode()); 433 434 r1.updated -= std::chrono::seconds(3); 435 r2.updated = std::chrono::high_resolution_clock::now(); 436 437 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 438 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 439 440 zone->updateFanTelemetry(); 441 EXPECT_TRUE(zone->getFailSafeMode()); 442 } 443 444 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) 445 { 446 // One can grab a sensor from the manager through the zone. 447 448 int64_t timeout = 1; 449 450 std::string name1 = "temp1"; 451 std::unique_ptr<Sensor> sensor1 = 452 std::make_unique<SensorMock>(name1, timeout); 453 SensorMock* sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 454 455 std::string type = "unchecked"; 456 mgr.addSensor(type, name1, std::move(sensor1)); 457 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 458 459 zone->addThermalInput(name1); 460 461 // Verify method under test returns the pointer we expect. 462 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 463 } 464 465 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) 466 { 467 // Tests adding a thermal PID controller to the zone, and verifies it's 468 // touched during processing. 469 470 std::unique_ptr<PIDController> tpid = 471 std::make_unique<ControllerMock>("thermal1", zone.get()); 472 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 473 474 // Access the internal pid configuration to clear it out (unrelated to the 475 // test). 476 ec::pid_info_t* info = tpid->getPIDInfo(); 477 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 478 479 zone->addThermalPID(std::move(tpid)); 480 481 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 482 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 483 EXPECT_CALL(*tmock, outputProc(_)); 484 485 // Method under test will, for each thermal PID, call setpt, input, and 486 // output. 487 zone->processThermals(); 488 } 489 490 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) 491 { 492 // Tests adding a fan PID controller to the zone, and verifies it's 493 // touched during processing. 494 495 std::unique_ptr<PIDController> tpid = 496 std::make_unique<ControllerMock>("fan1", zone.get()); 497 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 498 499 // Access the internal pid configuration to clear it out (unrelated to the 500 // test). 501 ec::pid_info_t* info = tpid->getPIDInfo(); 502 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 503 504 zone->addFanPID(std::move(tpid)); 505 506 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 507 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 508 EXPECT_CALL(*tmock, outputProc(_)); 509 510 // Method under test will, for each fan PID, call setpt, input, and output. 511 zone->processFans(); 512 } 513 514 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) 515 { 516 // The manual(bool) method is inherited from the dbus mode interface. 517 518 // Verifies that someone doesn't remove the internal call to the dbus 519 // object from which we're inheriting. 520 EXPECT_CALL(sdbus_mock_mode, 521 sd_bus_emit_properties_changed_strv( 522 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull())) 523 .WillOnce(Invoke([&](sd_bus* bus, const char* path, 524 const char* interface, char** names) { 525 EXPECT_STREQ("Manual", names[0]); 526 return 0; 527 })); 528 529 // Method under test will set the manual mode to true and broadcast this 530 // change on dbus. 531 zone->manual(true); 532 EXPECT_TRUE(zone->getManualMode()); 533 } 534 535 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) 536 { 537 // This property is implemented by us as read-only, such that trying to 538 // write to it will have no effect. 539 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 540 } 541