xref: /openbmc/boost-dbus/test/avahi.cpp (revision 458a9c10)
1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <dbus/connection.hpp>
7 #include <dbus/endpoint.hpp>
8 #include <dbus/filter.hpp>
9 #include <dbus/match.hpp>
10 #include <dbus/message.hpp>
11 #include <dbus/utility.hpp>
12 #include <functional>
13 
14 #include <unistd.h>
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17 
18 TEST(AvahiTest, GetHostName) {
19   dbus::endpoint test_daemon("org.freedesktop.Avahi", "/",
20                              "org.freedesktop.Avahi.Server");
21   boost::asio::io_service io;
22   dbus::connection system_bus(io, dbus::bus::system);
23 
24   dbus::message m = dbus::message::new_call(test_daemon, "GetHostName");
25 
26   system_bus.async_send(
27       m, [&](const boost::system::error_code ec, dbus::message r) {
28 
29         std::string avahi_hostname;
30         std::string hostname;
31 
32         // get hostname from a system call
33         char c[1024];
34         gethostname(c, 1024);
35         hostname = c;
36 
37         r.unpack(avahi_hostname);
38 
39         // Get only the host name, not the fqdn
40         auto unix_hostname = hostname.substr(0, hostname.find("."));
41         EXPECT_EQ(unix_hostname, avahi_hostname);
42 
43         io.stop();
44       });
45   boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
46   t.async_wait([&](const boost::system::error_code& /*e*/) {
47     io.stop();
48     FAIL() << "Callback was never called\n";
49   });
50   io.run();
51 }
52 
53 TEST(AvahiTest, ServiceBrowser) {
54   boost::asio::io_service io;
55   dbus::connection system_bus(io, dbus::bus::system);
56 
57   dbus::endpoint test_daemon("org.freedesktop.Avahi", "/",
58                              "org.freedesktop.Avahi.Server");
59   // create new service browser
60   dbus::message m1 = dbus::message::new_call(test_daemon, "ServiceBrowserNew");
61   m1.pack<int32_t>(-1)
62       .pack<int32_t>(-1)
63       .pack<std::string>("_http._tcp")
64       .pack<std::string>("local")
65       .pack<uint32_t>(0);
66 
67   dbus::message r = system_bus.send(m1);
68   std::string browser_path;
69   r.unpack(browser_path);
70   testing::Test::RecordProperty("browserPath", browser_path);
71 
72   dbus::match ma(system_bus, "type='signal',path='" + browser_path + "'");
73   dbus::filter f(system_bus, [](dbus::message& m) {
74     auto member = m.get_member();
75     return member == "NameAcquired";
76   });
77 
78   std::function<void(boost::system::error_code, dbus::message)> event_handler =
79       [&](boost::system::error_code ec, dbus::message s) {
80         testing::Test::RecordProperty("firstSignal", s.get_member());
81         std::string a = s.get_member();
82         std::string dude;
83         s.unpack(dude);
84         f.async_dispatch(event_handler);
85         io.stop();
86       };
87   f.async_dispatch(event_handler);
88 
89   boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
90   t.async_wait([&](const boost::system::error_code& /*e*/) {
91     io.stop();
92     FAIL() << "Callback was never called\n";
93   });
94   io.run();
95 }
96 
97 TEST(BOOST_DBUS, ListServices) {
98   boost::asio::io_service io;
99   boost::asio::deadline_timer t(io, boost::posix_time::seconds(10));
100   t.async_wait([&](const boost::system::error_code& /*e*/) {
101     io.stop();
102     FAIL() << "Callback was never called\n";
103   });
104 
105   dbus::connection system_bus(io, dbus::bus::system);
106 
107   dbus::endpoint test_daemon("org.freedesktop.DBus", "/",
108                              "org.freedesktop.DBus");
109   // create new service browser
110   dbus::message m = dbus::message::new_call(test_daemon, "ListNames");
111   system_bus.async_send(
112       m, [&](const boost::system::error_code ec, dbus::message r) {
113         io.stop();
114         std::vector<std::string> services;
115         r.unpack(services);
116         // Test a couple things that should always be present.... adapt if
117         // neccesary
118         EXPECT_THAT(services, testing::Contains("org.freedesktop.DBus"));
119         EXPECT_THAT(services, testing::Contains("org.freedesktop.Accounts"));
120 
121       });
122 
123   io.run();
124 }
125 
126 void query_interfaces(dbus::connection& system_bus, std::string& service_name,
127                       std::string& object_name) {
128   dbus::endpoint service_daemon(service_name, object_name,
129                                 "org.freedestop.DBus.Introspectable");
130   dbus::message m = dbus::message::new_call(service_daemon, "Introspect");
131   try {
132     auto r = system_bus.send(m);
133     std::vector<std::string> names;
134     // Todo(ed) figure out why we're occassionally getting access
135     // denied errors
136     // EXPECT_EQ(ec, boost::system::errc::success);
137 
138     std::string xml;
139     r.unpack(xml);
140     // TODO(ed) names needs lock for multithreaded access
141     dbus::read_dbus_xml_names(xml, names);
142     // loop over the newly added items
143     for (auto name : names) {
144       std::cout << name << "\n";
145       auto new_service_string = object_name + "/" + name;
146       query_interfaces(system_bus, service_name, new_service_string);
147     }
148   } catch (boost::system::error_code e) {
149     std::cout << e;
150   }
151 }
152 
153 TEST(BOOST_DBUS, SingleSensorChanged) {
154   boost::asio::io_service io;
155   dbus::connection system_bus(io, dbus::bus::system);
156 
157   dbus::match ma(system_bus,
158                  "type='signal',path_namespace='/xyz/openbmc_project/sensors'");
159   dbus::filter f(system_bus, [](dbus::message& m) {
160     auto member = m.get_member();
161     return member == "PropertiesChanged";
162   });
163 
164   // std::function<void(boost::system::error_code, dbus::message)> event_handler
165   // =
166 
167   f.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
168     std::string object_name;
169     EXPECT_EQ(s.get_path(),
170               "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp");
171 
172     std::vector<std::pair<std::string, dbus::dbus_variant>> values;
173     s.unpack(object_name).unpack(values);
174 
175     EXPECT_EQ(object_name, "xyz.openbmc_project.Sensor.Value");
176 
177     EXPECT_EQ(values.size(), 1);
178     auto expected = std::pair<std::string, dbus::dbus_variant>("Value", 42);
179     EXPECT_EQ(values[0], expected);
180 
181     io.stop();
182   });
183 
184   dbus::endpoint test_endpoint(
185       "org.freedesktop.Avahi",
186       "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp",
187       "org.freedesktop.DBus.Properties");
188 
189   auto signal_name = std::string("PropertiesChanged");
190   auto m = dbus::message::new_signal(test_endpoint, signal_name);
191 
192   m.pack("xyz.openbmc_project.Sensor.Value");
193 
194   std::vector<std::pair<std::string, dbus::dbus_variant>> map2;
195 
196   map2.emplace_back("Value", 42);
197 
198   m.pack(map2);
199 
200   auto removed = std::vector<uint32_t>();
201   m.pack(removed);
202   system_bus.async_send(m,
203                         [&](boost::system::error_code ec, dbus::message s) {});
204 
205   io.run();
206 }
207 
208 TEST(BOOST_DBUS, MultipleSensorChanged) {
209   boost::asio::io_service io;
210   dbus::connection system_bus(io, dbus::bus::system);
211 
212   dbus::match ma(system_bus,
213                  "type='signal',path_namespace='/xyz/openbmc_project/sensors'");
214   dbus::filter f(system_bus, [](dbus::message& m) {
215     auto member = m.get_member();
216     return member == "PropertiesChanged";
217   });
218 
219   int count = 0;
220   f.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
221     std::string object_name;
222     EXPECT_EQ(s.get_path(),
223               "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp");
224 
225     std::vector<std::pair<std::string, dbus::dbus_variant>> values;
226     s.unpack(object_name).unpack(values);
227 
228     EXPECT_EQ(object_name, "xyz.openbmc_project.Sensor.Value");
229 
230     EXPECT_EQ(values.size(), 1);
231     auto expected = std::pair<std::string, dbus::dbus_variant>("Value", 42);
232     EXPECT_EQ(values[0], expected);
233     count++;
234     if (count == 2) {
235       io.stop();
236     }
237 
238   });
239 
240   dbus::endpoint test_endpoint(
241       "org.freedesktop.Avahi",
242       "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp",
243       "org.freedesktop.DBus.Properties");
244 
245   auto signal_name = std::string("PropertiesChanged");
246   auto m = dbus::message::new_signal(test_endpoint, signal_name);
247 
248   m.pack("xyz.openbmc_project.Sensor.Value");
249 
250   std::vector<std::pair<std::string, dbus::dbus_variant>> map2;
251 
252   map2.emplace_back("Value", 42);
253 
254   m.pack(map2);
255 
256   auto removed = std::vector<uint32_t>();
257   m.pack(removed);
258   system_bus.async_send(m,
259                         [&](boost::system::error_code ec, dbus::message s) {});
260   system_bus.async_send(m,
261                         [&](boost::system::error_code ec, dbus::message s) {});
262   io.run();
263 }
264 
265 TEST(BOOST_DBUS, MethodCall) {
266   boost::asio::io_service io;
267   boost::asio::deadline_timer t(io, boost::posix_time::seconds(30));
268   t.async_wait([&](const boost::system::error_code& /*e*/) {
269     io.stop();
270     FAIL() << "Callback was never called\n";
271   });
272   std::string requested_name = "xyz.openbmc_project.fwupdate1.server";
273   dbus::connection system_bus(io, dbus::bus::system);
274   system_bus.request_name(requested_name);
275 
276   /* not sure we even need to add a match for method calls,
277    * but this is how you might do it .... */
278   dbus::match ma(system_bus,
279                  "type='method_call',path_namespace='/xyz/openbmc_project/fwupdate1'");
280 
281   dbus::filter f(system_bus, [](dbus::message& m) {
282     // std::cerr << "filter called: " << m << std::endl;
283     return (m.get_member() == "Get" &&
284             m.get_interface() == "org.freedesktop.DBus.Properties" &&
285             m.get_signature() == "ss");
286   });
287 
288   std::function<void(boost::system::error_code, dbus::message)> method_handler =
289       [&](boost::system::error_code ec, dbus::message s) {
290         std::string intf_name, prop_name;
291         s.unpack(intf_name).unpack(prop_name);
292 
293         EXPECT_EQ(intf_name, "xyz.openbmc_project.fwupdate1");
294         EXPECT_EQ(prop_name, "State");
295 
296         // send a reply so dbus doesn't get angry?
297         auto r = system_bus.reply(s);
298         r.pack("IDLE");
299         system_bus.async_send(r,
300                 [&](boost::system::error_code ec, dbus::message s) {} );
301         io.stop();
302       };
303   f.async_dispatch(method_handler);
304 
305   dbus::endpoint test_endpoint(
306       requested_name,
307       "/xyz/openbmc_project/fwupdate1",
308       "org.freedesktop.DBus.Properties");
309 
310   auto method_name = std::string("Get");
311   auto m = dbus::message::new_call(test_endpoint, method_name);
312 
313   m.pack("xyz.openbmc_project.fwupdate1");
314   m.pack("State");
315 
316   system_bus.async_send(m,
317                         [&](boost::system::error_code ec, dbus::message s) {
318                         std::cerr <<"received s: " << s << std::endl;
319                         });
320 
321   io.run();
322 }
323