blob: 550e83f98f167187d54130aa0377f15e1718607c [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 Bishop49aefb32016-10-19 11:54:14 -040021
22constexpr auto SERVICE = "phosphor.inventory.test";
23constexpr auto INTERFACE = IFACE;
24constexpr auto ROOT = "/testing/inventory";
25
Brad Bishop7b337772017-01-12 16:11:24 -050026auto server_thread(void* data)
Brad Bishop49aefb32016-10-19 11:54:14 -040027{
28 auto mgr = static_cast<phosphor::inventory::manager::Manager*>(data);
29
30 mgr->run();
31
Brad Bishop7b337772017-01-12 16:11:24 -050032 return static_cast<void*>(nullptr);
Brad Bishop49aefb32016-10-19 11:54:14 -040033}
34
Brad Bishopabb2a1a2016-11-30 22:02:28 -050035/** @class SignalQueue
36 * @brief Store DBus signals in a queue.
37 */
38class SignalQueue
39{
40 public:
Brad Bishop7b337772017-01-12 16:11:24 -050041 ~SignalQueue() = default;
42 SignalQueue() = delete;
43 SignalQueue(const SignalQueue&) = delete;
44 SignalQueue(SignalQueue&&) = default;
45 SignalQueue& operator=(const SignalQueue&) = delete;
46 SignalQueue& operator=(SignalQueue&&) = default;
47 explicit SignalQueue(const std::string& match) :
48 _bus(sdbusplus::bus::new_default()),
49 _match(_bus, match.c_str(), &callback, this),
50 _next(nullptr)
Brad Bishopabb2a1a2016-11-30 22:02:28 -050051 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -050052 }
Brad Bishop7b337772017-01-12 16:11:24 -050053
54 auto&& pop(unsigned timeout = 1000000)
55 {
56 while (timeout > 0 && !_next)
57 {
58 _bus.process_discard();
59 _bus.wait(50000);
60 timeout -= 50000;
61 }
62 return std::move(_next);
63 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -050064
65 private:
Brad Bishop7b337772017-01-12 16:11:24 -050066 static int callback(sd_bus_message* m, void* context, sd_bus_error*)
67 {
68 auto* me = static_cast<SignalQueue*>(context);
69 sd_bus_message_ref(m);
70 sdbusplus::message::message msg{m};
71 me->_next = std::move(msg);
72 return 0;
73 }
Brad Bishopabb2a1a2016-11-30 22:02:28 -050074
Brad Bishop7b337772017-01-12 16:11:24 -050075 sdbusplus::bus::bus _bus;
76 sdbusplus::server::match::match _match;
77 sdbusplus::message::message _next;
Brad Bishopabb2a1a2016-11-30 22:02:28 -050078};
79
80template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -050081using Object = std::map <
82 std::string,
83 std::map <
84 std::string,
85 sdbusplus::message::variant<T... >>>;
Brad Bishopabb2a1a2016-11-30 22:02:28 -050086
87using ObjectPath = std::string;
88
89/**@brief Find a subset of interfaces and properties in an object. */
90template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -050091auto hasProperties(const Object<T...>& l, const Object<T...>& r)
Brad Bishopabb2a1a2016-11-30 22:02:28 -050092{
93 Object<T...> result;
94 std::set_difference(
Brad Bishop7b337772017-01-12 16:11:24 -050095 r.cbegin(),
96 r.cend(),
97 l.cbegin(),
98 l.cend(),
99 std::inserter(result, result.end()));
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500100 return result.empty();
101}
102
Brad Bishop432e3522016-12-01 00:24:14 -0500103/**@brief Check an object for one or more interfaces. */
104template <typename ...T>
Brad Bishop7b337772017-01-12 16:11:24 -0500105auto hasInterfaces(const std::vector<std::string>& l, const Object<T...>& r)
Brad Bishop432e3522016-12-01 00:24:14 -0500106{
107 std::vector<std::string> stripped, interfaces;
108 std::transform(
Brad Bishop7b337772017-01-12 16:11:24 -0500109 r.cbegin(),
110 r.cend(),
111 std::back_inserter(stripped),
112 [](auto & p)
113 {
114 return p.first;
115 });
Brad Bishop432e3522016-12-01 00:24:14 -0500116 std::set_difference(
Brad Bishop7b337772017-01-12 16:11:24 -0500117 stripped.cbegin(),
118 stripped.cend(),
119 l.cbegin(),
120 l.cend(),
121 std::back_inserter(interfaces));
Brad Bishop432e3522016-12-01 00:24:14 -0500122 return interfaces.empty();
123}
124
Brad Bishop7b337772017-01-12 16:11:24 -0500125void runTests(phosphor::inventory::manager::Manager& mgr)
Brad Bishop49aefb32016-10-19 11:54:14 -0400126{
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500127 const std::string root{ROOT};
Brad Bishop49aefb32016-10-19 11:54:14 -0400128 auto b = sdbusplus::bus::new_default();
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500129 auto notify = [&]()
Brad Bishop49aefb32016-10-19 11:54:14 -0400130 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500131 return b.new_method_call(
Brad Bishop7b337772017-01-12 16:11:24 -0500132 SERVICE,
133 ROOT,
134 INTERFACE,
135 "Notify");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500136 };
Brad Bishop7b337772017-01-12 16:11:24 -0500137 auto set = [&](const std::string & path)
Brad Bishop432e3522016-12-01 00:24:14 -0500138 {
139 return b.new_method_call(
Brad Bishop7b337772017-01-12 16:11:24 -0500140 SERVICE,
141 path.c_str(),
142 "org.freedesktop.DBus.Properties",
143 "Set");
Brad Bishop432e3522016-12-01 00:24:14 -0500144 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400145
Brad Bishop7b337772017-01-12 16:11:24 -0500146 Object<std::string> obj
147 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500148 {
149 "xyz.openbmc_project.Example.Iface1",
150 {{"ExampleProperty1", "test1"}}
151 },
152 {
153 "xyz.openbmc_project.Example.Iface2",
154 {{"ExampleProperty2", "test2"}}
155 },
156 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400157
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500158 // Make sure the notify method works.
159 {
160 ObjectPath relPath{"/foo"};
161 ObjectPath path{root + relPath};
Brad Bishop49aefb32016-10-19 11:54:14 -0400162
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500163 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500164 "path='" + root + "',member='InterfacesAdded'");
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500165
166 auto m = notify();
167 m.append(relPath);
168 m.append(obj);
169 b.call(m);
170
171 auto sig{queue.pop()};
172 assert(sig);
173 ObjectPath signalPath;
174 Object<std::string> signalObject;
175 sig.read(signalPath);
176 assert(path == signalPath);
177 sig.read(signalObject);
178 assert(hasProperties(signalObject, obj));
179 auto moreSignals{queue.pop()};
180 assert(!moreSignals);
Brad Bishop49aefb32016-10-19 11:54:14 -0400181 }
182
Brad Bishop432e3522016-12-01 00:24:14 -0500183 // Make sure DBus signals are handled.
184 {
185 ObjectPath relDeleteMeOne{"/deleteme1"};
186 ObjectPath relDeleteMeTwo{"/deleteme2"};
187 ObjectPath relTriggerOne{"/trigger1"};
188 ObjectPath deleteMeOne{root + relDeleteMeOne};
189 ObjectPath deleteMeTwo{root + relDeleteMeTwo};
190 ObjectPath triggerOne{root + relTriggerOne};
191
192 // Create some objects to be deleted by an action.
193 {
194 auto m = notify();
195 m.append(relDeleteMeOne);
196 m.append(obj);
197 b.call(m);
198 }
199 {
200 auto m = notify();
201 m.append(relDeleteMeTwo);
202 m.append(obj);
203 b.call(m);
204 }
205
206 // Create the triggering object.
207 {
208 auto m = notify();
209 m.append(relTriggerOne);
210 m.append(obj);
211 b.call(m);
212 }
213
214 // Set a property that should not trigger due to a filter.
215 {
216 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500217 "path='" + root + "',member='InterfacesRemoved'");
Brad Bishop432e3522016-12-01 00:24:14 -0500218 auto m = set(triggerOne);
219 m.append("xyz.openbmc_project.Example.Iface2");
220 m.append("ExampleProperty2");
221 m.append(sdbusplus::message::variant<std::string>("abc123"));
222 b.call(m);
223 auto sig{queue.pop()};
224 assert(!sig);
225 }
226
227 // Set a property that should trigger.
228 {
229 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500230 "path='" + root + "',member='InterfacesRemoved'");
Brad Bishop432e3522016-12-01 00:24:14 -0500231
232 auto m = set(triggerOne);
233 m.append("xyz.openbmc_project.Example.Iface2");
234 m.append("ExampleProperty2");
235 m.append(sdbusplus::message::variant<std::string>("xxxyyy"));
236 b.call(m);
237
238 ObjectPath sigpath;
239 std::vector<std::string> interfaces;
240 {
241 std::vector<std::string> interfaces;
242 auto sig{queue.pop()};
243 assert(sig);
244 sig.read(sigpath);
245 assert(sigpath == deleteMeOne);
246 sig.read(interfaces);
247 std::sort(interfaces.begin(), interfaces.end());
248 assert(hasInterfaces(interfaces, obj));
249 }
250 {
251 std::vector<std::string> interfaces;
252 auto sig{queue.pop()};
253 assert(sig);
254 sig.read(sigpath);
255 assert(sigpath == deleteMeTwo);
256 sig.read(interfaces);
257 std::sort(interfaces.begin(), interfaces.end());
258 assert(hasInterfaces(interfaces, obj));
259 }
260 {
261 // Make sure there were only two signals.
262 auto sig{queue.pop()};
263 assert(!sig);
264 }
265 }
266 }
267
Brad Bishop22ecacc2016-12-01 08:38:06 -0500268 // Validate the set property action.
269 {
270 ObjectPath relChangeMe{"/changeme"};
271 ObjectPath relTriggerTwo{"/trigger2"};
272 ObjectPath changeMe{root + relChangeMe};
273 ObjectPath triggerTwo{root + relTriggerTwo};
274
275 // Create an object to be updated by the set property action.
276 {
277 auto m = notify();
278 m.append(relChangeMe);
279 m.append(obj);
280 b.call(m);
281 }
282
283 // Create the triggering object.
284 {
285 auto m = notify();
286 m.append(relTriggerTwo);
287 m.append(obj);
288 b.call(m);
289 }
290
291 // Trigger and validate the change.
292 {
293 SignalQueue queue(
Brad Bishop7b337772017-01-12 16:11:24 -0500294 "path='" + changeMe + "',member='PropertiesChanged'");
Brad Bishop22ecacc2016-12-01 08:38:06 -0500295 auto m = set(triggerTwo);
296 m.append("xyz.openbmc_project.Example.Iface2");
297 m.append("ExampleProperty2");
298 m.append(sdbusplus::message::variant<std::string>("yyyxxx"));
299 b.call(m);
300
301 std::string sigInterface;
Brad Bishop7b337772017-01-12 16:11:24 -0500302 std::map <
303 std::string,
304 sdbusplus::message::variant<std::string >> sigProperties;
Brad Bishop22ecacc2016-12-01 08:38:06 -0500305 {
306 std::vector<std::string> interfaces;
307 auto sig{queue.pop()};
308 sig.read(sigInterface);
309 assert(sigInterface == "xyz.openbmc_project.Example.Iface1");
310 sig.read(sigProperties);
311 assert(sigProperties["ExampleProperty1"] == "changed");
312 }
313 }
314 }
315
Brad Bishop49aefb32016-10-19 11:54:14 -0400316 mgr.shutdown();
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500317 std::cout << "Success!" << std::endl;
Brad Bishop49aefb32016-10-19 11:54:14 -0400318}
319
320int main()
321{
322 auto mgr = phosphor::inventory::manager::Manager(
Brad Bishop7b337772017-01-12 16:11:24 -0500323 sdbusplus::bus::new_default(),
324 SERVICE, ROOT, INTERFACE);
Brad Bishop49aefb32016-10-19 11:54:14 -0400325
326 pthread_t t;
327 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500328 pthread_create(&t, nullptr, server_thread, &mgr);
Brad Bishop49aefb32016-10-19 11:54:14 -0400329 }
330
331 runTests(mgr);
332
333 // Wait for server thread to exit.
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500334 pthread_join(t, nullptr);
Brad Bishop49aefb32016-10-19 11:54:14 -0400335
336 return 0;
337}
338
339// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4