1 #include "pid/zone.hpp" 2 3 #include <chrono> 4 #include <cstring> 5 #include <gmock/gmock.h> 6 #include <gtest/gtest.h> 7 #include <sdbusplus/test/sdbus_mock.hpp> 8 #include <vector> 9 10 #include "pid/ec/pid.hpp" 11 #include "sensors/manager.hpp" 12 #include "test/controller_mock.hpp" 13 #include "test/sensor_mock.hpp" 14 #include "test/helpers.hpp" 15 16 using ::testing::IsNull; 17 using ::testing::Return; 18 using ::testing::StrEq; 19 using ::testing::_; 20 21 static std::string modeInterface = "xyz.openbmc_project.Control.Mode"; 22 23 namespace { 24 25 TEST(PidZoneConstructorTest, BoringConstructorTest) { 26 // Build a PID Zone. 27 28 sdbusplus::SdBusMock sdbus_mock_passive, sdbus_mock_host, sdbus_mock_mode; 29 auto bus_mock_passive = sdbusplus::get_mocked_new(&sdbus_mock_passive); 30 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 31 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 32 33 EXPECT_CALL(sdbus_mock_host, 34 sd_bus_add_object_manager( 35 IsNull(), 36 _, 37 StrEq("/xyz/openbmc_project/extsensors"))) 38 .WillOnce(Return(0)); 39 40 SensorManager m(std::move(bus_mock_passive), 41 std::move(bus_mock_host)); 42 43 bool defer = true; 44 const char *objPath = "/path/"; 45 int64_t zone = 1; 46 float minThermalRpm = 1000.0; 47 float failSafePercent = 0.75; 48 49 int i; 50 std::vector<std::string> properties; 51 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 52 properties, &i); 53 54 PIDZone p(zone, minThermalRpm, failSafePercent, m, bus_mock_mode, objPath, 55 defer); 56 // Success. 57 } 58 59 } 60 61 class PidZoneTest : public ::testing::Test { 62 protected: 63 PidZoneTest() 64 : property_index(), 65 properties(), 66 sdbus_mock_passive(), 67 sdbus_mock_host(), 68 sdbus_mock_mode() 69 { 70 EXPECT_CALL(sdbus_mock_host, 71 sd_bus_add_object_manager( 72 IsNull(), 73 _, 74 StrEq("/xyz/openbmc_project/extsensors"))) 75 .WillOnce(Return(0)); 76 77 auto bus_mock_passive = 78 sdbusplus::get_mocked_new(&sdbus_mock_passive); 79 auto bus_mock_host = sdbusplus::get_mocked_new(&sdbus_mock_host); 80 auto bus_mock_mode = sdbusplus::get_mocked_new(&sdbus_mock_mode); 81 82 // Compiler weirdly not happy about just instantiating mgr(...); 83 SensorManager m(std::move(bus_mock_passive), 84 std::move(bus_mock_host)); 85 mgr = std::move(m); 86 87 SetupDbusObject(&sdbus_mock_mode, defer, objPath, modeInterface, 88 properties, &property_index); 89 90 zone = std::make_unique<PIDZone>(zoneId, minThermalRpm, 91 failSafePercent, mgr, 92 bus_mock_mode, objPath, defer); 93 } 94 95 // unused 96 int property_index; 97 std::vector<std::string> properties; 98 99 sdbusplus::SdBusMock sdbus_mock_passive; 100 sdbusplus::SdBusMock sdbus_mock_host; 101 sdbusplus::SdBusMock sdbus_mock_mode; 102 int64_t zoneId = 1; 103 float minThermalRpm = 1000.0; 104 float failSafePercent = 0.75; 105 bool defer = true; 106 const char *objPath = "/path/"; 107 SensorManager mgr; 108 109 std::unique_ptr<PIDZone> zone; 110 }; 111 112 TEST_F(PidZoneTest, GetZoneId_ReturnsExpected) { 113 // Verifies the zoneId returned is what we expect. 114 115 EXPECT_EQ(zoneId, zone->getZoneId()); 116 } 117 118 TEST_F(PidZoneTest, GetAndSetManualModeTest_BehavesAsExpected) { 119 // Verifies that the zone starts in manual mode. Verifies that one can set 120 // the mode. 121 EXPECT_FALSE(zone->getManualMode()); 122 123 zone->setManualMode(true); 124 EXPECT_TRUE(zone->getManualMode()); 125 } 126 127 TEST_F(PidZoneTest, RpmSetPoints_AddMaxClear_BehaveAsExpected) { 128 // Tests addRPMSetPoint, clearRPMSetPoints, determineMaxRPMRequest 129 // and getMinThermalRpmSetPt. 130 131 // At least one value must be above the minimum thermal setpoint used in 132 // the constructor otherwise it'll choose that value 133 std::vector<float> values = {100, 200, 300, 400, 500, 5000}; 134 for (auto v : values) 135 { 136 zone->addRPMSetPoint(v); 137 } 138 139 // This will pull the maximum RPM setpoint request. 140 zone->determineMaxRPMRequest(); 141 EXPECT_EQ(5000, zone->getMaxRPMRequest()); 142 143 // Clear the values, so it'll choose the minimum thermal setpoint. 144 zone->clearRPMSetPoints(); 145 146 // This will go through the RPM set point values and grab the maximum. 147 zone->determineMaxRPMRequest(); 148 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest()); 149 } 150 151 TEST_F(PidZoneTest, RpmSetPoints_AddBelowMinimum_BehavesAsExpected) { 152 // Tests adding several RPM setpoints, however, they're all lower than the 153 // configured minimal thermal set-point RPM value. 154 155 std::vector<float> values = {100, 200, 300, 400, 500}; 156 for (auto v : values) 157 { 158 zone->addRPMSetPoint(v); 159 } 160 161 // This will pull the maximum RPM setpoint request. 162 zone->determineMaxRPMRequest(); 163 164 // Verifies the value returned in the minimal thermal rpm set point. 165 EXPECT_EQ(zone->getMinThermalRpmSetPt(), zone->getMaxRPMRequest()); 166 } 167 168 TEST_F(PidZoneTest, GetFailSafePercent_ReturnsExpected) { 169 // Verify the value used to create the object is stored. 170 EXPECT_EQ(failSafePercent, zone->getFailSafePercent()); 171 } 172 173 TEST_F(PidZoneTest, ThermalInputs_FailsafeToValid_ReadsSensors) { 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 // 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 // On the second updateSensors call, the updated timestamp will be beyond 273 // the timeout limit. 274 275 int64_t timeout = 1; 276 277 std::string name1 = "temp1"; 278 std::unique_ptr<Sensor> sensor1 = 279 std::make_unique<SensorMock>(name1, timeout); 280 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 281 282 std::string name2 = "temp2"; 283 std::unique_ptr<Sensor> sensor2 = 284 std::make_unique<SensorMock>(name2, timeout); 285 SensorMock *sensor_ptr2 = reinterpret_cast<SensorMock*>(sensor2.get()); 286 287 std::string type = "unchecked"; 288 mgr.addSensor(type, name1, std::move(sensor1)); 289 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 290 mgr.addSensor(type, name2, std::move(sensor2)); 291 EXPECT_EQ(mgr.getSensor(name2), sensor_ptr2); 292 293 zone->addThermalInput(name1); 294 zone->addThermalInput(name2); 295 296 // Initialize Zone 297 zone->initializeCache(); 298 299 // Verify now in failsafe mode. 300 EXPECT_TRUE(zone->getFailSafeMode()); 301 302 ReadReturn r1; 303 r1.value = 10.0; 304 r1.updated = std::chrono::high_resolution_clock::now(); 305 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 306 307 ReadReturn r2; 308 r2.value = 11.0; 309 r2.updated = std::chrono::high_resolution_clock::now(); 310 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 311 312 zone->updateSensors(); 313 EXPECT_FALSE(zone->getFailSafeMode()); 314 315 // Ok, so we're not in failsafe mode, so let's set updated to the past. 316 // sensor1 will have an updated field older than its timeout value, but 317 // sensor2 will be fine. :D 318 r1.updated -= std::chrono::seconds(3); 319 r2.updated = std::chrono::high_resolution_clock::now(); 320 321 EXPECT_CALL(*sensor_ptr1, read()).WillOnce(Return(r1)); 322 EXPECT_CALL(*sensor_ptr2, read()).WillOnce(Return(r2)); 323 324 // Method under test will read each sensor. One sensor's value is older 325 // than the timeout for that sensor and this triggers failsafe mode. 326 zone->updateSensors(); 327 EXPECT_TRUE(zone->getFailSafeMode()); 328 } 329 330 TEST_F(PidZoneTest, GetSensorTest_ReturnsExpected) { 331 // One can grab a sensor from the manager through the zone. 332 333 int64_t timeout = 1; 334 335 std::string name1 = "temp1"; 336 std::unique_ptr<Sensor> sensor1 = 337 std::make_unique<SensorMock>(name1, timeout); 338 SensorMock *sensor_ptr1 = reinterpret_cast<SensorMock*>(sensor1.get()); 339 340 std::string type = "unchecked"; 341 mgr.addSensor(type, name1, std::move(sensor1)); 342 EXPECT_EQ(mgr.getSensor(name1), sensor_ptr1); 343 344 zone->addThermalInput(name1); 345 346 // Verify method under test returns the pointer we expect. 347 EXPECT_EQ(mgr.getSensor(name1), zone->getSensor(name1)); 348 } 349 350 TEST_F(PidZoneTest, AddThermalPIDTest_VerifiesThermalPIDsProcessed) { 351 // Tests adding a thermal PID controller to the zone, and verifies it's 352 // touched during processing. 353 354 std::unique_ptr<PIDController> tpid = 355 std::make_unique<ControllerMock>("thermal1", zone.get()); 356 ControllerMock *tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 357 358 // Access the internal pid configuration to clear it out (unrelated to the 359 // test). 360 ec::pid_info_t* info = tpid->get_pid_info(); 361 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 362 363 zone->addThermalPID(std::move(tpid)); 364 365 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0)); 366 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0)); 367 EXPECT_CALL(*tmock, output_proc(_)); 368 369 // Method under test will, for each thermal PID, call setpt, input, and 370 // output. 371 zone->process_thermals(); 372 } 373 374 TEST_F(PidZoneTest, AddFanPIDTest_VerifiesFanPIDsProcessed) { 375 // Tests adding a fan PID controller to the zone, and verifies it's 376 // touched during processing. 377 378 std::unique_ptr<PIDController> tpid = 379 std::make_unique<ControllerMock>("fan1", zone.get()); 380 ControllerMock *tmock = reinterpret_cast<ControllerMock*>(tpid.get()); 381 382 // Access the internal pid configuration to clear it out (unrelated to the 383 // test). 384 ec::pid_info_t* info = tpid->get_pid_info(); 385 std::memset(info, 0x00, sizeof(ec::pid_info_t)); 386 387 zone->addFanPID(std::move(tpid)); 388 389 EXPECT_CALL(*tmock, setpt_proc()).WillOnce(Return(10.0)); 390 EXPECT_CALL(*tmock, input_proc()).WillOnce(Return(11.0)); 391 EXPECT_CALL(*tmock, output_proc(_)); 392 393 // Method under test will, for each fan PID, call setpt, input, and output. 394 zone->process_fans(); 395 } 396 397 TEST_F(PidZoneTest, ManualModeDbusTest_VerifySetManualBehavesAsExpected) { 398 // The manual(bool) method is inherited from the dbus mode interface. 399 400 // Verifies that someone doesn't remove the internal call to the dbus 401 // object from which we're inheriting. 402 EXPECT_CALL(sdbus_mock_mode, 403 sd_bus_emit_properties_changed_strv(IsNull(), StrEq(objPath), 404 StrEq(modeInterface), 405 NotNull())) 406 .WillOnce(Invoke([&](sd_bus *bus, const char *path, 407 const char *interface, char **names) { 408 EXPECT_STREQ("Manual", names[0]); 409 return 0; 410 })); 411 412 // Method under test will set the manual mode to true and broadcast this 413 // change on dbus. 414 zone->manual(true); 415 EXPECT_TRUE(zone->getManualMode()); 416 } 417 418 TEST_F(PidZoneTest, FailsafeDbusTest_VerifiesReturnsExpected) { 419 // This property is implemented by us as read-only, such that trying to 420 // write to it will have no effect. 421 EXPECT_EQ(zone->failSafe(), zone->getFailSafeMode()); 422 } 423