diff options
author | Jonny Lamb <jonny.lamb@collabora.co.uk> | 2011-04-05 11:52:48 +0100 |
---|---|---|
committer | Jonny Lamb <jonny.lamb@collabora.co.uk> | 2011-04-05 11:52:48 +0100 |
commit | 381972fb479a62e1ec994335b75bf7cf01b851be (patch) | |
tree | 5d8ca0dbdf83fb836b4d950b0528f2700b442d08 | |
parent | 758dba3714c8c586c21478e8759800acaeb9dc34 (diff) | |
parent | ff4a8979d408daaebd19ff041b2d908aa3a3bc76 (diff) |
Merge remote branch 'smcv/mock-avahi'
-rw-r--r-- | tests/twisted/Makefile.am | 2 | ||||
-rw-r--r-- | tests/twisted/avahi/file-transfer/test-receive-file-ipv6.py | 3 | ||||
-rw-r--r-- | tests/twisted/avahi/file-transfer/test-send-file-to-unknown-contact.py | 3 | ||||
-rwxr-xr-x | tests/twisted/avahimock.py | 424 | ||||
-rw-r--r-- | tests/twisted/avahitest.py | 5 | ||||
-rw-r--r-- | tests/twisted/saluttest.py | 30 | ||||
-rw-r--r-- | tests/twisted/tools/exec-with-log.sh.in | 3 | ||||
-rw-r--r-- | tests/twisted/tools/org.freedesktop.Avahi.service | 3 | ||||
-rw-r--r-- | tests/twisted/tools/with-session-bus.sh | 9 |
9 files changed, 478 insertions, 4 deletions
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am index 3062af6a..e920b941 100644 --- a/tests/twisted/Makefile.am +++ b/tests/twisted/Makefile.am @@ -78,7 +78,7 @@ check-local: check-coding-style check-twisted check-twisted: $(MAKE) -C tools rm -f tools/core - sh $(srcdir)/tools/with-session-bus.sh --config-file=tools/tmp-session-bus.conf -- $(MAKE) check-TESTS \ + sh $(srcdir)/tools/with-session-bus.sh --also-for-system --config-file=tools/tmp-session-bus.conf -- $(MAKE) check-TESTS \ TESTS="$(TWISTED_TESTS)" \ TESTS_ENVIRONMENT="$(TESTS_ENVIRONMENT) $(TEST_PYTHON)" @if test -e tools/core; then\ diff --git a/tests/twisted/avahi/file-transfer/test-receive-file-ipv6.py b/tests/twisted/avahi/file-transfer/test-receive-file-ipv6.py index 8fd65b0b..ae0d65a8 100644 --- a/tests/twisted/avahi/file-transfer/test-receive-file-ipv6.py +++ b/tests/twisted/avahi/file-transfer/test-receive-file-ipv6.py @@ -36,7 +36,8 @@ class TestReceiveFileIPv6(ReceiveFileTest): service = e.service service.resolve() - e = self.q.expect('service-resolved', service = service) + e = self.q.expect('service-resolved', service = service, + protocol = avahi.PROTO_INET6) return str(e.pt), e.port def connect_to_salut(self): diff --git a/tests/twisted/avahi/file-transfer/test-send-file-to-unknown-contact.py b/tests/twisted/avahi/file-transfer/test-send-file-to-unknown-contact.py index d6871fdb..14f95242 100644 --- a/tests/twisted/avahi/file-transfer/test-send-file-to-unknown-contact.py +++ b/tests/twisted/avahi/file-transfer/test-send-file-to-unknown-contact.py @@ -19,7 +19,8 @@ class SendFileTransferToUnknownContactTest(SendFileTest): try: self.request_ft_channel() except dbus.DBusException, e: - assert e.get_dbus_name() == cs.NOT_AVAILABLE + if e.get_dbus_name() != cs.NOT_AVAILABLE: + raise else: assert False, "Should raise NotAvailable error" diff --git a/tests/twisted/avahimock.py b/tests/twisted/avahimock.py new file mode 100755 index 00000000..c4e0b049 --- /dev/null +++ b/tests/twisted/avahimock.py @@ -0,0 +1,424 @@ +#!/usr/bin/python + +import socket + +import dbus +import dbus.service +from dbus.lowlevel import SignalMessage +import gobject +import glib + +from dbus.mainloop.glib import DBusGMainLoop +DBusGMainLoop(set_as_default=True) + +AVAHI_NAME = 'org.freedesktop.Avahi' +AVAHI_IFACE_SERVER = 'org.freedesktop.Avahi.Server' +AVAHI_IFACE_ENTRY_GROUP = 'org.freedesktop.Avahi.EntryGroup' +AVAHI_IFACE_SERVICE_BROWSER = 'org.freedesktop.Avahi.ServiceBrowser' +AVAHI_IFACE_SERVICE_RESOLVER = 'org.freedesktop.Avahi.ServiceResolver' + +AVAHI_DNS_CLASS_IN = 1 +AVAHI_DNS_TYPE_A = 1 + +AVAHI_PROTO_INET = 0 +AVAHI_PROTO_INET6 = 1 +AVAHI_PROTO_UNSPEC = -1 + +def emit_signal(object_path, interface, name, destination, signature, *args): + message = SignalMessage(object_path, interface, name) + message.append(*args, signature=signature) + + if destination is not None: + message.set_destination(destination) + + dbus.SystemBus().send_message(message) + + +def get_domain(): + full_domain = socket.getfqdn() + if '.' in full_domain: + return full_domain.split('.', 1)[1] + else: + return '' + +class Model(object): + def __init__(self): + self._service_browsers = [] + self._service_resolvers = [] + self._entries = [] + self._address_records = {} + + def new_service_browser(self, type_, client): + index = len(self._service_browsers) + 1 + service_browser = ServiceBrowser(client, index, type_) + self._service_browsers.append(service_browser) + + glib.idle_add(self.__browse_idle_cb, service_browser) + + return service_browser.object_path + + def __browse_idle_cb(self, service_browser): + for entry in self._entries: + if entry.type == service_browser.type: + self._emit_new_item(service_browser, entry) + + def _find_entry(self, type_, name): + for entry in self._entries: + if entry.type == type_ and entry.name == name: + return entry + return None + + def new_service_resolver(self, type_, name, protocol, client): + index = len(self._service_resolvers) + 1 + entry = self._find_entry(type_, name) + service_resolver = ServiceResolver(index, client, type_, name, protocol) + self._service_resolvers.append(service_resolver) + + glib.idle_add(self.__entry_found_idle_cb, service_resolver, entry) + + return service_resolver.object_path + + def __entry_found_idle_cb(self, service_resolver, entry): + if entry is None: + emit_signal(service_resolver.object_path, + AVAHI_IFACE_SERVICE_RESOLVER, 'Failure', + service_resolver.client, 's', + 'fill with a proper error string') + else: + self._emit_found(service_resolver, entry) + + def _resolve_hostname(self, protocol, hostname): + if hostname in self._address_records: + return self._address_records[hostname] + else: + if protocol == AVAHI_PROTO_INET6: + for family, _, _, _, address in socket.getaddrinfo(hostname, None): + if family == socket.AF_INET6: + return address[0] + + # HACK: If resolving the given name doesn't give us an ip6 + # address, return '::1' if it is the local hostname. + if hostname == socket.gethostname(): + return '::1' + else: + return socket.gethostbyname(hostname) + + def update_entry(self, interface, protocol, flags, name, type_, domain, + host, port, txt): + entry = self._find_entry(type_, name) + + if interface == -1: + interface = 0 + + if host is None: + host = entry.host + + if port is None: + port = entry.port + + if entry is None: + entry = Entry(interface, protocol, flags, name, type_, domain, + host, port, txt) + self._entries.append(entry) + else: + entry.update(interface, protocol, flags, domain, host, port, txt) + + for service_browser in self._service_browsers: + if service_browser.type == type_: + self._emit_new_item(service_browser, entry) + + for service_resolver in self._service_resolvers: + if service_resolver.type == type_ and \ + service_resolver.name == name: + self._emit_found(service_resolver, entry) + + def add_record(self, interface, protocol, flags, name, clazz, type_, ttl, + rdata): + if clazz == AVAHI_DNS_CLASS_IN and type_ == AVAHI_DNS_TYPE_A: + self._address_records[name] = socket.inet_ntoa(rdata) + + def remove_entry(self, type_, name): + entry = self._find_entry(type_, name) + if entry is None: + # Entry may have been created by more than one EntryGroup + return + + for service_browser in self._service_browsers: + if service_browser.type == type_: + self._emit_item_remove(service_browser, entry) + + self._entries.remove(entry) + + def _emit_new_item(self, service_browser, entry): + if entry.protocol == AVAHI_PROTO_UNSPEC: + protocols = (AVAHI_PROTO_INET, AVAHI_PROTO_INET6) + else: + protocols = (entry.protocol,) + + for protocol in protocols: + emit_signal(service_browser.object_path, + AVAHI_IFACE_SERVICE_BROWSER, 'ItemNew', + service_browser.client, 'iisssu', + entry.interface, protocol, entry.name, entry.type, + entry.domain, entry.flags) + + def _emit_item_remove(self, service_browser, entry): + if entry.protocol == AVAHI_PROTO_UNSPEC: + protocols = (AVAHI_PROTO_INET, AVAHI_PROTO_INET6) + else: + protocols = (entry.protocol,) + + for protocol in protocols: + emit_signal(service_browser.object_path, + AVAHI_IFACE_SERVICE_BROWSER, 'ItemRemove', + service_browser.client, 'iisssu', + entry.interface, protocol, entry.name, entry.type, + entry.domain, entry.flags) + + def _emit_found(self, service_resolver, entry): + protocols = [] + if service_resolver.protocol in [AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET]: + if entry.protocol in [AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET]: + protocols.append(AVAHI_PROTO_INET) + + if service_resolver.protocol in [AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET6]: + if entry.protocol in [AVAHI_PROTO_UNSPEC, AVAHI_PROTO_INET6]: + protocols.append(AVAHI_PROTO_INET6) + + for protocol in protocols: + address = self._resolve_hostname(protocol, entry.host) + emit_signal(service_resolver.object_path, + AVAHI_IFACE_SERVICE_RESOLVER, 'Found', + service_resolver.client, 'iissssisqaayu', + entry.interface, protocol, entry.name, entry.type, + entry.domain, entry.host, entry.aprotocol, + address, entry.port, entry.txt, entry.flags) + + def remove_client(self, client): + for service_browser in self._service_browsers[:]: + if service_browser.client == client: + service_browser.Free() + service_browser.remove_from_connection() + self._service_browsers.remove(service_browser) + + for service_resolver in self._service_resolvers[:]: + if service_resolver.client == client: + service_resolver.Free() + service_resolver.remove_from_connection() + self._service_resolvers.remove(service_resolver) + + +class Entry(object): + def __init__(self, interface, protocol, flags, name, type_, domain, host, + port, txt): + self.name = name + self.type = type_ + + self.interface = None + self.protocol = None + self.aprotocol = None + self.flags = None + self.domain = None + self.host = None + self.port = None + self.txt = None + + self.update(interface, protocol, flags, domain, host, port, txt) + + def update(self, interface, protocol, flags, domain, host, port, txt): + self.interface = interface + self.protocol = protocol + self.aprotocol = protocol + self.flags = flags + self.domain = domain + self.host = host + self.port = port + self.txt = txt + +class Avahi(dbus.service.Object): + def __init__(self): + bus = dbus.SystemBus() + name = dbus.service.BusName(AVAHI_NAME, bus) + dbus.service.Object.__init__(self, conn=bus, object_path='/', + bus_name=name) + + bus.add_signal_receiver(self.__name_owner_changed_cb, + signal_name='NameOwnerChanged', + dbus_interface='org.freedesktop.DBus') + + self._entry_groups = [] + self._model = Model() + + def __name_owner_changed_cb(self, name, old_owner, new_owner): + if new_owner == '': + for entry_group in self._entry_groups[:]: + if entry_group.client == name: + entry_group.Free() + entry_group.remove_from_connection() + self._entry_groups.remove(entry_group) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='u') + def GetAPIVersion(self): + return 515 + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='s') + def GetHostName(self): + return socket.gethostname() + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='s') + def GetHostNameFqdn(self): + return socket.getfqdn() + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='s') + def GetDomainName(self): + return get_domain() + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='i') + def GetState(self): + return 2 + + @dbus.service.signal(dbus_interface=AVAHI_IFACE_SERVER, signature='is') + def StateChanged(self, state, error): + pass + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='', out_signature='o', + sender_keyword='sender') + def EntryGroupNew(self, sender): + index = len(self._entry_groups) + 1 + entry_group = EntryGroup(sender, index, self._model) + self._entry_groups.append(entry_group) + return entry_group.object_path + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='iissu', out_signature='o', + sender_keyword='sender') + def ServiceBrowserNew(self, interface, protocol, type_, domain, flags, sender): + return self._model.new_service_browser(type_, sender) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVER, + in_signature='iisssiu', out_signature='o', + sender_keyword='sender') + def ServiceResolverNew(self, interface, protocol, name, type_, domain, aprotocol, flags, sender): + return self._model.new_service_resolver(type_, name, protocol, sender) + + +class EntryGroup(dbus.service.Object): + def __init__(self, client, index, model): + bus = dbus.SystemBus() + self.object_path = '/Client%u/EntryGroup%u' % (1, index) + dbus.service.Object.__init__(self, conn=bus, + object_path=self.object_path) + + self._state = 0 + self.client = client + self._model = model + + self._entries = [] + + def get_service(self, name): + return self._services.get(name, None) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='iiussssqaay', out_signature='', + byte_arrays=True) + def AddService(self, interface, protocol, flags, name, type_, domain, host, + port, txt): + if not host: + host = socket.gethostname() + + if not domain: + domain = get_domain() + + self._model.update_entry(interface, protocol, flags, name, type_, domain, + host, port, txt) + self._entries.append((type_, name)) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='iiusssaay', out_signature='', + byte_arrays=True) + def UpdateServiceTxt(self, interface, protocol, flags, name, type_, domain, txt): + self._model.update_entry(interface, protocol, flags, name, type_, domain, + None, None, txt) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='', out_signature='') + def Commit(self): + self._set_state(1) + glib.idle_add(lambda: self._set_state(2)) + + def _set_state(self, new_state): + self._state = new_state + + message = SignalMessage(self.object_path, + AVAHI_IFACE_ENTRY_GROUP, + 'StateChanged') + message.append(self._state, 'org.freedesktop.Avahi.Success', + signature='is') + message.set_destination(self.client) + + dbus.SystemBus().send_message(message) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='', out_signature='i') + def GetState(self): + return self._state + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='iiusqquay', out_signature='', + byte_arrays=True) + def AddRecord(self, interface, protocol, flags, name, clazz, type_, ttl, + rdata): + self._model.add_record(interface, protocol, flags, name, clazz, type_, + ttl, rdata) + + @dbus.service.method(dbus_interface=AVAHI_IFACE_ENTRY_GROUP, + in_signature='', out_signature='') + def Free(self): + for type_, name in self._entries[:]: + self._model.remove_entry(type_, name) + self._entries.remove((type_, name)) + + +class ServiceBrowser(dbus.service.Object): + def __init__(self, client, index, type_): + bus = dbus.SystemBus() + self.object_path = '/Client%u/ServiceBrowser%u' % (1, index) + dbus.service.Object.__init__(self, conn=bus, + object_path=self.object_path) + + self.client = client + self.type = type_ + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVICE_BROWSER, + in_signature='', out_signature='') + def Free(self): + pass + + +class ServiceResolver(dbus.service.Object): + def __init__(self, index, client, type_, name, protocol): + bus = dbus.SystemBus() + self.object_path = '/Client%u/ServiceResolver%u' % (1, index) + dbus.service.Object.__init__(self, conn=bus, + object_path=self.object_path) + self.client = client + self.type = type_ + self.name = name + self.protocol = protocol + + @dbus.service.method(dbus_interface=AVAHI_IFACE_SERVICE_RESOLVER, + in_signature='', out_signature='') + def Free(self): + pass + + +avahi = Avahi() + +loop = gobject.MainLoop() +loop.run() diff --git a/tests/twisted/avahitest.py b/tests/twisted/avahitest.py index f9c1d7b2..d73fe557 100644 --- a/tests/twisted/avahitest.py +++ b/tests/twisted/avahitest.py @@ -144,7 +144,7 @@ class AvahiRecordAnnouncer: self.entry = entry class AvahiAnnouncer: - def __init__(self, name, type, port, txt, hostname = get_host_name_fqdn(), + def __init__(self, name, type, port, txt, hostname=None, proto=avahi.PROTO_INET): self.name = name self.type = type @@ -161,6 +161,9 @@ class AvahiAnnouncer: entry = dbus.Interface(entry_obj, avahi.DBUS_INTERFACE_ENTRY_GROUP) + if hostname is None: + hostname = get_host_name_fqdn() + entry.AddService(avahi.IF_UNSPEC, self.proto, dbus.UInt32(0), name, type, get_domain_name(), hostname, port, avahi.dict_to_txt_array(txt)) diff --git a/tests/twisted/saluttest.py b/tests/twisted/saluttest.py index 16a07e8f..345de7fb 100644 --- a/tests/twisted/saluttest.py +++ b/tests/twisted/saluttest.py @@ -6,6 +6,7 @@ Infrastructure code for testing Salut import os import sys import time +from subprocess import Popen import servicetest from servicetest import call_async, EventPattern @@ -15,6 +16,7 @@ from twisted.words.protocols.jabber.client import IQ import ns import dbus +import glib # keep sync with src/salut-capabilities.c:self_advertised_features fixed_features = [ns.SI, ns.IBB, ns.TUBES, ns.IQ_OOB, ns.X_OOB] @@ -52,10 +54,38 @@ def make_connection(bus, event_func, params=None): return servicetest.make_connection(bus, event_func, 'salut', 'local-xmpp', default_params) +def ensure_avahi_is_running(): + bus = dbus.SystemBus() + bus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + if bus_obj.NameHasOwner('org.freedesktop.Avahi', + dbus_interface='org.freedesktop.DBus'): + return + + loop = glib.MainLoop() + def name_owner_changed_cb(name, old_owner, new_owner): + loop.quit() + + bus.add_signal_receiver(name_owner_changed_cb, + signal_name='NameOwnerChanged', + dbus_interface='org.freedesktop.DBus', + arg0='org.freedesktop.Avahi') + + # Cannot use D-Bus activation because we have no way to pass to activated + # clients the address of the system bus and we cannot host the service in + # this process because we are going to make blocking calls and we would + # deadlock. + tests_dir = os.path.dirname(__file__) + avahimock_path = os.path.join(tests_dir, 'avahimock.py') + Popen([avahimock_path]) + + loop.run() + def exec_test_deferred (fun, params, protocol=None, timeout=None, make_conn=True): colourer = None + ensure_avahi_is_running() + if sys.stdout.isatty() or 'CHECK_FORCE_COLOR' in os.environ: colourer = servicetest.install_colourer() diff --git a/tests/twisted/tools/exec-with-log.sh.in b/tests/twisted/tools/exec-with-log.sh.in index bec346ed..6528a5d5 100644 --- a/tests/twisted/tools/exec-with-log.sh.in +++ b/tests/twisted/tools/exec-with-log.sh.in @@ -20,5 +20,8 @@ elif test -n "$SALUT_TEST_REFDBG"; then fi fi +# The bus-daemon that is activating us doesn't know it's also the system bus +export DBUS_SYSTEM_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" + export G_DEBUG=fatal-warnings" ${G_DEBUG}" exec @abs_top_builddir@/libtool --mode=execute $SALUT_WRAPPER @abs_top_builddir@/src/telepathy-salut diff --git a/tests/twisted/tools/org.freedesktop.Avahi.service b/tests/twisted/tools/org.freedesktop.Avahi.service new file mode 100644 index 00000000..c5c500c5 --- /dev/null +++ b/tests/twisted/tools/org.freedesktop.Avahi.service @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.freedesktop.Avahi +Exec=dumb diff --git a/tests/twisted/tools/with-session-bus.sh b/tests/twisted/tools/with-session-bus.sh index 3549b6ca..948f670f 100644 --- a/tests/twisted/tools/with-session-bus.sh +++ b/tests/twisted/tools/with-session-bus.sh @@ -44,6 +44,10 @@ while test "z$1" != "z--"; do dbus_daemon_args="$dbus_daemon_args $1" shift ;; + --also-for-system) + with_system_bus=1 + shift + ;; *) usage ;; @@ -80,6 +84,11 @@ e=0 DBUS_SESSION_BUS_ADDRESS="`cat $me-$$.address`" export DBUS_SESSION_BUS_ADDRESS +if [ -n "$with_system_bus" ] ; then + DBUS_SYSTEM_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS" + export DBUS_SYSTEM_BUS_ADDRESS +fi + if [ -n "$WITH_SESSION_BUS_FORK_DBUS_MONITOR" ] ; then echo -n "Forking dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT" >&2 dbus-monitor $WITH_SESSION_BUS_FORK_DBUS_MONITOR_OPT \ |