1 #include "propertywatchtest.hpp"
2 
3 #include "propertywatchimpl.hpp"
4 
5 #include <array>
6 #include <functional>
7 
8 using namespace std::string_literals;
9 using namespace phosphor::dbus::monitoring;
10 
11 const std::array<std::string, 4> paths = {
12     "/xyz/openbmc_project/testing/inst1"s,
13     "/xyz/openbmc_project/testing/inst2"s,
14     "/xyz/openbmc_project/testing/inst3"s,
15     "/xyz/openbmc_project/testing/inst4"s,
16 };
17 
18 const std::array<std::string, 2> interfaces = {
19     "xyz.openbmc_project.Iface1"s,
20     "xyz.openbmc_project.Iface2"s,
21 };
22 
23 const std::array<std::string, 2> properties = {
24     "Value1"s,
25     "Value2"s,
26 };
27 
28 const std::string meta;
29 
30 std::array<std::tuple<std::any, std::any>, 8> storage = {};
31 
32 const PropertyIndex watchIndex = {
33     {
34         {PropertyIndex::key_type{paths[0], interfaces[0], properties[0]},
35          PropertyIndex::mapped_type{meta, meta, storage[0]}},
36         {PropertyIndex::key_type{paths[0], interfaces[1], properties[1]},
37          PropertyIndex::mapped_type{meta, meta, storage[1]}},
38         {PropertyIndex::key_type{paths[1], interfaces[0], properties[0]},
39          PropertyIndex::mapped_type{meta, meta, storage[2]}},
40         {PropertyIndex::key_type{paths[1], interfaces[1], properties[1]},
41          PropertyIndex::mapped_type{meta, meta, storage[3]}},
42         {PropertyIndex::key_type{paths[2], interfaces[0], properties[0]},
43          PropertyIndex::mapped_type{meta, meta, storage[4]}},
44         {PropertyIndex::key_type{paths[2], interfaces[1], properties[1]},
45          PropertyIndex::mapped_type{meta, meta, storage[5]}},
46         {PropertyIndex::key_type{paths[3], interfaces[0], properties[0]},
47          PropertyIndex::mapped_type{meta, meta, storage[6]}},
48         {PropertyIndex::key_type{paths[3], interfaces[1], properties[1]},
49          PropertyIndex::mapped_type{meta, meta, storage[7]}},
50     },
51 };
52 
53 template <typename T>
54 struct Values
55 {
56 };
57 template <>
58 struct Values<uint8_t>
59 {
60     static auto& get(size_t i)
61     {
62         static const std::array<uint8_t, 8> values = {
63             {0, 1, 2, 3, 4, 5, 6, 7},
64         };
65         return values[i];
66     }
67 };
68 
69 template <>
70 struct Values<uint16_t>
71 {
72     static auto& get(size_t i)
73     {
74         static const std::array<uint16_t, 8> values = {
75             {88, 77, 66, 55, 44, 33, 22, 11},
76         };
77         return values[i];
78     }
79 };
80 
81 template <>
82 struct Values<uint32_t>
83 {
84     static auto& get(size_t i)
85     {
86         static const std::array<uint32_t, 8> values = {
87             {0xffffffff, 1, 3, 0, 5, 7, 9, 0xffffffff},
88         };
89         return values[i];
90     }
91 };
92 
93 template <>
94 struct Values<uint64_t>
95 {
96     static auto& get(size_t i)
97     {
98         static const std::array<uint64_t, 8> values = {
99             {0xffffffffffffffff, 3, 7, 12234, 0, 3, 9, 0xffffffff},
100         };
101         return values[i];
102     }
103 };
104 
105 template <>
106 struct Values<std::string>
107 {
108     static auto& get(size_t i)
109     {
110         static const std::array<std::string, 8> values = {
111             {""s, "foo"s, "bar"s, "baz"s, "hello"s, "string", "\x2\x3", "\\"},
112         };
113         return values[i];
114     }
115 };
116 
117 template <typename T>
118 void nonFilteredCheck(const std::any& value, const size_t ndx)
119 {
120     ASSERT_EQ(value.has_value(), true);
121     ASSERT_EQ(std::any_cast<T>(value), Values<T>::get(ndx));
122 }
123 
124 template <typename T>
125 struct FilteredValues
126 {
127 };
128 
129 template <>
130 struct FilteredValues<uint8_t>
131 {
132     static auto& opFilters()
133     {
134         static std::unique_ptr<OperandFilters<uint8_t>> filters =
135             std::make_unique<OperandFilters<uint8_t>>(
136                 std::vector<std::function<bool(uint8_t)>>{
137                     [](const auto& value) { return value < 4; }});
138         return filters;
139     }
140     static auto& expected(size_t i)
141     {
142         static const std::array<std::any, 8> values = {
143             {std::any(uint8_t(0)), std::any(uint8_t(1)), std::any(uint8_t(2)),
144              std::any(uint8_t(3)), std::any(), std::any(), std::any(),
145              std::any()}};
146         return values[i];
147     }
148 };
149 
150 template <>
151 struct FilteredValues<uint16_t>
152 {
153     static auto& opFilters()
154     {
155         static std::unique_ptr<OperandFilters<uint16_t>> filters =
156             std::make_unique<OperandFilters<uint16_t>>(
157                 std::vector<std::function<bool(uint16_t)>>{
158                     [](const auto& value) { return value > 44; },
159                     [](const auto& value) { return value != 88; }});
160         return filters;
161     }
162     static auto& expected(size_t i)
163     {
164         static const std::array<std::any, 8> values = {
165             {std::any(), std::any(uint16_t(77)), std::any(uint16_t(66)),
166              std::any(uint16_t(55)), std::any(), std::any(), std::any(),
167              std::any()}};
168         return values[i];
169     }
170 };
171 
172 template <>
173 struct FilteredValues<uint32_t>
174 {
175     static auto& opFilters()
176     {
177         static std::unique_ptr<OperandFilters<uint32_t>> filters =
178             std::make_unique<OperandFilters<uint32_t>>(
179                 std::vector<std::function<bool(uint32_t)>>{
180                     [](const auto& value) { return value != 0xffffffff; },
181                     [](const auto& value) { return value != 0; }});
182         return filters;
183     }
184     static auto& expected(size_t i)
185     {
186         static const std::array<std::any, 8> values = {
187             {std::any(), std::any(uint32_t(1)), std::any(uint32_t(3)),
188              std::any(), std::any(uint32_t(5)), std::any(uint32_t(7)),
189              std::any(uint32_t(9)), std::any()}};
190         return values[i];
191     }
192 };
193 
194 template <>
195 struct FilteredValues<uint64_t>
196 {
197     static auto& opFilters()
198     {
199         static std::unique_ptr<OperandFilters<uint64_t>> filters =
200             std::make_unique<OperandFilters<uint64_t>>(
201                 std::vector<std::function<bool(uint64_t)>>{
202                     [](const auto& value) { return (value % 3) != 0; }});
203         return filters;
204     }
205     static auto& expected(size_t i)
206     {
207         static const std::array<std::any, 8> values = {
208             {std::any(), std::any(), std::any(uint64_t(7)), std::any(),
209              std::any(), std::any(), std::any(), std::any()}};
210         return values[i];
211     }
212 };
213 
214 template <>
215 struct FilteredValues<std::string>
216 {
217     static auto& opFilters()
218     {
219         static std::unique_ptr<OperandFilters<std::string>> filters =
220             std::make_unique<OperandFilters<std::string>>(
221                 std::vector<std::function<bool(std::string)>>{
222                     [](const auto& value) { return value != ""s; },
223                     [](const auto& value) { return value != "string"s; }});
224         return filters;
225     }
226     static auto& expected(size_t i)
227     {
228         static const std::array<std::any, 8> values = {
229             {std::any(), std::any("foo"s), std::any("bar"s), std::any("baz"s),
230              std::any("hello"s), std::any(), std::any("\x2\x3"s),
231              std::any("\\"s)}};
232         return values[i];
233     }
234 };
235 
236 template <typename T>
237 void filteredCheck(const std::any& value, const size_t ndx)
238 {
239     ASSERT_EQ(value.has_value(), FilteredValues<T>::expected(ndx).has_value());
240     if (value.has_value())
241     {
242         ASSERT_EQ(std::any_cast<T>(value),
243                   std::any_cast<T>(FilteredValues<T>::expected(ndx)));
244     }
245 }
246 
247 template <typename T>
248 void testStart(std::function<void(const std::any&, const size_t)>&& checkState,
249                OperandFilters<T>* opFilters = nullptr)
250 {
251     using ::testing::_;
252     using ::testing::Return;
253 
254     MockDBusInterface dbus;
255     MockDBusInterface::instance(dbus);
256 
257     const std::vector<std::string> expectedMapperInterfaces;
258     PropertyWatchOfType<T, MockDBusInterface> watch(watchIndex, false,
259                                                     opFilters);
260 
261     auto ndx = static_cast<size_t>(0);
262     for (const auto& o : convert(watchIndex))
263     {
264         const auto& path = o.first.get();
265         const auto& interfaces = o.second;
266         std::vector<std::string> mapperResponse;
267         std::transform(interfaces.begin(), interfaces.end(),
268                        std::back_inserter(mapperResponse),
269                        // *INDENT-OFF*
270                        [](const auto& item) { return item.first; });
271         // *INDENT-ON*
272         EXPECT_CALL(dbus, mapperGetObject(MAPPER_BUSNAME, MAPPER_PATH,
273                                           MAPPER_INTERFACE, "GetObject", path,
274                                           expectedMapperInterfaces))
275             .WillOnce(Return(GetObject({{"", mapperResponse}})));
276         EXPECT_CALL(
277             dbus, fwdAddMatch(
278                       sdbusplus::bus::match::rules::interfacesAdded(path), _));
279         for (const auto& i : interfaces)
280         {
281             const auto& interface = i.first.get();
282             const auto& properties = i.second;
283             EXPECT_CALL(
284                 dbus,
285                 fwdAddMatch(sdbusplus::bus::match::rules::propertiesChanged(
286                                 path, interface),
287                             _));
288 
289             PropertiesChanged<T> serviceResponse;
290             for (const auto& p : properties)
291             {
292                 serviceResponse[p] = Values<T>::get(ndx);
293                 ++ndx;
294             }
295             Expect<T>::getProperties(dbus, path, interface)
296                 .WillOnce(Return(serviceResponse));
297         }
298     }
299 
300     watch.start();
301 
302     ndx = 0;
303     for (auto s : storage)
304     {
305         checkState(std::get<valueIndex>(s), ndx);
306         ++ndx;
307     }
308 
309     // Make sure start logic only runs the first time.
310     watch.start();
311 }
312 
313 TEST(PropertyWatchTest, TestStart)
314 {
315     testStart<uint8_t>(nonFilteredCheck<uint8_t>);
316     testStart<uint16_t>(nonFilteredCheck<uint16_t>);
317     testStart<uint32_t>(nonFilteredCheck<uint32_t>);
318     testStart<uint64_t>(nonFilteredCheck<uint64_t>);
319     testStart<std::string>(nonFilteredCheck<std::string>);
320 }
321 
322 TEST(PropertyWatchTest, TestFilters)
323 {
324     testStart<uint8_t>(filteredCheck<uint8_t>,
325                        FilteredValues<uint8_t>::opFilters().get());
326     testStart<uint16_t>(filteredCheck<uint16_t>,
327                         FilteredValues<uint16_t>::opFilters().get());
328     testStart<uint32_t>(filteredCheck<uint32_t>,
329                         FilteredValues<uint32_t>::opFilters().get());
330     testStart<uint64_t>(filteredCheck<uint64_t>,
331                         FilteredValues<uint64_t>::opFilters().get());
332     testStart<std::string>(filteredCheck<std::string>,
333                            FilteredValues<std::string>::opFilters().get());
334 }
335 
336 MockDBusInterface* MockDBusInterface::ptr = nullptr;
337