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