blob: e4b6a4544a69ef050371597320be08836ec387ab [file] [log] [blame]
Brad Bishop49aefb32016-10-19 11:54:14 -04001/**
2 * Copyright © 2016 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Brad Bishope30dd772016-11-12 21:39:28 -050016#include "../manager.hpp"
Brad Bishop49aefb32016-10-19 11:54:14 -040017#include "../config.h"
18#include <cassert>
Brad Bishopabb2a1a2016-11-30 22:02:28 -050019#include <iostream>
20#include <algorithm>
Brad Bishop21621432017-01-13 16:35:53 -050021#include <thread>
Brad Bishop8f868502017-01-23 13:13:58 -050022#include <chrono>
23#include <xyz/openbmc_project/Example/Iface1/server.hpp>
24#include <xyz/openbmc_project/Example/Iface2/server.hpp>
Brad Bishop49aefb32016-10-19 11:54:14 -040025
Brad Bishop8f868502017-01-23 13:13:58 -050026using namespace std::literals::chrono_literals;
27using namespace std::literals::string_literals;
28
29constexpr auto MGR_SERVICE = "phosphor.inventory.test.mgr";
30constexpr auto MGR_INTERFACE = IFACE;
31constexpr auto MGR_ROOT = "/testing/inventory";
32constexpr auto EXAMPLE_SERVICE = "phosphor.inventory.test.example";
33constexpr auto EXAMPLE_ROOT = "/testing";
34
35const auto trigger1 = sdbusplus::message::object_path(EXAMPLE_ROOT +
36 "/trigger1"s);
37const auto trigger2 = sdbusplus::message::object_path(EXAMPLE_ROOT +
38 "/trigger2"s);
Brad Bishopfb083c22017-01-19 09:22:04 -050039const auto trigger3 = sdbusplus::message::object_path(EXAMPLE_ROOT +
40 "/trigger3"s);
Brad Bishopfa51da72017-01-19 11:06:51 -050041const auto trigger4 = sdbusplus::message::object_path(EXAMPLE_ROOT +
42 "/trigger4"s);
43
Brad Bishopfb083c22017-01-19 09:22:04 -050044
45const sdbusplus::message::object_path relDeleteMeOne{"/deleteme1"};
46const sdbusplus::message::object_path relDeleteMeTwo{"/deleteme2"};
47const sdbusplus::message::object_path relDeleteMeThree{"/deleteme3"};
48
49const std::string root{MGR_ROOT};
50const std::string deleteMeOne{root + relDeleteMeOne.str};
51const std::string deleteMeTwo{root + relDeleteMeTwo.str};
52const std::string deleteMeThree{root + relDeleteMeThree.str};
Brad Bishop8f868502017-01-23 13:13:58 -050053
54using ExampleIface1 = sdbusplus::xyz::openbmc_project::Example::server::Iface1;
55using ExampleIface2 = sdbusplus::xyz::openbmc_project::Example::server::Iface2;
56
57/** @class ExampleService
58 * @brief Host an object for triggering events.
59 */
60struct ExampleService
61{
62 ~ExampleService() = default;
63 ExampleService() :
64 shutdown(false),
65 bus(sdbusplus::bus::new_default()),
66 objmgr(sdbusplus::server::manager::manager(bus, MGR_ROOT))
67 {
68 bus.request_name(EXAMPLE_SERVICE);
69 }
70
71 void run()
72 {
73 sdbusplus::server::object::object <
74 ExampleIface1, ExampleIface2 > t1(bus, trigger1.str.c_str());
75 sdbusplus::server::object::object <
76 ExampleIface1, ExampleIface2 > t2(bus, trigger2.str.c_str());
Brad Bishopfb083c22017-01-19 09:22:04 -050077 sdbusplus::server::object::object <
78 ExampleIface1, ExampleIface2 > t3(bus, trigger3.str.c_str());
Brad Bishopfa51da72017-01-19 11:06:51 -050079 sdbusplus::server::object::object <
80 ExampleIface1, ExampleIface2 > t4(bus, trigger4.str.c_str());
Brad Bishop8f868502017-01-23 13:13:58 -050081
82 while (!shutdown)
83 {
84 bus.process_discard();
85 bus.wait((5000000us).count());
86 }
87 }
88
89 volatile bool shutdown;
90 sdbusplus::bus::bus bus;
91 sdbusplus::server::manager::manager objmgr;
92};
Brad Bishop49aefb32016-10-19 11:54:14 -040093
Brad Bishopabb2a1a2016-11-30 22:02:28 -050094/** @class SignalQueue
95 * @brief Store DBus signals in a queue.
96 */
97class SignalQueue
98{
99 public:
Brad Bishop7b337772017-01-12 16:11:24 -0500100 ~SignalQueue() = default;
101 SignalQueue() = delete;
102 SignalQueue(const SignalQueue&) = delete;
103 SignalQueue(SignalQueue&&) = default;
104 SignalQueue& operator=(const SignalQueue&) = delete;
105 SignalQueue& operator=(SignalQueue&&) = default;
106 explicit SignalQueue(const std::string& match) :
107 _bus(sdbusplus::bus::new_default()),
108 _match(_bus, match.c_str(), &callback, this),
109 _next(nullptr)
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500110 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500111 }
Brad Bishop7b337772017-01-12 16:11:24 -0500112
113 auto&& pop(unsigned timeout = 1000000)
114 {
115 while (timeout > 0 && !_next)
116 {
117 _bus.process_discard();
118 _bus.wait(50000);
119 timeout -= 50000;
120 }
121 return std::move(_next);
122 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500123
124 private:
Brad Bishop7b337772017-01-12 16:11:24 -0500125 static int callback(sd_bus_message* m, void* context, sd_bus_error*)
126 {
127 auto* me = static_cast<SignalQueue*>(context);
128 sd_bus_message_ref(m);
129 sdbusplus::message::message msg{m};
130 me->_next = std::move(msg);
131 return 0;
132 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500133
Brad Bishop7b337772017-01-12 16:11:24 -0500134 sdbusplus::bus::bus _bus;
135 sdbusplus::server::match::match _match;
136 sdbusplus::message::message _next;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500137};
138
139template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -0500140using Object = std::map <
141 std::string,
142 std::map <
143 std::string,
144 sdbusplus::message::variant<T... >>>;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500145
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500146/**@brief Find a subset of interfaces and properties in an object. */
147template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -0500148auto hasProperties(const Object<T...>& l, const Object<T...>& r)
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500149{
150 Object<T...> result;
151 std::set_difference(
Brad Bishop7b337772017-01-12 16:11:24 -0500152 r.cbegin(),
153 r.cend(),
154 l.cbegin(),
155 l.cend(),
156 std::inserter(result, result.end()));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500157 return result.empty();
158}
159
Brad Bishop432e3522016-12-01 00:24:14 -0500160/**@brief Check an object for one or more interfaces. */
161template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -0500162auto hasInterfaces(const std::vector<std::string>& l, const Object<T...>& r)
Brad Bishop432e3522016-12-01 00:24:14 -0500163{
164 std::vector<std::string> stripped, interfaces;
165 std::transform(
Brad Bishop7b337772017-01-12 16:11:24 -0500166 r.cbegin(),
167 r.cend(),
168 std::back_inserter(stripped),
169 [](auto & p)
170 {
171 return p.first;
172 });
Brad Bishop432e3522016-12-01 00:24:14 -0500173 std::set_difference(
Brad Bishop7b337772017-01-12 16:11:24 -0500174 stripped.cbegin(),
175 stripped.cend(),
176 l.cbegin(),
177 l.cend(),
178 std::back_inserter(interfaces));
Brad Bishop432e3522016-12-01 00:24:14 -0500179 return interfaces.empty();
180}
181
Brad Bishop8f868502017-01-23 13:13:58 -0500182void runTests()
Brad Bishop49aefb32016-10-19 11:54:14 -0400183{
Brad Bishop8f868502017-01-23 13:13:58 -0500184 const std::string exampleRoot{EXAMPLE_ROOT};
Brad Bishop49aefb32016-10-19 11:54:14 -0400185 auto b = sdbusplus::bus::new_default();
Brad Bishop8f868502017-01-23 13:13:58 -0500186
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500187 auto notify = [&]()
Brad Bishop49aefb32016-10-19 11:54:14 -0400188 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500189 return b.new_method_call(
Brad Bishop8f868502017-01-23 13:13:58 -0500190 MGR_SERVICE,
191 MGR_ROOT,
192 MGR_INTERFACE,
Brad Bishop7b337772017-01-12 16:11:24 -0500193 "Notify");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500194 };
Brad Bishop7b337772017-01-12 16:11:24 -0500195 auto set = [&](const std::string & path)
Brad Bishop432e3522016-12-01 00:24:14 -0500196 {
197 return b.new_method_call(
Brad Bishop8f868502017-01-23 13:13:58 -0500198 EXAMPLE_SERVICE,
Brad Bishop7b337772017-01-12 16:11:24 -0500199 path.c_str(),
200 "org.freedesktop.DBus.Properties",
201 "Set");
Brad Bishop432e3522016-12-01 00:24:14 -0500202 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400203
Brad Bishop7b337772017-01-12 16:11:24 -0500204 Object<std::string> obj
205 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500206 {
207 "xyz.openbmc_project.Example.Iface1",
208 {{"ExampleProperty1", "test1"}}
209 },
210 {
211 "xyz.openbmc_project.Example.Iface2",
212 {{"ExampleProperty2", "test2"}}
213 },
214 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400215
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500216 // Make sure the notify method works.
217 {
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500218 sdbusplus::message::object_path relPath{"/foo"};
219 std::string path(root + relPath.str);
Brad Bishop49aefb32016-10-19 11:54:14 -0400220
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500221 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500222 "path='" + root + "',member='InterfacesAdded'");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500223
224 auto m = notify();
225 m.append(relPath);
226 m.append(obj);
227 b.call(m);
228
229 auto sig{queue.pop()};
230 assert(sig);
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500231 sdbusplus::message::object_path signalPath;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500232 Object<std::string> signalObject;
233 sig.read(signalPath);
Brad Bishope07a1642017-01-24 14:37:33 -0500234 assert(path == signalPath.str);
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500235 sig.read(signalObject);
236 assert(hasProperties(signalObject, obj));
237 auto moreSignals{queue.pop()};
238 assert(!moreSignals);
Brad Bishop49aefb32016-10-19 11:54:14 -0400239 }
240
Brad Bishopfa51da72017-01-19 11:06:51 -0500241 // Validate the propertyIs filter.
242 {
243 // Create an object to be deleted.
244 {
245 auto m = notify();
246 m.append(relDeleteMeThree);
247 m.append(obj);
248 b.call(m);
249 }
250
251 // Validate that the action does not run if the property doesn't match.
252 {
253 SignalQueue queue(
254 "path='" + root + "',member='InterfacesRemoved'");
255 auto m = set(trigger4.str);
256 m.append("xyz.openbmc_project.Example.Iface2");
257 m.append("ExampleProperty2");
258 m.append(sdbusplus::message::variant<std::string>("123"));
259 b.call(m);
260 auto sig{queue.pop()};
261 assert(!sig);
262 }
263
264 // Validate that the action does run if the property matches.
265 {
266 // Set ExampleProperty2 to something else to the 123 filter
267 // matches.
268 SignalQueue queue(
269 "path='" + root + "',member='InterfacesRemoved'");
270 auto m = set(trigger4.str);
271 m.append("xyz.openbmc_project.Example.Iface2");
272 m.append("ExampleProperty2");
273 m.append(sdbusplus::message::variant<std::string>("xyz"));
274 b.call(m);
275 auto sig{queue.pop()};
276 assert(!sig);
277 }
278 {
279 // Set ExampleProperty3 to 99.
280 SignalQueue queue(
281 "path='" + root + "',member='InterfacesRemoved'");
282 auto m = set(trigger4.str);
283 m.append("xyz.openbmc_project.Example.Iface2");
284 m.append("ExampleProperty3");
285 m.append(sdbusplus::message::variant<int64_t>(99));
286 b.call(m);
287 auto sig{queue.pop()};
288 assert(!sig);
289 }
290 {
291 SignalQueue queue(
292 "path='" + root + "',member='InterfacesRemoved'");
293 auto m = set(trigger4.str);
294 m.append("xyz.openbmc_project.Example.Iface2");
295 m.append("ExampleProperty2");
296 m.append(sdbusplus::message::variant<std::string>("123"));
297 b.call(m);
298
299 sdbusplus::message::object_path sigpath;
300 std::vector<std::string> interfaces;
301 {
302 std::vector<std::string> interfaces;
303 auto sig{queue.pop()};
304 assert(sig);
305 sig.read(sigpath);
306 assert(sigpath == deleteMeThree);
307 sig.read(interfaces);
308 std::sort(interfaces.begin(), interfaces.end());
309 assert(hasInterfaces(interfaces, obj));
310 }
311 }
312 }
313
Brad Bishop432e3522016-12-01 00:24:14 -0500314 // Make sure DBus signals are handled.
315 {
Brad Bishop432e3522016-12-01 00:24:14 -0500316 // Create some objects to be deleted by an action.
317 {
318 auto m = notify();
319 m.append(relDeleteMeOne);
320 m.append(obj);
321 b.call(m);
322 }
323 {
324 auto m = notify();
325 m.append(relDeleteMeTwo);
326 m.append(obj);
327 b.call(m);
328 }
Brad Bishopfb083c22017-01-19 09:22:04 -0500329 {
330 auto m = notify();
331 m.append(relDeleteMeThree);
332 m.append(obj);
333 b.call(m);
334 }
Brad Bishop432e3522016-12-01 00:24:14 -0500335
Brad Bishopfb083c22017-01-19 09:22:04 -0500336 // Set some properties that should not trigger due to a filter.
Brad Bishop432e3522016-12-01 00:24:14 -0500337 {
338 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500339 "path='" + root + "',member='InterfacesRemoved'");
Brad Bishop8f868502017-01-23 13:13:58 -0500340 auto m = set(trigger1.str);
Brad Bishop432e3522016-12-01 00:24:14 -0500341 m.append("xyz.openbmc_project.Example.Iface2");
342 m.append("ExampleProperty2");
343 m.append(sdbusplus::message::variant<std::string>("abc123"));
344 b.call(m);
345 auto sig{queue.pop()};
346 assert(!sig);
347 }
Brad Bishopfb083c22017-01-19 09:22:04 -0500348 {
349 SignalQueue queue(
350 "path='" + root + "',member='InterfacesRemoved'");
351 auto m = set(trigger3.str);
352 m.append("xyz.openbmc_project.Example.Iface2");
353 m.append("ExampleProperty3");
354 m.append(sdbusplus::message::variant<int64_t>(11));
355 b.call(m);
356 auto sig{queue.pop()};
357 assert(!sig);
358 }
Brad Bishop432e3522016-12-01 00:24:14 -0500359
Brad Bishopfb083c22017-01-19 09:22:04 -0500360 // Set some properties that should trigger.
Brad Bishop432e3522016-12-01 00:24:14 -0500361 {
362 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500363 "path='" + root + "',member='InterfacesRemoved'");
Brad Bishop432e3522016-12-01 00:24:14 -0500364
Brad Bishop8f868502017-01-23 13:13:58 -0500365 auto m = set(trigger1.str);
Brad Bishop432e3522016-12-01 00:24:14 -0500366 m.append("xyz.openbmc_project.Example.Iface2");
367 m.append("ExampleProperty2");
368 m.append(sdbusplus::message::variant<std::string>("xxxyyy"));
369 b.call(m);
370
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500371 sdbusplus::message::object_path sigpath;
Brad Bishop432e3522016-12-01 00:24:14 -0500372 std::vector<std::string> interfaces;
373 {
374 std::vector<std::string> interfaces;
375 auto sig{queue.pop()};
376 assert(sig);
377 sig.read(sigpath);
378 assert(sigpath == deleteMeOne);
379 sig.read(interfaces);
380 std::sort(interfaces.begin(), interfaces.end());
381 assert(hasInterfaces(interfaces, obj));
382 }
383 {
384 std::vector<std::string> interfaces;
385 auto sig{queue.pop()};
386 assert(sig);
387 sig.read(sigpath);
388 assert(sigpath == deleteMeTwo);
389 sig.read(interfaces);
390 std::sort(interfaces.begin(), interfaces.end());
391 assert(hasInterfaces(interfaces, obj));
392 }
393 {
394 // Make sure there were only two signals.
395 auto sig{queue.pop()};
396 assert(!sig);
397 }
398 }
Brad Bishopfb083c22017-01-19 09:22:04 -0500399 {
400 SignalQueue queue(
401 "path='" + root + "',member='InterfacesRemoved'");
402
403 auto m = set(trigger3.str);
404 m.append("xyz.openbmc_project.Example.Iface2");
405 m.append("ExampleProperty3");
406 m.append(sdbusplus::message::variant<int64_t>(10));
407 b.call(m);
408
409 sdbusplus::message::object_path sigpath;
410 std::vector<std::string> interfaces;
411 {
412 std::vector<std::string> interfaces;
413 auto sig{queue.pop()};
414 assert(sig);
415 sig.read(sigpath);
416 assert(sigpath == deleteMeThree);
417 sig.read(interfaces);
418 std::sort(interfaces.begin(), interfaces.end());
419 assert(hasInterfaces(interfaces, obj));
420 }
421 {
422 // Make sure there was only one signal.
423 auto sig{queue.pop()};
424 assert(!sig);
425 }
426 }
Brad Bishop432e3522016-12-01 00:24:14 -0500427 }
428
Brad Bishop22ecacc2016-12-01 08:38:06 -0500429 // Validate the set property action.
430 {
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500431 sdbusplus::message::object_path relChangeMe{"/changeme"};
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500432 std::string changeMe{root + relChangeMe.str};
Brad Bishop22ecacc2016-12-01 08:38:06 -0500433
434 // Create an object to be updated by the set property action.
435 {
436 auto m = notify();
437 m.append(relChangeMe);
438 m.append(obj);
439 b.call(m);
440 }
441
Brad Bishop22ecacc2016-12-01 08:38:06 -0500442 // Trigger and validate the change.
443 {
444 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500445 "path='" + changeMe + "',member='PropertiesChanged'");
Brad Bishop8f868502017-01-23 13:13:58 -0500446 auto m = set(trigger2.str);
Brad Bishop22ecacc2016-12-01 08:38:06 -0500447 m.append("xyz.openbmc_project.Example.Iface2");
448 m.append("ExampleProperty2");
449 m.append(sdbusplus::message::variant<std::string>("yyyxxx"));
450 b.call(m);
451
452 std::string sigInterface;
Brad Bishop7b337772017-01-12 16:11:24 -0500453 std::map <
454 std::string,
455 sdbusplus::message::variant<std::string >> sigProperties;
Brad Bishop22ecacc2016-12-01 08:38:06 -0500456 {
457 std::vector<std::string> interfaces;
458 auto sig{queue.pop()};
459 sig.read(sigInterface);
460 assert(sigInterface == "xyz.openbmc_project.Example.Iface1");
461 sig.read(sigProperties);
462 assert(sigProperties["ExampleProperty1"] == "changed");
463 }
464 }
465 }
Brad Bishop49aefb32016-10-19 11:54:14 -0400466}
467
468int main()
469{
Brad Bishop65247582017-01-15 19:48:41 -0500470 phosphor::inventory::manager::Manager mgr(
471 sdbusplus::bus::new_default(),
Brad Bishop8f868502017-01-23 13:13:58 -0500472 MGR_SERVICE, MGR_ROOT, MGR_INTERFACE);
473 ExampleService d;
Brad Bishop49aefb32016-10-19 11:54:14 -0400474
Brad Bishop8f868502017-01-23 13:13:58 -0500475 auto f1 = [](auto mgr)
Brad Bishop49aefb32016-10-19 11:54:14 -0400476 {
Brad Bishop21621432017-01-13 16:35:53 -0500477 mgr->run();
478 };
Brad Bishop8f868502017-01-23 13:13:58 -0500479 auto f2 = [](auto d)
480 {
481 d->run();
482 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400483
Brad Bishop8f868502017-01-23 13:13:58 -0500484 auto t1 = std::thread(f1, &mgr);
485 auto t2 = std::thread(f2, &d);
486
487 runTests();
488
489 mgr.shutdown();
490 d.shutdown = true;
491
492 // Wait for server threads to exit.
493 t1.join();
494 t2.join();
495 std::cout << "Success! Waiting for threads to exit..." << std::endl;
Brad Bishop49aefb32016-10-19 11:54:14 -0400496
497 return 0;
498}
499
500// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4