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