1 #include "dbus/dbuspassive.hpp"
2 #include "test/dbushelper_mock.hpp"
3 
4 #include <sdbusplus/test/sdbus_mock.hpp>
5 #include <string>
6 
7 #include <gmock/gmock.h>
8 #include <gtest/gtest.h>
9 
10 using ::testing::_;
11 using ::testing::InSequence;
12 using ::testing::Invoke;
13 using ::testing::IsNull;
14 using ::testing::NotNull;
15 using ::testing::Return;
16 using ::testing::StrEq;
17 
18 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value";
19 
20 TEST(DbusPassiveTest, FactoryFailsWithInvalidType)
21 {
22     // Verify the type is checked by the factory.
23 
24     sdbusplus::SdBusMock sdbus_mock;
25     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
26     std::string type = "invalid";
27     std::string id = "id";
28 
29     DbusHelperMock helper;
30 
31     std::unique_ptr<ReadInterface> ri =
32         DbusPassive::createDbusPassive(bus_mock, type, id, &helper);
33 
34     EXPECT_EQ(ri, nullptr);
35 }
36 
37 TEST(DbusPassiveTest, BoringConstructorTest)
38 {
39     // Just build the object, which should be avoided as this does no error
40     // checking at present.
41 
42     sdbusplus::SdBusMock sdbus_mock;
43     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
44     std::string type = "invalid";
45     std::string id = "id";
46     std::string path = "/xyz/openbmc_project/sensors/unknown/id";
47 
48     DbusHelperMock helper;
49     EXPECT_CALL(helper, getService(_, StrEq(SensorIntf), StrEq(path)))
50         .WillOnce(Return("asdf"));
51 
52     EXPECT_CALL(helper, getProperties(_, StrEq("asdf"), StrEq(path), NotNull()))
53         .WillOnce(
54             Invoke([&](sdbusplus::bus::bus& bus, const std::string& service,
55                        const std::string& path, struct SensorProperties* prop) {
56                 prop->scale = -3;
57                 prop->value = 10;
58                 prop->unit = "x";
59             }));
60     EXPECT_CALL(helper, thresholdsAsserted(_, StrEq("asdf"), StrEq(path)))
61         .WillOnce(Return(false));
62 
63     DbusPassive(bus_mock, type, id, &helper);
64     // Success
65 }
66 
67 class DbusPassiveTestObj : public ::testing::Test
68 {
69   protected:
70     DbusPassiveTestObj() :
71         sdbus_mock(),
72         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper()
73     {
74         EXPECT_CALL(helper, getService(_, StrEq(SensorIntf), StrEq(path)))
75             .WillOnce(Return("asdf"));
76 
77         EXPECT_CALL(helper,
78                     getProperties(_, StrEq("asdf"), StrEq(path), NotNull()))
79             .WillOnce(Invoke(
80                 [&](sdbusplus::bus::bus& bus, const std::string& service,
81                     const std::string& path, struct SensorProperties* prop) {
82                     prop->scale = _scale;
83                     prop->value = _value;
84                     prop->unit = "x";
85                 }));
86         EXPECT_CALL(helper, thresholdsAsserted(_, StrEq("asdf"), StrEq(path)))
87             .WillOnce(Return(false));
88 
89         ri = DbusPassive::createDbusPassive(bus_mock, type, id, &helper);
90         passive = reinterpret_cast<DbusPassive*>(ri.get());
91         EXPECT_FALSE(passive == nullptr);
92     }
93 
94     sdbusplus::SdBusMock sdbus_mock;
95     sdbusplus::bus::bus bus_mock;
96     DbusHelperMock helper;
97     std::string type = "temp";
98     std::string id = "id";
99     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
100     int64_t _scale = -3;
101     int64_t _value = 10;
102 
103     std::unique_ptr<ReadInterface> ri;
104     DbusPassive* passive;
105 };
106 
107 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues)
108 {
109     // Verify read is returning the values.
110     ReadReturn v;
111     v.value = 0.01;
112     // TODO: updated is set when the value is created, so we can range check
113     // it.
114     ReadReturn r = passive->read();
115     EXPECT_EQ(v.value, r.value);
116 }
117 
118 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue)
119 {
120     // Verify setvalue does as advertised.
121 
122     double value = 0.01;
123     passive->setValue(value);
124 
125     // TODO: updated is set when the value is set, so we can range check it.
126     ReadReturn r = passive->read();
127     EXPECT_EQ(value, r.value);
128 }
129 
130 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue)
131 {
132     // Verify the scale is returned as expected.
133     EXPECT_EQ(_scale, passive->getScale());
134 }
135 
136 TEST_F(DbusPassiveTestObj, getIDReturnsExpectedValue)
137 {
138     // Verify getID returns the expected value.
139     EXPECT_EQ(id, passive->getID());
140 }
141 
142 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal)
143 {
144     // The dbus passive sensor listens for updates and if it's the Value
145     // property, it needs to handle it.
146 
147     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
148         .WillOnce(Return(nullptr));
149     sdbusplus::message::message msg(nullptr, &sdbus_mock);
150 
151     const char* Value = "Value";
152     int64_t xValue = 10000;
153     const char* intf = "xyz.openbmc_project.Sensor.Value";
154     // string, std::map<std::string, sdbusplus::message::variant<int64_t>>
155     // msg.read(msgSensor, msgData);
156 
157     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
158         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
159             const char** s = static_cast<const char**>(p);
160             // Read the first parameter, the string.
161             *s = intf;
162             return 0;
163         }))
164         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
165             const char** s = static_cast<const char**>(p);
166             *s = Value;
167             // Read the string in the pair (dictionary).
168             return 0;
169         }));
170 
171     // std::map
172     EXPECT_CALL(sdbus_mock,
173                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
174         .WillOnce(Return(0));
175 
176     // while !at_end()
177     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
178         .WillOnce(Return(0))
179         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
180 
181     // std::pair
182     EXPECT_CALL(sdbus_mock,
183                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
184         .WillOnce(Return(0));
185 
186     EXPECT_CALL(sdbus_mock,
187                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
188         .WillOnce(Return(1));
189     EXPECT_CALL(sdbus_mock,
190                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
191         .WillOnce(Return(0));
192 
193     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
194         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
195             int64_t* s = static_cast<int64_t*>(p);
196             *s = xValue;
197             return 0;
198         }));
199 
200     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
201         .WillOnce(Return(0))  /* variant. */
202         .WillOnce(Return(0))  /* std::pair */
203         .WillOnce(Return(0)); /* std::map */
204 
205     int rv = handleSensorValue(msg, passive);
206     EXPECT_EQ(rv, 0); // It's always 0.
207 
208     ReadReturn r = passive->read();
209     EXPECT_EQ(10, r.value);
210 }
211 
212 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal)
213 {
214     // The dbus passive sensor listens for updates and if it's the Value
215     // property, it needs to handle it.  In this case, it won't be.
216 
217     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
218         .WillOnce(Return(nullptr));
219     sdbusplus::message::message msg(nullptr, &sdbus_mock);
220 
221     const char* Scale = "Scale";
222     int64_t xScale = -6;
223     const char* intf = "xyz.openbmc_project.Sensor.Value";
224     // string, std::map<std::string, sdbusplus::message::variant<int64_t>>
225     // msg.read(msgSensor, msgData);
226 
227     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
228         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
229             const char** s = static_cast<const char**>(p);
230             // Read the first parameter, the string.
231             *s = intf;
232             return 0;
233         }))
234         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
235             const char** s = static_cast<const char**>(p);
236             *s = Scale;
237             // Read the string in the pair (dictionary).
238             return 0;
239         }));
240 
241     // std::map
242     EXPECT_CALL(sdbus_mock,
243                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
244         .WillOnce(Return(0));
245 
246     // while !at_end()
247     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
248         .WillOnce(Return(0))
249         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
250 
251     // std::pair
252     EXPECT_CALL(sdbus_mock,
253                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
254         .WillOnce(Return(0));
255 
256     EXPECT_CALL(sdbus_mock,
257                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
258         .WillOnce(Return(1));
259     EXPECT_CALL(sdbus_mock,
260                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
261         .WillOnce(Return(0));
262 
263     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
264         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
265             int64_t* s = static_cast<int64_t*>(p);
266             *s = xScale;
267             return 0;
268         }));
269 
270     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
271         .WillOnce(Return(0))  /* variant. */
272         .WillOnce(Return(0))  /* std::pair */
273         .WillOnce(Return(0)); /* std::map */
274 
275     int rv = handleSensorValue(msg, passive);
276     EXPECT_EQ(rv, 0); // It's always 0.
277 
278     ReadReturn r = passive->read();
279     EXPECT_EQ(0.01, r.value);
280 }
281 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdAssert)
282 {
283 
284     // Verifies when a threshold is crossed the sensor goes into error state
285     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
286         .WillOnce(Return(nullptr));
287     sdbusplus::message::message msg(nullptr, &sdbus_mock);
288 
289     const char* criticalAlarm = "CriticalAlarmHigh";
290     bool alarm = true;
291     const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
292 
293     passive->setFailed(false);
294 
295     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
296         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
297             const char** s = static_cast<const char**>(p);
298             // Read the first parameter, the string.
299             *s = intf;
300             return 0;
301         }))
302         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
303             const char** s = static_cast<const char**>(p);
304             *s = criticalAlarm;
305             // Read the string in the pair (dictionary).
306             return 0;
307         }));
308 
309     // std::map
310     EXPECT_CALL(sdbus_mock,
311                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
312         .WillOnce(Return(0));
313 
314     // while !at_end()
315     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
316         .WillOnce(Return(0))
317         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
318 
319     // std::pair
320     EXPECT_CALL(sdbus_mock,
321                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
322         .WillOnce(Return(0));
323 
324     EXPECT_CALL(sdbus_mock,
325                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
326         .WillOnce(Return(0));
327     EXPECT_CALL(sdbus_mock,
328                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
329         .WillOnce(Return(0));
330     EXPECT_CALL(sdbus_mock,
331                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
332         .WillOnce(Return(1));
333     EXPECT_CALL(sdbus_mock,
334                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
335         .WillOnce(Return(0));
336 
337     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
338         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
339             bool* s = static_cast<bool*>(p);
340             *s = alarm;
341             return 0;
342         }));
343 
344     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
345         .WillOnce(Return(0))  /* variant. */
346         .WillOnce(Return(0))  /* std::pair */
347         .WillOnce(Return(0)); /* std::map */
348 
349     int rv = handleSensorValue(msg, passive);
350     EXPECT_EQ(rv, 0); // It's always 0.
351     bool failed = passive->getFailed();
352     EXPECT_EQ(failed, true);
353 }
354 
355 TEST_F(DbusPassiveTestObj, VerifyCriticalThresholdDeassert)
356 {
357 
358     // Verifies when a threshold is deasserted a failed sensor goes back into
359     // the normal state
360     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
361         .WillOnce(Return(nullptr));
362     sdbusplus::message::message msg(nullptr, &sdbus_mock);
363 
364     const char* criticalAlarm = "CriticalAlarmHigh";
365     bool alarm = false;
366     const char* intf = "xyz.openbmc_project.Sensor.Threshold.Critical";
367 
368     passive->setFailed(true);
369 
370     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
371         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
372             const char** s = static_cast<const char**>(p);
373             // Read the first parameter, the string.
374             *s = intf;
375             return 0;
376         }))
377         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
378             const char** s = static_cast<const char**>(p);
379             *s = criticalAlarm;
380             // Read the string in the pair (dictionary).
381             return 0;
382         }));
383 
384     // std::map
385     EXPECT_CALL(sdbus_mock,
386                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
387         .WillOnce(Return(0));
388 
389     // while !at_end()
390     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
391         .WillOnce(Return(0))
392         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
393 
394     // std::pair
395     EXPECT_CALL(sdbus_mock,
396                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
397         .WillOnce(Return(0));
398 
399     EXPECT_CALL(sdbus_mock,
400                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
401         .WillOnce(Return(0));
402     EXPECT_CALL(sdbus_mock,
403                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("d")))
404         .WillOnce(Return(0));
405     EXPECT_CALL(sdbus_mock,
406                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("b")))
407         .WillOnce(Return(1));
408     EXPECT_CALL(sdbus_mock,
409                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("b")))
410         .WillOnce(Return(0));
411 
412     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'b', NotNull()))
413         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
414             bool* s = static_cast<bool*>(p);
415             *s = alarm;
416             return 0;
417         }));
418 
419     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
420         .WillOnce(Return(0))  /* variant. */
421         .WillOnce(Return(0))  /* std::pair */
422         .WillOnce(Return(0)); /* std::map */
423 
424     int rv = handleSensorValue(msg, passive);
425     EXPECT_EQ(rv, 0); // It's always 0.
426     bool failed = passive->getFailed();
427     EXPECT_EQ(failed, false);
428 }
429