diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-06-30 15:26:46 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2010-06-30 15:26:46 +0100 |
commit | 5773a99bd3a2a976740c6cc075b176d0296eb31a (patch) | |
tree | 11db78a1fc3047e96ba4ff9283417a283ec91ec0 | |
parent | 18803b5cdce0f2bac597a77e02eba01206bf4f2a (diff) |
Sync servicetest from Gabble
-rw-r--r-- | tests/twisted/hazetest.py | 45 | ||||
-rw-r--r-- | tests/twisted/servicetest.py | 253 | ||||
-rw-r--r-- | tests/twisted/text/destroy.py | 5 | ||||
-rw-r--r-- | tests/twisted/text/respawn.py | 11 |
4 files changed, 223 insertions, 91 deletions
diff --git a/tests/twisted/hazetest.py b/tests/twisted/hazetest.py index b7e5839..fc2537d 100644 --- a/tests/twisted/hazetest.py +++ b/tests/twisted/hazetest.py @@ -13,6 +13,7 @@ import random import ns import servicetest import twisted +from servicetest import Event, unwrap from twisted.words.xish import domish, xpath from twisted.words.protocols.jabber.client import IQ from twisted.words.protocols.jabber import xmlstream @@ -303,38 +304,13 @@ def make_stream(event_func, authenticator=None, protocol=None, port=4242): port = reactor.listenTCP(port, factory) return (stream, port) -def install_colourer(): - def red(s): - return '\x1b[31m%s\x1b[0m' % s - - def green(s): - return '\x1b[32m%s\x1b[0m' % s - - patterns = { - 'handled': green, - 'not handled': red, - } - - class Colourer: - def __init__(self, fh, patterns): - self.fh = fh - self.patterns = patterns - - def write(self, s): - f = self.patterns.get(s, lambda x: x) - self.fh.write(f(s)) - - sys.stdout = Colourer(sys.stdout, patterns) - return sys.stdout - - def exec_test_deferred (funs, params, protocol=None, timeout=None): # hack to ease debugging domish.Element.__repr__ = domish.Element.toXml colourer = None if sys.stdout.isatty(): - colourer = install_colourer() + colourer = servicetest.install_colourer() queue = servicetest.IteratingEventQueue(timeout) queue.verbose = ( @@ -345,6 +321,23 @@ def exec_test_deferred (funs, params, protocol=None, timeout=None): # conn = make_connection(bus, queue.append, params) (stream, port) = make_stream(queue.append, protocol=protocol) + def signal_receiver(*args, **kw): + queue.append(Event('dbus-signal', + path=unwrap(kw['path']), + signal=kw['member'], args=map(unwrap, args), + interface=kw['interface'])) + + bus.add_signal_receiver( + signal_receiver, + None, # signal name + None, # interface + None, + path_keyword='path', + member_keyword='member', + interface_keyword='interface', + byte_arrays=True + ) + error = None try: diff --git a/tests/twisted/servicetest.py b/tests/twisted/servicetest.py index 6b4dd05..bb00d7a 100644 --- a/tests/twisted/servicetest.py +++ b/tests/twisted/servicetest.py @@ -6,15 +6,17 @@ Infrastructure code for testing connection managers. from twisted.internet import glib2reactor from twisted.internet.protocol import Protocol, Factory, ClientFactory glib2reactor.install() +import sys import pprint -import traceback import unittest import dbus.glib from twisted.internet import reactor +import constants as cs + tp_name_prefix = 'org.freedesktop.Telepathy' tp_path_prefix = '/org/freedesktop/Telepathy' @@ -39,12 +41,21 @@ def format_event(event): class EventPattern: def __init__(self, type, **properties): self.type = type - self.predicate = lambda x: True + self.predicate = None if 'predicate' in properties: self.predicate = properties['predicate'] del properties['predicate'] self.properties = properties + def __repr__(self): + properties = dict(self.properties) + + if self.predicate is not None: + properties['predicate'] = self.predicate + + return '%s(%r, **%r)' % ( + self.__class__.__name__, self.type, properties) + def match(self, event): if event.type != self.type: return False @@ -56,7 +67,7 @@ class EventPattern: except AttributeError: return False - if self.predicate(event): + if self.predicate is None or self.predicate(event): return True return False @@ -73,6 +84,7 @@ class BaseEventQueue: def __init__(self, timeout=None): self.verbose = False + self.forbidden_events = set() if timeout is None: self.timeout = 5 @@ -83,20 +95,50 @@ class BaseEventQueue: if self.verbose: print s + def log_event(self, event): + if self.verbose: + self.log('got event:') + + if self.verbose: + map(self.log, format_event(event)) + + def forbid_events(self, patterns): + """ + Add patterns (an iterable of EventPattern) to the set of forbidden + events. If a forbidden event occurs during an expect or expect_many, + the test will fail. + """ + self.forbidden_events.update(set(patterns)) + + def unforbid_events(self, patterns): + """ + Remove 'patterns' (an iterable of EventPattern) from the set of + forbidden events. These must be the same EventPattern pointers that + were passed to forbid_events. + """ + self.forbidden_events.difference_update(set(patterns)) + + def _check_forbidden(self, event): + for e in self.forbidden_events: + if e.match(event): + print "forbidden event occurred:" + for x in format_event(event): + print x + assert False + def expect(self, type, **kw): pattern = EventPattern(type, **kw) while True: event = self.wait() - self.log('got event:') - map(self.log, format_event(event)) + self.log_event(event) + self._check_forbidden(event) if pattern.match(event): self.log('handled') self.log('') return event - self.past_events.append(event) self.log('not handled') self.log('') @@ -104,18 +146,25 @@ class BaseEventQueue: ret = [None] * len(patterns) while None in ret: - event = self.wait() - self.log('got event:') - map(self.log, format_event(event)) + try: + event = self.wait() + except TimeoutError: + self.log('timeout') + self.log('still expecting:') + for i, pattern in enumerate(patterns): + if ret[i] is None: + self.log(' - %r' % pattern) + raise + self.log_event(event) + self._check_forbidden(event) for i, pattern in enumerate(patterns): - if pattern.match(event): + if ret[i] is None and pattern.match(event): self.log('handled') self.log('') ret[i] = event break else: - self.past_events.append(event) self.log('not handled') self.log('') @@ -125,8 +174,7 @@ class BaseEventQueue: pattern = EventPattern(type, **kw) event = self.wait() - self.log('got event:') - map(self.log, format_event(event)) + self.log_event(event) if pattern.match(event): self.log('handled') @@ -191,6 +239,16 @@ class EventQueueTest(unittest.TestCase): assert bar.type == 'bar' assert foo.type == 'foo' + def test_expect_many2(self): + # Test that events are only matched against patterns that haven't yet + # been matched. This tests a regression. + queue = TestEventQueue([Event('foo', x=1), Event('foo', x=2)]) + foo1, foo2 = queue.expect_many( + EventPattern('foo'), + EventPattern('foo')) + assert foo1.type == 'foo' and foo1.x == 1 + assert foo2.type == 'foo' and foo2.x == 2 + def test_timeout(self): queue = TestEventQueue([]) self.assertRaises(TimeoutError, queue.expect, 'foo') @@ -217,7 +275,10 @@ def unwrap(x): if isinstance(x, dict): return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) - for t in [unicode, str, long, int, float, bool]: + if isinstance(x, dbus.Boolean): + return bool(x) + + for t in [unicode, str, long, int, float]: if isinstance(x, t): return t(x) @@ -232,7 +293,8 @@ def call_async(test, proxy, method, *args, **kw): value=unwrap(ret))) def error_func(err): - test.handle_event(Event('dbus-error', method=method, error=err)) + test.handle_event(Event('dbus-error', method=method, error=err, + name=err.get_dbus_name(), message=str(err))) method_proxy = getattr(proxy, method) kw.update({'reply_handler': reply_func, 'error_handler': error_func}) @@ -240,14 +302,20 @@ def call_async(test, proxy, method, *args, **kw): def sync_dbus(bus, q, conn): # Dummy D-Bus method call - call_async(q, conn, "InspectHandles", 1, []) - - event = q.expect('dbus-return', method='InspectHandles') + # This won't do the right thing unless the proxy has a unique name. + assert conn.object.bus_name.startswith(':') + root_object = bus.get_object(conn.object.bus_name, '/') + call_async( + q, dbus.Interface(root_object, 'org.freedesktop.DBus.Peer'), 'Ping') + q.expect('dbus-return', method='Ping') class ProxyWrapper: def __init__(self, object, default, others): self.object = object self.default_interface = dbus.Interface(object, default) + self.Properties = dbus.Interface(object, dbus.PROPERTIES_IFACE) + self.TpProperties = \ + dbus.Interface(object, tp_name_prefix + '.Properties') self.interfaces = dict([ (name, dbus.Interface(object, iface)) for name, iface in others.iteritems()]) @@ -261,6 +329,33 @@ class ProxyWrapper: return getattr(self.default_interface, name) +def wrap_connection(conn): + return ProxyWrapper(conn, tp_name_prefix + '.Connection', + dict([ + (name, tp_name_prefix + '.Connection.Interface.' + name) + for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', + 'Presence', 'SimplePresence', 'Requests']] + + [('Peer', 'org.freedesktop.DBus.Peer'), + ('ContactCapabilities', cs.CONN_IFACE_CONTACT_CAPS), + ('ContactInfo', cs.CONN_IFACE_CONTACT_INFO), + ('Location', cs.CONN_IFACE_LOCATION), + ('Future', tp_name_prefix + '.Connection.FUTURE'), + ('MailNotification', cs.CONN_IFACE_MAIL_NOTIFICATION), + ])) + +def wrap_channel(chan, type_, extra=None): + interfaces = { + type_: tp_name_prefix + '.Channel.Type.' + type_, + 'Group': tp_name_prefix + '.Channel.Interface.Group', + } + + if extra: + interfaces.update(dict([ + (name, tp_name_prefix + '.Channel.Interface.' + name) + for name in extra])) + + return ProxyWrapper(chan, tp_name_prefix + '.Channel', interfaces) + def make_connection(bus, event_func, name, proto, params): cm = bus.get_object( tp_name_prefix + '.ConnectionManager.%s' % name, @@ -269,29 +364,7 @@ def make_connection(bus, event_func, name, proto, params): connection_name, connection_path = cm_iface.RequestConnection( proto, params) - conn = bus.get_object(connection_name, connection_path) - conn = ProxyWrapper(conn, tp_name_prefix + '.Connection', - dict([ - (name, tp_name_prefix + '.Connection.Interface.' + name) - for name in ['Aliasing', 'Avatars', 'Capabilities', 'Contacts', - 'Presence', 'SimplePresence', 'Requests']] + - [('Peer', 'org.freedesktop.DBus.Peer')])) - - bus.add_signal_receiver( - lambda *args, **kw: - event_func( - Event('dbus-signal', - path=unwrap(kw['path'])[len(tp_path_prefix):], - signal=kw['member'], args=map(unwrap, args), - interface=kw['interface'])), - None, # signal name - None, # interface - cm._named_service, - path_keyword='path', - member_keyword='member', - interface_keyword='interface', - byte_arrays=True - ) + conn = wrap_connection(bus.get_object(connection_name, connection_path)) return conn @@ -301,20 +374,12 @@ def make_channel_proxy(conn, path, iface): chan = dbus.Interface(chan, tp_name_prefix + '.' + iface) return chan -def load_event_handlers(): - path, _, _, _ = traceback.extract_stack()[0] - import compiler - import __main__ - ast = compiler.parseFile(path) - return [ - getattr(__main__, node.name) - for node in ast.node.asList() - if node.__class__ == compiler.ast.Function and - node.name.startswith('expect_')] - +# block_reading can be used if the test want to choose when we start to read +# data from the socket. class EventProtocol(Protocol): - def __init__(self, queue=None): + def __init__(self, queue=None, block_reading=False): self.queue = queue + self.block_reading = block_reading def dataReceived(self, data): if self.queue is not None: @@ -324,12 +389,24 @@ class EventProtocol(Protocol): def sendData(self, data): self.transport.write(data) + def connectionMade(self): + if self.block_reading: + self.transport.stopReading() + + def connectionLost(self, reason=None): + if self.queue is not None: + self.queue.handle_event(Event('socket-disconnected', protocol=self)) + class EventProtocolFactory(Factory): - def __init__(self, queue): + def __init__(self, queue, block_reading=False): self.queue = queue + self.block_reading = block_reading + + def _create_protocol(self): + return EventProtocol(self.queue, self.block_reading) def buildProtocol(self, addr): - proto = EventProtocol(self.queue) + proto = self._create_protocol() self.queue.handle_event(Event('socket-connected', protocol=proto)) return proto @@ -348,6 +425,72 @@ def watch_tube_signals(q, tube): path_keyword='path', member_keyword='member', byte_arrays=True) +def pretty(x): + return pprint.pformat(unwrap(x)) + +def assertEquals(expected, value): + if expected != value: + raise AssertionError( + "expected:\n%s\ngot:\n%s" % (pretty(expected), pretty(value))) + +def assertNotEquals(expected, value): + if expected == value: + raise AssertionError( + "expected something other than:\n%s" % pretty(value)) + +def assertContains(element, value): + if element not in value: + raise AssertionError( + "expected:\n%s\nin:\n%s" % (pretty(element), pretty(value))) + +def assertDoesNotContain(element, value): + if element in value: + raise AssertionError( + "expected:\n%s\nnot in:\n%s" % (pretty(element), pretty(value))) + +def assertLength(length, value): + if len(value) != length: + raise AssertionError("expected: length %d, got length %d:\n%s" % ( + length, len(value), pretty(value))) + +def assertFlagsSet(flags, value): + masked = value & flags + if masked != flags: + raise AssertionError( + "expected flags %u, of which only %u are set in %u" % ( + flags, masked, value)) + +def assertFlagsUnset(flags, value): + masked = value & flags + if masked != 0: + raise AssertionError( + "expected none of flags %u, but %u are set in %u" % ( + flags, masked, value)) + +def install_colourer(): + def red(s): + return '\x1b[31m%s\x1b[0m' % s + + def green(s): + return '\x1b[32m%s\x1b[0m' % s + + patterns = { + 'handled': green, + 'not handled': red, + } + + class Colourer: + def __init__(self, fh, patterns): + self.fh = fh + self.patterns = patterns + + def write(self, s): + f = self.patterns.get(s, lambda x: x) + self.fh.write(f(s)) + + sys.stdout = Colourer(sys.stdout, patterns) + return sys.stdout + if __name__ == '__main__': unittest.main() diff --git a/tests/twisted/text/destroy.py b/tests/twisted/text/destroy.py index fdf4f34..cd3805b 100644 --- a/tests/twisted/text/destroy.py +++ b/tests/twisted/text/destroy.py @@ -8,7 +8,7 @@ import dbus from twisted.words.xish import domish from hazetest import exec_test -from servicetest import call_async, EventPattern, tp_path_prefix +from servicetest import call_async, EventPattern, assertEquals def test(q, bus, conn, stream): conn.Connect() @@ -122,8 +122,7 @@ def test(q, bus, conn, stream): call_async(q, destroyable_iface, 'Destroy') event = q.expect('dbus-signal', signal='Closed') - assert tp_path_prefix + event.path == text_chan.object_path,\ - (tp_path_prefix + event.path, text_chan.object_path) + assertEquals(text_chan.object_path, event.path) event = q.expect('dbus-return', method='Destroy') diff --git a/tests/twisted/text/respawn.py b/tests/twisted/text/respawn.py index 966c417..f716676 100644 --- a/tests/twisted/text/respawn.py +++ b/tests/twisted/text/respawn.py @@ -7,7 +7,7 @@ import dbus from twisted.words.xish import domish from hazetest import exec_test -from servicetest import call_async, EventPattern, tp_path_prefix +from servicetest import call_async, EventPattern, assertEquals def test(q, bus, conn, stream): conn.Connect() @@ -122,10 +122,8 @@ def test(q, bus, conn, stream): EventPattern('dbus-signal', signal='Closed'), EventPattern('dbus-signal', signal='ChannelClosed'), ) - assert tp_path_prefix + old.path == text_chan.object_path,\ - (tp_path_prefix + old.path, text_chan.object_path) - assert new.args[0] == text_chan.object_path,\ - (new.args[0], text_chan.object_path) + assertEquals(text_chan.object_path, old.path) + assertEquals(text_chan.object_path, new.args[0]) event = q.expect('dbus-signal', signal='NewChannel') assert event.args[0] == text_chan.object_path @@ -171,8 +169,7 @@ def test(q, bus, conn, stream): call_async(q, chan_iface, 'Close') event = q.expect('dbus-signal', signal='Closed') - assert tp_path_prefix + event.path == text_chan.object_path,\ - (tp_path_prefix + event.path, text_chan.object_path) + assertEquals(text_chan.object_path, event.path) event = q.expect('dbus-return', method='Close') |