1 #include <unistd.h>
2 #include <dbus/connection.hpp>
3 #include <dbus/endpoint.hpp>
4 #include <dbus/filter.hpp>
5 #include <dbus/match.hpp>
6 #include <dbus/message.hpp>
7 #include <dbus/properties.hpp>
8 #include <functional>
9 #include <vector>
10 #include <gmock/gmock.h>
11 #include <gtest/gtest.h>
12
13 static const std::string dbus_boilerplate(
14 "<!DOCTYPE node PUBLIC "
15 "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
16 "\"http://www.freedesktop.org/standards/dbus/1.0/"
17 "introspect.dtd\">\n");
18
TEST(DbusPropertiesInterface,EmptyObjectServer)19 TEST(DbusPropertiesInterface, EmptyObjectServer) {
20 boost::asio::io_service io;
21 auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
22
23 // Set up the object server, and send some test events
24 dbus::DbusObjectServer foo(system_bus);
25
26 EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + "<node></node>");
27 EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + "<node></node>");
28 }
29
TEST(DbusPropertiesInterface,BasicObjectServer)30 TEST(DbusPropertiesInterface, BasicObjectServer) {
31 boost::asio::io_service io;
32 auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
33
34 // Set up the object server, and send some test events
35 dbus::DbusObjectServer foo(system_bus);
36
37 foo.register_object(std::make_shared<dbus::DbusObject>(
38 system_bus, "/org/freedesktop/NetworkManager"));
39
40 EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
41 "<node><node "
42 "name=\"org\"></node></node>");
43 EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
44 "<node><node "
45 "name=\"org\"></node></node>");
46
47 EXPECT_EQ(foo.get_xml_for_path("/org"),
48 dbus_boilerplate +
49 "<node><node "
50 "name=\"freedesktop\"></node></node>");
51 EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
52 dbus_boilerplate +
53 "<node><node "
54 "name=\"NetworkManager\"></node></node>");
55 // TODO(Ed) turn this test back on once the signal interface stabilizes
56 /*EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/NetworkManager"),
57 dbus_boilerplate + "<node></node>");*/
58 }
59
TEST(DbusPropertiesInterface,SharedNodeObjectServer)60 TEST(DbusPropertiesInterface, SharedNodeObjectServer) {
61 boost::asio::io_service io;
62 auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
63
64 // Set up the object server, and send some test events
65 dbus::DbusObjectServer foo(system_bus);
66
67 foo.register_object(
68 std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test1"));
69 foo.register_object(
70 std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test2"));
71
72 EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
73 "<node><node "
74 "name=\"org\"></node></node>");
75 EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
76 "<node><node "
77 "name=\"org\"></node></node>");
78
79 EXPECT_EQ(foo.get_xml_for_path("/org"),
80 dbus_boilerplate +
81 "<node><node "
82 "name=\"freedesktop\"></node></node>");
83 EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
84 dbus_boilerplate +
85 "<node><node "
86 "name=\"test1\"></node><node name=\"test2\"></node></node>");
87 // TODO(Ed) turn this test back on once the signal interface stabilizes
88 /*
89 EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test1"),
90 dbus_boilerplate + "<node></node>");
91 EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test2"),
92 dbus_boilerplate + "<node></node>");
93 */
94 }
95
TEST(LambdaDbusMethodTest,Basic)96 TEST(LambdaDbusMethodTest, Basic) {
97 bool lambda_called = false;
98 auto lambda = [&](int32_t x) {
99 EXPECT_EQ(x, 18);
100 lambda_called = true;
101 return std::make_tuple<int64_t, int32_t>(4L, 2);
102 };
103 boost::asio::io_service io;
104 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
105 auto dbus_method =
106 dbus::LambdaDbusMethod<decltype(lambda)>("foo", bus, lambda);
107
108 dbus::message m =
109 dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/",
110 "org.freedesktop.Avahi.Server"),
111 "GetHostName");
112 m.pack(static_cast<int32_t>(18));
113 // Small thing that the dbus library normally does for us, but because we're
114 // bypassing it, we need to fill it in as if it was done
115 m.set_serial(585);
116 dbus_method.call(m);
117 EXPECT_EQ(lambda_called, true);
118 }
119
TEST(DbusPropertiesInterface,ObjectServer)120 TEST(DbusPropertiesInterface, ObjectServer) {
121 boost::asio::io_service io;
122 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
123
124 // Set up the object server, and send some test objects
125 dbus::DbusObjectServer foo(bus);
126
127 foo.register_object(
128 std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
129 foo.register_object(
130 std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test2"));
131 int completion_count(0);
132
133 std::array<std::string, 4> paths_to_test(
134 {{"/", "/org", "/org/freedesktop", "/org/freedesktop/test1"}});
135
136 for (auto& path : paths_to_test) {
137 dbus::endpoint test_daemon(bus->get_unique_name(), path,
138 "org.freedesktop.DBus.Introspectable");
139 dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
140 completion_count++;
141 bus->async_send(
142 m, [&](const boost::system::error_code ec, dbus::message r) {
143 if (ec) {
144 std::string error;
145 r.unpack(error);
146 FAIL() << ec << error;
147 } else {
148 std::string xml;
149 r.unpack(xml);
150 EXPECT_EQ(r.get_type(), "method_return");
151 if (path == "/") {
152 EXPECT_EQ(xml, dbus_boilerplate +
153 "<node><node "
154 "name=\"org\"></node></node>");
155 } else if (path == "/org") {
156 EXPECT_EQ(xml, dbus_boilerplate +
157 "<node><node "
158 "name=\"freedesktop\"></node></node>");
159 } else if (path == "/org/freedesktop") {
160 EXPECT_EQ(xml, dbus_boilerplate +
161 "<node><node "
162 "name=\"test1\"></node><node "
163 "name=\"test2\"></node></node>");
164 } else if (path == "/org/freedesktop/test1") {
165 } else {
166 FAIL() << "Unknown path: " << path;
167 }
168 }
169 completion_count--;
170 if (completion_count == 0) {
171 io.stop();
172 }
173 });
174 }
175 io.run();
176 }
177
TEST(DbusPropertiesInterface,EmptyMethodServer)178 TEST(DbusPropertiesInterface, EmptyMethodServer) {
179 boost::asio::io_service io;
180 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
181
182 // Set up the object server, and send some test objects
183 dbus::DbusObjectServer foo(bus);
184 foo.register_object(
185 std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
186
187 dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
188 "org.freedesktop.DBus.Introspectable");
189 dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
190
191 bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
192 if (ec) {
193 std::string error;
194 r.unpack(error);
195 FAIL() << ec << error;
196 } else {
197 std::cout << r;
198 std::string xml;
199 r.unpack(xml);
200 EXPECT_EQ(r.get_type(), "method_return");
201 // TODO(ed) turn back on when method interface stabilizes
202 // EXPECT_EQ(xml, dbus_boilerplate + "<node></node>");
203 }
204
205 io.stop();
206
207 });
208
209 io.run();
210 }
211
test_method(uint32_t x)212 std::tuple<int> test_method(uint32_t x) {
213 std::cout << "method called.\n";
214 return std::make_tuple<int>(42);
215 }
216
217 class TestClass {
218 public:
test_method(uint32_t x)219 static std::tuple<int> test_method(uint32_t x) {
220 std::cout << "method called.\n";
221 return std::make_tuple<int>(42);
222 }
223 };
224
TEST(DbusPropertiesInterface,MethodServer)225 TEST(DbusPropertiesInterface, MethodServer) {
226 boost::asio::io_service io;
227 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
228
229 // Set up the object server, and send some test objects
230 dbus::DbusObjectServer foo(bus);
231 auto object =
232 std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
233 foo.register_object(object);
234
235 auto iface = std::make_shared<dbus::DbusInterface>(
236 "org.freedesktop.My.Interface", bus);
237 object->register_interface(iface);
238
239 // Test multiple ways to register methods
240 // Basic lambda
241 iface->register_method("MyMethodLambda", [](uint32_t x) {
242
243 std::cout << "method called. Got:" << x << "\n";
244 return std::make_tuple<int>(42);
245 });
246
247 // std::function
248 std::function<typename std::tuple<int>(uint32_t)> my_function =
249 [](uint32_t x) {
250
251 std::cout << "method called. Got:" << x << "\n";
252 return std::make_tuple<int>(42);
253 };
254
255 iface->register_method("MyMethodStdFunction", my_function);
256
257 // Function pointer
258 iface->register_method("MyMethodFunctionPointer", &test_method);
259
260 // Class function pointer
261 TestClass t;
262 iface->register_method("MyClassFunctionPointer", t.test_method);
263
264 // const class function pointer
265 const TestClass t2;
266 iface->register_method("MyClassFunctionPointer", t2.test_method);
267
268 iface->register_method("VoidMethod", []() {
269
270 std::cout << "method called.\n";
271 return (uint32_t)42;
272 });
273
274 iface->register_method("VoidMethod", []() {
275
276 std::cout << "method called.\n";
277 return std::make_tuple<int>(42);
278 });
279
280 // Test multiple ways to register methods
281 // Basic lambda
282 iface->register_method("MyMethodLambda", {"x"}, {"return_value_name"},
283 [](uint32_t x) {
284 std::cout << "method called. Got:" << x << "\n";
285 return 42;
286 });
287
288 dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
289 "org.freedesktop.DBus.Introspectable");
290 dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
291
292 bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
293 if (ec) {
294 std::string error;
295 r.unpack(error);
296
297 FAIL() << ec << error;
298 } else {
299 std::string xml;
300 r.unpack(xml);
301 EXPECT_EQ(r.get_type(), "method_return");
302 // todo(ED)
303 /*
304 EXPECT_EQ(xml, dbus_boilerplate +
305 "<node><interface "
306 "name=\"MyInterface\"><method "
307 "name=\"MyMethod\"></method></interface></node>");
308 */
309 }
310 io.stop();
311 });
312
313 io.run();
314 }
315
TEST(DbusPropertiesInterface,PropertiesInterface)316 TEST(DbusPropertiesInterface, PropertiesInterface) {
317 boost::asio::io_service io;
318 auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
319
320 // Set up the object server, and send some test objects
321 dbus::DbusObjectServer foo(bus);
322 auto object =
323 std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
324 foo.register_object(object);
325
326 auto iface = std::make_shared<dbus::DbusInterface>(
327 "org.freedesktop.My.Interface", bus);
328 object->register_interface(iface);
329
330 iface->set_property("foo", (uint32_t)26);
331
332 dbus::endpoint get_dbus_properties(bus->get_unique_name(),
333 "/org/freedesktop/test1",
334 "org.freedesktop.DBus.Properties", "Get");
335 size_t outstanding_async_calls = 0;
336
337 outstanding_async_calls++;
338 bus->async_method_call(
339 [&](const boost::system::error_code ec, dbus::dbus_variant value) {
340 outstanding_async_calls--;
341 if (ec) {
342 FAIL() << ec;
343 } else {
344 EXPECT_EQ(boost::get<uint32_t>(value), 26);
345 }
346 if (outstanding_async_calls == 0) {
347 io.stop();
348 }
349 },
350 get_dbus_properties, "org.freedesktop.My.Interface", "foo");
351
352 dbus::endpoint getall_dbus_properties(
353 bus->get_unique_name(), "/org/freedesktop/test1",
354 "org.freedesktop.DBus.Properties", "GetAll");
355
356 outstanding_async_calls++;
357 bus->async_method_call(
358 [&](const boost::system::error_code ec,
359 std::vector<std::pair<std::string, dbus::dbus_variant>> value) {
360 outstanding_async_calls--;
361 if (ec) {
362 FAIL() << ec;
363 } else {
364 EXPECT_EQ(value.size(), 1);
365 EXPECT_EQ(value[0].first, "foo");
366 EXPECT_EQ(value[0].second, dbus::dbus_variant((uint32_t)26));
367 }
368 if (outstanding_async_calls == 0) {
369 io.stop();
370 }
371 },
372 getall_dbus_properties, "org.freedesktop.My.Interface");
373
374 io.run();
375 }
376