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