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