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