1*377e76abSEd Tanous #include <dbus/connection.hpp> 2*377e76abSEd Tanous #include <dbus/endpoint.hpp> 3*377e76abSEd Tanous #include <dbus/filter.hpp> 4*377e76abSEd Tanous #include <dbus/match.hpp> 5*377e76abSEd Tanous #include <dbus/message.hpp> 6*377e76abSEd Tanous #include <dbus/properties.hpp> 7*377e76abSEd Tanous #include <functional> 8*377e76abSEd Tanous 9*377e76abSEd Tanous #include <unistd.h> 10*377e76abSEd Tanous #include <gmock/gmock.h> 11*377e76abSEd Tanous #include <gtest/gtest.h> 12*377e76abSEd Tanous 13*377e76abSEd Tanous static const std::string dbus_boilerplate( 14*377e76abSEd Tanous "<!DOCTYPE node PUBLIC " 15*377e76abSEd Tanous "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " 16*377e76abSEd Tanous "\"http://www.freedesktop.org/standards/dbus/1.0/" 17*377e76abSEd Tanous "introspect.dtd\">\n"); 18*377e76abSEd Tanous 19*377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyObjectServer) { 20*377e76abSEd Tanous boost::asio::io_service io; 21*377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 22*377e76abSEd Tanous 23*377e76abSEd Tanous // Set up the object server, and send some test events 24*377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 25*377e76abSEd Tanous 26*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + "<node></node>"); 27*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + "<node></node>"); 28*377e76abSEd Tanous } 29*377e76abSEd Tanous 30*377e76abSEd Tanous TEST(DbusPropertiesInterface, BasicObjectServer) { 31*377e76abSEd Tanous boost::asio::io_service io; 32*377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 33*377e76abSEd Tanous 34*377e76abSEd Tanous // Set up the object server, and send some test events 35*377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 36*377e76abSEd Tanous 37*377e76abSEd Tanous foo.register_object(std::make_shared<dbus::DbusObject>( 38*377e76abSEd Tanous system_bus, "/org/freedesktop/NetworkManager")); 39*377e76abSEd Tanous 40*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + 41*377e76abSEd Tanous "<node><node " 42*377e76abSEd Tanous "name=\"org\"></node></node>"); 43*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + 44*377e76abSEd Tanous "<node><node " 45*377e76abSEd Tanous "name=\"org\"></node></node>"); 46*377e76abSEd Tanous 47*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"), 48*377e76abSEd Tanous dbus_boilerplate + 49*377e76abSEd Tanous "<node><node " 50*377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 51*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"), 52*377e76abSEd Tanous dbus_boilerplate + 53*377e76abSEd Tanous "<node><node " 54*377e76abSEd Tanous "name=\"NetworkManager\"></node></node>"); 55*377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes 56*377e76abSEd Tanous /*EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/NetworkManager"), 57*377e76abSEd Tanous dbus_boilerplate + "<node></node>");*/ 58*377e76abSEd Tanous } 59*377e76abSEd Tanous 60*377e76abSEd Tanous TEST(DbusPropertiesInterface, SharedNodeObjectServer) { 61*377e76abSEd Tanous boost::asio::io_service io; 62*377e76abSEd Tanous auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system); 63*377e76abSEd Tanous 64*377e76abSEd Tanous // Set up the object server, and send some test events 65*377e76abSEd Tanous dbus::DbusObjectServer foo(system_bus); 66*377e76abSEd Tanous 67*377e76abSEd Tanous foo.register_object( 68*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test1")); 69*377e76abSEd Tanous foo.register_object( 70*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test2")); 71*377e76abSEd Tanous 72*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + 73*377e76abSEd Tanous "<node><node " 74*377e76abSEd Tanous "name=\"org\"></node></node>"); 75*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + 76*377e76abSEd Tanous "<node><node " 77*377e76abSEd Tanous "name=\"org\"></node></node>"); 78*377e76abSEd Tanous 79*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org"), 80*377e76abSEd Tanous dbus_boilerplate + 81*377e76abSEd Tanous "<node><node " 82*377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 83*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"), 84*377e76abSEd Tanous dbus_boilerplate + 85*377e76abSEd Tanous "<node><node " 86*377e76abSEd Tanous "name=\"test1\"></node><node name=\"test2\"></node></node>"); 87*377e76abSEd Tanous // TODO(Ed) turn this test back on once the signal interface stabilizes 88*377e76abSEd Tanous /* 89*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test1"), 90*377e76abSEd Tanous dbus_boilerplate + "<node></node>"); 91*377e76abSEd Tanous EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test2"), 92*377e76abSEd Tanous dbus_boilerplate + "<node></node>"); 93*377e76abSEd Tanous */ 94*377e76abSEd Tanous } 95*377e76abSEd Tanous 96*377e76abSEd Tanous TEST(LambdaDbusMethodTest, Basic) { 97*377e76abSEd Tanous bool lambda_called = false; 98*377e76abSEd Tanous auto lambda = [&](int32_t x) { 99*377e76abSEd Tanous EXPECT_EQ(x, 18); 100*377e76abSEd Tanous lambda_called = true; 101*377e76abSEd Tanous return std::make_tuple<int64_t, int32_t>(4L, 2); 102*377e76abSEd Tanous }; 103*377e76abSEd Tanous boost::asio::io_service io; 104*377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 105*377e76abSEd Tanous auto dbus_method = 106*377e76abSEd Tanous dbus::LambdaDbusMethod<decltype(lambda)>("foo", bus, lambda); 107*377e76abSEd Tanous 108*377e76abSEd Tanous dbus::message m = 109*377e76abSEd Tanous dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/", 110*377e76abSEd Tanous "org.freedesktop.Avahi.Server"), 111*377e76abSEd Tanous "GetHostName"); 112*377e76abSEd Tanous m.pack(static_cast<int32_t>(18)); 113*377e76abSEd Tanous // Small thing that the dbus library normally does for us, but because we're 114*377e76abSEd Tanous // bypassing it, we need to fill it in as if it was done 115*377e76abSEd Tanous m.set_serial(585); 116*377e76abSEd Tanous dbus_method.call(m); 117*377e76abSEd Tanous EXPECT_EQ(lambda_called, true); 118*377e76abSEd Tanous } 119*377e76abSEd Tanous 120*377e76abSEd Tanous TEST(DbusPropertiesInterface, ObjectServer) { 121*377e76abSEd Tanous boost::asio::io_service io; 122*377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 123*377e76abSEd Tanous 124*377e76abSEd Tanous // Set up the object server, and send some test objects 125*377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 126*377e76abSEd Tanous 127*377e76abSEd Tanous foo.register_object( 128*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1")); 129*377e76abSEd Tanous foo.register_object( 130*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test2")); 131*377e76abSEd Tanous std::atomic<int> completion_count(0); 132*377e76abSEd Tanous 133*377e76abSEd Tanous std::array<std::string, 4> paths_to_test( 134*377e76abSEd Tanous {{"/", "/org", "/org/freedesktop", "/org/freedesktop/test1"}}); 135*377e76abSEd Tanous 136*377e76abSEd Tanous for (auto& path : paths_to_test) { 137*377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), path, 138*377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 139*377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 140*377e76abSEd Tanous completion_count++; 141*377e76abSEd Tanous bus->async_send( 142*377e76abSEd Tanous m, [&](const boost::system::error_code ec, dbus::message r) { 143*377e76abSEd Tanous if (ec) { 144*377e76abSEd Tanous std::string error; 145*377e76abSEd Tanous r.unpack(error); 146*377e76abSEd Tanous FAIL() << ec << error; 147*377e76abSEd Tanous } else { 148*377e76abSEd Tanous std::string xml; 149*377e76abSEd Tanous r.unpack(xml); 150*377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 151*377e76abSEd Tanous if (path == "/") { 152*377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 153*377e76abSEd Tanous "<node><node " 154*377e76abSEd Tanous "name=\"org\"></node></node>"); 155*377e76abSEd Tanous } else if (path == "/org") { 156*377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 157*377e76abSEd Tanous "<node><node " 158*377e76abSEd Tanous "name=\"freedesktop\"></node></node>"); 159*377e76abSEd Tanous } else if (path == "/org/freedesktop") { 160*377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 161*377e76abSEd Tanous "<node><node " 162*377e76abSEd Tanous "name=\"test1\"></node><node " 163*377e76abSEd Tanous "name=\"test2\"></node></node>"); 164*377e76abSEd Tanous } else if (path == "/org/freedesktop/test1") { 165*377e76abSEd Tanous } else { 166*377e76abSEd Tanous FAIL() << "Unknown path: " << path; 167*377e76abSEd Tanous } 168*377e76abSEd Tanous } 169*377e76abSEd Tanous completion_count--; 170*377e76abSEd Tanous if (completion_count == 0) { 171*377e76abSEd Tanous io.stop(); 172*377e76abSEd Tanous } 173*377e76abSEd Tanous }); 174*377e76abSEd Tanous } 175*377e76abSEd Tanous io.run(); 176*377e76abSEd Tanous } 177*377e76abSEd Tanous 178*377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyMethodServer) { 179*377e76abSEd Tanous boost::asio::io_service io; 180*377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 181*377e76abSEd Tanous 182*377e76abSEd Tanous // Set up the object server, and send some test objects 183*377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 184*377e76abSEd Tanous foo.register_object( 185*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1")); 186*377e76abSEd Tanous 187*377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 188*377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 189*377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 190*377e76abSEd Tanous 191*377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 192*377e76abSEd Tanous if (ec) { 193*377e76abSEd Tanous std::string error; 194*377e76abSEd Tanous r.unpack(error); 195*377e76abSEd Tanous FAIL() << ec << error; 196*377e76abSEd Tanous } else { 197*377e76abSEd Tanous std::cout << r; 198*377e76abSEd Tanous std::string xml; 199*377e76abSEd Tanous r.unpack(xml); 200*377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 201*377e76abSEd Tanous // TODO(ed) turn back on when method interface stabilizes 202*377e76abSEd Tanous // EXPECT_EQ(xml, dbus_boilerplate + "<node></node>"); 203*377e76abSEd Tanous } 204*377e76abSEd Tanous 205*377e76abSEd Tanous io.stop(); 206*377e76abSEd Tanous 207*377e76abSEd Tanous }); 208*377e76abSEd Tanous 209*377e76abSEd Tanous io.run(); 210*377e76abSEd Tanous } 211*377e76abSEd Tanous 212*377e76abSEd Tanous TEST(DbusPropertiesInterface, MethodServer) { 213*377e76abSEd Tanous boost::asio::io_service io; 214*377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 215*377e76abSEd Tanous 216*377e76abSEd Tanous // Set up the object server, and send some test objects 217*377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 218*377e76abSEd Tanous auto object = 219*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"); 220*377e76abSEd Tanous foo.register_object(object); 221*377e76abSEd Tanous 222*377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>( 223*377e76abSEd Tanous "org.freedesktop.My.Interface", bus); 224*377e76abSEd Tanous object->register_interface(iface); 225*377e76abSEd Tanous 226*377e76abSEd Tanous iface->register_method("MyMethod", [](uint32_t x) { 227*377e76abSEd Tanous 228*377e76abSEd Tanous std::cout << "method called. Got:" << x << "\n"; 229*377e76abSEd Tanous return std::make_tuple<int>(42); 230*377e76abSEd Tanous }); 231*377e76abSEd Tanous 232*377e76abSEd Tanous iface->register_method("VoidMethod", []() { 233*377e76abSEd Tanous 234*377e76abSEd Tanous std::cout << "method called.\n"; 235*377e76abSEd Tanous return std::make_tuple<int>(42); 236*377e76abSEd Tanous }); 237*377e76abSEd Tanous 238*377e76abSEd Tanous 239*377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 240*377e76abSEd Tanous "org.freedesktop.DBus.Introspectable"); 241*377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Introspect"); 242*377e76abSEd Tanous 243*377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 244*377e76abSEd Tanous if (ec) { 245*377e76abSEd Tanous std::string error; 246*377e76abSEd Tanous r.unpack(error); 247*377e76abSEd Tanous 248*377e76abSEd Tanous FAIL() << ec << error; 249*377e76abSEd Tanous } else { 250*377e76abSEd Tanous std::string xml; 251*377e76abSEd Tanous r.unpack(xml); 252*377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 253*377e76abSEd Tanous // todo(ED) 254*377e76abSEd Tanous /* 255*377e76abSEd Tanous EXPECT_EQ(xml, dbus_boilerplate + 256*377e76abSEd Tanous "<node><interface " 257*377e76abSEd Tanous "name=\"MyInterface\"><method " 258*377e76abSEd Tanous "name=\"MyMethod\"></method></interface></node>"); 259*377e76abSEd Tanous */ 260*377e76abSEd Tanous } 261*377e76abSEd Tanous io.stop(); 262*377e76abSEd Tanous }); 263*377e76abSEd Tanous 264*377e76abSEd Tanous io.run(); 265*377e76abSEd Tanous } 266*377e76abSEd Tanous 267*377e76abSEd Tanous TEST(DbusPropertiesInterface, PropertiesInterface) { 268*377e76abSEd Tanous boost::asio::io_service io; 269*377e76abSEd Tanous auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session); 270*377e76abSEd Tanous 271*377e76abSEd Tanous // Set up the object server, and send some test objects 272*377e76abSEd Tanous dbus::DbusObjectServer foo(bus); 273*377e76abSEd Tanous auto object = 274*377e76abSEd Tanous std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"); 275*377e76abSEd Tanous foo.register_object(object); 276*377e76abSEd Tanous 277*377e76abSEd Tanous auto iface = std::make_shared<dbus::DbusInterface>( 278*377e76abSEd Tanous "org.freedesktop.My.Interface", bus); 279*377e76abSEd Tanous object->register_interface(iface); 280*377e76abSEd Tanous 281*377e76abSEd Tanous iface->set_property("foo", (uint32_t)26); 282*377e76abSEd Tanous 283*377e76abSEd Tanous dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1", 284*377e76abSEd Tanous "org.freedesktop.DBus.Properties"); 285*377e76abSEd Tanous dbus::message m = dbus::message::new_call(test_daemon, "Get"); 286*377e76abSEd Tanous m.pack("org.freedesktop.My.Interface", "foo"); 287*377e76abSEd Tanous 288*377e76abSEd Tanous bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) { 289*377e76abSEd Tanous if (ec) { 290*377e76abSEd Tanous std::string error; 291*377e76abSEd Tanous r.unpack(error); 292*377e76abSEd Tanous 293*377e76abSEd Tanous FAIL() << ec << error; 294*377e76abSEd Tanous } else { 295*377e76abSEd Tanous std::cout << r; 296*377e76abSEd Tanous 297*377e76abSEd Tanous EXPECT_EQ(r.get_type(), "method_return"); 298*377e76abSEd Tanous 299*377e76abSEd Tanous dbus::dbus_variant value; 300*377e76abSEd Tanous r.unpack(value); 301*377e76abSEd Tanous 302*377e76abSEd Tanous EXPECT_EQ(boost::get<uint32_t>(value), 26); 303*377e76abSEd Tanous } 304*377e76abSEd Tanous io.stop(); 305*377e76abSEd Tanous }); 306*377e76abSEd Tanous 307*377e76abSEd Tanous io.run(); 308*377e76abSEd Tanous }