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