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