blob: 5cadbe0a6d844ff1ac06600a14612f3785b326cb [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 "manager.hpp"
17#include "config.h"
Brad Bishop49aefb32016-10-19 11:54:14 -040018#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>
Brad Bishopa6fcd562017-02-03 11:00:27 -050023#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
Brad Bishop03f4cd92017-02-03 15:17:21 -050029using Object = phosphor::inventory::manager::Object;
30using ObjectMap = std::map<sdbusplus::message::object_path, Object>;
31
Brad Bishop8f868502017-01-23 13:13:58 -050032constexpr auto MGR_SERVICE = "phosphor.inventory.test.mgr";
33constexpr auto MGR_INTERFACE = IFACE;
34constexpr auto MGR_ROOT = "/testing/inventory";
35constexpr auto EXAMPLE_SERVICE = "phosphor.inventory.test.example";
36constexpr auto EXAMPLE_ROOT = "/testing";
37
Brad Bishop615b2a82018-03-29 10:32:41 -040038const auto trigger1 =
39 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger1"s);
40const auto trigger2 =
41 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger2"s);
42const auto trigger3 =
43 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger3"s);
44const auto trigger4 =
45 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger4"s);
46const auto trigger5 =
47 sdbusplus::message::object_path(EXAMPLE_ROOT + "/trigger5"s);
Brad Bishopfb083c22017-01-19 09:22:04 -050048
49const sdbusplus::message::object_path relDeleteMeOne{"/deleteme1"};
50const sdbusplus::message::object_path relDeleteMeTwo{"/deleteme2"};
51const sdbusplus::message::object_path relDeleteMeThree{"/deleteme3"};
52
53const std::string root{MGR_ROOT};
54const std::string deleteMeOne{root + relDeleteMeOne.str};
55const std::string deleteMeTwo{root + relDeleteMeTwo.str};
56const std::string deleteMeThree{root + relDeleteMeThree.str};
Brad Bishop8f868502017-01-23 13:13:58 -050057
58using ExampleIface1 = sdbusplus::xyz::openbmc_project::Example::server::Iface1;
59using ExampleIface2 = sdbusplus::xyz::openbmc_project::Example::server::Iface2;
60
61/** @class ExampleService
62 * @brief Host an object for triggering events.
63 */
64struct ExampleService
65{
66 ~ExampleService() = default;
67 ExampleService() :
Brad Bishop615b2a82018-03-29 10:32:41 -040068 shutdown(false), bus(sdbusplus::bus::new_default()),
Brad Bishop8f868502017-01-23 13:13:58 -050069 objmgr(sdbusplus::server::manager::manager(bus, MGR_ROOT))
70 {
71 bus.request_name(EXAMPLE_SERVICE);
72 }
73
74 void run()
75 {
Brad Bishop615b2a82018-03-29 10:32:41 -040076 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t1(
77 bus, trigger1.str.c_str());
78 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t2(
79 bus, trigger2.str.c_str());
80 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t3(
81 bus, trigger3.str.c_str());
82 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t4(
83 bus, trigger4.str.c_str());
84 sdbusplus::server::object::object<ExampleIface1, ExampleIface2> t5(
85 bus, trigger5.str.c_str());
Brad Bishop8f868502017-01-23 13:13:58 -050086
87 while (!shutdown)
88 {
89 bus.process_discard();
90 bus.wait((5000000us).count());
91 }
92 }
93
94 volatile bool shutdown;
95 sdbusplus::bus::bus bus;
96 sdbusplus::server::manager::manager objmgr;
97};
Brad Bishop49aefb32016-10-19 11:54:14 -040098
Brad Bishopabb2a1a2016-11-30 22:02:28 -050099/** @class SignalQueue
100 * @brief Store DBus signals in a queue.
101 */
102class SignalQueue
103{
Brad Bishop615b2a82018-03-29 10:32:41 -0400104 public:
105 ~SignalQueue() = default;
106 SignalQueue() = delete;
107 SignalQueue(const SignalQueue&) = delete;
108 SignalQueue(SignalQueue&&) = default;
109 SignalQueue& operator=(const SignalQueue&) = delete;
110 SignalQueue& operator=(SignalQueue&&) = default;
111 explicit SignalQueue(const std::string& match) :
112 _bus(sdbusplus::bus::new_default()),
113 _match(_bus, match.c_str(), &callback, this), _next(nullptr)
114 {
115 }
Brad Bishop7b337772017-01-12 16:11:24 -0500116
Brad Bishop615b2a82018-03-29 10:32:41 -0400117 auto&& pop(unsigned timeout = 1000000)
118 {
119 while (timeout > 0 && !_next)
Brad Bishop7b337772017-01-12 16:11:24 -0500120 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400121 _bus.process_discard();
122 _bus.wait(50000);
123 timeout -= 50000;
Brad Bishop7b337772017-01-12 16:11:24 -0500124 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400125 return std::move(_next);
126 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500127
Brad Bishop615b2a82018-03-29 10:32:41 -0400128 private:
129 static int callback(sd_bus_message* m, void* context, sd_bus_error*)
130 {
131 auto* me = static_cast<SignalQueue*>(context);
132 sd_bus_message_ref(m);
133 sdbusplus::message::message msg{m};
134 me->_next = std::move(msg);
135 return 0;
136 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500137
Brad Bishop615b2a82018-03-29 10:32:41 -0400138 sdbusplus::bus::bus _bus;
139 sdbusplus::bus::match_t _match;
140 sdbusplus::message::message _next;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500141};
142
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500143/**@brief Find a subset of interfaces and properties in an object. */
Brad Bishop1157af12017-01-22 01:03:02 -0500144auto hasProperties(const Object& l, const Object& r)
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500145{
Brad Bishop1157af12017-01-22 01:03:02 -0500146 Object result;
Brad Bishop615b2a82018-03-29 10:32:41 -0400147 std::set_difference(r.cbegin(), r.cend(), l.cbegin(), l.cend(),
148 std::inserter(result, result.end()));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500149 return result.empty();
150}
151
Brad Bishop432e3522016-12-01 00:24:14 -0500152/**@brief Check an object for one or more interfaces. */
Brad Bishop1157af12017-01-22 01:03:02 -0500153auto hasInterfaces(const std::vector<std::string>& l, const Object& r)
Brad Bishop432e3522016-12-01 00:24:14 -0500154{
155 std::vector<std::string> stripped, interfaces;
Brad Bishop615b2a82018-03-29 10:32:41 -0400156 std::transform(r.cbegin(), r.cend(), std::back_inserter(stripped),
157 [](auto& p) { return p.first; });
158 std::set_difference(stripped.cbegin(), stripped.cend(), l.cbegin(),
159 l.cend(), std::back_inserter(interfaces));
Brad Bishop432e3522016-12-01 00:24:14 -0500160 return interfaces.empty();
161}
162
Brad Bishop8f868502017-01-23 13:13:58 -0500163void runTests()
Brad Bishop49aefb32016-10-19 11:54:14 -0400164{
Brad Bishop8f868502017-01-23 13:13:58 -0500165 const std::string exampleRoot{EXAMPLE_ROOT};
Brad Bishop49aefb32016-10-19 11:54:14 -0400166 auto b = sdbusplus::bus::new_default();
Brad Bishop8f868502017-01-23 13:13:58 -0500167
Brad Bishop615b2a82018-03-29 10:32:41 -0400168 auto notify = [&]() {
169 return b.new_method_call(MGR_SERVICE, MGR_ROOT, MGR_INTERFACE,
170 "Notify");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500171 };
Brad Bishop615b2a82018-03-29 10:32:41 -0400172 auto set = [&](const std::string& path) {
173 return b.new_method_call(EXAMPLE_SERVICE, path.c_str(),
174 "org.freedesktop.DBus.Properties", "Set");
Brad Bishop432e3522016-12-01 00:24:14 -0500175 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400176
Brad Bishop615b2a82018-03-29 10:32:41 -0400177 Object obj{
178 {"xyz.openbmc_project.Example.Iface1",
179 {{"ExampleProperty1", "test1"s}}},
180 {"xyz.openbmc_project.Example.Iface2",
181 {{"ExampleProperty2", "test2"s},
182 {"ExampleProperty3", static_cast<int64_t>(0ll)}}},
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500183 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400184
Brad Bishop3e4a19a2017-01-21 22:17:09 -0500185 // Validate startup events occurred.
186 {
187 sdbusplus::message::object_path relCreateMe3{"/createme3"};
188 std::string createMe3{root + relCreateMe3.str};
189
Brad Bishop615b2a82018-03-29 10:32:41 -0400190 auto get =
191 b.new_method_call(MGR_SERVICE, createMe3.c_str(),
192 "org.freedesktop.DBus.Properties", "GetAll");
Brad Bishop3e4a19a2017-01-21 22:17:09 -0500193 get.append("xyz.openbmc_project.Example.Iface1");
194 auto resp = b.call(get);
195
196 Object::mapped_type properties;
197 assert(!resp.is_method_error());
198 resp.read(properties);
199 }
200
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500201 // Make sure the notify method works.
202 {
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500203 sdbusplus::message::object_path relPath{"/foo"};
204 std::string path(root + relPath.str);
Brad Bishop49aefb32016-10-19 11:54:14 -0400205
Brad Bishop615b2a82018-03-29 10:32:41 -0400206 SignalQueue queue("path='" + root + "',member='InterfacesAdded'");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500207
208 auto m = notify();
Brad Bishop03f4cd92017-02-03 15:17:21 -0500209 m.append(ObjectMap({{relPath, obj}}));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500210 b.call(m);
211
212 auto sig{queue.pop()};
Brad Bishop6211a432018-02-21 13:24:41 -0500213 assert(static_cast<bool>(sig));
Brad Bishop9aa5e2f2017-01-15 19:45:40 -0500214 sdbusplus::message::object_path signalPath;
Brad Bishop1157af12017-01-22 01:03:02 -0500215 Object signalObjectType;
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500216 sig.read(signalPath);
Brad Bishope07a1642017-01-24 14:37:33 -0500217 assert(path == signalPath.str);
Brad Bishop1157af12017-01-22 01:03:02 -0500218 sig.read(signalObjectType);
219 assert(hasProperties(signalObjectType, obj));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500220 auto moreSignals{queue.pop()};
221 assert(!moreSignals);
Brad Bishop49aefb32016-10-19 11:54:14 -0400222 }
223
Brad Bishopfa51da72017-01-19 11:06:51 -0500224 // Validate the propertyIs filter.
Brad Bishop615b2a82018-03-29 10:32:41 -0400225 {// Create an object to be deleted.
226 {auto m = notify();
227 m.append(ObjectMap({{relDeleteMeThree, obj}}));
228 b.call(m);
229}
230
231// Validate that the action does not run if the property doesn't match.
232{
233 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
234 auto m = set(trigger4.str);
235 m.append("xyz.openbmc_project.Example.Iface2");
236 m.append("ExampleProperty2");
237 m.append(sdbusplus::message::variant<std::string>("123"));
238 b.call(m);
239 auto sig{queue.pop()};
240 assert(!sig);
241}
242
243// Validate that the action does run if the property matches.
244{
245 // Set ExampleProperty2 to something else to the 123 filter
246 // matches.
247 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
248 auto m = set(trigger4.str);
249 m.append("xyz.openbmc_project.Example.Iface2");
250 m.append("ExampleProperty2");
251 m.append(sdbusplus::message::variant<std::string>("xyz"));
252 b.call(m);
253 auto sig{queue.pop()};
254 assert(!sig);
255}
256{
257 // Set ExampleProperty3 to 99.
258 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
259 auto m = set(trigger4.str);
260 m.append("xyz.openbmc_project.Example.Iface2");
261 m.append("ExampleProperty3");
262 m.append(sdbusplus::message::variant<int64_t>(99));
263 b.call(m);
264 auto sig{queue.pop()};
265 assert(!sig);
266}
267{
268 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
269 auto m = set(trigger4.str);
270 m.append("xyz.openbmc_project.Example.Iface2");
271 m.append("ExampleProperty2");
272 m.append(sdbusplus::message::variant<std::string>("123"));
273 b.call(m);
274
275 sdbusplus::message::object_path sigpath;
276 std::vector<std::string> interfaces;
Brad Bishopfa51da72017-01-19 11:06:51 -0500277 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400278 std::vector<std::string> interfaces;
279 auto sig{queue.pop()};
280 assert(static_cast<bool>(sig));
281 sig.read(sigpath);
282 assert(sigpath == deleteMeThree);
283 sig.read(interfaces);
284 std::sort(interfaces.begin(), interfaces.end());
285 assert(hasInterfaces(interfaces, obj));
286 }
287}
288}
Brad Bishopfa51da72017-01-19 11:06:51 -0500289
Brad Bishop615b2a82018-03-29 10:32:41 -0400290// Make sure DBus signals are handled.
291{// Create some objects to be deleted by an action.
292 {auto m = notify();
293m.append(ObjectMap({{relDeleteMeOne, obj}}));
294b.call(m);
295}
296{
297 auto m = notify();
298 m.append(ObjectMap({{relDeleteMeTwo, obj}}));
299 b.call(m);
300}
301{
302 auto m = notify();
303 m.append(ObjectMap({{relDeleteMeThree, obj}}));
304 b.call(m);
305}
Brad Bishopfa51da72017-01-19 11:06:51 -0500306
Brad Bishop615b2a82018-03-29 10:32:41 -0400307// Set some properties that should not trigger due to a filter.
308{
309 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
310 auto m = set(trigger1.str);
311 m.append("xyz.openbmc_project.Example.Iface2");
312 m.append("ExampleProperty2");
313 m.append(sdbusplus::message::variant<std::string>("abc123"));
314 b.call(m);
315 auto sig{queue.pop()};
316 assert(!sig);
317}
318{
319 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
320 auto m = set(trigger3.str);
321 m.append("xyz.openbmc_project.Example.Iface2");
322 m.append("ExampleProperty3");
323 m.append(sdbusplus::message::variant<int64_t>(11));
324 b.call(m);
325 auto sig{queue.pop()};
326 assert(!sig);
327}
Brad Bishopfa51da72017-01-19 11:06:51 -0500328
Brad Bishop615b2a82018-03-29 10:32:41 -0400329// Set some properties that should trigger.
330{
331 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
332
333 auto m = set(trigger1.str);
334 m.append("xyz.openbmc_project.Example.Iface2");
335 m.append("ExampleProperty2");
336 m.append(sdbusplus::message::variant<std::string>("xxxyyy"));
337 b.call(m);
338
339 sdbusplus::message::object_path sigpath;
340 std::vector<std::string> interfaces;
341 {
342 std::vector<std::string> interfaces;
343 auto sig{queue.pop()};
344 assert(static_cast<bool>(sig));
345 sig.read(sigpath);
346 assert(sigpath == deleteMeOne);
347 sig.read(interfaces);
348 std::sort(interfaces.begin(), interfaces.end());
349 assert(hasInterfaces(interfaces, obj));
350 }
351 {
352 std::vector<std::string> interfaces;
353 auto sig{queue.pop()};
354 assert(static_cast<bool>(sig));
355 sig.read(sigpath);
356 assert(sigpath == deleteMeTwo);
357 sig.read(interfaces);
358 std::sort(interfaces.begin(), interfaces.end());
359 assert(hasInterfaces(interfaces, obj));
360 }
361 {
362 // Make sure there were only two signals.
363 auto sig{queue.pop()};
364 assert(!sig);
365 }
366}
367{
368 SignalQueue queue("path='" + root + "',member='InterfacesRemoved'");
369
370 auto m = set(trigger3.str);
371 m.append("xyz.openbmc_project.Example.Iface2");
372 m.append("ExampleProperty3");
373 m.append(sdbusplus::message::variant<int64_t>(10));
374 b.call(m);
375
376 sdbusplus::message::object_path sigpath;
377 std::vector<std::string> interfaces;
378 {
379 std::vector<std::string> interfaces;
380 auto sig{queue.pop()};
381 assert(static_cast<bool>(sig));
382 sig.read(sigpath);
383 assert(sigpath == deleteMeThree);
384 sig.read(interfaces);
385 std::sort(interfaces.begin(), interfaces.end());
386 assert(hasInterfaces(interfaces, obj));
387 }
388 {
389 // Make sure there was only one signal.
390 auto sig{queue.pop()};
391 assert(!sig);
392 }
393}
394}
395
396// Validate the set property action.
397{
398 sdbusplus::message::object_path relChangeMe{"/changeme"};
399 std::string changeMe{root + relChangeMe.str};
400
401 // Create an object to be updated by the set property action.
402 {
403 auto m = notify();
404 m.append(ObjectMap({{relChangeMe, obj}}));
405 b.call(m);
406 }
407
408 // Trigger and validate the change.
409 {
410 SignalQueue queue("path='" + changeMe + "',member='PropertiesChanged'");
411 auto m = set(trigger2.str);
412 m.append("xyz.openbmc_project.Example.Iface2");
413 m.append("ExampleProperty2");
414 m.append(sdbusplus::message::variant<std::string>("yyyxxx"));
415 b.call(m);
416
417 std::string sigInterface;
418 std::map<std::string, sdbusplus::message::variant<std::string>>
419 sigProperties;
420 {
Brad Bishopfa51da72017-01-19 11:06:51 -0500421 std::vector<std::string> interfaces;
Brad Bishop432e3522016-12-01 00:24:14 -0500422 auto sig{queue.pop()};
Brad Bishop615b2a82018-03-29 10:32:41 -0400423 sig.read(sigInterface);
424 assert(sigInterface == "xyz.openbmc_project.Example.Iface1");
425 sig.read(sigProperties);
426 assert(sigProperties["ExampleProperty1"] == "changed");
Brad Bishop432e3522016-12-01 00:24:14 -0500427 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400428 }
429}
430
431// Validate the create object action.
432{
433 sdbusplus::message::object_path relCreateMe1{"/createme1"};
434 sdbusplus::message::object_path relCreateMe2{"/createme2"};
435 std::string createMe1{root + relCreateMe1.str};
436 std::string createMe2{root + relCreateMe2.str};
437
438 // Trigger the action.
439 {
440 sdbusplus::message::object_path signalPath;
441 Object signalObject;
442
443 SignalQueue queue("path='" + root + "',member='InterfacesAdded'");
444
445 auto m = set(trigger5.str);
446 m.append("xyz.openbmc_project.Example.Iface2");
447 m.append("ExampleProperty2");
448 m.append(sdbusplus::message::variant<std::string>("abc123"));
449 b.call(m);
Brad Bishopfb083c22017-01-19 09:22:04 -0500450 {
Brad Bishopfb083c22017-01-19 09:22:04 -0500451 auto sig{queue.pop()};
Brad Bishop615b2a82018-03-29 10:32:41 -0400452 assert(static_cast<bool>(sig));
453 sig.read(signalPath);
454 assert(createMe1 == signalPath.str);
455 sig.read(signalObject);
Brad Bishop432e3522016-12-01 00:24:14 -0500456 }
Brad Bishopfb083c22017-01-19 09:22:04 -0500457 {
Brad Bishop615b2a82018-03-29 10:32:41 -0400458 auto sig{queue.pop()};
459 assert(static_cast<bool>(sig));
460 sig.read(signalPath);
461 assert(createMe2 == signalPath.str);
462 sig.read(signalObject);
Brad Bishopfb083c22017-01-19 09:22:04 -0500463 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400464
465 auto moreSignals{queue.pop()};
466 assert(!moreSignals);
Brad Bishop432e3522016-12-01 00:24:14 -0500467 }
Brad Bishop615b2a82018-03-29 10:32:41 -0400468}
Brad Bishop49aefb32016-10-19 11:54:14 -0400469}
470
471int main()
472{
Brad Bishop65247582017-01-15 19:48:41 -0500473 phosphor::inventory::manager::Manager mgr(
Brad Bishop615b2a82018-03-29 10:32:41 -0400474 sdbusplus::bus::new_default(), MGR_SERVICE, MGR_ROOT, MGR_INTERFACE);
Brad Bishop8f868502017-01-23 13:13:58 -0500475 ExampleService d;
Brad Bishop49aefb32016-10-19 11:54:14 -0400476
Brad Bishop615b2a82018-03-29 10:32:41 -0400477 auto f1 = [](auto mgr) { mgr->run(); };
478 auto f2 = [](auto d) { d->run(); };
Brad Bishop49aefb32016-10-19 11:54:14 -0400479
Brad Bishop8f868502017-01-23 13:13:58 -0500480 auto t1 = std::thread(f1, &mgr);
481 auto t2 = std::thread(f2, &d);
482
483 runTests();
484
485 mgr.shutdown();
486 d.shutdown = true;
487
488 // Wait for server threads to exit.
489 t1.join();
490 t2.join();
491 std::cout << "Success! Waiting for threads to exit..." << std::endl;
Brad Bishop49aefb32016-10-19 11:54:14 -0400492
493 return 0;
494}
495
496// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4