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