1377e76abSEd Tanous #include <dbus/connection.hpp>
2377e76abSEd Tanous #include <dbus/endpoint.hpp>
3377e76abSEd Tanous #include <dbus/filter.hpp>
4377e76abSEd Tanous #include <dbus/match.hpp>
5377e76abSEd Tanous #include <dbus/message.hpp>
6377e76abSEd Tanous #include <dbus/properties.hpp>
7377e76abSEd Tanous #include <functional>
8377e76abSEd Tanous 
9377e76abSEd Tanous #include <unistd.h>
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 
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 
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 
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 
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 
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"));
131377e76abSEd Tanous   std::atomic<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 
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 
212*e3b0bf5bSEd Tanous std::tuple<int> test_method(uint32_t x) {
213*e3b0bf5bSEd Tanous   std::cout << "method called.\n";
214*e3b0bf5bSEd Tanous   return std::make_tuple<int>(42);
215*e3b0bf5bSEd Tanous }
216*e3b0bf5bSEd Tanous 
217*e3b0bf5bSEd Tanous class TestClass {
218*e3b0bf5bSEd Tanous  public:
219*e3b0bf5bSEd Tanous   static std::tuple<int> test_method(uint32_t x) {
220*e3b0bf5bSEd Tanous     std::cout << "method called.\n";
221*e3b0bf5bSEd Tanous     return std::make_tuple<int>(42);
222*e3b0bf5bSEd Tanous   }
223*e3b0bf5bSEd Tanous };
224*e3b0bf5bSEd Tanous 
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 
239*e3b0bf5bSEd Tanous   // Test multiple ways to register methods
240*e3b0bf5bSEd Tanous   // Basic lambda
241*e3b0bf5bSEd 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 
247*e3b0bf5bSEd Tanous   // std::function
248*e3b0bf5bSEd Tanous   std::function<typename std::tuple<int>(uint32_t)> my_function =
249*e3b0bf5bSEd Tanous       [](uint32_t x) {
250*e3b0bf5bSEd Tanous 
251*e3b0bf5bSEd Tanous         std::cout << "method called.  Got:" << x << "\n";
252*e3b0bf5bSEd Tanous         return std::make_tuple<int>(42);
253*e3b0bf5bSEd Tanous       };
254*e3b0bf5bSEd Tanous 
255*e3b0bf5bSEd Tanous   iface->register_method("MyMethodStdFunction", my_function);
256*e3b0bf5bSEd Tanous 
257*e3b0bf5bSEd Tanous   // Function pointer
258*e3b0bf5bSEd Tanous   iface->register_method("MyMethodFunctionPointer", &test_method);
259*e3b0bf5bSEd Tanous 
260*e3b0bf5bSEd Tanous   // Class function pointer
261*e3b0bf5bSEd Tanous   TestClass t;
262*e3b0bf5bSEd Tanous   iface->register_method("MyClassFunctionPointer", t.test_method);
263*e3b0bf5bSEd Tanous 
264*e3b0bf5bSEd Tanous   // const class function pointer
265*e3b0bf5bSEd Tanous   const TestClass t2;
266*e3b0bf5bSEd Tanous   iface->register_method("MyClassFunctionPointer", t2.test_method);
267*e3b0bf5bSEd Tanous 
268*e3b0bf5bSEd Tanous   iface->register_method("VoidMethod", []() {
269*e3b0bf5bSEd Tanous 
270*e3b0bf5bSEd Tanous     std::cout << "method called.\n";
271*e3b0bf5bSEd Tanous     return (uint32_t)42;
272*e3b0bf5bSEd Tanous   });
273*e3b0bf5bSEd 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 
280*e3b0bf5bSEd Tanous   // Test multiple ways to register methods
281*e3b0bf5bSEd Tanous   // Basic lambda
282*e3b0bf5bSEd Tanous   iface->register_method("MyMethodLambda", {"x"}, {"return_value_name"},
283*e3b0bf5bSEd Tanous                          [](uint32_t x) {
284*e3b0bf5bSEd Tanous                            std::cout << "method called.  Got:" << x << "\n";
285*e3b0bf5bSEd Tanous                            return 42;
286*e3b0bf5bSEd 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 
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 
332377e76abSEd Tanous   dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
333377e76abSEd Tanous                              "org.freedesktop.DBus.Properties");
334377e76abSEd Tanous   dbus::message m = dbus::message::new_call(test_daemon, "Get");
335377e76abSEd Tanous   m.pack("org.freedesktop.My.Interface", "foo");
336377e76abSEd Tanous 
337377e76abSEd Tanous   bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
338377e76abSEd Tanous     if (ec) {
339377e76abSEd Tanous       std::string error;
340377e76abSEd Tanous       r.unpack(error);
341377e76abSEd Tanous 
342377e76abSEd Tanous       FAIL() << ec << error;
343377e76abSEd Tanous     } else {
344377e76abSEd Tanous       std::cout << r;
345377e76abSEd Tanous 
346377e76abSEd Tanous       EXPECT_EQ(r.get_type(), "method_return");
347377e76abSEd Tanous 
348377e76abSEd Tanous       dbus::dbus_variant value;
349377e76abSEd Tanous       r.unpack(value);
350377e76abSEd Tanous 
351377e76abSEd Tanous       EXPECT_EQ(boost::get<uint32_t>(value), 26);
352377e76abSEd Tanous     }
353377e76abSEd Tanous     io.stop();
354377e76abSEd Tanous   });
355377e76abSEd Tanous 
356377e76abSEd Tanous   io.run();
357377e76abSEd Tanous }
358