#include "wvdbusmsg.h" #include "wvdbusconn.h" #include "wvdbusserver.h" #include "wvfileutils.h" #include "wvfork.h" #include "wvtest.h" #include "wvloopback.h" #include "wvuid.h" class TestDBusServer { public: WvString moniker; WvDBusServer *s; TestDBusServer() { fprintf(stderr, "Creating a test DBus server.\n"); // We might prefer to use a unix: moniker, but get_addr() only // supports tcp: monikers just now. WvString smoniker("tcp:0.0.0.0"); s = new WvDBusServer(); s->listen(smoniker); moniker = s->get_addr(); fprintf(stderr, "Server address is '%s'\n", moniker.cstr()); WvIStreamList::globallist.append(s, false, "dbus server listener"); } ~TestDBusServer() { WVRELEASE(s); /* Flush connections out of the globallist, necessary to trigger * the actual killing of the WvDBusServer object (it's ref- * counted based on #connections). No self-respecting program would * need to do this, but we don't want Valgrind thinking we're leaking * memory, or the open file descriptor checker freaking out. */ for (int i = 0; i < 1; ++i) WvIStreamList::globallist.runonce(); WVPASS(WvIStreamList::globallist.isempty()); } }; static int mysignal_count = 0; static bool mysignal(WvDBusMsg &msg) { if (msg.get_interface() == "x.y.z.anything") { fprintf(stderr, "Got a message! (%s)\n", ((WvString)msg).cstr()); mysignal_count++; return true; // we handle *any* message } else { fprintf(stderr, "Ignored a message! (%s)\n", ((WvString)msg).cstr()); return false; } } WVTEST_MAIN("dbusserver basics") { int junk; TestDBusServer serv; WvDBusConn conn1(serv.moniker); WvIStreamList::globallist.append(&conn1, false, "dbus connection"); conn1.request_name("ca.nit.MySender"); conn1.add_callback(WvDBusConn::PriNormal, mysignal, &junk); WVPASSEQ(mysignal_count, 0); WvDBusMsg("ca.nit.MySender", "/foo", "x.y.z.anything", "testmethod") .append("hello").send(conn1); WvDBusSignal("/foo", "x.y.z.anything", "testsignal") .append("hello").send(conn1); while (mysignal_count < 2) WvIStreamList::globallist.runonce(); WVPASSEQ(mysignal_count, 2); } static int replies_received = 0; static bool reply_received(WvDBusMsg &msg) { WvDBusMsg::Iter i(msg); WvString s = i.getnext(); fprintf(stderr, "wow! reply received! (%s)\n", s.cstr()); WVPASS(!!msg.get_sender()); replies_received++; return true; } static int messages_received = 0; static bool msg_received(WvDBusConn &conn, WvDBusMsg &msg) { WvDBusMsg::Iter i(msg); WvString arg1 = i.getnext(); if (msg.get_dest() == "ca.nit.MyListener" && msg.get_path() == "/ca/nit/foo" && msg.get_member() == "bar") { fprintf(stderr, "Message received (%s)\n", ((WvString)msg).cstr()); WVPASS(!!msg.get_sender()); messages_received++; msg.reply().append(WvString("baz %s", arg1)).send(conn); return true; } else { fprintf(stderr, "msg_received: not my message.\n"); return false; } } static int reg_count = 0; bool name_registered(WvDBusMsg &msg) { reg_count++; return true; } WVTEST_MAIN("dbusserver two connections") { TestDBusServer serv; WvDBusConn conn1(serv.moniker); WvDBusConn conn2(serv.moniker); WvIStreamList::globallist.append(&conn1, false, "dbus connection 1"); WvIStreamList::globallist.append(&conn2, false, "dbus connection 2"); conn2.add_callback(WvDBusConn::PriNormal, wv::bind(msg_received, wv::ref(conn2), _1)); reg_count = 0; conn1.request_name("ca.nit.MySender", name_registered); conn2.request_name("ca.nit.MyListener", name_registered); while (reg_count < 2) WvIStreamList::globallist.runonce(); WvDBusMsg msg("ca.nit.MyListener", "/ca/nit/foo", "ca.nit.foo", "bar"); msg.append("bee"); conn1.send(msg, reply_received); while (replies_received < 1 || messages_received < 1) WvIStreamList::globallist.runonce(); WVPASSEQ(messages_received, 1); WVPASSEQ(replies_received, 1); } WVTEST_MAIN("dbusserver overlapping registrations") { TestDBusServer serv; WvDBusConn *cli = new WvDBusConn(serv.moniker); WvDBusConn *l1 = new WvDBusConn(serv.moniker); WvDBusConn *l2 = new WvDBusConn(serv.moniker); WvIStreamList::globallist.append(cli, false, "dbus connection 1"); WvIStreamList::globallist.append(l1, false, "dbus connection 2"); WvIStreamList::globallist.append(l2, false, "dbus connection 3"); reg_count = 0; l1->request_name("ca.nit.MySender", name_registered); while (reg_count < 1) WvIStreamList::globallist.runonce(); l1->add_callback(WvDBusConn::PriNormal, mysignal); l2->add_callback(WvDBusConn::PriNormal, mysignal); WvDBusMsg meth("ca.nit.MySender", "/foo", "x.y.z.anything", "testmethod"); meth.append("methhello"); WvDBusSignal sig("/foo", "x.y.z.anything", "testsignal"); sig.append("sighello"); mysignal_count = 0; cli->send(sig); cli->send(meth, mysignal); while (mysignal_count < 3 || WvIStreamList::globallist.select(200)) WvIStreamList::globallist.runonce(); WVPASSEQ(mysignal_count, 3); // one method, two signals mysignal_count = 0; delete l1; cli->send(sig); #if NO_DBUS_PENDING_MEMORY_LEAK // this seems to cause a memory leak in dbus 0.60, since nobody cleans up // the remaining DBusPendingCall objects if a connection closes. Browsing // the source code for later versions, this seems to have been fixed, but // I haven't tried it yet. Anyway, it's not our bug. The cleanup // activity in newer dbus may reveal that we have a bug too, of course. cli->send(meth, mysignal); #endif while (mysignal_count < 1 || WvIStreamList::globallist.select(200)) WvIStreamList::globallist.runonce(); WVPASSEQ(mysignal_count, 1); // no method receiver, one signal mysignal_count = 0; reg_count = 0; l2->request_name("ca.nit.MySender", name_registered); while (reg_count < 1) WvIStreamList::globallist.runonce(); cli->send(sig); cli->send(meth, mysignal); while (mysignal_count < 2 || WvIStreamList::globallist.select(200)) WvIStreamList::globallist.runonce(); WVPASSEQ(mysignal_count, 2); // one method receiver, one signal delete cli; delete l2; } static bool got_uid = false; static bool check_uid(WvDBusMsg &msg) { fprintf(stderr, "Got a uid message! (%s)\n", ((WvString)msg).cstr()); if (msg.iserror()) WVPASS(wvgetuid() == WVUID_INVALID); else { WvDBusMsg::Iter i(msg); wvuid_t uid = i.getnext(); WVPASSEQ(wvgetuid(), uid); } got_uid = true; return true; // we handle *any* message } static bool got_uname = false; static bool check_uname(WvDBusMsg &msg) { fprintf(stderr, "Got a uname message! (%s)\n", ((WvString)msg).cstr()); if (msg.iserror()) WVPASS(wvgetuid() == WVUID_INVALID); else { WvDBusMsg::Iter i(msg); WvString uname = i.getnext(); WVPASSEQ(wv_username_from_uid(wvgetuid()), uname); } got_uname = true; return true; // we handle *any* message } WVTEST_MAIN("GetConnectionUnixUser") { TestDBusServer serv; WvDBusConn conn1(serv.moniker); WvIStreamList::globallist.append(&conn1, false, "dbus connection"); while (!conn1.uniquename()) WvIStreamList::globallist.runonce(); WvLog log("GetConnection", WvLog::Notice); log("Connection's uniquename: %s\n", conn1.uniquename()); conn1.send(WvDBusMsg("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionUnixUser") .append(conn1.uniquename().cstr()), check_uid); while (!got_uid) WvIStreamList::globallist.runonce(); WVPASS(got_uid); conn1.send(WvDBusMsg("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionUnixUserName") .append(conn1.uniquename().cstr()), check_uname); while (!got_uname) WvIStreamList::globallist.runonce(); WVPASS(got_uname); conn1.close(); }