summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon McVittie <simon.mcvittie@collabora.co.uk>2010-06-30 15:26:46 +0100
committerSimon McVittie <simon.mcvittie@collabora.co.uk>2010-06-30 15:26:46 +0100
commit5773a99bd3a2a976740c6cc075b176d0296eb31a (patch)
tree11db78a1fc3047e96ba4ff9283417a283ec91ec0
parent18803b5cdce0f2bac597a77e02eba01206bf4f2a (diff)
Sync servicetest from Gabble
-rw-r--r--tests/twisted/hazetest.py45
-rw-r--r--tests/twisted/servicetest.py253
-rw-r--r--tests/twisted/text/destroy.py5
-rw-r--r--tests/twisted/text/respawn.py11
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')