blob: 0a1fbd9a8bff5beada047bf8e670129412cdf16e [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
26auto server_thread(void *data)
27{
28 auto mgr = static_cast<phosphor::inventory::manager::Manager*>(data);
29
30 mgr->run();
31
32 return static_cast<void *>(nullptr);
33}
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:
41 ~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)
51 {
52 }
53
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 }
64
65 private:
66 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 }
74
75 sdbusplus::bus::bus _bus;
76 sdbusplus::server::match::match _match;
77 sdbusplus::message::message _next;
78};
79
80template <typename ...T>
81using Object = std::map<
82 std::string,
83 std::map<
84 std::string,
85 sdbusplus::message::variant<T...>>>;
86
87using ObjectPath = std::string;
88
89/**@brief Find a subset of interfaces and properties in an object. */
90template <typename ...T>
91auto hasProperties(const Object<T...> &l, const Object<T...> &r)
92{
93 Object<T...> result;
94 std::set_difference(
95 r.cbegin(),
96 r.cend(),
97 l.cbegin(),
98 l.cend(),
99 std::inserter(result, result.end()));
100 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>
105auto hasInterfaces(const std::vector<std::string> &l, const Object<T...> &r)
106{
107 std::vector<std::string> stripped, interfaces;
108 std::transform(
109 r.cbegin(),
110 r.cend(),
111 std::back_inserter(stripped),
112 [](auto &p){ return p.first; });
113 std::set_difference(
114 stripped.cbegin(),
115 stripped.cend(),
116 l.cbegin(),
117 l.cend(),
118 std::back_inserter(interfaces));
119 return interfaces.empty();
120}
121
Brad Bishop49aefb32016-10-19 11:54:14 -0400122void runTests(phosphor::inventory::manager::Manager &mgr)
123{
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500124 const std::string root{ROOT};
Brad Bishop49aefb32016-10-19 11:54:14 -0400125 auto b = sdbusplus::bus::new_default();
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500126 auto notify = [&]()
Brad Bishop49aefb32016-10-19 11:54:14 -0400127 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500128 return b.new_method_call(
129 SERVICE,
130 ROOT,
131 INTERFACE,
132 "Notify");
133 };
Brad Bishop432e3522016-12-01 00:24:14 -0500134 auto set = [&](const std::string &path)
135 {
136 return b.new_method_call(
137 SERVICE,
138 path.c_str(),
139 "org.freedesktop.DBus.Properties",
140 "Set");
141 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400142
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500143 Object<std::string> obj{
144 {
145 "xyz.openbmc_project.Example.Iface1",
146 {{"ExampleProperty1", "test1"}}
147 },
148 {
149 "xyz.openbmc_project.Example.Iface2",
150 {{"ExampleProperty2", "test2"}}
151 },
152 };
Brad Bishop49aefb32016-10-19 11:54:14 -0400153
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500154 // Make sure the notify method works.
155 {
156 ObjectPath relPath{"/foo"};
157 ObjectPath path{root + relPath};
Brad Bishop49aefb32016-10-19 11:54:14 -0400158
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500159 SignalQueue queue(
160 "path='" + root + "',member='InterfacesAdded'");
161
162 auto m = notify();
163 m.append(relPath);
164 m.append(obj);
165 b.call(m);
166
167 auto sig{queue.pop()};
168 assert(sig);
169 ObjectPath signalPath;
170 Object<std::string> signalObject;
171 sig.read(signalPath);
172 assert(path == signalPath);
173 sig.read(signalObject);
174 assert(hasProperties(signalObject, obj));
175 auto moreSignals{queue.pop()};
176 assert(!moreSignals);
Brad Bishop49aefb32016-10-19 11:54:14 -0400177 }
178
Brad Bishop432e3522016-12-01 00:24:14 -0500179 // Make sure DBus signals are handled.
180 {
181 ObjectPath relDeleteMeOne{"/deleteme1"};
182 ObjectPath relDeleteMeTwo{"/deleteme2"};
183 ObjectPath relTriggerOne{"/trigger1"};
184 ObjectPath deleteMeOne{root + relDeleteMeOne};
185 ObjectPath deleteMeTwo{root + relDeleteMeTwo};
186 ObjectPath triggerOne{root + relTriggerOne};
187
188 // Create some objects to be deleted by an action.
189 {
190 auto m = notify();
191 m.append(relDeleteMeOne);
192 m.append(obj);
193 b.call(m);
194 }
195 {
196 auto m = notify();
197 m.append(relDeleteMeTwo);
198 m.append(obj);
199 b.call(m);
200 }
201
202 // Create the triggering object.
203 {
204 auto m = notify();
205 m.append(relTriggerOne);
206 m.append(obj);
207 b.call(m);
208 }
209
210 // Set a property that should not trigger due to a filter.
211 {
212 SignalQueue queue(
213 "path='" + root + "',member='InterfacesRemoved'");
214 auto m = set(triggerOne);
215 m.append("xyz.openbmc_project.Example.Iface2");
216 m.append("ExampleProperty2");
217 m.append(sdbusplus::message::variant<std::string>("abc123"));
218 b.call(m);
219 auto sig{queue.pop()};
220 assert(!sig);
221 }
222
223 // Set a property that should trigger.
224 {
225 SignalQueue queue(
226 "path='" + root + "',member='InterfacesRemoved'");
227
228 auto m = set(triggerOne);
229 m.append("xyz.openbmc_project.Example.Iface2");
230 m.append("ExampleProperty2");
231 m.append(sdbusplus::message::variant<std::string>("xxxyyy"));
232 b.call(m);
233
234 ObjectPath sigpath;
235 std::vector<std::string> interfaces;
236 {
237 std::vector<std::string> interfaces;
238 auto sig{queue.pop()};
239 assert(sig);
240 sig.read(sigpath);
241 assert(sigpath == deleteMeOne);
242 sig.read(interfaces);
243 std::sort(interfaces.begin(), interfaces.end());
244 assert(hasInterfaces(interfaces, obj));
245 }
246 {
247 std::vector<std::string> interfaces;
248 auto sig{queue.pop()};
249 assert(sig);
250 sig.read(sigpath);
251 assert(sigpath == deleteMeTwo);
252 sig.read(interfaces);
253 std::sort(interfaces.begin(), interfaces.end());
254 assert(hasInterfaces(interfaces, obj));
255 }
256 {
257 // Make sure there were only two signals.
258 auto sig{queue.pop()};
259 assert(!sig);
260 }
261 }
262 }
263
Brad Bishop49aefb32016-10-19 11:54:14 -0400264 mgr.shutdown();
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500265 std::cout << "Success!" << std::endl;
Brad Bishop49aefb32016-10-19 11:54:14 -0400266}
267
268int main()
269{
270 auto mgr = phosphor::inventory::manager::Manager(
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500271 sdbusplus::bus::new_default(),
Brad Bishop49aefb32016-10-19 11:54:14 -0400272 SERVICE, ROOT, INTERFACE);
273
274 pthread_t t;
275 {
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500276 pthread_create(&t, nullptr, server_thread, &mgr);
Brad Bishop49aefb32016-10-19 11:54:14 -0400277 }
278
279 runTests(mgr);
280
281 // Wait for server thread to exit.
Brad Bishopabb2a1a2016-11-30 22:02:28 -0500282 pthread_join(t, nullptr);
Brad Bishop49aefb32016-10-19 11:54:14 -0400283
284 return 0;
285}
286
287// vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4