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