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