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 }