1 #include <dbus/connection.hpp>
2 #include <dbus/endpoint.hpp>
3 #include <dbus/filter.hpp>
4 #include <dbus/match.hpp>
5 #include <dbus/message.hpp>
6 #include <dbus/properties.hpp>
7 #include <functional>
8 
9 #include <unistd.h>
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 
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 
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 
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 
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 
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   std::atomic<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 
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 
212 TEST(DbusPropertiesInterface, MethodServer) {
213   boost::asio::io_service io;
214   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
215 
216   // Set up the object server, and send some test objects
217   dbus::DbusObjectServer foo(bus);
218   auto object =
219       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
220   foo.register_object(object);
221 
222   auto iface = std::make_shared<dbus::DbusInterface>(
223       "org.freedesktop.My.Interface", bus);
224   object->register_interface(iface);
225 
226   iface->register_method("MyMethod", [](uint32_t x) {
227 
228     std::cout << "method called.  Got:" << x << "\n";
229     return std::make_tuple<int>(42);
230   });
231 
232     iface->register_method("VoidMethod", []() {
233 
234     std::cout << "method called.\n";
235     return std::make_tuple<int>(42);
236   });
237 
238 
239   dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
240                              "org.freedesktop.DBus.Introspectable");
241   dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
242 
243   bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
244     if (ec) {
245       std::string error;
246       r.unpack(error);
247 
248       FAIL() << ec << error;
249     } else {
250       std::string xml;
251       r.unpack(xml);
252       EXPECT_EQ(r.get_type(), "method_return");
253       // todo(ED)
254       /*
255       EXPECT_EQ(xml, dbus_boilerplate +
256                          "<node><interface "
257                          "name=\"MyInterface\"><method "
258                          "name=\"MyMethod\"></method></interface></node>");
259                          */
260     }
261     io.stop();
262   });
263 
264   io.run();
265 }
266 
267 TEST(DbusPropertiesInterface, PropertiesInterface) {
268   boost::asio::io_service io;
269   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
270 
271   // Set up the object server, and send some test objects
272   dbus::DbusObjectServer foo(bus);
273   auto object =
274       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
275   foo.register_object(object);
276 
277   auto iface = std::make_shared<dbus::DbusInterface>(
278       "org.freedesktop.My.Interface", bus);
279   object->register_interface(iface);
280 
281   iface->set_property("foo", (uint32_t)26);
282 
283   dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
284                              "org.freedesktop.DBus.Properties");
285   dbus::message m = dbus::message::new_call(test_daemon, "Get");
286   m.pack("org.freedesktop.My.Interface", "foo");
287 
288   bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
289     if (ec) {
290       std::string error;
291       r.unpack(error);
292 
293       FAIL() << ec << error;
294     } else {
295       std::cout << r;
296 
297       EXPECT_EQ(r.get_type(), "method_return");
298 
299       dbus::dbus_variant value;
300       r.unpack(value);
301 
302       EXPECT_EQ(boost::get<uint32_t>(value), 26);
303     }
304     io.stop();
305   });
306 
307   io.run();
308 }