1 #include "dbus/dbuspassive.hpp"
2 
3 #include <gmock/gmock.h>
4 #include <gtest/gtest.h>
5 #include <sdbusplus/test/sdbus_mock.hpp>
6 #include <string>
7 
8 #include "test/dbushelper_mock.hpp"
9 
10 using ::testing::InSequence;
11 using ::testing::Invoke;
12 using ::testing::IsNull;
13 using ::testing::NotNull;
14 using ::testing::Return;
15 using ::testing::StrEq;
16 using ::testing::_;
17 
18 std::string SensorIntf = "xyz.openbmc_project.Sensor.Value";
19 
20 TEST(DbusPassiveTest, FactoryFailsWithInvalidType) {
21     // Verify the type is checked by the factory.
22 
23     sdbusplus::SdBusMock sdbus_mock;
24     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
25     std::string type = "invalid";
26     std::string id = "id";
27 
28     DbusHelperMock helper;
29 
30     std::unique_ptr<ReadInterface> ri =
31         DbusPassive::CreateDbusPassive(bus_mock, type, id, &helper);
32 
33     EXPECT_EQ(ri, nullptr);
34 }
35 
36 TEST(DbusPassiveTest, BoringConstructorTest) {
37     // Just build the object, which should be avoided as this does no error
38     // checking at present.
39 
40     sdbusplus::SdBusMock sdbus_mock;
41     auto bus_mock = sdbusplus::get_mocked_new(&sdbus_mock);
42     std::string type = "invalid";
43     std::string id = "id";
44     std::string path = "/xyz/openbmc_project/sensors/unknown/id";
45 
46     DbusHelperMock helper;
47     EXPECT_CALL(helper, GetService(_, StrEq(SensorIntf), StrEq(path)))
48         .WillOnce(Return("asdf"));
49 
50     EXPECT_CALL(helper, GetProperties(_, StrEq("asdf"), StrEq(path),
51                                       NotNull()))
52         .WillOnce(Invoke([&](sdbusplus::bus::bus& bus,
53                              const std::string& service,
54                              const std::string& path,
55                              struct SensorProperties* prop)  {
56             prop->scale = -3;
57             prop->value = 10;
58             prop->unit = "x";
59         }));
60 
61     DbusPassive(bus_mock, type, id, &helper);
62     // Success
63 }
64 
65 class DbusPassiveTestObj : public ::testing::Test {
66     protected:
67         DbusPassiveTestObj()
68         : sdbus_mock(),
69           bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))),
70           helper()
71         {
72             EXPECT_CALL(helper, GetService(_, StrEq(SensorIntf), StrEq(path)))
73                 .WillOnce(Return("asdf"));
74 
75             EXPECT_CALL(helper, GetProperties(_, StrEq("asdf"), StrEq(path),
76                                              NotNull()))
77                 .WillOnce(Invoke([&](sdbusplus::bus::bus& bus,
78                                      const std::string& service,
79                                      const std::string& path,
80                                      struct SensorProperties* prop)  {
81                     prop->scale = _scale;
82                     prop->value = _value;
83                     prop->unit = "x";
84                 }));
85 
86             ri = DbusPassive::CreateDbusPassive(bus_mock, type, id, &helper);
87             passive = reinterpret_cast<DbusPassive*>(ri.get());
88             EXPECT_FALSE(passive == nullptr);
89         }
90 
91         sdbusplus::SdBusMock sdbus_mock;
92         sdbusplus::bus::bus bus_mock;
93         DbusHelperMock helper;
94         std::string type = "temp";
95         std::string id = "id";
96         std::string path = "/xyz/openbmc_project/sensors/temperature/id";
97         int64_t _scale = -3;
98         int64_t _value = 10;
99 
100         std::unique_ptr<ReadInterface> ri;
101         DbusPassive *passive;
102 };
103 
104 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues) {
105     // Verify read is returning the values.
106     ReadReturn v;
107     v.value = 0.01;
108     // TODO: updated is set when the value is created, so we can range check
109     // it.
110     ReadReturn r = passive->read();
111     EXPECT_EQ(v.value, r.value);
112 }
113 
114 TEST_F(DbusPassiveTestObj, SetValueUpdatesValue) {
115     // Verify setvalue does as advertised.
116 
117     double value = 0.01;
118     passive->setValue(value);
119 
120     // TODO: updated is set when the value is set, so we can range check it.
121     ReadReturn r = passive->read();
122     EXPECT_EQ(value, r.value);
123 }
124 
125 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue) {
126     // Verify the scale is returned as expected.
127     EXPECT_EQ(_scale, passive->getScale());
128 }
129 
130 TEST_F(DbusPassiveTestObj, GetIdReturnsExpectedValue) {
131     // Verify getId returns the expected value.
132     EXPECT_EQ(id, passive->getId());
133 }
134 
135 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal) {
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, sdbusplus::message::variant<int64_t>>
147     // msg.read(msgSensor, msgData);
148 
149     EXPECT_CALL(sdbus_mock,
150                 sd_bus_message_read_basic(IsNull(), 's', NotNull()))
151         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
152             const char **s = static_cast<const char **>(p);
153             // Read the first parameter, the string.
154             *s = intf;
155             return 0;
156         }))
157         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
158             const char **s = static_cast<const char **>(p);
159             *s = Value;
160             // Read the string in the pair (dictionary).
161             return 0;
162         }));
163 
164     // std::map
165     EXPECT_CALL(sdbus_mock,
166                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
167         .WillOnce(Return(0));
168 
169     // while !at_end()
170     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
171         .WillOnce(Return(0))
172         .WillOnce(Return(1));  // So it exits the loop after reading one pair.
173 
174     // std::pair
175     EXPECT_CALL(sdbus_mock,
176                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
177         .WillOnce(Return(0));
178 
179     EXPECT_CALL(sdbus_mock,
180                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
181         .WillOnce(Return(1));
182     EXPECT_CALL(sdbus_mock,
183                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
184         .WillOnce(Return(0));
185 
186     EXPECT_CALL(sdbus_mock,
187                 sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
188         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
189             int64_t *s = static_cast<int64_t *>(p);
190             *s = xValue;
191             return 0;
192         }));
193 
194     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
195         .WillOnce(Return(0))  /* variant. */
196         .WillOnce(Return(0))  /* std::pair */
197         .WillOnce(Return(0)); /* std::map */
198 
199     int rv = HandleSensorValue(msg, passive);
200     EXPECT_EQ(rv, 0); // It's always 0.
201 
202     ReadReturn r = passive->read();
203     EXPECT_EQ(10, r.value);
204 }
205 
206 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal) {
207     // The dbus passive sensor listens for updates and if it's the Value
208     // property, it needs to handle it.  In this case, it won't be.
209 
210     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
211         .WillOnce(Return(nullptr));
212     sdbusplus::message::message msg(nullptr, &sdbus_mock);
213 
214     const char *Scale = "Scale";
215     int64_t xScale = -6;
216     const char *intf = "xyz.openbmc_project.Sensor.Value";
217     // string, std::map<std::string, sdbusplus::message::variant<int64_t>>
218     // msg.read(msgSensor, msgData);
219 
220     EXPECT_CALL(sdbus_mock,
221                 sd_bus_message_read_basic(IsNull(), 's', NotNull()))
222         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
223             const char **s = static_cast<const char **>(p);
224             // Read the first parameter, the string.
225             *s = intf;
226             return 0;
227         }))
228         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
229             const char **s = static_cast<const char **>(p);
230             *s = Scale;
231             // Read the string in the pair (dictionary).
232             return 0;
233         }));
234 
235     // std::map
236     EXPECT_CALL(sdbus_mock,
237                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
238         .WillOnce(Return(0));
239 
240     // while !at_end()
241     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
242         .WillOnce(Return(0))
243         .WillOnce(Return(1));  // So it exits the loop after reading one pair.
244 
245     // std::pair
246     EXPECT_CALL(sdbus_mock,
247                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
248         .WillOnce(Return(0));
249 
250     EXPECT_CALL(sdbus_mock,
251                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
252         .WillOnce(Return(1));
253     EXPECT_CALL(sdbus_mock,
254                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
255         .WillOnce(Return(0));
256 
257     EXPECT_CALL(sdbus_mock,
258                 sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
259         .WillOnce(Invoke([&](sd_bus_message *m, char type, void *p) {
260             int64_t *s = static_cast<int64_t *>(p);
261             *s = xScale;
262             return 0;
263         }));
264 
265     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
266         .WillOnce(Return(0))  /* variant. */
267         .WillOnce(Return(0))  /* std::pair */
268         .WillOnce(Return(0)); /* std::map */
269 
270     int rv = HandleSensorValue(msg, passive);
271     EXPECT_EQ(rv, 0); // It's always 0.
272 
273     ReadReturn r = passive->read();
274     EXPECT_EQ(0.01, r.value);
275 }
276