1 #include "conf.hpp"
2 #include "dbus/dbuspassive.hpp"
3 #include "test/dbushelper_mock.hpp"
4 
5 #include <sdbusplus/test/sdbus_mock.hpp>
6 
7 #include <functional>
8 #include <memory>
9 #include <string>
10 #include <variant>
11 
12 #include <gmock/gmock.h>
13 #include <gtest/gtest.h>
14 
15 namespace pid_control
16 {
17 namespace
18 {
19 
20 using ::testing::_;
21 using ::testing::InSequence;
22 using ::testing::Invoke;
23 using ::testing::IsNull;
24 using ::testing::NotNull;
25 using ::testing::Return;
26 using ::testing::StrEq;
27 
28 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value";
29 
30 TEST(DbusPassiveTest, FactoryFailsWithInvalidType)
31 {
32     // Verify the type is checked by the factory.
33 
34     sdbusplus::SdBusMock sdbus_mock;
35     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
36     std::string type = "invalid";
37     std::string id = "id";
38 
39     auto helper = std::make_unique<DbusHelperMock>();
40     auto info = conf::SensorConfig();
41 
42     std::unique_ptr<ReadInterface> ri = DbusPassive::createDbusPassive(
43         bus_mock, type, id, std::move(helper), &info, nullptr);
44 
45     EXPECT_EQ(ri, nullptr);
46 }
47 
48 TEST(DbusPassiveTest, BoringConstructorTest)
49 {
50     // Simply build the object, does no error checking.
51 
52     sdbusplus::SdBusMock sdbus_mock;
53     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
54     std::string type = "invalid";
55     std::string id = "id";
56     std::string path = "/xyz/openbmc_project/sensors/unknown/id";
57 
58     auto helper = std::make_unique<DbusHelperMock>();
59     SensorProperties properties;
60 
61     DbusPassive(bus_mock, type, id, std::move(helper), properties, false, path,
62                 nullptr);
63     // Success
64 }
65 
66 class DbusPassiveTestObj : public ::testing::Test
67 {
68   protected:
69     DbusPassiveTestObj() :
70         sdbus_mock(),
71         bus_mock(std::move(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 Availble 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 Availble 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(),
613         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
614         helper(std::make_unique<DbusHelperMock>())
615     {
616         EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
617             .WillOnce(Return("asdf"));
618 
619         EXPECT_CALL(*helper,
620                     getProperties(StrEq("asdf"), StrEq(path), NotNull()))
621             .WillOnce(Invoke([&]([[maybe_unused]] const std::string& service,
622                                  [[maybe_unused]] const std::string& path,
623                                  SensorProperties* prop) {
624                 prop->scale = _scale;
625                 prop->value = _value;
626                 prop->unit = "x";
627                 prop->min = 0;
628                 prop->max = 0;
629                 prop->available = true;
630             }));
631         EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
632             .WillOnce(Return(false));
633 
634         auto info = conf::SensorConfig();
635         info.unavailableAsFailed = false;
636         ri = DbusPassive::createDbusPassive(bus_mock, type, id,
637                                             std::move(helper), &info, nullptr);
638         passive = reinterpret_cast<DbusPassive*>(ri.get());
639         EXPECT_FALSE(passive == nullptr);
640     }
641 
642     sdbusplus::SdBusMock sdbus_mock;
643     sdbusplus::bus_t bus_mock;
644     std::unique_ptr<DbusHelperMock> helper;
645     std::string type = "temp";
646     std::string id = "id";
647     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
648     int64_t _scale = -3;
649     int64_t _value = 10;
650 
651     std::unique_ptr<ReadInterface> ri;
652     DbusPassive* passive;
653 };
654 
655 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert)
656 {
657     // Verifies when Availble is deasserted && unavailableAsFailed == false,
658     // the sensor remains at OK state but reading goes to NaN.
659     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
660         .WillOnce(Return(nullptr));
661     sdbusplus::message_t msg(nullptr, &sdbus_mock);
662 
663     const char* property = "Available";
664     bool asserted = false;
665     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
666 
667     passive->setAvailable(true);
668 
669     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
670         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
671                              [[maybe_unused]] char type, void* p) {
672             const char** s = static_cast<const char**>(p);
673             // Read the first parameter, the string.
674             *s = intf;
675             return 0;
676         }))
677         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
678                              [[maybe_unused]] char type, void* p) {
679             const char** s = static_cast<const char**>(p);
680             *s = property;
681             // Read the string in the pair (dictionary).
682             return 0;
683         }));
684 
685     // std::map
686     EXPECT_CALL(sdbus_mock,
687                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
688         .WillOnce(Return(0));
689 
690     // while !at_end()
691     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
692         .WillOnce(Return(0))
693         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
694 
695     // std::pair
696     EXPECT_CALL(sdbus_mock,
697                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
698         .WillOnce(Return(0));
699 
700     EXPECT_CALL(sdbus_mock,
701                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
702         .WillOnce(Return(0));
703     EXPECT_CALL(sdbus_mock,
704                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
705         .WillOnce(Return(0));
706     EXPECT_CALL(sdbus_mock,
707                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
708         .WillOnce(Return(1));
709     EXPECT_CALL(sdbus_mock,
710                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
711         .WillOnce(Return(0));
712 
713     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
714         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
715                              [[maybe_unused]] char type, void* p) {
716             bool* s = static_cast<bool*>(p);
717             *s = asserted;
718             return 0;
719         }));
720 
721     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
722         .WillOnce(Return(0))  /* variant. */
723         .WillOnce(Return(0))  /* std::pair */
724         .WillOnce(Return(0)); /* std::map */
725 
726     int rv = handleSensorValue(msg, passive);
727     EXPECT_EQ(rv, 0); // It's always 0.
728     bool failed = passive->getFailed();
729     EXPECT_EQ(failed, false);
730     ReadReturn r = passive->read();
731     EXPECT_FALSE(std::isfinite(r.value));
732 }
733 
734 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert)
735 {
736     // Verifies when a sensor's state goes from unavailble to available
737     // && unavailableAsFailed == false, this sensor remains at OK state.
738     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
739         .WillOnce(Return(nullptr));
740     sdbusplus::message_t msg(nullptr, &sdbus_mock);
741 
742     const char* property = "Available";
743     bool asserted = true;
744     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
745 
746     passive->setAvailable(false);
747     bool failed = passive->getFailed();
748     EXPECT_EQ(failed, false);
749 
750     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
751         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
752                              [[maybe_unused]] char type, void* p) {
753             const char** s = static_cast<const char**>(p);
754             // Read the first parameter, the string.
755             *s = intf;
756             return 0;
757         }))
758         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
759                              [[maybe_unused]] char type, void* p) {
760             const char** s = static_cast<const char**>(p);
761             *s = property;
762             // Read the string in the pair (dictionary).
763             return 0;
764         }));
765 
766     // std::map
767     EXPECT_CALL(sdbus_mock,
768                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
769         .WillOnce(Return(0));
770 
771     // while !at_end()
772     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
773         .WillOnce(Return(0))
774         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
775 
776     // std::pair
777     EXPECT_CALL(sdbus_mock,
778                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
779         .WillOnce(Return(0));
780 
781     EXPECT_CALL(sdbus_mock,
782                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
783         .WillOnce(Return(0));
784     EXPECT_CALL(sdbus_mock,
785                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
786         .WillOnce(Return(0));
787     EXPECT_CALL(sdbus_mock,
788                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
789         .WillOnce(Return(1));
790     EXPECT_CALL(sdbus_mock,
791                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
792         .WillOnce(Return(0));
793 
794     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
795         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
796                              [[maybe_unused]] char type, void* p) {
797             bool* s = static_cast<bool*>(p);
798             *s = asserted;
799             return 0;
800         }));
801 
802     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
803         .WillOnce(Return(0))  /* variant. */
804         .WillOnce(Return(0))  /* std::pair */
805         .WillOnce(Return(0)); /* std::map */
806 
807     int rv = handleSensorValue(msg, passive);
808     EXPECT_EQ(rv, 0); // It's always 0.
809     failed = passive->getFailed();
810     EXPECT_EQ(failed, false);
811 }
812 
813 void GetPropertiesMax3k([[maybe_unused]] const std::string& service,
814                         [[maybe_unused]] const std::string& path,
815                         SensorProperties* prop)
816 {
817     prop->scale = -3;
818     prop->value = 10;
819     prop->unit = "x";
820     prop->min = 0;
821     prop->max = 3000;
822 }
823 
824 using GetPropertiesFunction = std::function<void(
825     const std::string&, const std::string&, SensorProperties*)>;
826 
827 // TODO: There is definitely a cleaner way to do this.
828 class DbusPassiveTest3kMaxObj : public ::testing::Test
829 {
830   protected:
831     DbusPassiveTest3kMaxObj() :
832         sdbus_mock(),
833         bus_mock(std::move(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 
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:
875     DbusPassiveTest3kMaxIgnoredObj() :
876         sdbus_mock(),
877         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
878         helper(std::make_unique<DbusHelperMock>())
879     {
880         EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
881             .WillOnce(Return("asdf"));
882 
883         EXPECT_CALL(*helper,
884                     getProperties(StrEq("asdf"), StrEq(path), NotNull()))
885             .WillOnce(_getProps);
886         EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
887             .WillOnce(Return(false));
888 
889         auto info = conf::SensorConfig();
890         info.ignoreDbusMinMax = true;
891         ri = DbusPassive::createDbusPassive(bus_mock, type, id,
892                                             std::move(helper), &info, nullptr);
893         passive = reinterpret_cast<DbusPassive*>(ri.get());
894         EXPECT_FALSE(passive == nullptr);
895     }
896 
897     sdbusplus::SdBusMock sdbus_mock;
898     sdbusplus::bus_t bus_mock;
899     std::unique_ptr<DbusHelperMock> helper;
900     std::string type = "temp";
901     std::string id = "id";
902     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
903     int64_t _scale = -3;
904     int64_t _value = 10;
905 
906     std::unique_ptr<ReadInterface> ri;
907     DbusPassive* passive;
908     GetPropertiesFunction _getProps = &GetPropertiesMax3k;
909 };
910 
911 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected)
912 {
913     EXPECT_DOUBLE_EQ(0, passive->getMin());
914     EXPECT_DOUBLE_EQ(0, passive->getMax());
915 }
916 
917 } // namespace
918 } // namespace pid_control
919