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(), 71 bus_mock(std::move(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 302 // Verifies when a threshold is crossed the sensor goes into error state 303 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 304 .WillOnce(Return(nullptr)); 305 sdbusplus::message_t msg(nullptr, &sdbus_mock); 306 307 const char* criticalAlarm = "CriticalAlarmHigh"; 308 bool alarm = true; 309 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 310 311 passive->setFailed(false); 312 313 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 314 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 315 [[maybe_unused]] char type, void* p) { 316 const char** s = static_cast<const char**>(p); 317 // Read the first parameter, the string. 318 *s = intf; 319 return 0; 320 })) 321 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 322 [[maybe_unused]] char type, void* p) { 323 const char** s = static_cast<const char**>(p); 324 *s = criticalAlarm; 325 // Read the string in the pair (dictionary). 326 return 0; 327 })); 328 329 // std::map 330 EXPECT_CALL(sdbus_mock, 331 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 332 .WillOnce(Return(0)); 333 334 // while !at_end() 335 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 336 .WillOnce(Return(0)) 337 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 338 339 // std::pair 340 EXPECT_CALL(sdbus_mock, 341 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 342 .WillOnce(Return(0)); 343 344 EXPECT_CALL(sdbus_mock, 345 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 346 .WillOnce(Return(0)); 347 EXPECT_CALL(sdbus_mock, 348 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 349 .WillOnce(Return(0)); 350 EXPECT_CALL(sdbus_mock, 351 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 352 .WillOnce(Return(1)); 353 EXPECT_CALL(sdbus_mock, 354 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 355 .WillOnce(Return(0)); 356 357 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 358 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 359 [[maybe_unused]] char type, void* p) { 360 bool* s = static_cast<bool*>(p); 361 *s = alarm; 362 return 0; 363 })); 364 365 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 366 .WillOnce(Return(0)) /* variant. */ 367 .WillOnce(Return(0)) /* std::pair */ 368 .WillOnce(Return(0)); /* std::map */ 369 370 int rv = handleSensorValue(msg, passive); 371 EXPECT_EQ(rv, 0); // It's always 0. 372 bool failed = passive->getFailed(); 373 EXPECT_EQ(failed, true); 374 } 375 376 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert) 377 { 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 457 // Verifies when Availble is deasserted && unavailableAsFailed == true, 458 // the sensor goes into error state 459 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 460 .WillOnce(Return(nullptr)); 461 sdbusplus::message_t msg(nullptr, &sdbus_mock); 462 463 const char* property = "Available"; 464 bool asserted = false; 465 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 466 467 passive->setAvailable(true); 468 469 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 470 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 471 [[maybe_unused]] char type, void* p) { 472 const char** s = static_cast<const char**>(p); 473 // Read the first parameter, the string. 474 *s = intf; 475 return 0; 476 })) 477 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 478 [[maybe_unused]] char type, void* p) { 479 const char** s = static_cast<const char**>(p); 480 *s = property; 481 // Read the string in the pair (dictionary). 482 return 0; 483 })); 484 485 // std::map 486 EXPECT_CALL(sdbus_mock, 487 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 488 .WillOnce(Return(0)); 489 490 // while !at_end() 491 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 492 .WillOnce(Return(0)) 493 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 494 495 // std::pair 496 EXPECT_CALL(sdbus_mock, 497 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 498 .WillOnce(Return(0)); 499 500 EXPECT_CALL(sdbus_mock, 501 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 502 .WillOnce(Return(0)); 503 EXPECT_CALL(sdbus_mock, 504 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 505 .WillOnce(Return(0)); 506 EXPECT_CALL(sdbus_mock, 507 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 508 .WillOnce(Return(1)); 509 EXPECT_CALL(sdbus_mock, 510 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 511 .WillOnce(Return(0)); 512 513 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 514 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 515 [[maybe_unused]] char type, void* p) { 516 bool* s = static_cast<bool*>(p); 517 *s = asserted; 518 return 0; 519 })); 520 521 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 522 .WillOnce(Return(0)) /* variant. */ 523 .WillOnce(Return(0)) /* std::pair */ 524 .WillOnce(Return(0)); /* std::map */ 525 526 int rv = handleSensorValue(msg, passive); 527 EXPECT_EQ(rv, 0); // It's always 0. 528 bool failed = passive->getFailed(); 529 EXPECT_EQ(failed, true); 530 } 531 532 TEST_F(DbusPassiveTestObj, VerifyAvailableAssert) 533 { 534 535 // Verifies when Availble is asserted && unavailableAsFailed == true, 536 // an error sensor goes back to normal state 537 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 538 .WillOnce(Return(nullptr)); 539 sdbusplus::message_t msg(nullptr, &sdbus_mock); 540 541 const char* property = "Available"; 542 bool asserted = true; 543 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 544 545 passive->setAvailable(false); 546 bool failed = passive->getFailed(); 547 EXPECT_EQ(failed, true); 548 549 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 550 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 551 [[maybe_unused]] char type, void* p) { 552 const char** s = static_cast<const char**>(p); 553 // Read the first parameter, the string. 554 *s = intf; 555 return 0; 556 })) 557 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 558 [[maybe_unused]] char type, void* p) { 559 const char** s = static_cast<const char**>(p); 560 *s = property; 561 // Read the string in the pair (dictionary). 562 return 0; 563 })); 564 565 // std::map 566 EXPECT_CALL(sdbus_mock, 567 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 568 .WillOnce(Return(0)); 569 570 // while !at_end() 571 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 572 .WillOnce(Return(0)) 573 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 574 575 // std::pair 576 EXPECT_CALL(sdbus_mock, 577 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 578 .WillOnce(Return(0)); 579 580 EXPECT_CALL(sdbus_mock, 581 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 582 .WillOnce(Return(0)); 583 EXPECT_CALL(sdbus_mock, 584 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 585 .WillOnce(Return(0)); 586 EXPECT_CALL(sdbus_mock, 587 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 588 .WillOnce(Return(1)); 589 EXPECT_CALL(sdbus_mock, 590 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 591 .WillOnce(Return(0)); 592 593 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 594 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 595 [[maybe_unused]] char type, void* p) { 596 bool* s = static_cast<bool*>(p); 597 *s = asserted; 598 return 0; 599 })); 600 601 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 602 .WillOnce(Return(0)) /* variant. */ 603 .WillOnce(Return(0)) /* std::pair */ 604 .WillOnce(Return(0)); /* std::map */ 605 606 int rv = handleSensorValue(msg, passive); 607 EXPECT_EQ(rv, 0); // It's always 0. 608 failed = passive->getFailed(); 609 EXPECT_EQ(failed, false); 610 } 611 612 class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test 613 { 614 protected: 615 DbusPassiveTestUnaSensorNotAsFailedObj() : 616 sdbus_mock(), 617 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 618 helper(std::make_unique<DbusHelperMock>()) 619 { 620 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 621 .WillOnce(Return("asdf")); 622 623 EXPECT_CALL(*helper, 624 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 625 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service, 626 [[maybe_unused]] const std::string& path, 627 SensorProperties* prop) { 628 prop->scale = _scale; 629 prop->value = _value; 630 prop->unit = "x"; 631 prop->min = 0; 632 prop->max = 0; 633 prop->available = true; 634 })); 635 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 636 .WillOnce(Return(false)); 637 638 auto info = conf::SensorConfig(); 639 info.unavailableAsFailed = false; 640 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 641 std::move(helper), &info, nullptr); 642 passive = reinterpret_cast<DbusPassive*>(ri.get()); 643 EXPECT_FALSE(passive == nullptr); 644 } 645 646 sdbusplus::SdBusMock sdbus_mock; 647 sdbusplus::bus_t bus_mock; 648 std::unique_ptr<DbusHelperMock> helper; 649 std::string type = "temp"; 650 std::string id = "id"; 651 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 652 int64_t _scale = -3; 653 int64_t _value = 10; 654 655 std::unique_ptr<ReadInterface> ri; 656 DbusPassive* passive; 657 }; 658 659 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert) 660 { 661 662 // Verifies when Availble is deasserted && unavailableAsFailed == false, 663 // the sensor remains at OK state but reading goes to NaN. 664 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 665 .WillOnce(Return(nullptr)); 666 sdbusplus::message_t msg(nullptr, &sdbus_mock); 667 668 const char* property = "Available"; 669 bool asserted = false; 670 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 671 672 passive->setAvailable(true); 673 674 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 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 // Read the first parameter, the string. 679 *s = intf; 680 return 0; 681 })) 682 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 683 [[maybe_unused]] char type, void* p) { 684 const char** s = static_cast<const char**>(p); 685 *s = property; 686 // Read the string in the pair (dictionary). 687 return 0; 688 })); 689 690 // std::map 691 EXPECT_CALL(sdbus_mock, 692 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 693 .WillOnce(Return(0)); 694 695 // while !at_end() 696 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 697 .WillOnce(Return(0)) 698 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 699 700 // std::pair 701 EXPECT_CALL(sdbus_mock, 702 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 703 .WillOnce(Return(0)); 704 705 EXPECT_CALL(sdbus_mock, 706 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 707 .WillOnce(Return(0)); 708 EXPECT_CALL(sdbus_mock, 709 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 710 .WillOnce(Return(0)); 711 EXPECT_CALL(sdbus_mock, 712 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 713 .WillOnce(Return(1)); 714 EXPECT_CALL(sdbus_mock, 715 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 716 .WillOnce(Return(0)); 717 718 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 719 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 720 [[maybe_unused]] char type, void* p) { 721 bool* s = static_cast<bool*>(p); 722 *s = asserted; 723 return 0; 724 })); 725 726 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 727 .WillOnce(Return(0)) /* variant. */ 728 .WillOnce(Return(0)) /* std::pair */ 729 .WillOnce(Return(0)); /* std::map */ 730 731 int rv = handleSensorValue(msg, passive); 732 EXPECT_EQ(rv, 0); // It's always 0. 733 bool failed = passive->getFailed(); 734 EXPECT_EQ(failed, false); 735 ReadReturn r = passive->read(); 736 EXPECT_FALSE(std::isfinite(r.value)); 737 } 738 739 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert) 740 { 741 742 // Verifies when a sensor's state goes from unavailble to available 743 // && unavailableAsFailed == false, this sensor remains at OK state. 744 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 745 .WillOnce(Return(nullptr)); 746 sdbusplus::message_t msg(nullptr, &sdbus_mock); 747 748 const char* property = "Available"; 749 bool asserted = true; 750 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 751 752 passive->setAvailable(false); 753 bool failed = passive->getFailed(); 754 EXPECT_EQ(failed, false); 755 756 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 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 // Read the first parameter, the string. 761 *s = intf; 762 return 0; 763 })) 764 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 765 [[maybe_unused]] char type, void* p) { 766 const char** s = static_cast<const char**>(p); 767 *s = property; 768 // Read the string in the pair (dictionary). 769 return 0; 770 })); 771 772 // std::map 773 EXPECT_CALL(sdbus_mock, 774 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 775 .WillOnce(Return(0)); 776 777 // while !at_end() 778 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 779 .WillOnce(Return(0)) 780 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 781 782 // std::pair 783 EXPECT_CALL(sdbus_mock, 784 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 785 .WillOnce(Return(0)); 786 787 EXPECT_CALL(sdbus_mock, 788 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 789 .WillOnce(Return(0)); 790 EXPECT_CALL(sdbus_mock, 791 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 792 .WillOnce(Return(0)); 793 EXPECT_CALL(sdbus_mock, 794 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 795 .WillOnce(Return(1)); 796 EXPECT_CALL(sdbus_mock, 797 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 798 .WillOnce(Return(0)); 799 800 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 801 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 802 [[maybe_unused]] char type, void* p) { 803 bool* s = static_cast<bool*>(p); 804 *s = asserted; 805 return 0; 806 })); 807 808 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 809 .WillOnce(Return(0)) /* variant. */ 810 .WillOnce(Return(0)) /* std::pair */ 811 .WillOnce(Return(0)); /* std::map */ 812 813 int rv = handleSensorValue(msg, passive); 814 EXPECT_EQ(rv, 0); // It's always 0. 815 failed = passive->getFailed(); 816 EXPECT_EQ(failed, false); 817 } 818 819 void GetPropertiesMax3k([[maybe_unused]] const std::string& service, 820 [[maybe_unused]] const std::string& path, 821 SensorProperties* prop) 822 { 823 prop->scale = -3; 824 prop->value = 10; 825 prop->unit = "x"; 826 prop->min = 0; 827 prop->max = 3000; 828 } 829 830 using GetPropertiesFunction = std::function<void( 831 const std::string&, const std::string&, SensorProperties*)>; 832 833 // TODO: There is definitely a cleaner way to do this. 834 class DbusPassiveTest3kMaxObj : public ::testing::Test 835 { 836 protected: 837 DbusPassiveTest3kMaxObj() : 838 sdbus_mock(), 839 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 840 helper(std::make_unique<DbusHelperMock>()) 841 { 842 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 843 .WillOnce(Return("asdf")); 844 845 EXPECT_CALL(*helper, 846 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 847 .WillOnce(_getProps); 848 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 849 .WillOnce(Return(false)); 850 851 auto info = conf::SensorConfig(); 852 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 853 std::move(helper), &info, nullptr); 854 passive = reinterpret_cast<DbusPassive*>(ri.get()); 855 EXPECT_FALSE(passive == nullptr); 856 } 857 858 sdbusplus::SdBusMock sdbus_mock; 859 sdbusplus::bus_t bus_mock; 860 std::unique_ptr<DbusHelperMock> helper; 861 std::string type = "temp"; 862 std::string id = "id"; 863 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 864 int64_t _scale = -3; 865 int64_t _value = 10; 866 867 std::unique_ptr<ReadInterface> ri; 868 DbusPassive* passive; 869 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 870 }; 871 872 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected) 873 { 874 EXPECT_DOUBLE_EQ(0, passive->getMin()); 875 EXPECT_DOUBLE_EQ(3, passive->getMax()); 876 } 877 878 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test 879 { 880 protected: 881 DbusPassiveTest3kMaxIgnoredObj() : 882 sdbus_mock(), 883 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 884 helper(std::make_unique<DbusHelperMock>()) 885 { 886 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 887 .WillOnce(Return("asdf")); 888 889 EXPECT_CALL(*helper, 890 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 891 .WillOnce(_getProps); 892 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 893 .WillOnce(Return(false)); 894 895 auto info = conf::SensorConfig(); 896 info.ignoreDbusMinMax = true; 897 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 898 std::move(helper), &info, nullptr); 899 passive = reinterpret_cast<DbusPassive*>(ri.get()); 900 EXPECT_FALSE(passive == nullptr); 901 } 902 903 sdbusplus::SdBusMock sdbus_mock; 904 sdbusplus::bus_t bus_mock; 905 std::unique_ptr<DbusHelperMock> helper; 906 std::string type = "temp"; 907 std::string id = "id"; 908 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 909 int64_t _scale = -3; 910 int64_t _value = 10; 911 912 std::unique_ptr<ReadInterface> ri; 913 DbusPassive* passive; 914 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 915 }; 916 917 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected) 918 { 919 EXPECT_DOUBLE_EQ(0, passive->getMin()); 920 EXPECT_DOUBLE_EQ(0, passive->getMax()); 921 } 922 923 } // namespace 924 } // namespace pid_control 925