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