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(std::move(bus_mock_passive), std::move(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(std::move(bus_mock_passive), std::move(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 addRPMSetPoint, clearRPMSetPoints, determineMaxRPMRequest 125 // and getMinThermalRPMSetpoint. 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->addRPMSetPoint(v); 133 } 134 135 // This will pull the maximum RPM setpoint request. 136 zone->determineMaxRPMRequest(); 137 EXPECT_EQ(5000, zone->getMaxRPMRequest()); 138 139 // Clear the values, so it'll choose the minimum thermal setpoint. 140 zone->clearRPMSetPoints(); 141 142 // This will go through the RPM set point values and grab the maximum. 143 zone->determineMaxRPMRequest(); 144 EXPECT_EQ(zone->getMinThermalRPMSetpoint(), zone->getMaxRPMRequest()); 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->addRPMSetPoint(v); 156 } 157 158 // This will pull the maximum RPM setpoint request. 159 zone->determineMaxRPMRequest(); 160 161 // Verifies the value returned in the minimal thermal rpm set point. 162 EXPECT_EQ(zone->getMinThermalRPMSetpoint(), zone->getMaxRPMRequest()); 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, GetSensorTest_ReturnsExpected) 332 { 333 // One can grab a sensor from the manager through the zone. 334 335 int64_t timeout = 1; 336 337 std::string name1 = "temp1"; 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 type = "unchecked"; 343 mgr.addSensor(type, name1, std::move(sensor1)); 344 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 345 346 zone->addThermalInput(name1); 347 348 // Verify method under test returns the pointer we expect. 349 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 350 } 351 352 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) 353 { 354 // Tests adding a thermal PID controller to the zone, and verifies it's 355 // touched during processing. 356 357 std::unique_ptr<PIDController> tpid = 358 std::make_unique<ControllerMock>("thermal1", zone.get()); 359 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 360 361 // Access the internal pid configuration to clear it out (unrelated to the 362 // test). 363 ec::pid_info_t* info = tpid->getPIDInfo(); 364 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 365 366 zone->addThermalPID(std::move(tpid)); 367 368 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 369 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 370 EXPECT_CALL(*tmock, outputProc(_)); 371 372 // Method under test will, for each thermal PID, call setpt, input, and 373 // output. 374 zone->processThermals(); 375 } 376 377 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) 378 { 379 // Tests adding a fan PID controller to the zone, and verifies it's 380 // touched during processing. 381 382 std::unique_ptr<PIDController> tpid = 383 std::make_unique<ControllerMock>("fan1", zone.get()); 384 ControllerMock* tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 385 386 // Access the internal pid configuration to clear it out (unrelated to the 387 // test). 388 ec::pid_info_t* info = tpid->getPIDInfo(); 389 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 390 391 zone->addFanPID(std::move(tpid)); 392 393 EXPECT_CALL(*tmock, setptProc()).WillOnce(Return(10.0)); 394 EXPECT_CALL(*tmock, inputProc()).WillOnce(Return(11.0)); 395 EXPECT_CALL(*tmock, outputProc(_)); 396 397 // Method under test will, for each fan PID, call setpt, input, and output. 398 zone->processFans(); 399 } 400 401 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) 402 { 403 // The manual(bool) method is inherited from the dbus mode interface. 404 405 // Verifies that someone doesn't remove the internal call to the dbus 406 // object from which we're inheriting. 407 EXPECT_CALL(sdbus_mock_mode, 408 sd_bus_emit_properties_changed_strv( 409 IsNull(), StrEq(objPath), StrEq(modeInterface), NotNull())) 410 .WillOnce(Invoke([&](sd_bus* bus, const char* path, 411 const char* interface, char** names) { 412 EXPECT_STREQ("Manual", names[0]); 413 return 0; 414 })); 415 416 // Method under test will set the manual mode to true and broadcast this 417 // change on dbus. 418 zone->manual(true); 419 EXPECT_TRUE(zone->getManualMode()); 420 } 421 422 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) 423 { 424 // This property is implemented by us as read-only, such that trying to 425 // write to it will have no effect. 426 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 427 } 428