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