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 // 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 Availble 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 Availble 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(), 613 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 614 helper(std::make_unique<DbusHelperMock>()) 615 { 616 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 617 .WillOnce(Return("asdf")); 618 619 EXPECT_CALL(*helper, 620 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 621 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service, 622 [[maybe_unused]] const std::string& path, 623 SensorProperties* prop) { 624 prop->scale = _scale; 625 prop->value = _value; 626 prop->unit = "x"; 627 prop->min = 0; 628 prop->max = 0; 629 prop->available = true; 630 })); 631 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 632 .WillOnce(Return(false)); 633 634 auto info = conf::SensorConfig(); 635 info.unavailableAsFailed = false; 636 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 637 std::move(helper), &info, nullptr); 638 passive = reinterpret_cast<DbusPassive*>(ri.get()); 639 EXPECT_FALSE(passive == nullptr); 640 } 641 642 sdbusplus::SdBusMock sdbus_mock; 643 sdbusplus::bus_t bus_mock; 644 std::unique_ptr<DbusHelperMock> helper; 645 std::string type = "temp"; 646 std::string id = "id"; 647 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 648 int64_t _scale = -3; 649 int64_t _value = 10; 650 651 std::unique_ptr<ReadInterface> ri; 652 DbusPassive* passive; 653 }; 654 655 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert) 656 { 657 // Verifies when Availble is deasserted && unavailableAsFailed == false, 658 // the sensor remains at OK state but reading goes to NaN. 659 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 660 .WillOnce(Return(nullptr)); 661 sdbusplus::message_t msg(nullptr, &sdbus_mock); 662 663 const char* property = "Available"; 664 bool asserted = false; 665 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 666 667 passive->setAvailable(true); 668 669 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 670 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 671 [[maybe_unused]] char type, void* p) { 672 const char** s = static_cast<const char**>(p); 673 // Read the first parameter, the string. 674 *s = intf; 675 return 0; 676 })) 677 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 678 [[maybe_unused]] char type, void* p) { 679 const char** s = static_cast<const char**>(p); 680 *s = property; 681 // Read the string in the pair (dictionary). 682 return 0; 683 })); 684 685 // std::map 686 EXPECT_CALL(sdbus_mock, 687 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 688 .WillOnce(Return(0)); 689 690 // while !at_end() 691 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 692 .WillOnce(Return(0)) 693 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 694 695 // std::pair 696 EXPECT_CALL(sdbus_mock, 697 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 698 .WillOnce(Return(0)); 699 700 EXPECT_CALL(sdbus_mock, 701 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 702 .WillOnce(Return(0)); 703 EXPECT_CALL(sdbus_mock, 704 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 705 .WillOnce(Return(0)); 706 EXPECT_CALL(sdbus_mock, 707 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 708 .WillOnce(Return(1)); 709 EXPECT_CALL(sdbus_mock, 710 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 711 .WillOnce(Return(0)); 712 713 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 714 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 715 [[maybe_unused]] char type, void* p) { 716 bool* s = static_cast<bool*>(p); 717 *s = asserted; 718 return 0; 719 })); 720 721 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 722 .WillOnce(Return(0)) /* variant. */ 723 .WillOnce(Return(0)) /* std::pair */ 724 .WillOnce(Return(0)); /* std::map */ 725 726 int rv = handleSensorValue(msg, passive); 727 EXPECT_EQ(rv, 0); // It's always 0. 728 bool failed = passive->getFailed(); 729 EXPECT_EQ(failed, false); 730 ReadReturn r = passive->read(); 731 EXPECT_FALSE(std::isfinite(r.value)); 732 } 733 734 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert) 735 { 736 // Verifies when a sensor's state goes from unavailble to available 737 // && unavailableAsFailed == false, this sensor remains at OK state. 738 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 739 .WillOnce(Return(nullptr)); 740 sdbusplus::message_t msg(nullptr, &sdbus_mock); 741 742 const char* property = "Available"; 743 bool asserted = true; 744 const char* intf = "xyz.openbmc_project.State.Decorator.Availability"; 745 746 passive->setAvailable(false); 747 bool failed = passive->getFailed(); 748 EXPECT_EQ(failed, false); 749 750 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 751 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 752 [[maybe_unused]] char type, void* p) { 753 const char** s = static_cast<const char**>(p); 754 // Read the first parameter, the string. 755 *s = intf; 756 return 0; 757 })) 758 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 759 [[maybe_unused]] char type, void* p) { 760 const char** s = static_cast<const char**>(p); 761 *s = property; 762 // Read the string in the pair (dictionary). 763 return 0; 764 })); 765 766 // std::map 767 EXPECT_CALL(sdbus_mock, 768 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 769 .WillOnce(Return(0)); 770 771 // while !at_end() 772 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 773 .WillOnce(Return(0)) 774 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 775 776 // std::pair 777 EXPECT_CALL(sdbus_mock, 778 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 779 .WillOnce(Return(0)); 780 781 EXPECT_CALL(sdbus_mock, 782 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 783 .WillOnce(Return(0)); 784 EXPECT_CALL(sdbus_mock, 785 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 786 .WillOnce(Return(0)); 787 EXPECT_CALL(sdbus_mock, 788 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 789 .WillOnce(Return(1)); 790 EXPECT_CALL(sdbus_mock, 791 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 792 .WillOnce(Return(0)); 793 794 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 795 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m, 796 [[maybe_unused]] char type, void* p) { 797 bool* s = static_cast<bool*>(p); 798 *s = asserted; 799 return 0; 800 })); 801 802 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 803 .WillOnce(Return(0)) /* variant. */ 804 .WillOnce(Return(0)) /* std::pair */ 805 .WillOnce(Return(0)); /* std::map */ 806 807 int rv = handleSensorValue(msg, passive); 808 EXPECT_EQ(rv, 0); // It's always 0. 809 failed = passive->getFailed(); 810 EXPECT_EQ(failed, false); 811 } 812 813 void GetPropertiesMax3k([[maybe_unused]] const std::string& service, 814 [[maybe_unused]] const std::string& path, 815 SensorProperties* prop) 816 { 817 prop->scale = -3; 818 prop->value = 10; 819 prop->unit = "x"; 820 prop->min = 0; 821 prop->max = 3000; 822 } 823 824 using GetPropertiesFunction = std::function<void( 825 const std::string&, const std::string&, SensorProperties*)>; 826 827 // TODO: There is definitely a cleaner way to do this. 828 class DbusPassiveTest3kMaxObj : public ::testing::Test 829 { 830 protected: 831 DbusPassiveTest3kMaxObj() : 832 sdbus_mock(), 833 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 834 helper(std::make_unique<DbusHelperMock>()) 835 { 836 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 837 .WillOnce(Return("asdf")); 838 839 EXPECT_CALL(*helper, 840 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 841 .WillOnce(_getProps); 842 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 843 .WillOnce(Return(false)); 844 845 auto info = conf::SensorConfig(); 846 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 847 std::move(helper), &info, nullptr); 848 passive = reinterpret_cast<DbusPassive*>(ri.get()); 849 EXPECT_FALSE(passive == nullptr); 850 } 851 852 sdbusplus::SdBusMock sdbus_mock; 853 sdbusplus::bus_t bus_mock; 854 std::unique_ptr<DbusHelperMock> helper; 855 std::string type = "temp"; 856 std::string id = "id"; 857 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 858 int64_t _scale = -3; 859 int64_t _value = 10; 860 861 std::unique_ptr<ReadInterface> ri; 862 DbusPassive* passive; 863 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 864 }; 865 866 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected) 867 { 868 EXPECT_DOUBLE_EQ(0, passive->getMin()); 869 EXPECT_DOUBLE_EQ(3, passive->getMax()); 870 } 871 872 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test 873 { 874 protected: 875 DbusPassiveTest3kMaxIgnoredObj() : 876 sdbus_mock(), 877 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), 878 helper(std::make_unique<DbusHelperMock>()) 879 { 880 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path))) 881 .WillOnce(Return("asdf")); 882 883 EXPECT_CALL(*helper, 884 getProperties(StrEq("asdf"), StrEq(path), NotNull())) 885 .WillOnce(_getProps); 886 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path))) 887 .WillOnce(Return(false)); 888 889 auto info = conf::SensorConfig(); 890 info.ignoreDbusMinMax = true; 891 ri = DbusPassive::createDbusPassive(bus_mock, type, id, 892 std::move(helper), &info, nullptr); 893 passive = reinterpret_cast<DbusPassive*>(ri.get()); 894 EXPECT_FALSE(passive == nullptr); 895 } 896 897 sdbusplus::SdBusMock sdbus_mock; 898 sdbusplus::bus_t bus_mock; 899 std::unique_ptr<DbusHelperMock> helper; 900 std::string type = "temp"; 901 std::string id = "id"; 902 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 903 int64_t _scale = -3; 904 int64_t _value = 10; 905 906 std::unique_ptr<ReadInterface> ri; 907 DbusPassive* passive; 908 GetPropertiesFunction _getProps = &GetPropertiesMax3k; 909 }; 910 911 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected) 912 { 913 EXPECT_DOUBLE_EQ(0, passive->getMin()); 914 EXPECT_DOUBLE_EQ(0, passive->getMax()); 915 } 916 917 } // namespace 918 } // namespace pid_control 919