xref: /openbmc/boost-dbus/test/avahi.cpp (revision b573e22e)
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   auto system_bus = std::make_shared<dbus::connection>(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   auto system_bus = std::make_shared<dbus::connection>(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   auto system_bus = std::make_shared<dbus::connection>(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_ptr system_bus,
127                       std::string& service_name, 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 
156   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
157 
158   dbus::match ma(system_bus, "type='signal',path_namespace='/xyz/openbmc_project/sensors'");
159 
160   dbus::filter f(system_bus, [](dbus::message& m) {
161     auto member = m.get_member();
162     return member == "PropertiesChanged";
163   });
164 
165   // std::function<void(boost::system::error_code, dbus::message)> event_handler
166   // =
167 
168   f.async_dispatch([&](boost::system::error_code ec, dbus::message s) {
169     std::string object_name;
170     EXPECT_EQ(s.get_path(),
171               "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp");
172 
173     std::vector<std::pair<std::string, dbus::dbus_variant>> values;
174     s.unpack(object_name).unpack(values);
175 
176     EXPECT_EQ(object_name, "xyz.openbmc_project.Sensor.Value");
177 
178     EXPECT_EQ(values.size(), 1);
179     auto expected = std::pair<std::string, dbus::dbus_variant>("Value", 42);
180     EXPECT_EQ(values[0], expected);
181 
182     io.stop();
183   });
184 
185   dbus::endpoint test_endpoint(
186       "org.freedesktop.Avahi",
187       "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp",
188       "org.freedesktop.DBus.Properties");
189 
190   auto signal_name = std::string("PropertiesChanged");
191   auto m = dbus::message::new_signal(test_endpoint, signal_name);
192 
193   m.pack("xyz.openbmc_project.Sensor.Value");
194 
195   std::vector<std::pair<std::string, dbus::dbus_variant>> map2;
196 
197   map2.emplace_back("Value", 42);
198 
199   m.pack(map2);
200 
201   auto removed = std::vector<uint32_t>();
202   m.pack(removed);
203   system_bus->async_send(m,
204                          [&](boost::system::error_code ec, dbus::message s) {});
205 
206   io.run();
207 }
208 
209 TEST(BOOST_DBUS, MultipleSensorChanged) {
210   boost::asio::io_service io;
211   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
212 
213   dbus::match ma(system_bus,
214                  "type='signal',path_namespace='/xyz/openbmc_project/sensors'");
215   dbus::filter f(system_bus, [](dbus::message& m) {
216     auto member = m.get_member();
217     return member == "PropertiesChanged";
218   });
219 
220   int count = 0;
221   std::function<void(boost::system::error_code, dbus::message)> callback = [&](
222       boost::system::error_code ec, dbus::message s) {
223     std::string object_name;
224     EXPECT_EQ(s.get_path(),
225               "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp");
226 
227     std::vector<std::pair<std::string, dbus::dbus_variant>> values;
228     s.unpack(object_name).unpack(values);
229 
230     EXPECT_EQ(object_name, "xyz.openbmc_project.Sensor.Value");
231 
232     EXPECT_EQ(values.size(), 1);
233     auto expected = std::pair<std::string, dbus::dbus_variant>("Value", 42);
234     EXPECT_EQ(values[0], expected);
235     count++;
236     if (count == 2) {
237       io.stop();
238     } else {
239       f.async_dispatch(callback);
240     }
241 
242   };
243   f.async_dispatch(callback);
244 
245   dbus::endpoint test_endpoint(
246       "org.freedesktop.Avahi",
247       "/xyz/openbmc_project/sensors/temperature/LR_Brd_Temp",
248       "org.freedesktop.DBus.Properties");
249 
250   auto signal_name = std::string("PropertiesChanged");
251   auto m = dbus::message::new_signal(test_endpoint, signal_name);
252 
253   m.pack("xyz.openbmc_project.Sensor.Value");
254 
255   std::vector<std::pair<std::string, dbus::dbus_variant>> map2;
256 
257   map2.emplace_back("Value", 42);
258 
259   m.pack(map2);
260 
261   auto removed = std::vector<uint32_t>();
262   m.pack(removed);
263   system_bus->async_send(m,
264                          [&](boost::system::error_code ec, dbus::message s) {});
265   system_bus->async_send(m,
266                          [&](boost::system::error_code ec, dbus::message s) {});
267   io.run();
268 }
269 
270 TEST(BOOST_DBUS, MethodCall) {
271   boost::asio::io_service io;
272   boost::asio::deadline_timer t(io, boost::posix_time::seconds(30));
273   t.async_wait([&](const boost::system::error_code& /*e*/) {
274     io.stop();
275     FAIL() << "Callback was never called\n";
276   });
277   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
278   std::string requested_name = "xyz.openbmc_project.fwupdate1.server";
279   system_bus->request_name(requested_name);
280 
281   /* not sure we even need to add a match for method calls,
282    * but this is how you might do it .... */
283   dbus::match ma(system_bus,
284                  "type='method_call',path_namespace='/xyz/openbmc_project/fwupdate1'");
285 
286   dbus::filter f(system_bus, [](dbus::message& m) {
287     // std::cerr << "filter called: " << m << std::endl;
288     return (m.get_member() == "Get" &&
289             m.get_interface() == "org.freedesktop.DBus.Properties" &&
290             m.get_signature() == "ss");
291   });
292 
293   std::function<void(boost::system::error_code, dbus::message)> method_handler =
294       [&](boost::system::error_code ec, dbus::message s) {
295         std::string intf_name, prop_name;
296         s.unpack(intf_name).unpack(prop_name);
297 
298         EXPECT_EQ(intf_name, "xyz.openbmc_project.fwupdate1");
299         EXPECT_EQ(prop_name, "State");
300 
301         // send a reply so dbus doesn't get angry?
302         auto r = system_bus->reply(s);
303         r.pack("IDLE");
304         system_bus->async_send(r,
305                 [&](boost::system::error_code ec, dbus::message s) {} );
306         io.stop();
307       };
308   f.async_dispatch(method_handler);
309 
310   dbus::endpoint test_endpoint(
311       requested_name,
312       "/xyz/openbmc_project/fwupdate1",
313       "org.freedesktop.DBus.Properties");
314 
315   auto method_name = std::string("Get");
316   auto m = dbus::message::new_call(test_endpoint, method_name);
317 
318   m.pack("xyz.openbmc_project.fwupdate1");
319   m.pack("State");
320 
321   system_bus->async_send(m,
322                         [&](boost::system::error_code ec, dbus::message s) {
323                         std::cerr <<"received s: " << s << std::endl;
324                         });
325 
326   io.run();
327 }
328