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