blob: cf61b8489ac1e2cad705f7e00bd25a8b3ede1563 [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 Bishopa6fcd562017-02-03 11:00:27 -050016#include "config.h"
Patrick Venturea680d1e2018-10-14 13:34:26 -070017
18#include "manager.hpp"
Brad Bishopa6fcd562017-02-03 11:00:27 -050019#include "xyz/openbmc_project/Example/Iface1/server.hpp"
20#include "xyz/openbmc_project/Example/Iface2/server.hpp"
Brad Bishop49aefb32016-10-19 11:54:14 -040021
Patrick Venturea680d1e2018-10-14 13:34:26 -070022#include <algorithm>
23#include <cassert>
24#include <chrono>
25#include <iostream>
26#include <thread>
27
Brad Bishop8f868502017-01-23 13:13:58 -050028using namespace std::literals::chrono_literals;
29using namespace std::literals::string_literals;
30
Brad Bishop03f4cd92017-02-03 15:17:21 -050031using Object = phosphor::inventory::manager::Object;
32using ObjectMap = std::map<sdbusplus::message::object_path, Object>;
33
Brad Bishop8f868502017-01-23 13:13:58 -050034constexpr auto MGR_SERVICE = "phosphor.inventory.test.mgr";
35constexpr auto MGR_INTERFACE = IFACE;
36constexpr auto MGR_ROOT = "/testing/inventory";
37constexpr auto EXAMPLE_SERVICE = "phosphor.inventory.test.example";
38constexpr auto EXAMPLE_ROOT = "/testing";
39
Brad Bishop615b2a82018-03-29 10:32:41 -040040const auto trigger1 =
41 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger1"s);
42const auto trigger2 =
43 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger2"s);
44const auto trigger3 =
45 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger3"s);
46const auto trigger4 =
47 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger4"s);
48const auto trigger5 =
49 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger5"s);
Brad Bishopfb083c22017-01-19 09:22:04 -050050
51const sdbusplus::message::object_path relDeleteMeOne{"/deleteme1"};
52const sdbusplus::message::object_path relDeleteMeTwo{"/deleteme2"};
53const sdbusplus::message::object_path relDeleteMeThree{"/deleteme3"};
54
55const std::string root{MGR_ROOT};
56const std::string deleteMeOne{root + relDeleteMeOne.str};
57const std::string deleteMeTwo{root + relDeleteMeTwo.str};
58const std::string deleteMeThree{root + relDeleteMeThree.str};
Brad Bishop8f868502017-01-23 13:13:58 -050059
60using ExampleIface1 = sdbusplus::xyz::openbmc_project::Example::server::Iface1;
61using ExampleIface2 = sdbusplus::xyz::openbmc_project::Example::server::Iface2;
62
63/** @class ExampleService
64 * @brief Host an object for triggering events.
65 */
66struct ExampleService
67{
68 ~ExampleService() = default;
69 ExampleService() :
Brad Bishop615b2a82018-03-29 10:32:41 -040070 shutdown(false), bus(sdbusplus::bus::new_default()),
Brad Bishop8f868502017-01-23 13:13:58 -050071 objmgr(sdbusplus::server::manager::manager(bus, MGR_ROOT))
72 {
73 bus.request_name(EXAMPLE_SERVICE);
74 }
75
76 void run()
77 {
Brad Bishop615b2a82018-03-29 10:32:41 -040078 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t1(
79 bus, trigger1.str.c_str());
80 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t2(
81 bus, trigger2.str.c_str());
82 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t3(
83 bus, trigger3.str.c_str());
84 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t4(
85 bus, trigger4.str.c_str());
86 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t5(
87 bus, trigger5.str.c_str());
Brad Bishop8f868502017-01-23 13:13:58 -050088
89 while (!shutdown)
90 {
91 bus.process_discard();
92 bus.wait((5000000us).count());
93 }
94 }
95
96 volatile bool shutdown;
97 sdbusplus::bus::bus bus;
98 sdbusplus::server::manager::manager objmgr;
99};
Brad Bishop49aefb32016-10-19 11:54:14 -0400100
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500101/** @class SignalQueue
102 * @brief Store DBus signals in a queue.
103 */
104class SignalQueue
105{
Brad Bishop615b2a82018-03-29 10:32:41 -0400106 public:
107 ~SignalQueue() = default;
108 SignalQueue() = delete;
109 SignalQueue(const SignalQueue&) = delete;
110 SignalQueue(SignalQueue&&) = default;
111 SignalQueue& operator=(const SignalQueue&) = delete;
112 SignalQueue& operator=(SignalQueue&&) = default;
113 explicit SignalQueue(const std::string& match) :
114 _bus(sdbusplus::bus::new_default()),
115 _match(_bus, match.c_str(), &callback, this), _next(nullptr)
116 {
117 }
Brad Bishop7b337772017-01-12 16:11:24 -0500118
Brad Bishop615b2a82018-03-29 10:32:41 -0400119 auto&& pop(unsigned timeout = 1000000)
120 {
121 while (timeout > 0 && !_next)
Brad Bishop7b337772017-01-12 16:11:24 -0500122 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400123 _bus.process_discard();
124 _bus.wait(50000);
125 timeout -= 50000;
Brad Bishop7b337772017-01-12 16:11:24 -0500126 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400127 return std::move(_next);
128 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500129
Brad Bishop615b2a82018-03-29 10:32:41 -0400130 private:
131 static int callback(sd_bus_message* m, void* context, sd_bus_error*)
132 {
133 auto* me = static_cast<SignalQueue*>(context);
134 sd_bus_message_ref(m);
135 sdbusplus::message::message msg{m};
136 me->_next = std::move(msg);
137 return 0;
138 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500139
Brad Bishop615b2a82018-03-29 10:32:41 -0400140 sdbusplus::bus::bus _bus;
141 sdbusplus::bus::match_t _match;
142 sdbusplus::message::message _next;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500143};
144
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500145/**@brief Find a subset of interfaces and properties in an object. */
Brad Bishop1157af12017-01-22 01:03:02 -0500146auto hasProperties(const Object& l, const Object& r)
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500147{
Brad Bishop1157af12017-01-22 01:03:02 -0500148 Object result;
Brad Bishop615b2a82018-03-29 10:32:41 -0400149 std::set_difference(r.cbegin(), r.cend(), l.cbegin(), l.cend(),
150 std::inserter(result, result.end()));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500151 return result.empty();
152}
153
Brad Bishop432e3522016-12-01 00:24:14 -0500154/**@brief Check an object for one or more interfaces. */
Brad Bishop1157af12017-01-22 01:03:02 -0500155auto hasInterfaces(const std::vector<std::string>& l, const Object& r)
Brad Bishop432e3522016-12-01 00:24:14 -0500156{
157 std::vector<std::string> stripped, interfaces;
Brad Bishop615b2a82018-03-29 10:32:41 -0400158 std::transform(r.cbegin(), r.cend(), std::back_inserter(stripped),
159 [](auto& p) { return p.first; });
160 std::set_difference(stripped.cbegin(), stripped.cend(), l.cbegin(),
161 l.cend(), std::back_inserter(interfaces));
Brad Bishop432e3522016-12-01 00:24:14 -0500162 return interfaces.empty();
163}
164
Brad Bishop8f868502017-01-23 13:13:58 -0500165void runTests()
Brad Bishop49aefb32016-10-19 11:54:14 -0400166{
Brad Bishop8f868502017-01-23 13:13:58 -0500167 const std::string exampleRoot{EXAMPLE_ROOT};
Brad Bishop49aefb32016-10-19 11:54:14 -0400168 auto b = sdbusplus::bus::new_default();
Brad Bishop8f868502017-01-23 13:13:58 -0500169
Brad Bishop615b2a82018-03-29 10:32:41 -0400170 auto notify = [&]() {
171 return b.new_method_call(MGR_SERVICE, MGR_ROOT, MGR_INTERFACE,
172 "Notify");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500173 };
Brad Bishop615b2a82018-03-29 10:32:41 -0400174 auto set = [&](const std::string& path) {
175 return b.new_method_call(EXAMPLE_SERVICE, path.c_str(),
176 "org.freedesktop.DBus.Properties", "Set");
Brad Bishop432e3522016-12-01 00:24:14 -0500177 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400178
Brad Bishop615b2a82018-03-29 10:32:41 -0400179 Object obj{
180 {"xyz.openbmc_project.Example.Iface1",
181 {{"ExampleProperty1", "test1"s}}},
182 {"xyz.openbmc_project.Example.Iface2",
183 {{"ExampleProperty2", "test2"s},
184 {"ExampleProperty3", static_cast<int64_t>(0ll)}}},
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500185 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400186
Brad Bishop3e4a19a2017-01-21 22:17:09 -0500187 // Validate startup events occurred.
188 {
189 sdbusplus::message::object_path relCreateMe3{"/createme3"};
190 std::string createMe3{root + relCreateMe3.str};
191
Brad Bishop615b2a82018-03-29 10:32:41 -0400192 auto get =
193 b.new_method_call(MGR_SERVICE, createMe3.c_str(),
194 "org.freedesktop.DBus.Properties", "GetAll");
Brad Bishop3e4a19a2017-01-21 22:17:09 -0500195 get.append("xyz.openbmc_project.Example.Iface1");
196 auto resp = b.call(get);
197
198 Object::mapped_type properties;
199 assert(!resp.is_method_error());
200 resp.read(properties);
201 }
202
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500203 // Make sure the notify method works.
204 {
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500205 sdbusplus::message::object_path relPath{"/foo"};
206 std::string path(root + relPath.str);
Brad Bishop49aefb32016-10-19 11:54:14 -0400207
Brad Bishop615b2a82018-03-29 10:32:41 -0400208 SignalQueue queue("path='" + root + "',member='InterfacesAdded'");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500209
210 auto m = notify();
Brad Bishop03f4cd92017-02-03 15:17:21 -0500211 m.append(ObjectMap({{relPath, obj}}));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500212 b.call(m);
213
214 auto sig{queue.pop()};
Brad Bishop6211a432018-02-21 13:24:41 -0500215 assert(static_cast<bool>(sig));
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500216 sdbusplus::message::object_path signalPath;
Brad Bishop1157af12017-01-22 01:03:02 -0500217 Object signalObjectType;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500218 sig.read(signalPath);
Brad Bishope07a1642017-01-24 14:37:33 -0500219 assert(path == signalPath.str);
Brad Bishop1157af12017-01-22 01:03:02 -0500220 sig.read(signalObjectType);
221 assert(hasProperties(signalObjectType, obj));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500222 auto moreSignals{queue.pop()};
223 assert(!moreSignals);
Brad Bishop49aefb32016-10-19 11:54:14 -0400224 }
225
Brad Bishopfa51da72017-01-19 11:06:51 -0500226 // Validate the propertyIs filter.
Brad Bishop615b2a82018-03-29 10:32:41 -0400227 {// Create an object to be deleted.
228 {auto m = notify();
229 m.append(ObjectMap({{relDeleteMeThree, obj}}));
230 b.call(m);
231}
232
233// Validate that the action does not run if the property doesn't match.
234{
235 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
236 auto m = set(trigger4.str);
237 m.append("xyz.openbmc_project.Example.Iface2");
238 m.append("ExampleProperty2");
239 m.append(sdbusplus::message::variant<std::string>("123"));
240 b.call(m);
241 auto sig{queue.pop()};
242 assert(!sig);
243}
244
245// Validate that the action does run if the property matches.
246{
247 // Set ExampleProperty2 to something else to the 123 filter
248 // matches.
249 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
250 auto m = set(trigger4.str);
251 m.append("xyz.openbmc_project.Example.Iface2");
252 m.append("ExampleProperty2");
253 m.append(sdbusplus::message::variant<std::string>("xyz"));
254 b.call(m);
255 auto sig{queue.pop()};
256 assert(!sig);
257}
258{
259 // Set ExampleProperty3 to 99.
260 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
261 auto m = set(trigger4.str);
262 m.append("xyz.openbmc_project.Example.Iface2");
263 m.append("ExampleProperty3");
264 m.append(sdbusplus::message::variant<int64_t>(99));
265 b.call(m);
266 auto sig{queue.pop()};
267 assert(!sig);
268}
269{
270 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
271 auto m = set(trigger4.str);
272 m.append("xyz.openbmc_project.Example.Iface2");
273 m.append("ExampleProperty2");
274 m.append(sdbusplus::message::variant<std::string>("123"));
275 b.call(m);
276
277 sdbusplus::message::object_path sigpath;
278 std::vector<std::string> interfaces;
Brad Bishopfa51da72017-01-19 11:06:51 -0500279 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400280 std::vector<std::string> interfaces;
281 auto sig{queue.pop()};
282 assert(static_cast<bool>(sig));
283 sig.read(sigpath);
284 assert(sigpath == deleteMeThree);
285 sig.read(interfaces);
286 std::sort(interfaces.begin(), interfaces.end());
287 assert(hasInterfaces(interfaces, obj));
288 }
289}
290}
Brad Bishopfa51da72017-01-19 11:06:51 -0500291
Brad Bishop615b2a82018-03-29 10:32:41 -0400292// Make sure DBus signals are handled.
293{// Create some objects to be deleted by an action.
294 {auto m = notify();
295m.append(ObjectMap({{relDeleteMeOne, obj}}));
296b.call(m);
297}
298{
299 auto m = notify();
300 m.append(ObjectMap({{relDeleteMeTwo, obj}}));
301 b.call(m);
302}
303{
304 auto m = notify();
305 m.append(ObjectMap({{relDeleteMeThree, obj}}));
306 b.call(m);
307}
Brad Bishopfa51da72017-01-19 11:06:51 -0500308
Brad Bishop615b2a82018-03-29 10:32:41 -0400309// Set some properties that should not trigger due to a filter.
310{
311 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
312 auto m = set(trigger1.str);
313 m.append("xyz.openbmc_project.Example.Iface2");
314 m.append("ExampleProperty2");
315 m.append(sdbusplus::message::variant<std::string>("abc123"));
316 b.call(m);
317 auto sig{queue.pop()};
318 assert(!sig);
319}
320{
321 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
322 auto m = set(trigger3.str);
323 m.append("xyz.openbmc_project.Example.Iface2");
324 m.append("ExampleProperty3");
325 m.append(sdbusplus::message::variant<int64_t>(11));
326 b.call(m);
327 auto sig{queue.pop()};
328 assert(!sig);
329}
Brad Bishopfa51da72017-01-19 11:06:51 -0500330
Brad Bishop615b2a82018-03-29 10:32:41 -0400331// Set some properties that should trigger.
332{
333 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
334
335 auto m = set(trigger1.str);
336 m.append("xyz.openbmc_project.Example.Iface2");
337 m.append("ExampleProperty2");
338 m.append(sdbusplus::message::variant<std::string>("xxxyyy"));
339 b.call(m);
340
341 sdbusplus::message::object_path sigpath;
342 std::vector<std::string> interfaces;
343 {
344 std::vector<std::string> interfaces;
345 auto sig{queue.pop()};
346 assert(static_cast<bool>(sig));
347 sig.read(sigpath);
348 assert(sigpath == deleteMeOne);
349 sig.read(interfaces);
350 std::sort(interfaces.begin(), interfaces.end());
351 assert(hasInterfaces(interfaces, obj));
352 }
353 {
354 std::vector<std::string> interfaces;
355 auto sig{queue.pop()};
356 assert(static_cast<bool>(sig));
357 sig.read(sigpath);
358 assert(sigpath == deleteMeTwo);
359 sig.read(interfaces);
360 std::sort(interfaces.begin(), interfaces.end());
361 assert(hasInterfaces(interfaces, obj));
362 }
363 {
364 // Make sure there were only two signals.
365 auto sig{queue.pop()};
366 assert(!sig);
367 }
368}
369{
370 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
371
372 auto m = set(trigger3.str);
373 m.append("xyz.openbmc_project.Example.Iface2");
374 m.append("ExampleProperty3");
375 m.append(sdbusplus::message::variant<int64_t>(10));
376 b.call(m);
377
378 sdbusplus::message::object_path sigpath;
379 std::vector<std::string> interfaces;
380 {
381 std::vector<std::string> interfaces;
382 auto sig{queue.pop()};
383 assert(static_cast<bool>(sig));
384 sig.read(sigpath);
385 assert(sigpath == deleteMeThree);
386 sig.read(interfaces);
387 std::sort(interfaces.begin(), interfaces.end());
388 assert(hasInterfaces(interfaces, obj));
389 }
390 {
391 // Make sure there was only one signal.
392 auto sig{queue.pop()};
393 assert(!sig);
394 }
395}
396}
397
398// Validate the set property action.
399{
400 sdbusplus::message::object_path relChangeMe{"/changeme"};
401 std::string changeMe{root + relChangeMe.str};
402
403 // Create an object to be updated by the set property action.
404 {
405 auto m = notify();
406 m.append(ObjectMap({{relChangeMe, obj}}));
407 b.call(m);
408 }
409
410 // Trigger and validate the change.
411 {
412 SignalQueue queue("path='" + changeMe + "',member='PropertiesChanged'");
413 auto m = set(trigger2.str);
414 m.append("xyz.openbmc_project.Example.Iface2");
415 m.append("ExampleProperty2");
416 m.append(sdbusplus::message::variant<std::string>("yyyxxx"));
417 b.call(m);
418
419 std::string sigInterface;
420 std::map<std::string, sdbusplus::message::variant<std::string>>
421 sigProperties;
422 {
Brad Bishopfa51da72017-01-19 11:06:51 -0500423 std::vector<std::string> interfaces;
Brad Bishop432e3522016-12-01 00:24:14 -0500424 auto sig{queue.pop()};
Brad Bishop615b2a82018-03-29 10:32:41 -0400425 sig.read(sigInterface);
426 assert(sigInterface == "xyz.openbmc_project.Example.Iface1");
427 sig.read(sigProperties);
William A. Kennington III6e94b652018-11-06 17:11:28 -0800428 assert(sdbusplus::message::variant_ns::get<std::string>(
429 sigProperties["ExampleProperty1"]) == "changed");
Brad Bishop432e3522016-12-01 00:24:14 -0500430 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400431 }
432}
433
434// Validate the create object action.
435{
436 sdbusplus::message::object_path relCreateMe1{"/createme1"};
437 sdbusplus::message::object_path relCreateMe2{"/createme2"};
438 std::string createMe1{root + relCreateMe1.str};
439 std::string createMe2{root + relCreateMe2.str};
440
441 // Trigger the action.
442 {
443 sdbusplus::message::object_path signalPath;
444 Object signalObject;
445
446 SignalQueue queue("path='" + root + "',member='InterfacesAdded'");
447
448 auto m = set(trigger5.str);
449 m.append("xyz.openbmc_project.Example.Iface2");
450 m.append("ExampleProperty2");
451 m.append(sdbusplus::message::variant<std::string>("abc123"));
452 b.call(m);
Brad Bishopfb083c22017-01-19 09:22:04 -0500453 {
Brad Bishopfb083c22017-01-19 09:22:04 -0500454 auto sig{queue.pop()};
Brad Bishop615b2a82018-03-29 10:32:41 -0400455 assert(static_cast<bool>(sig));
456 sig.read(signalPath);
457 assert(createMe1 == signalPath.str);
458 sig.read(signalObject);
Brad Bishop432e3522016-12-01 00:24:14 -0500459 }
Brad Bishopfb083c22017-01-19 09:22:04 -0500460 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400461 auto sig{queue.pop()};
462 assert(static_cast<bool>(sig));
463 sig.read(signalPath);
464 assert(createMe2 == signalPath.str);
465 sig.read(signalObject);
Brad Bishopfb083c22017-01-19 09:22:04 -0500466 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400467
468 auto moreSignals{queue.pop()};
469 assert(!moreSignals);
Brad Bishop432e3522016-12-01 00:24:14 -0500470 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400471}
Brad Bishop49aefb32016-10-19 11:54:14 -0400472}
473
474int main()
475{
Brad Bishop65247582017-01-15 19:48:41 -0500476 phosphor::inventory::manager::Manager mgr(
Brad Bishop615b2a82018-03-29 10:32:41 -0400477 sdbusplus::bus::new_default(), MGR_SERVICE, MGR_ROOT, MGR_INTERFACE);
Brad Bishop8f868502017-01-23 13:13:58 -0500478 ExampleService d;
Brad Bishop49aefb32016-10-19 11:54:14 -0400479
Brad Bishop615b2a82018-03-29 10:32:41 -0400480 auto f1 = [](auto mgr) { mgr->run(); };
481 auto f2 = [](auto d) { d->run(); };
Brad Bishop49aefb32016-10-19 11:54:14 -0400482
Brad Bishop8f868502017-01-23 13:13:58 -0500483 auto t1 = std::thread(f1, &mgr);
484 auto t2 = std::thread(f2, &d);
485
486 runTests();
487
488 mgr.shutdown();
489 d.shutdown = true;
490
491 // Wait for server threads to exit.
492 t1.join();
493 t2.join();
494 std::cout << "Success! Waiting for threads to exit..." << std::endl;
Brad Bishop49aefb32016-10-19 11:54:14 -0400495
496 return 0;
497}
498
499// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4