1 #include "conf.hpp"
2 #include "dbus/dbuspassive.hpp"
3 #include "failsafeloggers/builder.hpp"
4 #include "failsafeloggers/failsafe_logger.hpp"
5 #include "failsafeloggers/failsafe_logger_utility.hpp"
6 #include "test/dbushelper_mock.hpp"
7
8 #include <sdbusplus/test/sdbus_mock.hpp>
9
10 #include <functional>
11 #include <memory>
12 #include <string>
13 #include <variant>
14
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17
18 namespace pid_control
19 {
20 namespace
21 {
22
23 using ::testing::_;
24 using ::testing::InSequence;
25 using ::testing::Invoke;
26 using ::testing::IsNull;
27 using ::testing::NotNull;
28 using ::testing::Return;
29 using ::testing::StrEq;
30
31 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value";
32
TEST(DbusPassiveTest,FactoryFailsWithInvalidType)33 TEST(DbusPassiveTest, FactoryFailsWithInvalidType)
34 {
35 // Verify the type is checked by the factory.
36
37 sdbusplus::SdBusMock sdbus_mock;
38 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
39 std::string type = "invalid";
40 std::string id = "id";
41
42 auto helper = std::make_unique<DbusHelperMock>();
43 auto info = conf::SensorConfig();
44
45 std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive(
46 bus_mock, type, id, std::move(helper), &info, nullptr);
47
48 EXPECT_EQ(ri, nullptr);
49 }
50
TEST(DbusPassiveTest,BoringConstructorTest)51 TEST(DbusPassiveTest, BoringConstructorTest)
52 {
53 // Simply build the object, does no error checking.
54
55 sdbusplus::SdBusMock sdbus_mock;
56 auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
57 std::string type = "invalid";
58 std::string id = "id";
59 std::string path = "/xyz/openbmc_project/sensors/unknown/id";
60
61 auto helper = std::make_unique<DbusHelperMock>();
62 SensorProperties properties;
63
64 DbusPassive(bus_mock, type, id, std::move(helper), properties, false, path,
65 nullptr);
66 // Success
67 }
68
69 class DbusPassiveTestObj : public ::testing::Test
70 {
71 protected:
DbusPassiveTestObj()72 DbusPassiveTestObj() :
73 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)),
74 helper(std::make_unique<DbusHelperMock>())
75 {
76 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
77 .WillOnce(Return("asdf"));
78
79 EXPECT_CALL(*helper,
80 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
81 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service,
82 [[maybe_unused]] const std::string& path,
83 SensorProperties* prop) {
84 prop->scale = _scale;
85 prop->value = _value;
86 prop->unit = "x";
87 prop->min = 0;
88 prop->max = 0;
89 prop->available = true;
90 }));
91 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
92 .WillOnce(Return(false));
93
94 auto info = conf::SensorConfig();
95 info.unavailableAsFailed = true;
96 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
97 std::move(helper), &info, nullptr);
98 passive = reinterpret_cast<DbusPassive*>(ri.get());
99 EXPECT_FALSE(passive == nullptr);
100 }
101
102 sdbusplus::SdBusMock sdbus_mock;
103 sdbusplus::bus_t bus_mock;
104 std::unique_ptr<DbusHelperMock> helper;
105 std::string type = "temp";
106 std::string id = "id";
107 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
108 int64_t _scale = -3;
109 int64_t _value = 10;
110
111 std::unique_ptr<ReadInterface> ri;
112 DbusPassive* passive;
113 };
114
TEST_F(DbusPassiveTestObj,ReadReturnsExpectedValues)115 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues)
116 {
117 // Verify read is returning the values.
118 ReadReturn v;
119 v.value = 0.01;
120 // TODO: updated is set when the value is created, so we can range check
121 // it.
122 ReadReturn r = passive->read();
123 EXPECT_EQ(v.value, r.value);
124 }
125
TEST_F(DbusPassiveTestObj,SetValueUpdatesValue)126 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue)
127 {
128 // Verify setvalue does as advertised.
129
130 double value = 0.01;
131 passive->setValue(value);
132
133 // TODO: updated is set when the value is set, so we can range check it.
134 ReadReturn r = passive->read();
135 EXPECT_EQ(value, r.value);
136 }
137
TEST_F(DbusPassiveTestObj,GetScaleReturnsExpectedValue)138 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue)
139 {
140 // Verify the scale is returned as expected.
141 EXPECT_EQ(_scale, passive->getScale());
142 }
143
TEST_F(DbusPassiveTestObj,getIDReturnsExpectedValue)144 TEST_F(DbusPassiveTestObj, getIDReturnsExpectedValue)
145 {
146 // Verify getID returns the expected value.
147 EXPECT_EQ(id, passive->getID());
148 }
149
TEST_F(DbusPassiveTestObj,GetMinValueReturnsExpectedValue)150 TEST_F(DbusPassiveTestObj, GetMinValueReturnsExpectedValue)
151 {
152 EXPECT_DOUBLE_EQ(0, passive->getMin());
153 }
154
TEST_F(DbusPassiveTestObj,VerifyHandlesDbusSignal)155 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal)
156 {
157 // The dbus passive sensor listens for updates and if it's the Value
158 // property, it needs to handle it.
159
160 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
161 .WillOnce(Return(nullptr));
162 sdbusplus::message_t msg(nullptr, &sdbus_mock);
163
164 const char* Value = "Value";
165 int64_t xValue = 10000;
166 const char* intf = "xyz.openbmc_project.Sensor.Value";
167 // string, std::map<std::string, std::variant<int64_t>>
168 // msg.read(msgSensor, msgData);
169
170 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
171 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
172 [[maybe_unused]] char type, void* p) {
173 const char** s = static_cast<const char**>(p);
174 // Read the first parameter, the string.
175 *s = intf;
176 return 0;
177 }))
178 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
179 [[maybe_unused]] char type, void* p) {
180 const char** s = static_cast<const char**>(p);
181 *s = Value;
182 // Read the string in the pair (dictionary).
183 return 0;
184 }));
185
186 // std::map
187 EXPECT_CALL(sdbus_mock,
188 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
189 .WillOnce(Return(0));
190
191 // while !at_end()
192 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
193 .WillOnce(Return(0))
194 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
195
196 // std::pair
197 EXPECT_CALL(sdbus_mock,
198 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
199 .WillOnce(Return(0));
200
201 EXPECT_CALL(sdbus_mock,
202 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
203 .WillOnce(Return(1));
204 EXPECT_CALL(sdbus_mock,
205 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
206 .WillOnce(Return(0));
207
208 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
209 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
210 [[maybe_unused]] char type, void* p) {
211 int64_t* s = static_cast<int64_t*>(p);
212 *s = xValue;
213 return 0;
214 }));
215
216 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
217 .WillOnce(Return(0)) /* variant. */
218 .WillOnce(Return(0)) /* std::pair */
219 .WillOnce(Return(0)); /* std::map */
220
221 int rv = handleSensorValue(msg, passive);
222 EXPECT_EQ(rv, 0); // It's always 0.
223
224 ReadReturn r = passive->read();
225 EXPECT_EQ(10, r.value);
226 }
227
TEST_F(DbusPassiveTestObj,VerifyIgnoresOtherPropertySignal)228 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal)
229 {
230 // The dbus passive sensor listens for updates and if it's the Value
231 // property, it needs to handle it. In this case, it won't be.
232
233 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
234 .WillOnce(Return(nullptr));
235 sdbusplus::message_t msg(nullptr, &sdbus_mock);
236
237 const char* Scale = "Scale";
238 int64_t xScale = -6;
239 const char* intf = "xyz.openbmc_project.Sensor.Value";
240 // string, std::map<std::string, std::variant<int64_t>>
241 // msg.read(msgSensor, msgData);
242
243 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
244 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
245 [[maybe_unused]] char type, void* p) {
246 const char** s = static_cast<const char**>(p);
247 // Read the first parameter, the string.
248 *s = intf;
249 return 0;
250 }))
251 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
252 [[maybe_unused]] char type, void* p) {
253 const char** s = static_cast<const char**>(p);
254 *s = Scale;
255 // Read the string in the pair (dictionary).
256 return 0;
257 }));
258
259 // std::map
260 EXPECT_CALL(sdbus_mock,
261 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
262 .WillOnce(Return(0));
263
264 // while !at_end()
265 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
266 .WillOnce(Return(0))
267 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
268
269 // std::pair
270 EXPECT_CALL(sdbus_mock,
271 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
272 .WillOnce(Return(0));
273
274 EXPECT_CALL(sdbus_mock,
275 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
276 .WillOnce(Return(1));
277 EXPECT_CALL(sdbus_mock,
278 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
279 .WillOnce(Return(0));
280
281 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
282 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
283 [[maybe_unused]] char type, void* p) {
284 int64_t* s = static_cast<int64_t*>(p);
285 *s = xScale;
286 return 0;
287 }));
288
289 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
290 .WillOnce(Return(0)) /* variant. */
291 .WillOnce(Return(0)) /* std::pair */
292 .WillOnce(Return(0)); /* std::map */
293
294 int rv = handleSensorValue(msg, passive);
295 EXPECT_EQ(rv, 0); // It's always 0.
296
297 ReadReturn r = passive->read();
298 EXPECT_EQ(0.01, r.value);
299 }
300
TEST_F(DbusPassiveTestObj,VerifyCriticalThresholdAssert)301 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert)
302 {
303 // Verifies when a threshold is crossed the sensor goes into error state
304 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
305 .WillOnce(Return(nullptr));
306 sdbusplus::message_t msg(nullptr, &sdbus_mock);
307
308 const char* criticalAlarm = "CriticalAlarmHigh";
309 bool alarm = true;
310 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
311
312 passive->setFailed(false);
313
314 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
315 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
316 [[maybe_unused]] char type, void* p) {
317 const char** s = static_cast<const char**>(p);
318 // Read the first parameter, the string.
319 *s = intf;
320 return 0;
321 }))
322 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
323 [[maybe_unused]] char type, void* p) {
324 const char** s = static_cast<const char**>(p);
325 *s = criticalAlarm;
326 // Read the string in the pair (dictionary).
327 return 0;
328 }));
329
330 // std::map
331 EXPECT_CALL(sdbus_mock,
332 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
333 .WillOnce(Return(0));
334
335 // while !at_end()
336 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
337 .WillOnce(Return(0))
338 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
339
340 // std::pair
341 EXPECT_CALL(sdbus_mock,
342 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
343 .WillOnce(Return(0));
344
345 EXPECT_CALL(sdbus_mock,
346 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
347 .WillOnce(Return(0));
348 EXPECT_CALL(sdbus_mock,
349 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
350 .WillOnce(Return(0));
351 EXPECT_CALL(sdbus_mock,
352 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
353 .WillOnce(Return(1));
354 EXPECT_CALL(sdbus_mock,
355 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
356 .WillOnce(Return(0));
357
358 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
359 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
360 [[maybe_unused]] char type, void* p) {
361 bool* s = static_cast<bool*>(p);
362 *s = alarm;
363 return 0;
364 }));
365
366 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
367 .WillOnce(Return(0)) /* variant. */
368 .WillOnce(Return(0)) /* std::pair */
369 .WillOnce(Return(0)); /* std::map */
370
371 int rv = handleSensorValue(msg, passive);
372 EXPECT_EQ(rv, 0); // It's always 0.
373 bool failed = passive->getFailed();
374 EXPECT_EQ(failed, true);
375 }
376
TEST_F(DbusPassiveTestObj,VerifyCriticalThresholdDeassert)377 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert)
378 {
379 // Verifies when a threshold is deasserted a failed sensor goes back into
380 // the normal state
381 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
382 .WillOnce(Return(nullptr));
383 sdbusplus::message_t msg(nullptr, &sdbus_mock);
384
385 const char* criticalAlarm = "CriticalAlarmHigh";
386 bool alarm = false;
387 const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
388
389 passive->setFailed(true);
390
391 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
392 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
393 [[maybe_unused]] char type, void* p) {
394 const char** s = static_cast<const char**>(p);
395 // Read the first parameter, the string.
396 *s = intf;
397 return 0;
398 }))
399 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
400 [[maybe_unused]] char type, void* p) {
401 const char** s = static_cast<const char**>(p);
402 *s = criticalAlarm;
403 // Read the string in the pair (dictionary).
404 return 0;
405 }));
406
407 // std::map
408 EXPECT_CALL(sdbus_mock,
409 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
410 .WillOnce(Return(0));
411
412 // while !at_end()
413 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
414 .WillOnce(Return(0))
415 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
416
417 // std::pair
418 EXPECT_CALL(sdbus_mock,
419 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
420 .WillOnce(Return(0));
421
422 EXPECT_CALL(sdbus_mock,
423 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
424 .WillOnce(Return(0));
425 EXPECT_CALL(sdbus_mock,
426 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
427 .WillOnce(Return(0));
428 EXPECT_CALL(sdbus_mock,
429 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
430 .WillOnce(Return(1));
431 EXPECT_CALL(sdbus_mock,
432 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
433 .WillOnce(Return(0));
434
435 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
436 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
437 [[maybe_unused]] char type, void* p) {
438 bool* s = static_cast<bool*>(p);
439 *s = alarm;
440 return 0;
441 }));
442
443 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
444 .WillOnce(Return(0)) /* variant. */
445 .WillOnce(Return(0)) /* std::pair */
446 .WillOnce(Return(0)); /* std::map */
447
448 int rv = handleSensorValue(msg, passive);
449 EXPECT_EQ(rv, 0); // It's always 0.
450 bool failed = passive->getFailed();
451 EXPECT_EQ(failed, false);
452 }
453
TEST_F(DbusPassiveTestObj,VerifyAvailableDeassert)454 TEST_F(DbusPassiveTestObj, VerifyAvailableDeassert)
455 {
456 // Verifies when Availble is deasserted && unavailableAsFailed == true,
457 // the sensor goes into error state
458 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
459 .WillOnce(Return(nullptr));
460 sdbusplus::message_t msg(nullptr, &sdbus_mock);
461
462 const char* property = "Available";
463 bool asserted = false;
464 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
465
466 passive->setAvailable(true);
467
468 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
469 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
470 [[maybe_unused]] char type, void* p) {
471 const char** s = static_cast<const char**>(p);
472 // Read the first parameter, the string.
473 *s = intf;
474 return 0;
475 }))
476 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
477 [[maybe_unused]] char type, void* p) {
478 const char** s = static_cast<const char**>(p);
479 *s = property;
480 // Read the string in the pair (dictionary).
481 return 0;
482 }));
483
484 // std::map
485 EXPECT_CALL(sdbus_mock,
486 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
487 .WillOnce(Return(0));
488
489 // while !at_end()
490 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
491 .WillOnce(Return(0))
492 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
493
494 // std::pair
495 EXPECT_CALL(sdbus_mock,
496 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
497 .WillOnce(Return(0));
498
499 EXPECT_CALL(sdbus_mock,
500 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
501 .WillOnce(Return(0));
502 EXPECT_CALL(sdbus_mock,
503 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
504 .WillOnce(Return(0));
505 EXPECT_CALL(sdbus_mock,
506 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
507 .WillOnce(Return(1));
508 EXPECT_CALL(sdbus_mock,
509 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
510 .WillOnce(Return(0));
511
512 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
513 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
514 [[maybe_unused]] char type, void* p) {
515 bool* s = static_cast<bool*>(p);
516 *s = asserted;
517 return 0;
518 }));
519
520 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
521 .WillOnce(Return(0)) /* variant. */
522 .WillOnce(Return(0)) /* std::pair */
523 .WillOnce(Return(0)); /* std::map */
524
525 int rv = handleSensorValue(msg, passive);
526 EXPECT_EQ(rv, 0); // It's always 0.
527 bool failed = passive->getFailed();
528 EXPECT_EQ(failed, true);
529 }
530
TEST_F(DbusPassiveTestObj,VerifyAvailableAssert)531 TEST_F(DbusPassiveTestObj, VerifyAvailableAssert)
532 {
533 // Verifies when Availble is asserted && unavailableAsFailed == true,
534 // an error sensor goes back to normal state
535 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
536 .WillOnce(Return(nullptr));
537 sdbusplus::message_t msg(nullptr, &sdbus_mock);
538
539 const char* property = "Available";
540 bool asserted = true;
541 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
542
543 passive->setAvailable(false);
544 bool failed = passive->getFailed();
545 EXPECT_EQ(failed, true);
546
547 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
548 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
549 [[maybe_unused]] char type, void* p) {
550 const char** s = static_cast<const char**>(p);
551 // Read the first parameter, the string.
552 *s = intf;
553 return 0;
554 }))
555 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
556 [[maybe_unused]] char type, void* p) {
557 const char** s = static_cast<const char**>(p);
558 *s = property;
559 // Read the string in the pair (dictionary).
560 return 0;
561 }));
562
563 // std::map
564 EXPECT_CALL(sdbus_mock,
565 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
566 .WillOnce(Return(0));
567
568 // while !at_end()
569 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
570 .WillOnce(Return(0))
571 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
572
573 // std::pair
574 EXPECT_CALL(sdbus_mock,
575 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
576 .WillOnce(Return(0));
577
578 EXPECT_CALL(sdbus_mock,
579 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
580 .WillOnce(Return(0));
581 EXPECT_CALL(sdbus_mock,
582 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
583 .WillOnce(Return(0));
584 EXPECT_CALL(sdbus_mock,
585 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
586 .WillOnce(Return(1));
587 EXPECT_CALL(sdbus_mock,
588 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
589 .WillOnce(Return(0));
590
591 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
592 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
593 [[maybe_unused]] char type, void* p) {
594 bool* s = static_cast<bool*>(p);
595 *s = asserted;
596 return 0;
597 }));
598
599 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
600 .WillOnce(Return(0)) /* variant. */
601 .WillOnce(Return(0)) /* std::pair */
602 .WillOnce(Return(0)); /* std::map */
603
604 int rv = handleSensorValue(msg, passive);
605 EXPECT_EQ(rv, 0); // It's always 0.
606 failed = passive->getFailed();
607 EXPECT_EQ(failed, false);
608 }
609
610 class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test
611 {
612 protected:
DbusPassiveTestUnaSensorNotAsFailedObj()613 DbusPassiveTestUnaSensorNotAsFailedObj() :
614 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)),
615 helper(std::make_unique<DbusHelperMock>())
616 {
617 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
618 .WillOnce(Return("asdf"));
619
620 EXPECT_CALL(*helper,
621 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
622 .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service,
623 [[maybe_unused]] const std::string& path,
624 SensorProperties* prop) {
625 prop->scale = _scale;
626 prop->value = _value;
627 prop->unit = "x";
628 prop->min = 0;
629 prop->max = 0;
630 prop->available = true;
631 }));
632 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
633 .WillOnce(Return(false));
634
635 auto info = conf::SensorConfig();
636 info.unavailableAsFailed = false;
637 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
638 std::move(helper), &info, nullptr);
639 passive = reinterpret_cast<DbusPassive*>(ri.get());
640 EXPECT_FALSE(passive == nullptr);
641 }
642
643 sdbusplus::SdBusMock sdbus_mock;
644 sdbusplus::bus_t bus_mock;
645 std::unique_ptr<DbusHelperMock> helper;
646 std::string type = "temp";
647 std::string id = "id";
648 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
649 int64_t _scale = -3;
650 int64_t _value = 10;
651
652 std::unique_ptr<ReadInterface> ri;
653 DbusPassive* passive;
654 };
655
TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj,VerifyAvailableDeassert)656 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert)
657 {
658 // Verifies when Availble is deasserted && unavailableAsFailed == false,
659 // the sensor remains at OK state but reading goes to NaN.
660 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
661 .WillOnce(Return(nullptr));
662 sdbusplus::message_t msg(nullptr, &sdbus_mock);
663
664 const char* property = "Available";
665 bool asserted = false;
666 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
667
668 passive->setAvailable(true);
669
670 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
671 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
672 [[maybe_unused]] char type, void* p) {
673 const char** s = static_cast<const char**>(p);
674 // Read the first parameter, the string.
675 *s = intf;
676 return 0;
677 }))
678 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
679 [[maybe_unused]] char type, void* p) {
680 const char** s = static_cast<const char**>(p);
681 *s = property;
682 // Read the string in the pair (dictionary).
683 return 0;
684 }));
685
686 // std::map
687 EXPECT_CALL(sdbus_mock,
688 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
689 .WillOnce(Return(0));
690
691 // while !at_end()
692 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
693 .WillOnce(Return(0))
694 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
695
696 // std::pair
697 EXPECT_CALL(sdbus_mock,
698 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
699 .WillOnce(Return(0));
700
701 EXPECT_CALL(sdbus_mock,
702 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
703 .WillOnce(Return(0));
704 EXPECT_CALL(sdbus_mock,
705 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
706 .WillOnce(Return(0));
707 EXPECT_CALL(sdbus_mock,
708 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
709 .WillOnce(Return(1));
710 EXPECT_CALL(sdbus_mock,
711 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
712 .WillOnce(Return(0));
713
714 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
715 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
716 [[maybe_unused]] char type, void* p) {
717 bool* s = static_cast<bool*>(p);
718 *s = asserted;
719 return 0;
720 }));
721
722 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
723 .WillOnce(Return(0)) /* variant. */
724 .WillOnce(Return(0)) /* std::pair */
725 .WillOnce(Return(0)); /* std::map */
726
727 int rv = handleSensorValue(msg, passive);
728 EXPECT_EQ(rv, 0); // It's always 0.
729 bool failed = passive->getFailed();
730 EXPECT_EQ(failed, false);
731 ReadReturn r = passive->read();
732 EXPECT_FALSE(std::isfinite(r.value));
733 }
734
TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj,VerifyAvailableAssert)735 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert)
736 {
737 // Verifies when a sensor's state goes from unavailble to available
738 // && unavailableAsFailed == false, this sensor remains at OK state.
739 EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
740 .WillOnce(Return(nullptr));
741 sdbusplus::message_t msg(nullptr, &sdbus_mock);
742
743 const char* property = "Available";
744 bool asserted = true;
745 const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
746
747 passive->setAvailable(false);
748 bool failed = passive->getFailed();
749 EXPECT_EQ(failed, false);
750
751 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
752 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
753 [[maybe_unused]] char type, void* p) {
754 const char** s = static_cast<const char**>(p);
755 // Read the first parameter, the string.
756 *s = intf;
757 return 0;
758 }))
759 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
760 [[maybe_unused]] char type, void* p) {
761 const char** s = static_cast<const char**>(p);
762 *s = property;
763 // Read the string in the pair (dictionary).
764 return 0;
765 }));
766
767 // std::map
768 EXPECT_CALL(sdbus_mock,
769 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
770 .WillOnce(Return(0));
771
772 // while !at_end()
773 EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
774 .WillOnce(Return(0))
775 .WillOnce(Return(1)); // So it exits the loop after reading one pair.
776
777 // std::pair
778 EXPECT_CALL(sdbus_mock,
779 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
780 .WillOnce(Return(0));
781
782 EXPECT_CALL(sdbus_mock,
783 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
784 .WillOnce(Return(0));
785 EXPECT_CALL(sdbus_mock,
786 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
787 .WillOnce(Return(0));
788 EXPECT_CALL(sdbus_mock,
789 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
790 .WillOnce(Return(1));
791 EXPECT_CALL(sdbus_mock,
792 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
793 .WillOnce(Return(0));
794
795 EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
796 .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
797 [[maybe_unused]] char type, void* p) {
798 bool* s = static_cast<bool*>(p);
799 *s = asserted;
800 return 0;
801 }));
802
803 EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
804 .WillOnce(Return(0)) /* variant. */
805 .WillOnce(Return(0)) /* std::pair */
806 .WillOnce(Return(0)); /* std::map */
807
808 int rv = handleSensorValue(msg, passive);
809 EXPECT_EQ(rv, 0); // It's always 0.
810 failed = passive->getFailed();
811 EXPECT_EQ(failed, false);
812 }
813
GetPropertiesMax3k(const std::string & service,const std::string & path,SensorProperties * prop)814 void GetPropertiesMax3k([[maybe_unused]] const std::string& service,
815 [[maybe_unused]] const std::string& path,
816 SensorProperties* prop)
817 {
818 prop->scale = -3;
819 prop->value = 10;
820 prop->unit = "x";
821 prop->min = 0;
822 prop->max = 3000;
823 }
824
825 using GetPropertiesFunction = std::function<void(
826 const std::string&, const std::string&, SensorProperties*)>;
827
828 // TODO: There is definitely a cleaner way to do this.
829 class DbusPassiveTest3kMaxObj : public ::testing::Test
830 {
831 protected:
DbusPassiveTest3kMaxObj()832 DbusPassiveTest3kMaxObj() :
833 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)),
834 helper(std::make_unique<DbusHelperMock>())
835 {
836 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
837 .WillOnce(Return("asdf"));
838
839 EXPECT_CALL(*helper,
840 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
841 .WillOnce(_getProps);
842 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
843 .WillOnce(Return(false));
844
845 auto info = conf::SensorConfig();
846 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
847 std::move(helper), &info, nullptr);
848 passive = reinterpret_cast<DbusPassive*>(ri.get());
849 EXPECT_FALSE(passive == nullptr);
850 }
851
852 sdbusplus::SdBusMock sdbus_mock;
853 sdbusplus::bus_t bus_mock;
854 std::unique_ptr<DbusHelperMock> helper;
855 std::string type = "temp";
856 std::string id = "id";
857 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
858 int64_t _scale = -3;
859 int64_t _value = 10;
860
861 std::unique_ptr<ReadInterface> ri;
862 DbusPassive* passive;
863 GetPropertiesFunction _getProps = &GetPropertiesMax3k;
864 };
865
TEST_F(DbusPassiveTest3kMaxObj,ReadMinAndMaxReturnsExpected)866 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected)
867 {
868 EXPECT_DOUBLE_EQ(0, passive->getMin());
869 EXPECT_DOUBLE_EQ(3, passive->getMax());
870 }
871
872 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test
873 {
874 protected:
DbusPassiveTest3kMaxIgnoredObj()875 DbusPassiveTest3kMaxIgnoredObj() :
876 sdbus_mock(), bus_mock(sdbusplus::get_mocked_new(&sdbus_mock)),
877 helper(std::make_unique<DbusHelperMock>())
878 {
879 EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
880 .WillOnce(Return("asdf"));
881
882 EXPECT_CALL(*helper,
883 getProperties(StrEq("asdf"), StrEq(path), NotNull()))
884 .WillOnce(_getProps);
885 EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
886 .WillOnce(Return(false));
887
888 auto info = conf::SensorConfig();
889 info.ignoreDbusMinMax = true;
890 ri = DbusPassive::createDbusPassive(bus_mock, type, id,
891 std::move(helper), &info, nullptr);
892 passive = reinterpret_cast<DbusPassive*>(ri.get());
893 EXPECT_FALSE(passive == nullptr);
894 }
895
896 sdbusplus::SdBusMock sdbus_mock;
897 sdbusplus::bus_t bus_mock;
898 std::unique_ptr<DbusHelperMock> helper;
899 std::string type = "temp";
900 std::string id = "id";
901 std::string path = "/xyz/openbmc_project/sensors/temperature/id";
902 int64_t _scale = -3;
903 int64_t _value = 10;
904
905 std::unique_ptr<ReadInterface> ri;
906 DbusPassive* passive;
907 GetPropertiesFunction _getProps = &GetPropertiesMax3k;
908 };
909
TEST_F(DbusPassiveTest3kMaxIgnoredObj,ReadMinAndMaxReturnsExpected)910 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected)
911 {
912 EXPECT_DOUBLE_EQ(0, passive->getMin());
913 EXPECT_DOUBLE_EQ(0, passive->getMax());
914 }
915
916 } // namespace
917 } // namespace pid_control
918