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     // Just build the object, which should be avoided as this does no error
40     // checking at present.
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     EXPECT_CALL(helper, GetService(_, StrEq(SensorIntf), StrEq(path)))
50         .WillOnce(Return("asdf"));
51 
52     EXPECT_CALL(helper, GetProperties(_, StrEq("asdf"), StrEq(path), NotNull()))
53         .WillOnce(
54             Invoke([&](sdbusplus::bus::bus& bus, const std::string& service,
55                        const std::string& path, 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 {
67   protected:
68     DbusPassiveTestObj() :
69         sdbus_mock(),
70         bus_mock(std::move(sdbusplus::get_mocked_new(&sdbus_mock))), helper()
71     {
72         EXPECT_CALL(helper, GetService(_, StrEq(SensorIntf), StrEq(path)))
73             .WillOnce(Return("asdf"));
74 
75         EXPECT_CALL(helper,
76                     GetProperties(_, StrEq("asdf"), StrEq(path), NotNull()))
77             .WillOnce(Invoke(
78                 [&](sdbusplus::bus::bus& bus, const std::string& service,
79                     const std::string& path, struct SensorProperties* prop) {
80                     prop->scale = _scale;
81                     prop->value = _value;
82                     prop->unit = "x";
83                 }));
84 
85         ri = DbusPassive::CreateDbusPassive(bus_mock, type, id, &helper);
86         passive = reinterpret_cast<DbusPassive*>(ri.get());
87         EXPECT_FALSE(passive == nullptr);
88     }
89 
90     sdbusplus::SdBusMock sdbus_mock;
91     sdbusplus::bus::bus bus_mock;
92     DbusHelperMock helper;
93     std::string type = "temp";
94     std::string id = "id";
95     std::string path = "/xyz/openbmc_project/sensors/temperature/id";
96     int64_t _scale = -3;
97     int64_t _value = 10;
98 
99     std::unique_ptr<ReadInterface> ri;
100     DbusPassive* passive;
101 };
102 
103 TEST_F(DbusPassiveTestObj, ReadReturnsExpectedValues)
104 {
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 {
116     // Verify setvalue does as advertised.
117 
118     double value = 0.01;
119     passive->setValue(value);
120 
121     // TODO: updated is set when the value is set, so we can range check it.
122     ReadReturn r = passive->read();
123     EXPECT_EQ(value, r.value);
124 }
125 
126 TEST_F(DbusPassiveTestObj, GetScaleReturnsExpectedValue)
127 {
128     // Verify the scale is returned as expected.
129     EXPECT_EQ(_scale, passive->getScale());
130 }
131 
132 TEST_F(DbusPassiveTestObj, GetIdReturnsExpectedValue)
133 {
134     // Verify getId returns the expected value.
135     EXPECT_EQ(id, passive->getId());
136 }
137 
138 TEST_F(DbusPassiveTestObj, VerifyHandlesDbusSignal)
139 {
140     // The dbus passive sensor listens for updates and if it's the Value
141     // property, it needs to handle it.
142 
143     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
144         .WillOnce(Return(nullptr));
145     sdbusplus::message::message msg(nullptr, &sdbus_mock);
146 
147     const char* Value = "Value";
148     int64_t xValue = 10000;
149     const char* intf = "xyz.openbmc_project.Sensor.Value";
150     // string, std::map<std::string, sdbusplus::message::variant<int64_t>>
151     // msg.read(msgSensor, msgData);
152 
153     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
154         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
155             const char** s = static_cast<const char**>(p);
156             // Read the first parameter, the string.
157             *s = intf;
158             return 0;
159         }))
160         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
161             const char** s = static_cast<const char**>(p);
162             *s = Value;
163             // Read the string in the pair (dictionary).
164             return 0;
165         }));
166 
167     // std::map
168     EXPECT_CALL(sdbus_mock,
169                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
170         .WillOnce(Return(0));
171 
172     // while !at_end()
173     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
174         .WillOnce(Return(0))
175         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
176 
177     // std::pair
178     EXPECT_CALL(sdbus_mock,
179                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
180         .WillOnce(Return(0));
181 
182     EXPECT_CALL(sdbus_mock,
183                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
184         .WillOnce(Return(1));
185     EXPECT_CALL(sdbus_mock,
186                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
187         .WillOnce(Return(0));
188 
189     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
190         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
191             int64_t* s = static_cast<int64_t*>(p);
192             *s = xValue;
193             return 0;
194         }));
195 
196     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
197         .WillOnce(Return(0))  /* variant. */
198         .WillOnce(Return(0))  /* std::pair */
199         .WillOnce(Return(0)); /* std::map */
200 
201     int rv = HandleSensorValue(msg, passive);
202     EXPECT_EQ(rv, 0); // It's always 0.
203 
204     ReadReturn r = passive->read();
205     EXPECT_EQ(10, r.value);
206 }
207 
208 TEST_F(DbusPassiveTestObj, VerifyIgnoresOtherPropertySignal)
209 {
210     // The dbus passive sensor listens for updates and if it's the Value
211     // property, it needs to handle it.  In this case, it won't be.
212 
213     EXPECT_CALL(sdbus_mock, sd_bus_message_ref(IsNull()))
214         .WillOnce(Return(nullptr));
215     sdbusplus::message::message msg(nullptr, &sdbus_mock);
216 
217     const char* Scale = "Scale";
218     int64_t xScale = -6;
219     const char* intf = "xyz.openbmc_project.Sensor.Value";
220     // string, std::map<std::string, sdbusplus::message::variant<int64_t>>
221     // msg.read(msgSensor, msgData);
222 
223     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 's', NotNull()))
224         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
225             const char** s = static_cast<const char**>(p);
226             // Read the first parameter, the string.
227             *s = intf;
228             return 0;
229         }))
230         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
231             const char** s = static_cast<const char**>(p);
232             *s = Scale;
233             // Read the string in the pair (dictionary).
234             return 0;
235         }));
236 
237     // std::map
238     EXPECT_CALL(sdbus_mock,
239                 sd_bus_message_enter_container(IsNull(), 'a', StrEq("{sv}")))
240         .WillOnce(Return(0));
241 
242     // while !at_end()
243     EXPECT_CALL(sdbus_mock, sd_bus_message_at_end(IsNull(), 0))
244         .WillOnce(Return(0))
245         .WillOnce(Return(1)); // So it exits the loop after reading one pair.
246 
247     // std::pair
248     EXPECT_CALL(sdbus_mock,
249                 sd_bus_message_enter_container(IsNull(), 'e', StrEq("sv")))
250         .WillOnce(Return(0));
251 
252     EXPECT_CALL(sdbus_mock,
253                 sd_bus_message_verify_type(IsNull(), 'v', StrEq("x")))
254         .WillOnce(Return(1));
255     EXPECT_CALL(sdbus_mock,
256                 sd_bus_message_enter_container(IsNull(), 'v', StrEq("x")))
257         .WillOnce(Return(0));
258 
259     EXPECT_CALL(sdbus_mock, sd_bus_message_read_basic(IsNull(), 'x', NotNull()))
260         .WillOnce(Invoke([&](sd_bus_message* m, char type, void* p) {
261             int64_t* s = static_cast<int64_t*>(p);
262             *s = xScale;
263             return 0;
264         }));
265 
266     EXPECT_CALL(sdbus_mock, sd_bus_message_exit_container(IsNull()))
267         .WillOnce(Return(0))  /* variant. */
268         .WillOnce(Return(0))  /* std::pair */
269         .WillOnce(Return(0)); /* std::map */
270 
271     int rv = HandleSensorValue(msg, passive);
272     EXPECT_EQ(rv, 0); // It's always 0.
273 
274     ReadReturn r = passive->read();
275     EXPECT_EQ(0.01, r.value);
276 }
277