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