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