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