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