1377e76abSEd Tanous #include <dbus/connection.hpp> 2377e76abSEd Tanous #include <dbus/endpoint.hpp> 3377e76abSEd Tanous #include <dbus/filter.hpp> 4377e76abSEd Tanous #include <dbus/match.hpp> 5377e76abSEd Tanous #include <dbus/message.hpp> 6377e76abSEd Tanous #include <dbus/properties.hpp> 7377e76abSEd Tanous #include <functional> 8377e76abSEd Tanous 9377e76abSEd Tanous #include <unistd.h> 10377e76abSEd Tanous #include <gmock/gmock.h> 11377e76abSEd Tanous #include <gtest/gtest.h> 12377e76abSEd Tanous 13377e76abSEd Tanous static const std::string dbus_boilerplate( 14377e76abSEd Tanous "<!DOCTYPE node PUBLIC " 15377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " 16377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/" 17377e76abSEd Tanous "introspect.dtd\">\n"); 18377e76abSEd Tanous 19377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyObjectServer) { 20377e76abSEd Tanous boost::asio::io_service io; 21377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 22377e76abSEd Tanous 23377e76abSEd Tanous // Set up the object server, and send some test events 24377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 25377e76abSEd Tanous 26377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + "<node></node>"); 27377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + "<node></node>"); 28377e76abSEd Tanous } 29377e76abSEd Tanous 30377e76abSEd Tanous TEST(DbusPropertiesInterface, BasicObjectServer) { 31377e76abSEd Tanous boost::asio::io_service io; 32377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 33377e76abSEd Tanous 34377e76abSEd Tanous // Set up the object server, and send some test events 35377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 36377e76abSEd Tanous 37377e76abSEd Tanous foo.register_object(std::make_shared<dbus::DbusObject>( 38377e76abSEd Tanous system_bus, "/org/freedesktop/NetworkManager")); 39377e76abSEd Tanous 40377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + 41377e76abSEd Tanous "<node><node " 42377e76abSEd Tanous "name=\"org\"></node></node>"); 43377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + 44377e76abSEd Tanous "<node><node " 45377e76abSEd Tanous "name=\"org\"></node></node>"); 46377e76abSEd Tanous 47377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"), 48377e76abSEd Tanous dbus_boilerplate + 49377e76abSEd Tanous "<node><node " 50377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 51377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"), 52377e76abSEd Tanous dbus_boilerplate + 53377e76abSEd Tanous "<node><node " 54377e76abSEd Tanous "name=\"NetworkManager\"></node></node>"); 55377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes 56377e76abSEd Tanous /*EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/NetworkManager"), 57377e76abSEd Tanous dbus_boilerplate + "<node></node>");*/ 58377e76abSEd Tanous } 59377e76abSEd Tanous 60377e76abSEd Tanous TEST(DbusPropertiesInterface, SharedNodeObjectServer) { 61377e76abSEd Tanous boost::asio::io_service io; 62377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 63377e76abSEd Tanous 64377e76abSEd Tanous // Set up the object server, and send some test events 65377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 66377e76abSEd Tanous 67377e76abSEd Tanous foo.register_object( 68377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test1")); 69377e76abSEd Tanous foo.register_object( 70377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test2")); 71377e76abSEd Tanous 72377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + 73377e76abSEd Tanous "<node><node " 74377e76abSEd Tanous "name=\"org\"></node></node>"); 75377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + 76377e76abSEd Tanous "<node><node " 77377e76abSEd Tanous "name=\"org\"></node></node>"); 78377e76abSEd Tanous 79377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"), 80377e76abSEd Tanous dbus_boilerplate + 81377e76abSEd Tanous "<node><node " 82377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 83377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"), 84377e76abSEd Tanous dbus_boilerplate + 85377e76abSEd Tanous "<node><node " 86377e76abSEd Tanous "name=\"test1\"></node><node name=\"test2\"></node></node>"); 87377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes 88377e76abSEd Tanous /* 89377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test1"), 90377e76abSEd Tanous dbus_boilerplate + "<node></node>"); 91377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test2"), 92377e76abSEd Tanous dbus_boilerplate + "<node></node>"); 93377e76abSEd Tanous */ 94377e76abSEd Tanous } 95377e76abSEd Tanous 96377e76abSEd Tanous TEST(LambdaDbusMethodTest, Basic) { 97377e76abSEd Tanous bool lambda_called = false; 98377e76abSEd Tanous auto lambda = [&](int32_t x) { 99377e76abSEd Tanous EXPECT_EQ(x, 18); 100377e76abSEd Tanous lambda_called = true; 101377e76abSEd Tanous return std::make_tuple<int64_t, int32_t>(4L, 2); 102377e76abSEd Tanous }; 103377e76abSEd Tanous boost::asio::io_service io; 104377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 105377e76abSEd Tanous auto dbus_method = 106377e76abSEd Tanous dbus::LambdaDbusMethod<decltype(lambda)>("foo", bus, lambda); 107377e76abSEd Tanous 108377e76abSEd Tanous dbus::message m = 109377e76abSEd Tanous dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/", 110377e76abSEd Tanous "org.freedesktop.Avahi.Server"), 111377e76abSEd Tanous "GetHostName"); 112377e76abSEd Tanous m.pack(static_cast<int32_t>(18)); 113377e76abSEd Tanous // Small thing that the dbus library normally does for us, but because we're 114377e76abSEd Tanous // bypassing it, we need to fill it in as if it was done 115377e76abSEd Tanous m.set_serial(585); 116377e76abSEd Tanous dbus_method.call(m); 117377e76abSEd Tanous EXPECT_EQ(lambda_called, true); 118377e76abSEd Tanous } 119377e76abSEd Tanous 120377e76abSEd Tanous TEST(DbusPropertiesInterface, ObjectServer) { 121377e76abSEd Tanous boost::asio::io_service io; 122377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 123377e76abSEd Tanous 124377e76abSEd Tanous // Set up the object server, and send some test objects 125377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 126377e76abSEd Tanous 127377e76abSEd Tanous foo.register_object( 128377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1")); 129377e76abSEd Tanous foo.register_object( 130377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test2")); 131377e76abSEd Tanous std::atomic<int> completion_count(0); 132377e76abSEd Tanous 133377e76abSEd Tanous std::array<std::string, 4> paths_to_test( 134377e76abSEd Tanous {{"/", "/org", "/org/freedesktop", "/org/freedesktop/test1"}}); 135377e76abSEd Tanous 136377e76abSEd Tanous for (auto& path : paths_to_test) { 137377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), path, 138377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 139377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 140377e76abSEd Tanous completion_count++; 141377e76abSEd Tanous bus->async_send( 142377e76abSEd Tanous m, [&](const boost::system::error_code ec, dbus::message r) { 143377e76abSEd Tanous if (ec) { 144377e76abSEd Tanous std::string error; 145377e76abSEd Tanous r.unpack(error); 146377e76abSEd Tanous FAIL() << ec << error; 147377e76abSEd Tanous } else { 148377e76abSEd Tanous std::string xml; 149377e76abSEd Tanous r.unpack(xml); 150377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 151377e76abSEd Tanous if (path == "/") { 152377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 153377e76abSEd Tanous "<node><node " 154377e76abSEd Tanous "name=\"org\"></node></node>"); 155377e76abSEd Tanous } else if (path == "/org") { 156377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 157377e76abSEd Tanous "<node><node " 158377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 159377e76abSEd Tanous } else if (path == "/org/freedesktop") { 160377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 161377e76abSEd Tanous "<node><node " 162377e76abSEd Tanous "name=\"test1\"></node><node " 163377e76abSEd Tanous "name=\"test2\"></node></node>"); 164377e76abSEd Tanous } else if (path == "/org/freedesktop/test1") { 165377e76abSEd Tanous } else { 166377e76abSEd Tanous FAIL() << "Unknown path: " << path; 167377e76abSEd Tanous } 168377e76abSEd Tanous } 169377e76abSEd Tanous completion_count--; 170377e76abSEd Tanous if (completion_count == 0) { 171377e76abSEd Tanous io.stop(); 172377e76abSEd Tanous } 173377e76abSEd Tanous }); 174377e76abSEd Tanous } 175377e76abSEd Tanous io.run(); 176377e76abSEd Tanous } 177377e76abSEd Tanous 178377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyMethodServer) { 179377e76abSEd Tanous boost::asio::io_service io; 180377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 181377e76abSEd Tanous 182377e76abSEd Tanous // Set up the object server, and send some test objects 183377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 184377e76abSEd Tanous foo.register_object( 185377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1")); 186377e76abSEd Tanous 187377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 188377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 189377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 190377e76abSEd Tanous 191377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 192377e76abSEd Tanous if (ec) { 193377e76abSEd Tanous std::string error; 194377e76abSEd Tanous r.unpack(error); 195377e76abSEd Tanous FAIL() << ec << error; 196377e76abSEd Tanous } else { 197377e76abSEd Tanous std::cout << r; 198377e76abSEd Tanous std::string xml; 199377e76abSEd Tanous r.unpack(xml); 200377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 201377e76abSEd Tanous // TODO(ed) turn back on when method interface stabilizes 202377e76abSEd Tanous // EXPECT_EQ(xml, dbus_boilerplate + "<node></node>"); 203377e76abSEd Tanous } 204377e76abSEd Tanous 205377e76abSEd Tanous io.stop(); 206377e76abSEd Tanous 207377e76abSEd Tanous }); 208377e76abSEd Tanous 209377e76abSEd Tanous io.run(); 210377e76abSEd Tanous } 211377e76abSEd Tanous 212*e3b0bf5bSEd Tanous std::tuple<int> test_method(uint32_t x) { 213*e3b0bf5bSEd Tanous std::cout << "method called.\n"; 214*e3b0bf5bSEd Tanous return std::make_tuple<int>(42); 215*e3b0bf5bSEd Tanous } 216*e3b0bf5bSEd Tanous 217*e3b0bf5bSEd Tanous class TestClass { 218*e3b0bf5bSEd Tanous public: 219*e3b0bf5bSEd Tanous static std::tuple<int> test_method(uint32_t x) { 220*e3b0bf5bSEd Tanous std::cout << "method called.\n"; 221*e3b0bf5bSEd Tanous return std::make_tuple<int>(42); 222*e3b0bf5bSEd Tanous } 223*e3b0bf5bSEd Tanous }; 224*e3b0bf5bSEd Tanous 225377e76abSEd Tanous TEST(DbusPropertiesInterface, MethodServer) { 226377e76abSEd Tanous boost::asio::io_service io; 227377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 228377e76abSEd Tanous 229377e76abSEd Tanous // Set up the object server, and send some test objects 230377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 231377e76abSEd Tanous auto object = 232377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"); 233377e76abSEd Tanous foo.register_object(object); 234377e76abSEd Tanous 235377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>( 236377e76abSEd Tanous "org.freedesktop.My.Interface", bus); 237377e76abSEd Tanous object->register_interface(iface); 238377e76abSEd Tanous 239*e3b0bf5bSEd Tanous // Test multiple ways to register methods 240*e3b0bf5bSEd Tanous // Basic lambda 241*e3b0bf5bSEd Tanous iface->register_method("MyMethodLambda", [](uint32_t x) { 242377e76abSEd Tanous 243377e76abSEd Tanous std::cout << "method called. Got:" << x << "\n"; 244377e76abSEd Tanous return std::make_tuple<int>(42); 245377e76abSEd Tanous }); 246377e76abSEd Tanous 247*e3b0bf5bSEd Tanous // std::function 248*e3b0bf5bSEd Tanous std::function<typename std::tuple<int>(uint32_t)> my_function = 249*e3b0bf5bSEd Tanous [](uint32_t x) { 250*e3b0bf5bSEd Tanous 251*e3b0bf5bSEd Tanous std::cout << "method called. Got:" << x << "\n"; 252*e3b0bf5bSEd Tanous return std::make_tuple<int>(42); 253*e3b0bf5bSEd Tanous }; 254*e3b0bf5bSEd Tanous 255*e3b0bf5bSEd Tanous iface->register_method("MyMethodStdFunction", my_function); 256*e3b0bf5bSEd Tanous 257*e3b0bf5bSEd Tanous // Function pointer 258*e3b0bf5bSEd Tanous iface->register_method("MyMethodFunctionPointer", &test_method); 259*e3b0bf5bSEd Tanous 260*e3b0bf5bSEd Tanous // Class function pointer 261*e3b0bf5bSEd Tanous TestClass t; 262*e3b0bf5bSEd Tanous iface->register_method("MyClassFunctionPointer", t.test_method); 263*e3b0bf5bSEd Tanous 264*e3b0bf5bSEd Tanous // const class function pointer 265*e3b0bf5bSEd Tanous const TestClass t2; 266*e3b0bf5bSEd Tanous iface->register_method("MyClassFunctionPointer", t2.test_method); 267*e3b0bf5bSEd Tanous 268*e3b0bf5bSEd Tanous iface->register_method("VoidMethod", []() { 269*e3b0bf5bSEd Tanous 270*e3b0bf5bSEd Tanous std::cout << "method called.\n"; 271*e3b0bf5bSEd Tanous return (uint32_t)42; 272*e3b0bf5bSEd Tanous }); 273*e3b0bf5bSEd Tanous 274377e76abSEd Tanous iface->register_method("VoidMethod", []() { 275377e76abSEd Tanous 276377e76abSEd Tanous std::cout << "method called.\n"; 277377e76abSEd Tanous return std::make_tuple<int>(42); 278377e76abSEd Tanous }); 279377e76abSEd Tanous 280*e3b0bf5bSEd Tanous // Test multiple ways to register methods 281*e3b0bf5bSEd Tanous // Basic lambda 282*e3b0bf5bSEd Tanous iface->register_method("MyMethodLambda", {"x"}, {"return_value_name"}, 283*e3b0bf5bSEd Tanous [](uint32_t x) { 284*e3b0bf5bSEd Tanous std::cout << "method called. Got:" << x << "\n"; 285*e3b0bf5bSEd Tanous return 42; 286*e3b0bf5bSEd Tanous }); 287377e76abSEd Tanous 288377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 289377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 290377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 291377e76abSEd Tanous 292377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 293377e76abSEd Tanous if (ec) { 294377e76abSEd Tanous std::string error; 295377e76abSEd Tanous r.unpack(error); 296377e76abSEd Tanous 297377e76abSEd Tanous FAIL() << ec << error; 298377e76abSEd Tanous } else { 299377e76abSEd Tanous std::string xml; 300377e76abSEd Tanous r.unpack(xml); 301377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 302377e76abSEd Tanous // todo(ED) 303377e76abSEd Tanous /* 304377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 305377e76abSEd Tanous "<node><interface " 306377e76abSEd Tanous "name=\"MyInterface\"><method " 307377e76abSEd Tanous "name=\"MyMethod\"></method></interface></node>"); 308377e76abSEd Tanous */ 309377e76abSEd Tanous } 310377e76abSEd Tanous io.stop(); 311377e76abSEd Tanous }); 312377e76abSEd Tanous 313377e76abSEd Tanous io.run(); 314377e76abSEd Tanous } 315377e76abSEd Tanous 316377e76abSEd Tanous TEST(DbusPropertiesInterface, PropertiesInterface) { 317377e76abSEd Tanous boost::asio::io_service io; 318377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 319377e76abSEd Tanous 320377e76abSEd Tanous // Set up the object server, and send some test objects 321377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 322377e76abSEd Tanous auto object = 323377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"); 324377e76abSEd Tanous foo.register_object(object); 325377e76abSEd Tanous 326377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>( 327377e76abSEd Tanous "org.freedesktop.My.Interface", bus); 328377e76abSEd Tanous object->register_interface(iface); 329377e76abSEd Tanous 330377e76abSEd Tanous iface->set_property("foo", (uint32_t)26); 331377e76abSEd Tanous 332377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 333377e76abSEd Tanous "org.freedesktop.DBus.Properties"); 334377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Get"); 335377e76abSEd Tanous m.pack("org.freedesktop.My.Interface", "foo"); 336377e76abSEd Tanous 337377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 338377e76abSEd Tanous if (ec) { 339377e76abSEd Tanous std::string error; 340377e76abSEd Tanous r.unpack(error); 341377e76abSEd Tanous 342377e76abSEd Tanous FAIL() << ec << error; 343377e76abSEd Tanous } else { 344377e76abSEd Tanous std::cout << r; 345377e76abSEd Tanous 346377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 347377e76abSEd Tanous 348377e76abSEd Tanous dbus::dbus_variant value; 349377e76abSEd Tanous r.unpack(value); 350377e76abSEd Tanous 351377e76abSEd Tanous EXPECT_EQ(boost::get<uint32_t>(value), 26); 352377e76abSEd Tanous } 353377e76abSEd Tanous io.stop(); 354377e76abSEd Tanous }); 355377e76abSEd Tanous 356377e76abSEd Tanous io.run(); 357377e76abSEd Tanous } 358