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