1 #include "conf.hpp" 2 #include "dbus/dbuspassive.hpp" 3 #include "test/dbushelper_mock.hpp" 4 5 #include <sdbusplus/test/sdbus_mock.hpp> 6 7 #include <functional> 8 #include <string> 9 #include <variant> 10 11 #include <gmock/gmock.h> 12 #include <gtest/gtest.h> 13 14 namespace pid_control 15 { 16 namespace 17 { 18 19 using ::testing::_; 20 using ::testing::InSequence; 21 using ::testing::Invoke; 22 using ::testing::IsNull; 23 using ::testing::NotNull; 24 using ::testing::Return; 25 using ::testing::StrEq; 26 27 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value"; 28 29 TEST(DbusPassiveTest, FactoryFailsWithInvalidType) 30 { 31 // Verify the type is checked by the factory. 32 33 sdbusplus::SdBusMock sdbus_mock; 34 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 35 std::string type = "invalid"; 36 std::string id = "id"; 37 38 DbusHelperMock helper; 39 auto info = conf::SensorConfig(); 40 41 std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive( 42 bus_mock, type, id, &helper, &info, nullptr); 43 44 EXPECT_EQ(ri, nullptr); 45 } 46 47 TEST(DbusPassiveTest, BoringConstructorTest) 48 { 49 // Simply build the object, does no error checking. 50 51 sdbusplus::SdBusMock sdbus_mock; 52 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 53 std::string type = "invalid"; 54 std::string id = "id"; 55 std::string path = "/xyz/openbmc_project/sensors/unknown/id"; 56 57 DbusHelperMock helper; 58 struct SensorProperties properties; 59 60 DbusPassive(bus_mock, type, id, &helper, properties, false, path, nullptr); 61 // Success 62 } 63 64 class DbusPassiveTestObj : public ::testing::Test 65 { 66 protected: 67 DbusPassiveTestObj() : 68 sdbus_mock(), 69 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper() 70 { 71 EXPECT_CALL(helper, getService(_, StrEq(SensorIntf), StrEq(path))) 72 .WillOnce(Return("asdf")); 73 74 EXPECT_CALL(helper, 75 getProperties(_, StrEq("asdf"), StrEq(path), NotNull())) 76 .WillOnce(Invoke( 77 [&](sdbusplus::bus::bus& bus, const std::string& service, 78 const std::string& path, struct SensorProperties* prop) { 79 prop->scale = _scale; 80 prop->value = _value; 81 prop->unit = "x"; 82 prop->min = 0; 83 prop->max = 0; 84 })); 85 EXPECT_CALL(helper, thresholdsAsserted(_, StrEq("asdf"), StrEq(path))) 86 .WillOnce(Return(false)); 87 88 auto info = conf::SensorConfig(); 89 ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info, 90 nullptr); 91 passive = reinterpret_cast<DbusPassive*>(ri.get()); 92 EXPECT_FALSE(passive == nullptr); 93 } 94 95 sdbusplus::SdBusMock sdbus_mock; 96 sdbusplus::bus::bus bus_mock; 97 DbusHelperMock helper; 98 std::string type = "temp"; 99 std::string id = "id"; 100 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 101 int64_t _scale = -3; 102 int64_t _value = 10; 103 104 std::unique_ptr<ReadInterface> ri; 105 DbusPassive* passive; 106 }; 107 108 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues) 109 { 110 // Verify read is returning the values. 111 ReadReturn v; 112 v.value = 0.01; 113 // TODO: updated is set when the value is created, so we can range check 114 // it. 115 ReadReturn r = passive->read(); 116 EXPECT_EQ(v.value, r.value); 117 } 118 119 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue) 120 { 121 // Verify setvalue does as advertised. 122 123 double value = 0.01; 124 passive->setValue(value); 125 126 // TODO: updated is set when the value is set, so we can range check it. 127 ReadReturn r = passive->read(); 128 EXPECT_EQ(value, r.value); 129 } 130 131 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue) 132 { 133 // Verify the scale is returned as expected. 134 EXPECT_EQ(_scale, passive->getScale()); 135 } 136 137 TEST_F(DbusPassiveTestObj, getIDReturnsExpectedValue) 138 { 139 // Verify getID returns the expected value. 140 EXPECT_EQ(id, passive->getID()); 141 } 142 143 TEST_F(DbusPassiveTestObj, GetMinValueReturnsExpectedValue) 144 { 145 EXPECT_DOUBLE_EQ(0, passive->getMin()); 146 } 147 148 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal) 149 { 150 // The dbus passive sensor listens for updates and if it's the Value 151 // property, it needs to handle it. 152 153 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 154 .WillOnce(Return(nullptr)); 155 sdbusplus::message::message msg(nullptr, &sdbus_mock); 156 157 const char* Value = "Value"; 158 int64_t xValue = 10000; 159 const char* intf = "xyz.openbmc_project.Sensor.Value"; 160 // string, std::map<std::string, std::variant<int64_t>> 161 // msg.read(msgSensor, msgData); 162 163 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 164 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 165 const char** s = static_cast<const char**>(p); 166 // Read the first parameter, the string. 167 *s = intf; 168 return 0; 169 })) 170 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 171 const char** s = static_cast<const char**>(p); 172 *s = Value; 173 // Read the string in the pair (dictionary). 174 return 0; 175 })); 176 177 // std::map 178 EXPECT_CALL(sdbus_mock, 179 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 180 .WillOnce(Return(0)); 181 182 // while !at_end() 183 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 184 .WillOnce(Return(0)) 185 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 186 187 // std::pair 188 EXPECT_CALL(sdbus_mock, 189 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 190 .WillOnce(Return(0)); 191 192 EXPECT_CALL(sdbus_mock, 193 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 194 .WillOnce(Return(1)); 195 EXPECT_CALL(sdbus_mock, 196 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 197 .WillOnce(Return(0)); 198 199 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 200 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 201 int64_t* s = static_cast<int64_t*>(p); 202 *s = xValue; 203 return 0; 204 })); 205 206 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 207 .WillOnce(Return(0)) /* variant. */ 208 .WillOnce(Return(0)) /* std::pair */ 209 .WillOnce(Return(0)); /* std::map */ 210 211 int rv = handleSensorValue(msg, passive); 212 EXPECT_EQ(rv, 0); // It's always 0. 213 214 ReadReturn r = passive->read(); 215 EXPECT_EQ(10, r.value); 216 } 217 218 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal) 219 { 220 // The dbus passive sensor listens for updates and if it's the Value 221 // property, it needs to handle it. In this case, it won't be. 222 223 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 224 .WillOnce(Return(nullptr)); 225 sdbusplus::message::message msg(nullptr, &sdbus_mock); 226 227 const char* Scale = "Scale"; 228 int64_t xScale = -6; 229 const char* intf = "xyz.openbmc_project.Sensor.Value"; 230 // string, std::map<std::string, std::variant<int64_t>> 231 // msg.read(msgSensor, msgData); 232 233 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 234 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 235 const char** s = static_cast<const char**>(p); 236 // Read the first parameter, the string. 237 *s = intf; 238 return 0; 239 })) 240 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 241 const char** s = static_cast<const char**>(p); 242 *s = Scale; 243 // Read the string in the pair (dictionary). 244 return 0; 245 })); 246 247 // std::map 248 EXPECT_CALL(sdbus_mock, 249 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 250 .WillOnce(Return(0)); 251 252 // while !at_end() 253 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 254 .WillOnce(Return(0)) 255 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 256 257 // std::pair 258 EXPECT_CALL(sdbus_mock, 259 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 260 .WillOnce(Return(0)); 261 262 EXPECT_CALL(sdbus_mock, 263 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 264 .WillOnce(Return(1)); 265 EXPECT_CALL(sdbus_mock, 266 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 267 .WillOnce(Return(0)); 268 269 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 270 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 271 int64_t* s = static_cast<int64_t*>(p); 272 *s = xScale; 273 return 0; 274 })); 275 276 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 277 .WillOnce(Return(0)) /* variant. */ 278 .WillOnce(Return(0)) /* std::pair */ 279 .WillOnce(Return(0)); /* std::map */ 280 281 int rv = handleSensorValue(msg, passive); 282 EXPECT_EQ(rv, 0); // It's always 0. 283 284 ReadReturn r = passive->read(); 285 EXPECT_EQ(0.01, r.value); 286 } 287 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert) 288 { 289 290 // Verifies when a threshold is crossed the sensor goes into error state 291 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 292 .WillOnce(Return(nullptr)); 293 sdbusplus::message::message msg(nullptr, &sdbus_mock); 294 295 const char* criticalAlarm = "CriticalAlarmHigh"; 296 bool alarm = true; 297 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 298 299 passive->setFailed(false); 300 301 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 302 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 303 const char** s = static_cast<const char**>(p); 304 // Read the first parameter, the string. 305 *s = intf; 306 return 0; 307 })) 308 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 309 const char** s = static_cast<const char**>(p); 310 *s = criticalAlarm; 311 // Read the string in the pair (dictionary). 312 return 0; 313 })); 314 315 // std::map 316 EXPECT_CALL(sdbus_mock, 317 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 318 .WillOnce(Return(0)); 319 320 // while !at_end() 321 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 322 .WillOnce(Return(0)) 323 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 324 325 // std::pair 326 EXPECT_CALL(sdbus_mock, 327 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 328 .WillOnce(Return(0)); 329 330 EXPECT_CALL(sdbus_mock, 331 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 332 .WillOnce(Return(0)); 333 EXPECT_CALL(sdbus_mock, 334 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 335 .WillOnce(Return(0)); 336 EXPECT_CALL(sdbus_mock, 337 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 338 .WillOnce(Return(1)); 339 EXPECT_CALL(sdbus_mock, 340 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 341 .WillOnce(Return(0)); 342 343 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 344 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 345 bool* s = static_cast<bool*>(p); 346 *s = alarm; 347 return 0; 348 })); 349 350 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 351 .WillOnce(Return(0)) /* variant. */ 352 .WillOnce(Return(0)) /* std::pair */ 353 .WillOnce(Return(0)); /* std::map */ 354 355 int rv = handleSensorValue(msg, passive); 356 EXPECT_EQ(rv, 0); // It's always 0. 357 bool failed = passive->getFailed(); 358 EXPECT_EQ(failed, true); 359 } 360 361 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert) 362 { 363 364 // Verifies when a threshold is deasserted a failed sensor goes back into 365 // the normal state 366 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 367 .WillOnce(Return(nullptr)); 368 sdbusplus::message::message msg(nullptr, &sdbus_mock); 369 370 const char* criticalAlarm = "CriticalAlarmHigh"; 371 bool alarm = false; 372 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 373 374 passive->setFailed(true); 375 376 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 377 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 378 const char** s = static_cast<const char**>(p); 379 // Read the first parameter, the string. 380 *s = intf; 381 return 0; 382 })) 383 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 384 const char** s = static_cast<const char**>(p); 385 *s = criticalAlarm; 386 // Read the string in the pair (dictionary). 387 return 0; 388 })); 389 390 // std::map 391 EXPECT_CALL(sdbus_mock, 392 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 393 .WillOnce(Return(0)); 394 395 // while !at_end() 396 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 397 .WillOnce(Return(0)) 398 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 399 400 // std::pair 401 EXPECT_CALL(sdbus_mock, 402 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 403 .WillOnce(Return(0)); 404 405 EXPECT_CALL(sdbus_mock, 406 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 407 .WillOnce(Return(0)); 408 EXPECT_CALL(sdbus_mock, 409 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 410 .WillOnce(Return(0)); 411 EXPECT_CALL(sdbus_mock, 412 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 413 .WillOnce(Return(1)); 414 EXPECT_CALL(sdbus_mock, 415 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 416 .WillOnce(Return(0)); 417 418 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 419 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 420 bool* s = static_cast<bool*>(p); 421 *s = alarm; 422 return 0; 423 })); 424 425 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 426 .WillOnce(Return(0)) /* variant. */ 427 .WillOnce(Return(0)) /* std::pair */ 428 .WillOnce(Return(0)); /* std::map */ 429 430 int rv = handleSensorValue(msg, passive); 431 EXPECT_EQ(rv, 0); // It's always 0. 432 bool failed = passive->getFailed(); 433 EXPECT_EQ(failed, false); 434 } 435 436 void GetPropertiesMax3k(sdbusplus::bus::bus& bus, const std::string& service, 437 const std::string& path, SensorProperties* prop) 438 { 439 prop->scale = -3; 440 prop->value = 10; 441 prop->unit = "x"; 442 prop->min = 0; 443 prop->max = 3000; 444 } 445 446 using GetPropertiesFunction = 447 std::function<void(sdbusplus::bus::bus&, const std::string&, 448 const std::string&, SensorProperties*)>; 449 450 // TODO: There is definitely a cleaner way to do this. 451 class DbusPassiveTest3kMaxObj : public ::testing::Test 452 { 453 protected: 454 DbusPassiveTest3kMaxObj() : 455 sdbus_mock(), 456 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper() 457 { 458 EXPECT_CALL(helper, getService(_, StrEq(SensorIntf), StrEq(path))) 459 .WillOnce(Return("asdf")); 460 461 EXPECT_CALL(helper, 462 getProperties(_, StrEq("asdf"), StrEq(path), NotNull())) 463 .WillOnce(_getProps); 464 EXPECT_CALL(helper, thresholdsAsserted(_, StrEq("asdf"), StrEq(path))) 465 .WillOnce(Return(false)); 466 467 auto info = conf::SensorConfig(); 468 ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info, 469 nullptr); 470 passive = reinterpret_cast<DbusPassive*>(ri.get()); 471 EXPECT_FALSE(passive == nullptr); 472 } 473 474 sdbusplus::SdBusMock sdbus_mock; 475 sdbusplus::bus::bus bus_mock; 476 DbusHelperMock helper; 477 std::string type = "temp"; 478 std::string id = "id"; 479 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 480 int64_t _scale = -3; 481 int64_t _value = 10; 482 483 std::unique_ptr<ReadInterface> ri; 484 DbusPassive* passive; 485 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 486 }; 487 488 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected) 489 { 490 EXPECT_DOUBLE_EQ(0, passive->getMin()); 491 EXPECT_DOUBLE_EQ(3, passive->getMax()); 492 } 493 494 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test 495 { 496 protected: 497 DbusPassiveTest3kMaxIgnoredObj() : 498 sdbus_mock(), 499 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper() 500 { 501 EXPECT_CALL(helper, getService(_, StrEq(SensorIntf), StrEq(path))) 502 .WillOnce(Return("asdf")); 503 504 EXPECT_CALL(helper, 505 getProperties(_, StrEq("asdf"), StrEq(path), NotNull())) 506 .WillOnce(_getProps); 507 EXPECT_CALL(helper, thresholdsAsserted(_, StrEq("asdf"), StrEq(path))) 508 .WillOnce(Return(false)); 509 510 auto info = conf::SensorConfig(); 511 info.ignoreDbusMinMax = true; 512 ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper, &info, 513 nullptr); 514 passive = reinterpret_cast<DbusPassive*>(ri.get()); 515 EXPECT_FALSE(passive == nullptr); 516 } 517 518 sdbusplus::SdBusMock sdbus_mock; 519 sdbusplus::bus::bus bus_mock; 520 DbusHelperMock helper; 521 std::string type = "temp"; 522 std::string id = "id"; 523 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 524 int64_t _scale = -3; 525 int64_t _value = 10; 526 527 std::unique_ptr<ReadInterface> ri; 528 DbusPassive* passive; 529 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 530 }; 531 532 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected) 533 { 534 EXPECT_DOUBLE_EQ(0, passive->getMin()); 535 EXPECT_DOUBLE_EQ(0, passive->getMax()); 536 } 537 538 } // namespace 539 } // namespace pid_control 540