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