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