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