1*a909a822SEd Tanous #include <unistd.h>
2377e76abSEd Tanous #include <dbus/connection.hpp>
3377e76abSEd Tanous #include <dbus/endpoint.hpp>
4377e76abSEd Tanous #include <dbus/filter.hpp>
5377e76abSEd Tanous #include <dbus/match.hpp>
6377e76abSEd Tanous #include <dbus/message.hpp>
7377e76abSEd Tanous #include <dbus/properties.hpp>
8377e76abSEd Tanous #include <functional>
9*a909a822SEd Tanous #include <vector>
10377e76abSEd Tanous #include <gmock/gmock.h>
11377e76abSEd Tanous #include <gtest/gtest.h>
12377e76abSEd Tanous
13377e76abSEd Tanous static const std::string dbus_boilerplate(
14377e76abSEd Tanous "<!DOCTYPE node PUBLIC "
15377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
16377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/"
17377e76abSEd Tanous "introspect.dtd\">\n");
18377e76abSEd Tanous
TEST(DbusPropertiesInterface,EmptyObjectServer)19377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyObjectServer) {
20377e76abSEd Tanous boost::asio::io_service io;
21377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
22377e76abSEd Tanous
23377e76abSEd Tanous // Set up the object server, and send some test events
24377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus);
25377e76abSEd Tanous
26377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + "<node></node>");
27377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + "<node></node>");
28377e76abSEd Tanous }
29377e76abSEd Tanous
TEST(DbusPropertiesInterface,BasicObjectServer)30377e76abSEd Tanous TEST(DbusPropertiesInterface, BasicObjectServer) {
31377e76abSEd Tanous boost::asio::io_service io;
32377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
33377e76abSEd Tanous
34377e76abSEd Tanous // Set up the object server, and send some test events
35377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus);
36377e76abSEd Tanous
37377e76abSEd Tanous foo.register_object(std::make_shared<dbus::DbusObject>(
38377e76abSEd Tanous system_bus, "/org/freedesktop/NetworkManager"));
39377e76abSEd Tanous
40377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
41377e76abSEd Tanous "<node><node "
42377e76abSEd Tanous "name=\"org\"></node></node>");
43377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
44377e76abSEd Tanous "<node><node "
45377e76abSEd Tanous "name=\"org\"></node></node>");
46377e76abSEd Tanous
47377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"),
48377e76abSEd Tanous dbus_boilerplate +
49377e76abSEd Tanous "<node><node "
50377e76abSEd Tanous "name=\"freedesktop\"></node></node>");
51377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
52377e76abSEd Tanous dbus_boilerplate +
53377e76abSEd Tanous "<node><node "
54377e76abSEd Tanous "name=\"NetworkManager\"></node></node>");
55377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes
56377e76abSEd Tanous /*EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/NetworkManager"),
57377e76abSEd Tanous dbus_boilerplate + "<node></node>");*/
58377e76abSEd Tanous }
59377e76abSEd Tanous
TEST(DbusPropertiesInterface,SharedNodeObjectServer)60377e76abSEd Tanous TEST(DbusPropertiesInterface, SharedNodeObjectServer) {
61377e76abSEd Tanous boost::asio::io_service io;
62377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
63377e76abSEd Tanous
64377e76abSEd Tanous // Set up the object server, and send some test events
65377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus);
66377e76abSEd Tanous
67377e76abSEd Tanous foo.register_object(
68377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test1"));
69377e76abSEd Tanous foo.register_object(
70377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test2"));
71377e76abSEd Tanous
72377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
73377e76abSEd Tanous "<node><node "
74377e76abSEd Tanous "name=\"org\"></node></node>");
75377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
76377e76abSEd Tanous "<node><node "
77377e76abSEd Tanous "name=\"org\"></node></node>");
78377e76abSEd Tanous
79377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"),
80377e76abSEd Tanous dbus_boilerplate +
81377e76abSEd Tanous "<node><node "
82377e76abSEd Tanous "name=\"freedesktop\"></node></node>");
83377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
84377e76abSEd Tanous dbus_boilerplate +
85377e76abSEd Tanous "<node><node "
86377e76abSEd Tanous "name=\"test1\"></node><node name=\"test2\"></node></node>");
87377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes
88377e76abSEd Tanous /*
89377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test1"),
90377e76abSEd Tanous dbus_boilerplate + "<node></node>");
91377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test2"),
92377e76abSEd Tanous dbus_boilerplate + "<node></node>");
93377e76abSEd Tanous */
94377e76abSEd Tanous }
95377e76abSEd Tanous
TEST(LambdaDbusMethodTest,Basic)96377e76abSEd Tanous TEST(LambdaDbusMethodTest, Basic) {
97377e76abSEd Tanous bool lambda_called = false;
98377e76abSEd Tanous auto lambda = [&](int32_t x) {
99377e76abSEd Tanous EXPECT_EQ(x, 18);
100377e76abSEd Tanous lambda_called = true;
101377e76abSEd Tanous return std::make_tuple<int64_t, int32_t>(4L, 2);
102377e76abSEd Tanous };
103377e76abSEd Tanous boost::asio::io_service io;
104377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
105377e76abSEd Tanous auto dbus_method =
106377e76abSEd Tanous dbus::LambdaDbusMethod<decltype(lambda)>("foo", bus, lambda);
107377e76abSEd Tanous
108377e76abSEd Tanous dbus::message m =
109377e76abSEd Tanous dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/",
110377e76abSEd Tanous "org.freedesktop.Avahi.Server"),
111377e76abSEd Tanous "GetHostName");
112377e76abSEd Tanous m.pack(static_cast<int32_t>(18));
113377e76abSEd Tanous // Small thing that the dbus library normally does for us, but because we're
114377e76abSEd Tanous // bypassing it, we need to fill it in as if it was done
115377e76abSEd Tanous m.set_serial(585);
116377e76abSEd Tanous dbus_method.call(m);
117377e76abSEd Tanous EXPECT_EQ(lambda_called, true);
118377e76abSEd Tanous }
119377e76abSEd Tanous
TEST(DbusPropertiesInterface,ObjectServer)120377e76abSEd Tanous TEST(DbusPropertiesInterface, ObjectServer) {
121377e76abSEd Tanous boost::asio::io_service io;
122377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
123377e76abSEd Tanous
124377e76abSEd Tanous // Set up the object server, and send some test objects
125377e76abSEd Tanous dbus::DbusObjectServer foo(bus);
126377e76abSEd Tanous
127377e76abSEd Tanous foo.register_object(
128377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
129377e76abSEd Tanous foo.register_object(
130377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test2"));
131a8b4eac4SEd Tanous int completion_count(0);
132377e76abSEd Tanous
133377e76abSEd Tanous std::array<std::string, 4> paths_to_test(
134377e76abSEd Tanous {{"/", "/org", "/org/freedesktop", "/org/freedesktop/test1"}});
135377e76abSEd Tanous
136377e76abSEd Tanous for (auto& path : paths_to_test) {
137377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), path,
138377e76abSEd Tanous "org.freedesktop.DBus.Introspectable");
139377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
140377e76abSEd Tanous completion_count++;
141377e76abSEd Tanous bus->async_send(
142377e76abSEd Tanous m, [&](const boost::system::error_code ec, dbus::message r) {
143377e76abSEd Tanous if (ec) {
144377e76abSEd Tanous std::string error;
145377e76abSEd Tanous r.unpack(error);
146377e76abSEd Tanous FAIL() << ec << error;
147377e76abSEd Tanous } else {
148377e76abSEd Tanous std::string xml;
149377e76abSEd Tanous r.unpack(xml);
150377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return");
151377e76abSEd Tanous if (path == "/") {
152377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate +
153377e76abSEd Tanous "<node><node "
154377e76abSEd Tanous "name=\"org\"></node></node>");
155377e76abSEd Tanous } else if (path == "/org") {
156377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate +
157377e76abSEd Tanous "<node><node "
158377e76abSEd Tanous "name=\"freedesktop\"></node></node>");
159377e76abSEd Tanous } else if (path == "/org/freedesktop") {
160377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate +
161377e76abSEd Tanous "<node><node "
162377e76abSEd Tanous "name=\"test1\"></node><node "
163377e76abSEd Tanous "name=\"test2\"></node></node>");
164377e76abSEd Tanous } else if (path == "/org/freedesktop/test1") {
165377e76abSEd Tanous } else {
166377e76abSEd Tanous FAIL() << "Unknown path: " << path;
167377e76abSEd Tanous }
168377e76abSEd Tanous }
169377e76abSEd Tanous completion_count--;
170377e76abSEd Tanous if (completion_count == 0) {
171377e76abSEd Tanous io.stop();
172377e76abSEd Tanous }
173377e76abSEd Tanous });
174377e76abSEd Tanous }
175377e76abSEd Tanous io.run();
176377e76abSEd Tanous }
177377e76abSEd Tanous
TEST(DbusPropertiesInterface,EmptyMethodServer)178377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyMethodServer) {
179377e76abSEd Tanous boost::asio::io_service io;
180377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
181377e76abSEd Tanous
182377e76abSEd Tanous // Set up the object server, and send some test objects
183377e76abSEd Tanous dbus::DbusObjectServer foo(bus);
184377e76abSEd Tanous foo.register_object(
185377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
186377e76abSEd Tanous
187377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
188377e76abSEd Tanous "org.freedesktop.DBus.Introspectable");
189377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
190377e76abSEd Tanous
191377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
192377e76abSEd Tanous if (ec) {
193377e76abSEd Tanous std::string error;
194377e76abSEd Tanous r.unpack(error);
195377e76abSEd Tanous FAIL() << ec << error;
196377e76abSEd Tanous } else {
197377e76abSEd Tanous std::cout << r;
198377e76abSEd Tanous std::string xml;
199377e76abSEd Tanous r.unpack(xml);
200377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return");
201377e76abSEd Tanous // TODO(ed) turn back on when method interface stabilizes
202377e76abSEd Tanous // EXPECT_EQ(xml, dbus_boilerplate + "<node></node>");
203377e76abSEd Tanous }
204377e76abSEd Tanous
205377e76abSEd Tanous io.stop();
206377e76abSEd Tanous
207377e76abSEd Tanous });
208377e76abSEd Tanous
209377e76abSEd Tanous io.run();
210377e76abSEd Tanous }
211377e76abSEd Tanous
test_method(uint32_t x)212e3b0bf5bSEd Tanous std::tuple<int> test_method(uint32_t x) {
213e3b0bf5bSEd Tanous std::cout << "method called.\n";
214e3b0bf5bSEd Tanous return std::make_tuple<int>(42);
215e3b0bf5bSEd Tanous }
216e3b0bf5bSEd Tanous
217e3b0bf5bSEd Tanous class TestClass {
218e3b0bf5bSEd Tanous public:
test_method(uint32_t x)219e3b0bf5bSEd Tanous static std::tuple<int> test_method(uint32_t x) {
220e3b0bf5bSEd Tanous std::cout << "method called.\n";
221e3b0bf5bSEd Tanous return std::make_tuple<int>(42);
222e3b0bf5bSEd Tanous }
223e3b0bf5bSEd Tanous };
224e3b0bf5bSEd Tanous
TEST(DbusPropertiesInterface,MethodServer)225377e76abSEd Tanous TEST(DbusPropertiesInterface, MethodServer) {
226377e76abSEd Tanous boost::asio::io_service io;
227377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
228377e76abSEd Tanous
229377e76abSEd Tanous // Set up the object server, and send some test objects
230377e76abSEd Tanous dbus::DbusObjectServer foo(bus);
231377e76abSEd Tanous auto object =
232377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
233377e76abSEd Tanous foo.register_object(object);
234377e76abSEd Tanous
235377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>(
236377e76abSEd Tanous "org.freedesktop.My.Interface", bus);
237377e76abSEd Tanous object->register_interface(iface);
238377e76abSEd Tanous
239e3b0bf5bSEd Tanous // Test multiple ways to register methods
240e3b0bf5bSEd Tanous // Basic lambda
241e3b0bf5bSEd Tanous iface->register_method("MyMethodLambda", [](uint32_t x) {
242377e76abSEd Tanous
243377e76abSEd Tanous std::cout << "method called. Got:" << x << "\n";
244377e76abSEd Tanous return std::make_tuple<int>(42);
245377e76abSEd Tanous });
246377e76abSEd Tanous
247e3b0bf5bSEd Tanous // std::function
248e3b0bf5bSEd Tanous std::function<typename std::tuple<int>(uint32_t)> my_function =
249e3b0bf5bSEd Tanous [](uint32_t x) {
250e3b0bf5bSEd Tanous
251e3b0bf5bSEd Tanous std::cout << "method called. Got:" << x << "\n";
252e3b0bf5bSEd Tanous return std::make_tuple<int>(42);
253e3b0bf5bSEd Tanous };
254e3b0bf5bSEd Tanous
255e3b0bf5bSEd Tanous iface->register_method("MyMethodStdFunction", my_function);
256e3b0bf5bSEd Tanous
257e3b0bf5bSEd Tanous // Function pointer
258e3b0bf5bSEd Tanous iface->register_method("MyMethodFunctionPointer", &test_method);
259e3b0bf5bSEd Tanous
260e3b0bf5bSEd Tanous // Class function pointer
261e3b0bf5bSEd Tanous TestClass t;
262e3b0bf5bSEd Tanous iface->register_method("MyClassFunctionPointer", t.test_method);
263e3b0bf5bSEd Tanous
264e3b0bf5bSEd Tanous // const class function pointer
265e3b0bf5bSEd Tanous const TestClass t2;
266e3b0bf5bSEd Tanous iface->register_method("MyClassFunctionPointer", t2.test_method);
267e3b0bf5bSEd Tanous
268e3b0bf5bSEd Tanous iface->register_method("VoidMethod", []() {
269e3b0bf5bSEd Tanous
270e3b0bf5bSEd Tanous std::cout << "method called.\n";
271e3b0bf5bSEd Tanous return (uint32_t)42;
272e3b0bf5bSEd Tanous });
273e3b0bf5bSEd Tanous
274377e76abSEd Tanous iface->register_method("VoidMethod", []() {
275377e76abSEd Tanous
276377e76abSEd Tanous std::cout << "method called.\n";
277377e76abSEd Tanous return std::make_tuple<int>(42);
278377e76abSEd Tanous });
279377e76abSEd Tanous
280e3b0bf5bSEd Tanous // Test multiple ways to register methods
281e3b0bf5bSEd Tanous // Basic lambda
282e3b0bf5bSEd Tanous iface->register_method("MyMethodLambda", {"x"}, {"return_value_name"},
283e3b0bf5bSEd Tanous [](uint32_t x) {
284e3b0bf5bSEd Tanous std::cout << "method called. Got:" << x << "\n";
285e3b0bf5bSEd Tanous return 42;
286e3b0bf5bSEd Tanous });
287377e76abSEd Tanous
288377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
289377e76abSEd Tanous "org.freedesktop.DBus.Introspectable");
290377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
291377e76abSEd Tanous
292377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
293377e76abSEd Tanous if (ec) {
294377e76abSEd Tanous std::string error;
295377e76abSEd Tanous r.unpack(error);
296377e76abSEd Tanous
297377e76abSEd Tanous FAIL() << ec << error;
298377e76abSEd Tanous } else {
299377e76abSEd Tanous std::string xml;
300377e76abSEd Tanous r.unpack(xml);
301377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return");
302377e76abSEd Tanous // todo(ED)
303377e76abSEd Tanous /*
304377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate +
305377e76abSEd Tanous "<node><interface "
306377e76abSEd Tanous "name=\"MyInterface\"><method "
307377e76abSEd Tanous "name=\"MyMethod\"></method></interface></node>");
308377e76abSEd Tanous */
309377e76abSEd Tanous }
310377e76abSEd Tanous io.stop();
311377e76abSEd Tanous });
312377e76abSEd Tanous
313377e76abSEd Tanous io.run();
314377e76abSEd Tanous }
315377e76abSEd Tanous
TEST(DbusPropertiesInterface,PropertiesInterface)316377e76abSEd Tanous TEST(DbusPropertiesInterface, PropertiesInterface) {
317377e76abSEd Tanous boost::asio::io_service io;
318377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
319377e76abSEd Tanous
320377e76abSEd Tanous // Set up the object server, and send some test objects
321377e76abSEd Tanous dbus::DbusObjectServer foo(bus);
322377e76abSEd Tanous auto object =
323377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
324377e76abSEd Tanous foo.register_object(object);
325377e76abSEd Tanous
326377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>(
327377e76abSEd Tanous "org.freedesktop.My.Interface", bus);
328377e76abSEd Tanous object->register_interface(iface);
329377e76abSEd Tanous
330377e76abSEd Tanous iface->set_property("foo", (uint32_t)26);
331377e76abSEd Tanous
332*a909a822SEd Tanous dbus::endpoint get_dbus_properties(bus->get_unique_name(),
333*a909a822SEd Tanous "/org/freedesktop/test1",
334*a909a822SEd Tanous "org.freedesktop.DBus.Properties", "Get");
335*a909a822SEd Tanous size_t outstanding_async_calls = 0;
336377e76abSEd Tanous
337*a909a822SEd Tanous outstanding_async_calls++;
338*a909a822SEd Tanous bus->async_method_call(
339*a909a822SEd Tanous [&](const boost::system::error_code ec, dbus::dbus_variant value) {
340*a909a822SEd Tanous outstanding_async_calls--;
341377e76abSEd Tanous if (ec) {
342*a909a822SEd Tanous FAIL() << ec;
343377e76abSEd Tanous } else {
344377e76abSEd Tanous EXPECT_EQ(boost::get<uint32_t>(value), 26);
345377e76abSEd Tanous }
346*a909a822SEd Tanous if (outstanding_async_calls == 0) {
347377e76abSEd Tanous io.stop();
348*a909a822SEd Tanous }
349*a909a822SEd Tanous },
350*a909a822SEd Tanous get_dbus_properties, "org.freedesktop.My.Interface", "foo");
351*a909a822SEd Tanous
352*a909a822SEd Tanous dbus::endpoint getall_dbus_properties(
353*a909a822SEd Tanous bus->get_unique_name(), "/org/freedesktop/test1",
354*a909a822SEd Tanous "org.freedesktop.DBus.Properties", "GetAll");
355*a909a822SEd Tanous
356*a909a822SEd Tanous outstanding_async_calls++;
357*a909a822SEd Tanous bus->async_method_call(
358*a909a822SEd Tanous [&](const boost::system::error_code ec,
359*a909a822SEd Tanous std::vector<std::pair<std::string, dbus::dbus_variant>> value) {
360*a909a822SEd Tanous outstanding_async_calls--;
361*a909a822SEd Tanous if (ec) {
362*a909a822SEd Tanous FAIL() << ec;
363*a909a822SEd Tanous } else {
364*a909a822SEd Tanous EXPECT_EQ(value.size(), 1);
365*a909a822SEd Tanous EXPECT_EQ(value[0].first, "foo");
366*a909a822SEd Tanous EXPECT_EQ(value[0].second, dbus::dbus_variant((uint32_t)26));
367*a909a822SEd Tanous }
368*a909a822SEd Tanous if (outstanding_async_calls == 0) {
369*a909a822SEd Tanous io.stop();
370*a909a822SEd Tanous }
371*a909a822SEd Tanous },
372*a909a822SEd Tanous getall_dbus_properties, "org.freedesktop.My.Interface");
373377e76abSEd Tanous
374377e76abSEd Tanous io.run();
375377e76abSEd Tanous }
376