1 #include "conf.hpp" 2 #include "dbus/dbuspassive.hpp" 3 #include "failsafeloggers/builder.hpp" 4 #include "failsafeloggers/failsafe_logger.hpp" 5 #include "failsafeloggers/failsafe_logger_utility.hpp" 6 #include "test/dbushelper_mock.hpp" 7 8 #include <sdbusplus/test/sdbus_mock.hpp> 9 10 #include <functional> 11 #include <memory> 12 #include <string> 13 #include <variant> 14 15 #include <gmock/gmock.h> 16 #include <gtest/gtest.h> 17 18 namespace pid_control 19 { 20 namespace 21 { 22 23 using ::testing::_; 24 using ::testing::InSequence; 25 using ::testing::Invoke; 26 using ::testing::IsNull; 27 using ::testing::NotNull; 28 using ::testing::Return; 29 using ::testing::StrEq; 30 31 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value"; 32 33 TEST(DbusPassiveTest, FactoryFailsWithInvalidType) 34 { 35 // Verify the type is checked by the factory. 36 37 sdbusplus::SdBusMock sdbus_mock; 38 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 39 std::string type = "invalid"; 40 std::string id = "id"; 41 42 auto helper = std::make_unique<DbusHelperMock>(); 43 auto info = conf::SensorConfig(); 44 45 std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive( 46 bus_mock, type, id, std::move(helper), &info, nullptr); 47 48 EXPECT_EQ(ri, nullptr); 49 } 50 51 TEST(DbusPassiveTest, BoringConstructorTest) 52 { 53 // Simply build the object, does no error checking. 54 55 sdbusplus::SdBusMock sdbus_mock; 56 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 57 std::string type = "invalid"; 58 std::string id = "id"; 59 std::string path = "/xyz/openbmc_project/sensors/unknown/id"; 60 61 auto helper = std::make_unique<DbusHelperMock>(); 62 SensorProperties properties; 63 64 DbusPassive(bus_mock, type, id, std::move(helper), properties, false, path, 65 nullptr); 66 // Success 67 } 68 69 class DbusPassiveTestObj : public ::testing::Test 70 { 71 protected: 72 DbusPassiveTestObj() : 73 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)), 74 helper(std::make_unique<DbusHelperMock>()) 75 { 76 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 77 .WillOnce(Return("asdf")); 78 79 EXPECT_CALL(*helper, 80 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 81 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service, 82 [[maybe_unused]] const std::string& path, 83 SensorProperties* prop) { 84 prop->scale = _scale; 85 prop->value = _value; 86 prop->unit = "x"; 87 prop->min = 0; 88 prop->max = 0; 89 prop->available = true; 90 })); 91 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 92 .WillOnce(Return(false)); 93 94 auto info = conf::SensorConfig(); 95 info.unavailableAsFailed = true; 96 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 97 std::move(helper), &info, nullptr); 98 passive = reinterpret_cast<DbusPassive*>(ri.get()); 99 EXPECT_FALSE(passive == nullptr); 100 } 101 102 sdbusplus::SdBusMock sdbus_mock; 103 sdbusplus::bus_t bus_mock; 104 std::unique_ptr<DbusHelperMock> helper; 105 std::string type = "temp"; 106 std::string id = "id"; 107 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 108 int64_t _scale = -3; 109 int64_t _value = 10; 110 111 std::unique_ptr<ReadInterface> ri; 112 DbusPassive* passive; 113 }; 114 115 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues) 116 { 117 // Verify read is returning the values. 118 ReadReturn v; 119 v.value = 0.01; 120 // TODO: updated is set when the value is created, so we can range check 121 // it. 122 ReadReturn r = passive->read(); 123 EXPECT_EQ(v.value, r.value); 124 } 125 126 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue) 127 { 128 // Verify setvalue does as advertised. 129 130 double value = 0.01; 131 passive->setValue(value); 132 133 // TODO: updated is set when the value is set, so we can range check it. 134 ReadReturn r = passive->read(); 135 EXPECT_EQ(value, r.value); 136 } 137 138 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue) 139 { 140 // Verify the scale is returned as expected. 141 EXPECT_EQ(_scale, passive->getScale()); 142 } 143 144 TEST_F(DbusPassiveTestObj, getIDReturnsExpectedValue) 145 { 146 // Verify getID returns the expected value. 147 EXPECT_EQ(id, passive->getID()); 148 } 149 150 TEST_F(DbusPassiveTestObj, GetMinValueReturnsExpectedValue) 151 { 152 EXPECT_DOUBLE_EQ(0, passive->getMin()); 153 } 154 155 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal) 156 { 157 // The dbus passive sensor listens for updates and if it's the Value 158 // property, it needs to handle it. 159 160 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 161 .WillOnce(Return(nullptr)); 162 sdbusplus::message_t msg(nullptr, &sdbus_mock); 163 164 const char* Value = "Value"; 165 int64_t xValue = 10000; 166 const char* intf = "xyz.openbmc_project.Sensor.Value"; 167 // string, std::map<std::string, std::variant<int64_t>> 168 // msg.read(msgSensor, msgData); 169 170 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 171 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 172 [[maybe_unused]] char type, void* p) { 173 const char** s = static_cast<const char**>(p); 174 // Read the first parameter, the string. 175 *s = intf; 176 return 0; 177 })) 178 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 179 [[maybe_unused]] char type, void* p) { 180 const char** s = static_cast<const char**>(p); 181 *s = Value; 182 // Read the string in the pair (dictionary). 183 return 0; 184 })); 185 186 // std::map 187 EXPECT_CALL(sdbus_mock, 188 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 189 .WillOnce(Return(0)); 190 191 // while !at_end() 192 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 193 .WillOnce(Return(0)) 194 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 195 196 // std::pair 197 EXPECT_CALL(sdbus_mock, 198 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 199 .WillOnce(Return(0)); 200 201 EXPECT_CALL(sdbus_mock, 202 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 203 .WillOnce(Return(1)); 204 EXPECT_CALL(sdbus_mock, 205 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 206 .WillOnce(Return(0)); 207 208 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 209 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 210 [[maybe_unused]] char type, void* p) { 211 int64_t* s = static_cast<int64_t*>(p); 212 *s = xValue; 213 return 0; 214 })); 215 216 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 217 .WillOnce(Return(0)) /* variant. */ 218 .WillOnce(Return(0)) /* std::pair */ 219 .WillOnce(Return(0)); /* std::map */ 220 221 int rv = handleSensorValue(msg, passive); 222 EXPECT_EQ(rv, 0); // It's always 0. 223 224 ReadReturn r = passive->read(); 225 EXPECT_EQ(10, r.value); 226 } 227 228 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal) 229 { 230 // The dbus passive sensor listens for updates and if it's the Value 231 // property, it needs to handle it. In this case, it won't be. 232 233 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 234 .WillOnce(Return(nullptr)); 235 sdbusplus::message_t msg(nullptr, &sdbus_mock); 236 237 const char* Scale = "Scale"; 238 int64_t xScale = -6; 239 const char* intf = "xyz.openbmc_project.Sensor.Value"; 240 // string, std::map<std::string, std::variant<int64_t>> 241 // msg.read(msgSensor, msgData); 242 243 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 244 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 245 [[maybe_unused]] char type, void* p) { 246 const char** s = static_cast<const char**>(p); 247 // Read the first parameter, the string. 248 *s = intf; 249 return 0; 250 })) 251 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 252 [[maybe_unused]] char type, void* p) { 253 const char** s = static_cast<const char**>(p); 254 *s = Scale; 255 // Read the string in the pair (dictionary). 256 return 0; 257 })); 258 259 // std::map 260 EXPECT_CALL(sdbus_mock, 261 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 262 .WillOnce(Return(0)); 263 264 // while !at_end() 265 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 266 .WillOnce(Return(0)) 267 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 268 269 // std::pair 270 EXPECT_CALL(sdbus_mock, 271 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 272 .WillOnce(Return(0)); 273 274 EXPECT_CALL(sdbus_mock, 275 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 276 .WillOnce(Return(1)); 277 EXPECT_CALL(sdbus_mock, 278 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 279 .WillOnce(Return(0)); 280 281 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 282 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 283 [[maybe_unused]] char type, void* p) { 284 int64_t* s = static_cast<int64_t*>(p); 285 *s = xScale; 286 return 0; 287 })); 288 289 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 290 .WillOnce(Return(0)) /* variant. */ 291 .WillOnce(Return(0)) /* std::pair */ 292 .WillOnce(Return(0)); /* std::map */ 293 294 int rv = handleSensorValue(msg, passive); 295 EXPECT_EQ(rv, 0); // It's always 0. 296 297 ReadReturn r = passive->read(); 298 EXPECT_EQ(0.01, r.value); 299 } 300 301 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert) 302 { 303 // Verifies when a threshold is crossed the sensor goes into error state 304 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 305 .WillOnce(Return(nullptr)); 306 sdbusplus::message_t msg(nullptr, &sdbus_mock); 307 308 const char* criticalAlarm = "CriticalAlarmHigh"; 309 bool alarm = true; 310 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 311 312 passive->setFailed(false); 313 314 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 315 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 316 [[maybe_unused]] char type, void* p) { 317 const char** s = static_cast<const char**>(p); 318 // Read the first parameter, the string. 319 *s = intf; 320 return 0; 321 })) 322 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 323 [[maybe_unused]] char type, void* p) { 324 const char** s = static_cast<const char**>(p); 325 *s = criticalAlarm; 326 // Read the string in the pair (dictionary). 327 return 0; 328 })); 329 330 // std::map 331 EXPECT_CALL(sdbus_mock, 332 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 333 .WillOnce(Return(0)); 334 335 // while !at_end() 336 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 337 .WillOnce(Return(0)) 338 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 339 340 // std::pair 341 EXPECT_CALL(sdbus_mock, 342 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 343 .WillOnce(Return(0)); 344 345 EXPECT_CALL(sdbus_mock, 346 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 347 .WillOnce(Return(0)); 348 EXPECT_CALL(sdbus_mock, 349 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 350 .WillOnce(Return(0)); 351 EXPECT_CALL(sdbus_mock, 352 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 353 .WillOnce(Return(1)); 354 EXPECT_CALL(sdbus_mock, 355 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 356 .WillOnce(Return(0)); 357 358 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 359 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 360 [[maybe_unused]] char type, void* p) { 361 bool* s = static_cast<bool*>(p); 362 *s = alarm; 363 return 0; 364 })); 365 366 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 367 .WillOnce(Return(0)) /* variant. */ 368 .WillOnce(Return(0)) /* std::pair */ 369 .WillOnce(Return(0)); /* std::map */ 370 371 int rv = handleSensorValue(msg, passive); 372 EXPECT_EQ(rv, 0); // It's always 0. 373 bool failed = passive->getFailed(); 374 EXPECT_EQ(failed, true); 375 } 376 377 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert) 378 { 379 // Verifies when a threshold is deasserted a failed sensor goes back into 380 // the normal state 381 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 382 .WillOnce(Return(nullptr)); 383 sdbusplus::message_t msg(nullptr, &sdbus_mock); 384 385 const char* criticalAlarm = "CriticalAlarmHigh"; 386 bool alarm = false; 387 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 388 389 passive->setFailed(true); 390 391 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 392 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 393 [[maybe_unused]] char type, void* p) { 394 const char** s = static_cast<const char**>(p); 395 // Read the first parameter, the string. 396 *s = intf; 397 return 0; 398 })) 399 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 400 [[maybe_unused]] char type, void* p) { 401 const char** s = static_cast<const char**>(p); 402 *s = criticalAlarm; 403 // Read the string in the pair (dictionary). 404 return 0; 405 })); 406 407 // std::map 408 EXPECT_CALL(sdbus_mock, 409 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 410 .WillOnce(Return(0)); 411 412 // while !at_end() 413 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 414 .WillOnce(Return(0)) 415 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 416 417 // std::pair 418 EXPECT_CALL(sdbus_mock, 419 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 420 .WillOnce(Return(0)); 421 422 EXPECT_CALL(sdbus_mock, 423 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 424 .WillOnce(Return(0)); 425 EXPECT_CALL(sdbus_mock, 426 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 427 .WillOnce(Return(0)); 428 EXPECT_CALL(sdbus_mock, 429 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 430 .WillOnce(Return(1)); 431 EXPECT_CALL(sdbus_mock, 432 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 433 .WillOnce(Return(0)); 434 435 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 436 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 437 [[maybe_unused]] char type, void* p) { 438 bool* s = static_cast<bool*>(p); 439 *s = alarm; 440 return 0; 441 })); 442 443 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 444 .WillOnce(Return(0)) /* variant. */ 445 .WillOnce(Return(0)) /* std::pair */ 446 .WillOnce(Return(0)); /* std::map */ 447 448 int rv = handleSensorValue(msg, passive); 449 EXPECT_EQ(rv, 0); // It's always 0. 450 bool failed = passive->getFailed(); 451 EXPECT_EQ(failed, false); 452 } 453 454 TEST_F(DbusPassiveTestObj, VerifyAvailableDeassert) 455 { 456 // Verifies when Available is deasserted && unavailableAsFailed == true, 457 // the sensor goes into error state 458 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 459 .WillOnce(Return(nullptr)); 460 sdbusplus::message_t msg(nullptr, &sdbus_mock); 461 462 const char* property = "Available"; 463 bool asserted = false; 464 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 465 466 passive->setAvailable(true); 467 468 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 469 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 470 [[maybe_unused]] char type, void* p) { 471 const char** s = static_cast<const char**>(p); 472 // Read the first parameter, the string. 473 *s = intf; 474 return 0; 475 })) 476 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 477 [[maybe_unused]] char type, void* p) { 478 const char** s = static_cast<const char**>(p); 479 *s = property; 480 // Read the string in the pair (dictionary). 481 return 0; 482 })); 483 484 // std::map 485 EXPECT_CALL(sdbus_mock, 486 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 487 .WillOnce(Return(0)); 488 489 // while !at_end() 490 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 491 .WillOnce(Return(0)) 492 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 493 494 // std::pair 495 EXPECT_CALL(sdbus_mock, 496 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 497 .WillOnce(Return(0)); 498 499 EXPECT_CALL(sdbus_mock, 500 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 501 .WillOnce(Return(0)); 502 EXPECT_CALL(sdbus_mock, 503 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 504 .WillOnce(Return(0)); 505 EXPECT_CALL(sdbus_mock, 506 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 507 .WillOnce(Return(1)); 508 EXPECT_CALL(sdbus_mock, 509 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 510 .WillOnce(Return(0)); 511 512 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 513 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 514 [[maybe_unused]] char type, void* p) { 515 bool* s = static_cast<bool*>(p); 516 *s = asserted; 517 return 0; 518 })); 519 520 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 521 .WillOnce(Return(0)) /* variant. */ 522 .WillOnce(Return(0)) /* std::pair */ 523 .WillOnce(Return(0)); /* std::map */ 524 525 int rv = handleSensorValue(msg, passive); 526 EXPECT_EQ(rv, 0); // It's always 0. 527 bool failed = passive->getFailed(); 528 EXPECT_EQ(failed, true); 529 } 530 531 TEST_F(DbusPassiveTestObj, VerifyAvailableAssert) 532 { 533 // Verifies when Available is asserted && unavailableAsFailed == true, 534 // an error sensor goes back to normal state 535 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 536 .WillOnce(Return(nullptr)); 537 sdbusplus::message_t msg(nullptr, &sdbus_mock); 538 539 const char* property = "Available"; 540 bool asserted = true; 541 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 542 543 passive->setAvailable(false); 544 bool failed = passive->getFailed(); 545 EXPECT_EQ(failed, true); 546 547 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 548 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 549 [[maybe_unused]] char type, void* p) { 550 const char** s = static_cast<const char**>(p); 551 // Read the first parameter, the string. 552 *s = intf; 553 return 0; 554 })) 555 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 556 [[maybe_unused]] char type, void* p) { 557 const char** s = static_cast<const char**>(p); 558 *s = property; 559 // Read the string in the pair (dictionary). 560 return 0; 561 })); 562 563 // std::map 564 EXPECT_CALL(sdbus_mock, 565 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 566 .WillOnce(Return(0)); 567 568 // while !at_end() 569 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 570 .WillOnce(Return(0)) 571 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 572 573 // std::pair 574 EXPECT_CALL(sdbus_mock, 575 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 576 .WillOnce(Return(0)); 577 578 EXPECT_CALL(sdbus_mock, 579 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 580 .WillOnce(Return(0)); 581 EXPECT_CALL(sdbus_mock, 582 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 583 .WillOnce(Return(0)); 584 EXPECT_CALL(sdbus_mock, 585 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 586 .WillOnce(Return(1)); 587 EXPECT_CALL(sdbus_mock, 588 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 589 .WillOnce(Return(0)); 590 591 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 592 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 593 [[maybe_unused]] char type, void* p) { 594 bool* s = static_cast<bool*>(p); 595 *s = asserted; 596 return 0; 597 })); 598 599 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 600 .WillOnce(Return(0)) /* variant. */ 601 .WillOnce(Return(0)) /* std::pair */ 602 .WillOnce(Return(0)); /* std::map */ 603 604 int rv = handleSensorValue(msg, passive); 605 EXPECT_EQ(rv, 0); // It's always 0. 606 failed = passive->getFailed(); 607 EXPECT_EQ(failed, false); 608 } 609 610 class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test 611 { 612 protected: 613 DbusPassiveTestUnaSensorNotAsFailedObj() : 614 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)), 615 helper(std::make_unique<DbusHelperMock>()) 616 { 617 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 618 .WillOnce(Return("asdf")); 619 620 EXPECT_CALL(*helper, 621 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 622 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service, 623 [[maybe_unused]] const std::string& path, 624 SensorProperties* prop) { 625 prop->scale = _scale; 626 prop->value = _value; 627 prop->unit = "x"; 628 prop->min = 0; 629 prop->max = 0; 630 prop->available = true; 631 })); 632 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 633 .WillOnce(Return(false)); 634 635 auto info = conf::SensorConfig(); 636 info.unavailableAsFailed = false; 637 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 638 std::move(helper), &info, nullptr); 639 passive = reinterpret_cast<DbusPassive*>(ri.get()); 640 EXPECT_FALSE(passive == nullptr); 641 } 642 643 sdbusplus::SdBusMock sdbus_mock; 644 sdbusplus::bus_t bus_mock; 645 std::unique_ptr<DbusHelperMock> helper; 646 std::string type = "temp"; 647 std::string id = "id"; 648 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 649 int64_t _scale = -3; 650 int64_t _value = 10; 651 652 std::unique_ptr<ReadInterface> ri; 653 DbusPassive* passive; 654 }; 655 656 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert) 657 { 658 // Verifies when Available is deasserted && unavailableAsFailed == false, 659 // the sensor remains at OK state but reading goes to NaN. 660 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 661 .WillOnce(Return(nullptr)); 662 sdbusplus::message_t msg(nullptr, &sdbus_mock); 663 664 const char* property = "Available"; 665 bool asserted = false; 666 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 667 668 passive->setAvailable(true); 669 670 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 671 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 672 [[maybe_unused]] char type, void* p) { 673 const char** s = static_cast<const char**>(p); 674 // Read the first parameter, the string. 675 *s = intf; 676 return 0; 677 })) 678 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 679 [[maybe_unused]] char type, void* p) { 680 const char** s = static_cast<const char**>(p); 681 *s = property; 682 // Read the string in the pair (dictionary). 683 return 0; 684 })); 685 686 // std::map 687 EXPECT_CALL(sdbus_mock, 688 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 689 .WillOnce(Return(0)); 690 691 // while !at_end() 692 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 693 .WillOnce(Return(0)) 694 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 695 696 // std::pair 697 EXPECT_CALL(sdbus_mock, 698 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 699 .WillOnce(Return(0)); 700 701 EXPECT_CALL(sdbus_mock, 702 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 703 .WillOnce(Return(0)); 704 EXPECT_CALL(sdbus_mock, 705 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 706 .WillOnce(Return(0)); 707 EXPECT_CALL(sdbus_mock, 708 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 709 .WillOnce(Return(1)); 710 EXPECT_CALL(sdbus_mock, 711 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 712 .WillOnce(Return(0)); 713 714 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 715 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 716 [[maybe_unused]] char type, void* p) { 717 bool* s = static_cast<bool*>(p); 718 *s = asserted; 719 return 0; 720 })); 721 722 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 723 .WillOnce(Return(0)) /* variant. */ 724 .WillOnce(Return(0)) /* std::pair */ 725 .WillOnce(Return(0)); /* std::map */ 726 727 int rv = handleSensorValue(msg, passive); 728 EXPECT_EQ(rv, 0); // It's always 0. 729 bool failed = passive->getFailed(); 730 EXPECT_EQ(failed, false); 731 ReadReturn r = passive->read(); 732 EXPECT_FALSE(std::isfinite(r.value)); 733 } 734 735 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert) 736 { 737 // Verifies when a sensor's state goes from unavailable to available 738 // && unavailableAsFailed == false, this sensor remains at OK state. 739 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 740 .WillOnce(Return(nullptr)); 741 sdbusplus::message_t msg(nullptr, &sdbus_mock); 742 743 const char* property = "Available"; 744 bool asserted = true; 745 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 746 747 passive->setAvailable(false); 748 bool failed = passive->getFailed(); 749 EXPECT_EQ(failed, false); 750 751 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 752 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 753 [[maybe_unused]] char type, void* p) { 754 const char** s = static_cast<const char**>(p); 755 // Read the first parameter, the string. 756 *s = intf; 757 return 0; 758 })) 759 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 760 [[maybe_unused]] char type, void* p) { 761 const char** s = static_cast<const char**>(p); 762 *s = property; 763 // Read the string in the pair (dictionary). 764 return 0; 765 })); 766 767 // std::map 768 EXPECT_CALL(sdbus_mock, 769 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 770 .WillOnce(Return(0)); 771 772 // while !at_end() 773 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 774 .WillOnce(Return(0)) 775 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 776 777 // std::pair 778 EXPECT_CALL(sdbus_mock, 779 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 780 .WillOnce(Return(0)); 781 782 EXPECT_CALL(sdbus_mock, 783 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 784 .WillOnce(Return(0)); 785 EXPECT_CALL(sdbus_mock, 786 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 787 .WillOnce(Return(0)); 788 EXPECT_CALL(sdbus_mock, 789 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 790 .WillOnce(Return(1)); 791 EXPECT_CALL(sdbus_mock, 792 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 793 .WillOnce(Return(0)); 794 795 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 796 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 797 [[maybe_unused]] char type, void* p) { 798 bool* s = static_cast<bool*>(p); 799 *s = asserted; 800 return 0; 801 })); 802 803 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 804 .WillOnce(Return(0)) /* variant. */ 805 .WillOnce(Return(0)) /* std::pair */ 806 .WillOnce(Return(0)); /* std::map */ 807 808 int rv = handleSensorValue(msg, passive); 809 EXPECT_EQ(rv, 0); // It's always 0. 810 failed = passive->getFailed(); 811 EXPECT_EQ(failed, false); 812 } 813 814 void GetPropertiesMax3k([[maybe_unused]] const std::string& service, 815 [[maybe_unused]] const std::string& path, 816 SensorProperties* prop) 817 { 818 prop->scale = -3; 819 prop->value = 10; 820 prop->unit = "x"; 821 prop->min = 0; 822 prop->max = 3000; 823 } 824 825 using GetPropertiesFunction = std::function<void( 826 const std::string&, const std::string&, SensorProperties*)>; 827 828 // TODO: There is definitely a cleaner way to do this. 829 class DbusPassiveTest3kMaxObj : public ::testing::Test 830 { 831 protected: 832 DbusPassiveTest3kMaxObj() : 833 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)), 834 helper(std::make_unique<DbusHelperMock>()) 835 { 836 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 837 .WillOnce(Return("asdf")); 838 839 EXPECT_CALL(*helper, 840 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 841 .WillOnce(_getProps); 842 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 843 .WillOnce(Return(false)); 844 845 auto info = conf::SensorConfig(); 846 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 847 std::move(helper), &info, nullptr); 848 passive = reinterpret_cast<DbusPassive*>(ri.get()); 849 EXPECT_FALSE(passive == nullptr); 850 } 851 852 sdbusplus::SdBusMock sdbus_mock; 853 sdbusplus::bus_t bus_mock; 854 std::unique_ptr<DbusHelperMock> helper; 855 std::string type = "temp"; 856 std::string id = "id"; 857 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 858 int64_t _scale = -3; 859 int64_t _value = 10; 860 861 std::unique_ptr<ReadInterface> ri; 862 DbusPassive* passive; 863 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 864 }; 865 866 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected) 867 { 868 EXPECT_DOUBLE_EQ(0, passive->getMin()); 869 EXPECT_DOUBLE_EQ(3, passive->getMax()); 870 } 871 872 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test 873 { 874 protected: 875 DbusPassiveTest3kMaxIgnoredObj() : 876 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)), 877 helper(std::make_unique<DbusHelperMock>()) 878 { 879 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 880 .WillOnce(Return("asdf")); 881 882 EXPECT_CALL(*helper, 883 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 884 .WillOnce(_getProps); 885 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 886 .WillOnce(Return(false)); 887 888 auto info = conf::SensorConfig(); 889 info.ignoreDbusMinMax = true; 890 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 891 std::move(helper), &info, nullptr); 892 passive = reinterpret_cast<DbusPassive*>(ri.get()); 893 EXPECT_FALSE(passive == nullptr); 894 } 895 896 sdbusplus::SdBusMock sdbus_mock; 897 sdbusplus::bus_t bus_mock; 898 std::unique_ptr<DbusHelperMock> helper; 899 std::string type = "temp"; 900 std::string id = "id"; 901 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 902 int64_t _scale = -3; 903 int64_t _value = 10; 904 905 std::unique_ptr<ReadInterface> ri; 906 DbusPassive* passive; 907 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 908 }; 909 910 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected) 911 { 912 EXPECT_DOUBLE_EQ(0, passive->getMin()); 913 EXPECT_DOUBLE_EQ(0, passive->getMax()); 914 } 915 916 } // namespace 917 } // namespace pid_control 918