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