1 #include "dbus/dbuspassive.hpp" 2 #include "test/dbushelper_mock.hpp" 3 4 #include <sdbusplus/test/sdbus_mock.hpp> 5 #include <string> 6 7 #include <gmock/gmock.h> 8 #include <gtest/gtest.h> 9 10 using ::testing::_; 11 using ::testing::InSequence; 12 using ::testing::Invoke; 13 using ::testing::IsNull; 14 using ::testing::NotNull; 15 using ::testing::Return; 16 using ::testing::StrEq; 17 18 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value"; 19 20 TEST(DbusPassiveTest, FactoryFailsWithInvalidType) 21 { 22 // Verify the type is checked by the factory. 23 24 sdbusplus::SdBusMock sdbus_mock; 25 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 26 std::string type = "invalid"; 27 std::string id = "id"; 28 29 DbusHelperMock helper; 30 31 std::unique_ptr<ReadInterface> ri = 32 DbusPassive::CreateDbusPassive(bus_mock, type, id, &helper); 33 34 EXPECT_EQ(ri, nullptr); 35 } 36 37 TEST(DbusPassiveTest, BoringConstructorTest) 38 { 39 // Just build the object, which should be avoided as this does no error 40 // checking at present. 41 42 sdbusplus::SdBusMock sdbus_mock; 43 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock); 44 std::string type = "invalid"; 45 std::string id = "id"; 46 std::string path = "/xyz/openbmc_project/sensors/unknown/id"; 47 48 DbusHelperMock helper; 49 EXPECT_CALL(helper, GetService(_, StrEq(SensorIntf), StrEq(path))) 50 .WillOnce(Return("asdf")); 51 52 EXPECT_CALL(helper, GetProperties(_, StrEq("asdf"), StrEq(path), NotNull())) 53 .WillOnce( 54 Invoke([&](sdbusplus::bus::bus& bus, const std::string& service, 55 const std::string& path, struct SensorProperties* prop) { 56 prop->scale = -3; 57 prop->value = 10; 58 prop->unit = "x"; 59 })); 60 EXPECT_CALL(helper, ThresholdsAsserted(_, StrEq("asdf"), StrEq(path))) 61 .WillOnce(Return(false)); 62 63 DbusPassive(bus_mock, type, id, &helper); 64 // Success 65 } 66 67 class DbusPassiveTestObj : public ::testing::Test 68 { 69 protected: 70 DbusPassiveTestObj() : 71 sdbus_mock(), 72 bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper() 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( 80 [&](sdbusplus::bus::bus& bus, const std::string& service, 81 const std::string& path, struct SensorProperties* prop) { 82 prop->scale = _scale; 83 prop->value = _value; 84 prop->unit = "x"; 85 })); 86 EXPECT_CALL(helper, ThresholdsAsserted(_, StrEq("asdf"), StrEq(path))) 87 .WillOnce(Return(false)); 88 89 ri = DbusPassive::CreateDbusPassive(bus_mock, type, id, &helper); 90 passive = reinterpret_cast<DbusPassive*>(ri.get()); 91 EXPECT_FALSE(passive == nullptr); 92 } 93 94 sdbusplus::SdBusMock sdbus_mock; 95 sdbusplus::bus::bus bus_mock; 96 DbusHelperMock helper; 97 std::string type = "temp"; 98 std::string id = "id"; 99 std::string path = "/xyz/openbmc_project/sensors/temperature/id"; 100 int64_t _scale = -3; 101 int64_t _value = 10; 102 103 std::unique_ptr<ReadInterface> ri; 104 DbusPassive* passive; 105 }; 106 107 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues) 108 { 109 // Verify read is returning the values. 110 ReadReturn v; 111 v.value = 0.01; 112 // TODO: updated is set when the value is created, so we can range check 113 // it. 114 ReadReturn r = passive->read(); 115 EXPECT_EQ(v.value, r.value); 116 } 117 118 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue) 119 { 120 // Verify setvalue does as advertised. 121 122 double value = 0.01; 123 passive->setValue(value); 124 125 // TODO: updated is set when the value is set, so we can range check it. 126 ReadReturn r = passive->read(); 127 EXPECT_EQ(value, r.value); 128 } 129 130 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue) 131 { 132 // Verify the scale is returned as expected. 133 EXPECT_EQ(_scale, passive->getScale()); 134 } 135 136 TEST_F(DbusPassiveTestObj, GetIdReturnsExpectedValue) 137 { 138 // Verify getId returns the expected value. 139 EXPECT_EQ(id, passive->getId()); 140 } 141 142 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal) 143 { 144 // The dbus passive sensor listens for updates and if it's the Value 145 // property, it needs to handle it. 146 147 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 148 .WillOnce(Return(nullptr)); 149 sdbusplus::message::message msg(nullptr, &sdbus_mock); 150 151 const char* Value = "Value"; 152 int64_t xValue = 10000; 153 const char* intf = "xyz.openbmc_project.Sensor.Value"; 154 // string, std::map<std::string, sdbusplus::message::variant<int64_t>> 155 // msg.read(msgSensor, msgData); 156 157 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 158 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 159 const char** s = static_cast<const char**>(p); 160 // Read the first parameter, the string. 161 *s = intf; 162 return 0; 163 })) 164 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 165 const char** s = static_cast<const char**>(p); 166 *s = Value; 167 // Read the string in the pair (dictionary). 168 return 0; 169 })); 170 171 // std::map 172 EXPECT_CALL(sdbus_mock, 173 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 174 .WillOnce(Return(0)); 175 176 // while !at_end() 177 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 178 .WillOnce(Return(0)) 179 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 180 181 // std::pair 182 EXPECT_CALL(sdbus_mock, 183 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 184 .WillOnce(Return(0)); 185 186 EXPECT_CALL(sdbus_mock, 187 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 188 .WillOnce(Return(1)); 189 EXPECT_CALL(sdbus_mock, 190 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 191 .WillOnce(Return(0)); 192 193 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 194 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 195 int64_t* s = static_cast<int64_t*>(p); 196 *s = xValue; 197 return 0; 198 })); 199 200 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 201 .WillOnce(Return(0)) /* variant. */ 202 .WillOnce(Return(0)) /* std::pair */ 203 .WillOnce(Return(0)); /* std::map */ 204 205 int rv = HandleSensorValue(msg, passive); 206 EXPECT_EQ(rv, 0); // It's always 0. 207 208 ReadReturn r = passive->read(); 209 EXPECT_EQ(10, r.value); 210 } 211 212 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal) 213 { 214 // The dbus passive sensor listens for updates and if it's the Value 215 // property, it needs to handle it. In this case, it won't be. 216 217 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 218 .WillOnce(Return(nullptr)); 219 sdbusplus::message::message msg(nullptr, &sdbus_mock); 220 221 const char* Scale = "Scale"; 222 int64_t xScale = -6; 223 const char* intf = "xyz.openbmc_project.Sensor.Value"; 224 // string, std::map<std::string, sdbusplus::message::variant<int64_t>> 225 // msg.read(msgSensor, msgData); 226 227 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 228 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 229 const char** s = static_cast<const char**>(p); 230 // Read the first parameter, the string. 231 *s = intf; 232 return 0; 233 })) 234 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 235 const char** s = static_cast<const char**>(p); 236 *s = Scale; 237 // Read the string in the pair (dictionary). 238 return 0; 239 })); 240 241 // std::map 242 EXPECT_CALL(sdbus_mock, 243 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 244 .WillOnce(Return(0)); 245 246 // while !at_end() 247 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 248 .WillOnce(Return(0)) 249 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 250 251 // std::pair 252 EXPECT_CALL(sdbus_mock, 253 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 254 .WillOnce(Return(0)); 255 256 EXPECT_CALL(sdbus_mock, 257 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 258 .WillOnce(Return(1)); 259 EXPECT_CALL(sdbus_mock, 260 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x"))) 261 .WillOnce(Return(0)); 262 263 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull())) 264 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 265 int64_t* s = static_cast<int64_t*>(p); 266 *s = xScale; 267 return 0; 268 })); 269 270 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 271 .WillOnce(Return(0)) /* variant. */ 272 .WillOnce(Return(0)) /* std::pair */ 273 .WillOnce(Return(0)); /* std::map */ 274 275 int rv = HandleSensorValue(msg, passive); 276 EXPECT_EQ(rv, 0); // It's always 0. 277 278 ReadReturn r = passive->read(); 279 EXPECT_EQ(0.01, r.value); 280 } 281 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert) 282 { 283 284 // Verifies when a threshold is crossed the sensor goes into error state 285 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 286 .WillOnce(Return(nullptr)); 287 sdbusplus::message::message msg(nullptr, &sdbus_mock); 288 289 const char* criticalAlarm = "CriticalAlarmHigh"; 290 bool alarm = true; 291 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 292 293 passive->setFailed(false); 294 295 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 296 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 297 const char** s = static_cast<const char**>(p); 298 // Read the first parameter, the string. 299 *s = intf; 300 return 0; 301 })) 302 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 303 const char** s = static_cast<const char**>(p); 304 *s = criticalAlarm; 305 // Read the string in the pair (dictionary). 306 return 0; 307 })); 308 309 // std::map 310 EXPECT_CALL(sdbus_mock, 311 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 312 .WillOnce(Return(0)); 313 314 // while !at_end() 315 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 316 .WillOnce(Return(0)) 317 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 318 319 // std::pair 320 EXPECT_CALL(sdbus_mock, 321 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 322 .WillOnce(Return(0)); 323 324 EXPECT_CALL(sdbus_mock, 325 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 326 .WillOnce(Return(0)); 327 EXPECT_CALL(sdbus_mock, 328 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 329 .WillOnce(Return(0)); 330 EXPECT_CALL(sdbus_mock, 331 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 332 .WillOnce(Return(1)); 333 EXPECT_CALL(sdbus_mock, 334 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 335 .WillOnce(Return(0)); 336 337 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 338 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 339 bool* s = static_cast<bool*>(p); 340 *s = alarm; 341 return 0; 342 })); 343 344 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 345 .WillOnce(Return(0)) /* variant. */ 346 .WillOnce(Return(0)) /* std::pair */ 347 .WillOnce(Return(0)); /* std::map */ 348 349 int rv = HandleSensorValue(msg, passive); 350 EXPECT_EQ(rv, 0); // It's always 0. 351 bool failed = passive->getFailed(); 352 EXPECT_EQ(failed, true); 353 } 354 355 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert) 356 { 357 358 // Verifies when a threshold is deasserted a failed sensor goes back into 359 // the normal state 360 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull())) 361 .WillOnce(Return(nullptr)); 362 sdbusplus::message::message msg(nullptr, &sdbus_mock); 363 364 const char* criticalAlarm = "CriticalAlarmHigh"; 365 bool alarm = false; 366 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical"; 367 368 passive->setFailed(true); 369 370 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull())) 371 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 372 const char** s = static_cast<const char**>(p); 373 // Read the first parameter, the string. 374 *s = intf; 375 return 0; 376 })) 377 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 378 const char** s = static_cast<const char**>(p); 379 *s = criticalAlarm; 380 // Read the string in the pair (dictionary). 381 return 0; 382 })); 383 384 // std::map 385 EXPECT_CALL(sdbus_mock, 386 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}"))) 387 .WillOnce(Return(0)); 388 389 // while !at_end() 390 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0)) 391 .WillOnce(Return(0)) 392 .WillOnce(Return(1)); // So it exits the loop after reading one pair. 393 394 // std::pair 395 EXPECT_CALL(sdbus_mock, 396 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv"))) 397 .WillOnce(Return(0)); 398 399 EXPECT_CALL(sdbus_mock, 400 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x"))) 401 .WillOnce(Return(0)); 402 EXPECT_CALL(sdbus_mock, 403 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d"))) 404 .WillOnce(Return(0)); 405 EXPECT_CALL(sdbus_mock, 406 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b"))) 407 .WillOnce(Return(1)); 408 EXPECT_CALL(sdbus_mock, 409 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b"))) 410 .WillOnce(Return(0)); 411 412 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull())) 413 .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) { 414 bool* s = static_cast<bool*>(p); 415 *s = alarm; 416 return 0; 417 })); 418 419 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull())) 420 .WillOnce(Return(0)) /* variant. */ 421 .WillOnce(Return(0)) /* std::pair */ 422 .WillOnce(Return(0)); /* std::map */ 423 424 int rv = HandleSensorValue(msg, passive); 425 EXPECT_EQ(rv, 0); // It's always 0. 426 bool failed = passive->getFailed(); 427 EXPECT_EQ(failed, false); 428 }