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