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