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 
302     // Verifies when a threshold is crossed the sensor goes into error state
303     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
304         .WillOnce(Return(nullptr));
305     sdbusplus::message_t msg(nullptr, &sdbus_mock);
306 
307     const char* criticalAlarm = "CriticalAlarmHigh";
308     bool alarm = true;
309     const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
310 
311     passive->setFailed(false);
312 
313     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
314         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
315                              [[maybe_unused]] char type, void* p) {
316             const char** s = static_cast<const char**>(p);
317             // Read the first parameter, the string.
318             *s = intf;
319             return 0;
320         }))
321         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
322                              [[maybe_unused]] char type, void* p) {
323             const char** s = static_cast<const char**>(p);
324             *s = criticalAlarm;
325             // Read the string in the pair (dictionary).
326             return 0;
327         }));
328 
329     // std::map
330     EXPECT_CALL(sdbus_mock,
331                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
332         .WillOnce(Return(0));
333 
334     // while !at_end()
335     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
336         .WillOnce(Return(0))
337         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
338 
339     // std::pair
340     EXPECT_CALL(sdbus_mock,
341                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
342         .WillOnce(Return(0));
343 
344     EXPECT_CALL(sdbus_mock,
345                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
346         .WillOnce(Return(0));
347     EXPECT_CALL(sdbus_mock,
348                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
349         .WillOnce(Return(0));
350     EXPECT_CALL(sdbus_mock,
351                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
352         .WillOnce(Return(1));
353     EXPECT_CALL(sdbus_mock,
354                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
355         .WillOnce(Return(0));
356 
357     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
358         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
359                              [[maybe_unused]] char type, void* p) {
360             bool* s = static_cast<bool*>(p);
361             *s = alarm;
362             return 0;
363         }));
364 
365     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
366         .WillOnce(Return(0))  /* variant. */
367         .WillOnce(Return(0))  /* std::pair */
368         .WillOnce(Return(0)); /* std::map */
369 
370     int rv = handleSensorValue(msg, passive);
371     EXPECT_EQ(rv, 0); // It's always 0.
372     bool failed = passive->getFailed();
373     EXPECT_EQ(failed, true);
374 }
375 
376 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert)
377 {
378 
379     // Verifies when a threshold is deasserted a failed sensor goes back into
380     // the normal state
381     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
382         .WillOnce(Return(nullptr));
383     sdbusplus::message_t msg(nullptr, &sdbus_mock);
384 
385     const char* criticalAlarm = "CriticalAlarmHigh";
386     bool alarm = false;
387     const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
388 
389     passive->setFailed(true);
390 
391     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
392         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
393                              [[maybe_unused]] char type, void* p) {
394             const char** s = static_cast<const char**>(p);
395             // Read the first parameter, the string.
396             *s = intf;
397             return 0;
398         }))
399         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
400                              [[maybe_unused]] char type, void* p) {
401             const char** s = static_cast<const char**>(p);
402             *s = criticalAlarm;
403             // Read the string in the pair (dictionary).
404             return 0;
405         }));
406 
407     // std::map
408     EXPECT_CALL(sdbus_mock,
409                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
410         .WillOnce(Return(0));
411 
412     // while !at_end()
413     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
414         .WillOnce(Return(0))
415         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
416 
417     // std::pair
418     EXPECT_CALL(sdbus_mock,
419                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
420         .WillOnce(Return(0));
421 
422     EXPECT_CALL(sdbus_mock,
423                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
424         .WillOnce(Return(0));
425     EXPECT_CALL(sdbus_mock,
426                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
427         .WillOnce(Return(0));
428     EXPECT_CALL(sdbus_mock,
429                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
430         .WillOnce(Return(1));
431     EXPECT_CALL(sdbus_mock,
432                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
433         .WillOnce(Return(0));
434 
435     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
436         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
437                              [[maybe_unused]] char type, void* p) {
438             bool* s = static_cast<bool*>(p);
439             *s = alarm;
440             return 0;
441         }));
442 
443     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
444         .WillOnce(Return(0))  /* variant. */
445         .WillOnce(Return(0))  /* std::pair */
446         .WillOnce(Return(0)); /* std::map */
447 
448     int rv = handleSensorValue(msg, passive);
449     EXPECT_EQ(rv, 0); // It's always 0.
450     bool failed = passive->getFailed();
451     EXPECT_EQ(failed, false);
452 }
453 
454 TEST_F(DbusPassiveTestObj, VerifyAvailableDeassert)
455 {
456 
457     // Verifies when Availble is deasserted && unavailableAsFailed == true,
458     // the sensor goes into error state
459     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
460         .WillOnce(Return(nullptr));
461     sdbusplus::message_t msg(nullptr, &sdbus_mock);
462 
463     const char* property = "Available";
464     bool asserted = false;
465     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
466 
467     passive->setAvailable(true);
468 
469     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
470         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
471                              [[maybe_unused]] char type, void* p) {
472             const char** s = static_cast<const char**>(p);
473             // Read the first parameter, the string.
474             *s = intf;
475             return 0;
476         }))
477         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
478                              [[maybe_unused]] char type, void* p) {
479             const char** s = static_cast<const char**>(p);
480             *s = property;
481             // Read the string in the pair (dictionary).
482             return 0;
483         }));
484 
485     // std::map
486     EXPECT_CALL(sdbus_mock,
487                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
488         .WillOnce(Return(0));
489 
490     // while !at_end()
491     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
492         .WillOnce(Return(0))
493         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
494 
495     // std::pair
496     EXPECT_CALL(sdbus_mock,
497                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
498         .WillOnce(Return(0));
499 
500     EXPECT_CALL(sdbus_mock,
501                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
502         .WillOnce(Return(0));
503     EXPECT_CALL(sdbus_mock,
504                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
505         .WillOnce(Return(0));
506     EXPECT_CALL(sdbus_mock,
507                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
508         .WillOnce(Return(1));
509     EXPECT_CALL(sdbus_mock,
510                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
511         .WillOnce(Return(0));
512 
513     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
514         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
515                              [[maybe_unused]] char type, void* p) {
516             bool* s = static_cast<bool*>(p);
517             *s = asserted;
518             return 0;
519         }));
520 
521     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
522         .WillOnce(Return(0))  /* variant. */
523         .WillOnce(Return(0))  /* std::pair */
524         .WillOnce(Return(0)); /* std::map */
525 
526     int rv = handleSensorValue(msg, passive);
527     EXPECT_EQ(rv, 0); // It's always 0.
528     bool failed = passive->getFailed();
529     EXPECT_EQ(failed, true);
530 }
531 
532 TEST_F(DbusPassiveTestObj, VerifyAvailableAssert)
533 {
534 
535     // Verifies when Availble is asserted && unavailableAsFailed == true,
536     // an error sensor goes back to normal state
537     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
538         .WillOnce(Return(nullptr));
539     sdbusplus::message_t msg(nullptr, &sdbus_mock);
540 
541     const char* property = "Available";
542     bool asserted = true;
543     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
544 
545     passive->setAvailable(false);
546     bool failed = passive->getFailed();
547     EXPECT_EQ(failed, true);
548 
549     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
550         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
551                              [[maybe_unused]] char type, void* p) {
552             const char** s = static_cast<const char**>(p);
553             // Read the first parameter, the string.
554             *s = intf;
555             return 0;
556         }))
557         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
558                              [[maybe_unused]] char type, void* p) {
559             const char** s = static_cast<const char**>(p);
560             *s = property;
561             // Read the string in the pair (dictionary).
562             return 0;
563         }));
564 
565     // std::map
566     EXPECT_CALL(sdbus_mock,
567                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
568         .WillOnce(Return(0));
569 
570     // while !at_end()
571     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
572         .WillOnce(Return(0))
573         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
574 
575     // std::pair
576     EXPECT_CALL(sdbus_mock,
577                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
578         .WillOnce(Return(0));
579 
580     EXPECT_CALL(sdbus_mock,
581                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
582         .WillOnce(Return(0));
583     EXPECT_CALL(sdbus_mock,
584                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
585         .WillOnce(Return(0));
586     EXPECT_CALL(sdbus_mock,
587                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
588         .WillOnce(Return(1));
589     EXPECT_CALL(sdbus_mock,
590                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
591         .WillOnce(Return(0));
592 
593     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
594         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
595                              [[maybe_unused]] char type, void* p) {
596             bool* s = static_cast<bool*>(p);
597             *s = asserted;
598             return 0;
599         }));
600 
601     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
602         .WillOnce(Return(0))  /* variant. */
603         .WillOnce(Return(0))  /* std::pair */
604         .WillOnce(Return(0)); /* std::map */
605 
606     int rv = handleSensorValue(msg, passive);
607     EXPECT_EQ(rv, 0); // It's always 0.
608     failed = passive->getFailed();
609     EXPECT_EQ(failed, false);
610 }
611 
612 class DbusPassiveTestUnaSensorNotAsFailedObj : public ::testing::Test
613 {
614   protected:
615     DbusPassiveTestUnaSensorNotAsFailedObj() :
616         sdbus_mock(),
617         bus_mock(std::move(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 
659 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableDeassert)
660 {
661 
662     // Verifies when Availble is deasserted && unavailableAsFailed == false,
663     // the sensor remains at OK state but reading goes to NaN.
664     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
665         .WillOnce(Return(nullptr));
666     sdbusplus::message_t msg(nullptr, &sdbus_mock);
667 
668     const char* property = "Available";
669     bool asserted = false;
670     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
671 
672     passive->setAvailable(true);
673 
674     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
675         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
676                              [[maybe_unused]] char type, void* p) {
677             const char** s = static_cast<const char**>(p);
678             // Read the first parameter, the string.
679             *s = intf;
680             return 0;
681         }))
682         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
683                              [[maybe_unused]] char type, void* p) {
684             const char** s = static_cast<const char**>(p);
685             *s = property;
686             // Read the string in the pair (dictionary).
687             return 0;
688         }));
689 
690     // std::map
691     EXPECT_CALL(sdbus_mock,
692                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
693         .WillOnce(Return(0));
694 
695     // while !at_end()
696     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
697         .WillOnce(Return(0))
698         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
699 
700     // std::pair
701     EXPECT_CALL(sdbus_mock,
702                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
703         .WillOnce(Return(0));
704 
705     EXPECT_CALL(sdbus_mock,
706                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
707         .WillOnce(Return(0));
708     EXPECT_CALL(sdbus_mock,
709                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
710         .WillOnce(Return(0));
711     EXPECT_CALL(sdbus_mock,
712                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
713         .WillOnce(Return(1));
714     EXPECT_CALL(sdbus_mock,
715                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
716         .WillOnce(Return(0));
717 
718     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
719         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
720                              [[maybe_unused]] char type, void* p) {
721             bool* s = static_cast<bool*>(p);
722             *s = asserted;
723             return 0;
724         }));
725 
726     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
727         .WillOnce(Return(0))  /* variant. */
728         .WillOnce(Return(0))  /* std::pair */
729         .WillOnce(Return(0)); /* std::map */
730 
731     int rv = handleSensorValue(msg, passive);
732     EXPECT_EQ(rv, 0); // It's always 0.
733     bool failed = passive->getFailed();
734     EXPECT_EQ(failed, false);
735     ReadReturn r = passive->read();
736     EXPECT_FALSE(std::isfinite(r.value));
737 }
738 
739 TEST_F(DbusPassiveTestUnaSensorNotAsFailedObj, VerifyAvailableAssert)
740 {
741 
742     // Verifies when a sensor's state goes from unavailble to available
743     // && unavailableAsFailed == false, this sensor remains at OK state.
744     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
745         .WillOnce(Return(nullptr));
746     sdbusplus::message_t msg(nullptr, &sdbus_mock);
747 
748     const char* property = "Available";
749     bool asserted = true;
750     const char* intf = "xyz.openbmc_project.State.Decorator.Availability";
751 
752     passive->setAvailable(false);
753     bool failed = passive->getFailed();
754     EXPECT_EQ(failed, false);
755 
756     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
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             // Read the first parameter, the string.
761             *s = intf;
762             return 0;
763         }))
764         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
765                              [[maybe_unused]] char type, void* p) {
766             const char** s = static_cast<const char**>(p);
767             *s = property;
768             // Read the string in the pair (dictionary).
769             return 0;
770         }));
771 
772     // std::map
773     EXPECT_CALL(sdbus_mock,
774                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
775         .WillOnce(Return(0));
776 
777     // while !at_end()
778     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
779         .WillOnce(Return(0))
780         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
781 
782     // std::pair
783     EXPECT_CALL(sdbus_mock,
784                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
785         .WillOnce(Return(0));
786 
787     EXPECT_CALL(sdbus_mock,
788                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
789         .WillOnce(Return(0));
790     EXPECT_CALL(sdbus_mock,
791                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
792         .WillOnce(Return(0));
793     EXPECT_CALL(sdbus_mock,
794                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
795         .WillOnce(Return(1));
796     EXPECT_CALL(sdbus_mock,
797                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
798         .WillOnce(Return(0));
799 
800     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
801         .WillOnce(Invoke([&]([[maybe_unused]] sd_bus_message* m,
802                              [[maybe_unused]] char type, void* p) {
803             bool* s = static_cast<bool*>(p);
804             *s = asserted;
805             return 0;
806         }));
807 
808     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
809         .WillOnce(Return(0))  /* variant. */
810         .WillOnce(Return(0))  /* std::pair */
811         .WillOnce(Return(0)); /* std::map */
812 
813     int rv = handleSensorValue(msg, passive);
814     EXPECT_EQ(rv, 0); // It's always 0.
815     failed = passive->getFailed();
816     EXPECT_EQ(failed, false);
817 }
818 
819 void GetPropertiesMax3k([[maybe_unused]] const std::string& service,
820                         [[maybe_unused]] const std::string& path,
821                         SensorProperties* prop)
822 {
823     prop->scale = -3;
824     prop->value = 10;
825     prop->unit = "x";
826     prop->min = 0;
827     prop->max = 3000;
828 }
829 
830 using GetPropertiesFunction = std::function<void(
831     const std::string&, const std::string&, SensorProperties*)>;
832 
833 // TODO: There is definitely a cleaner way to do this.
834 class DbusPassiveTest3kMaxObj : public ::testing::Test
835 {
836   protected:
837     DbusPassiveTest3kMaxObj() :
838         sdbus_mock(),
839         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
840         helper(std::make_unique<DbusHelperMock>())
841     {
842         EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
843             .WillOnce(Return("asdf"));
844 
845         EXPECT_CALL(*helper,
846                     getProperties(StrEq("asdf"), StrEq(path), NotNull()))
847             .WillOnce(_getProps);
848         EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
849             .WillOnce(Return(false));
850 
851         auto info = conf::SensorConfig();
852         ri = DbusPassive::createDbusPassive(bus_mock, type, id,
853                                             std::move(helper), &info, nullptr);
854         passive = reinterpret_cast<DbusPassive*>(ri.get());
855         EXPECT_FALSE(passive == nullptr);
856     }
857 
858     sdbusplus::SdBusMock sdbus_mock;
859     sdbusplus::bus_t bus_mock;
860     std::unique_ptr<DbusHelperMock> helper;
861     std::string type = "temp";
862     std::string id = "id";
863     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
864     int64_t _scale = -3;
865     int64_t _value = 10;
866 
867     std::unique_ptr<ReadInterface> ri;
868     DbusPassive* passive;
869     GetPropertiesFunction _getProps = &GetPropertiesMax3k;
870 };
871 
872 TEST_F(DbusPassiveTest3kMaxObj, ReadMinAndMaxReturnsExpected)
873 {
874     EXPECT_DOUBLE_EQ(0, passive->getMin());
875     EXPECT_DOUBLE_EQ(3, passive->getMax());
876 }
877 
878 class DbusPassiveTest3kMaxIgnoredObj : public ::testing::Test
879 {
880   protected:
881     DbusPassiveTest3kMaxIgnoredObj() :
882         sdbus_mock(),
883         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
884         helper(std::make_unique<DbusHelperMock>())
885     {
886         EXPECT_CALL(*helper, getService(StrEq(SensorIntf), StrEq(path)))
887             .WillOnce(Return("asdf"));
888 
889         EXPECT_CALL(*helper,
890                     getProperties(StrEq("asdf"), StrEq(path), NotNull()))
891             .WillOnce(_getProps);
892         EXPECT_CALL(*helper, thresholdsAsserted(StrEq("asdf"), StrEq(path)))
893             .WillOnce(Return(false));
894 
895         auto info = conf::SensorConfig();
896         info.ignoreDbusMinMax = true;
897         ri = DbusPassive::createDbusPassive(bus_mock, type, id,
898                                             std::move(helper), &info, nullptr);
899         passive = reinterpret_cast<DbusPassive*>(ri.get());
900         EXPECT_FALSE(passive == nullptr);
901     }
902 
903     sdbusplus::SdBusMock sdbus_mock;
904     sdbusplus::bus_t bus_mock;
905     std::unique_ptr<DbusHelperMock> helper;
906     std::string type = "temp";
907     std::string id = "id";
908     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
909     int64_t _scale = -3;
910     int64_t _value = 10;
911 
912     std::unique_ptr<ReadInterface> ri;
913     DbusPassive* passive;
914     GetPropertiesFunction _getProps = &GetPropertiesMax3k;
915 };
916 
917 TEST_F(DbusPassiveTest3kMaxIgnoredObj, ReadMinAndMaxReturnsExpected)
918 {
919     EXPECT_DOUBLE_EQ(0, passive->getMin());
920     EXPECT_DOUBLE_EQ(0, passive->getMax());
921 }
922 
923 } // namespace
924 } // namespace pid_control
925