source: wokkel/test/test_compat.py @ 160:33ef849a77d8

Last change on this file since 160:33ef849a77d8 was 160:33ef849a77d8, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Use symbolic constants instead of integers MUC status code.

Instead of using normal constant values for representing MUC status codes,
UserPresence now uses twisted.python.constants to define these. This
makes code better to understand and helps debugging.

If a user presence includes one or more status codes, they are stored in the
mucStatuses attribute, as an instance of Statuses. This replaces the
former statusCodes attribute.

  • Property exe set to *
File size: 18.6 KB
Line 
1# Copyright (c) Twisted Matrix Laboratories.
2# Copyright (c) Ralph Meijer.
3# See LICENSE for details.
4
5"""
6Tests for L{wokkel.compat}.
7"""
8
9from zope.interface import implements
10from zope.interface.verify import verifyObject
11from twisted.internet import protocol, task
12from twisted.internet.interfaces import IProtocolFactory, IReactorTime
13from twisted.trial import unittest
14from twisted.words.xish import utility
15from twisted.words.protocols.jabber import xmlstream
16
17from wokkel.compat import BootstrapMixin, IQ, XmlStreamServerFactory
18from wokkel.compat import NamedConstant, Names, ValueConstant, Values
19
20class DummyProtocol(protocol.Protocol, utility.EventDispatcher):
21    """
22    I am a protocol with an event dispatcher without further processing.
23
24    This protocol is only used for testing BootstrapMixin to make
25    sure the bootstrap observers are added to the protocol instance.
26    """
27
28    def __init__(self, *args, **kwargs):
29        self.args = args
30        self.kwargs = kwargs
31        self.observers = []
32
33        utility.EventDispatcher.__init__(self)
34
35
36
37class BootstrapMixinTest(unittest.TestCase):
38    """
39    Tests for L{BootstrapMixin}.
40
41    @ivar factory: Instance of the factory or mixin under test.
42    """
43
44    def setUp(self):
45        self.factory = BootstrapMixin()
46
47
48    def test_installBootstraps(self):
49        """
50        Dispatching an event should fire registered bootstrap observers.
51        """
52        called = []
53
54        def cb(data):
55            called.append(data)
56
57        dispatcher = DummyProtocol()
58        self.factory.addBootstrap('//event/myevent', cb)
59        self.factory.installBootstraps(dispatcher)
60
61        dispatcher.dispatch(None, '//event/myevent')
62        self.assertEquals(1, len(called))
63
64
65    def test_addAndRemoveBootstrap(self):
66        """
67        Test addition and removal of a bootstrap event handler.
68        """
69
70        called = []
71
72        def cb(data):
73            called.append(data)
74
75        self.factory.addBootstrap('//event/myevent', cb)
76        self.factory.removeBootstrap('//event/myevent', cb)
77
78        dispatcher = DummyProtocol()
79        self.factory.installBootstraps(dispatcher)
80
81        dispatcher.dispatch(None, '//event/myevent')
82        self.assertFalse(called)
83
84
85
86class XmlStreamServerFactoryTest(BootstrapMixinTest):
87    """
88    Tests for L{XmlStreamServerFactory}.
89    """
90
91    def setUp(self):
92        """
93        Set up a server factory with a authenticator factory function.
94        """
95        class TestAuthenticator(object):
96            def __init__(self):
97                self.xmlstreams = []
98
99            def associateWithStream(self, xs):
100                self.xmlstreams.append(xs)
101
102        def authenticatorFactory():
103            return TestAuthenticator()
104
105        self.factory = XmlStreamServerFactory(authenticatorFactory)
106
107
108    def test_interface(self):
109        """
110        L{XmlStreamServerFactory} is a L{Factory}.
111        """
112        verifyObject(IProtocolFactory, self.factory)
113
114
115    def test_buildProtocolAuthenticatorInstantiation(self):
116        """
117        The authenticator factory should be used to instantiate the
118        authenticator and pass it to the protocol.
119
120        The default protocol, L{XmlStream} stores the authenticator it is
121        passed, and calls its C{associateWithStream} method. so we use that to
122        check whether our authenticator factory is used and the protocol
123        instance gets an authenticator.
124        """
125        xs = self.factory.buildProtocol(None)
126        self.assertEquals([xs], xs.authenticator.xmlstreams)
127
128
129    def test_buildProtocolXmlStream(self):
130        """
131        The protocol factory creates Jabber XML Stream protocols by default.
132        """
133        xs = self.factory.buildProtocol(None)
134        self.assertIsInstance(xs, xmlstream.XmlStream)
135
136
137    def test_buildProtocolTwice(self):
138        """
139        Subsequent calls to buildProtocol should result in different instances
140        of the protocol, as well as their authenticators.
141        """
142        xs1 = self.factory.buildProtocol(None)
143        xs2 = self.factory.buildProtocol(None)
144        self.assertNotIdentical(xs1, xs2)
145        self.assertNotIdentical(xs1.authenticator, xs2.authenticator)
146
147
148    def test_buildProtocolInstallsBootstraps(self):
149        """
150        The protocol factory installs bootstrap event handlers on the protocol.
151        """
152        called = []
153
154        def cb(data):
155            called.append(data)
156
157        self.factory.addBootstrap('//event/myevent', cb)
158
159        xs = self.factory.buildProtocol(None)
160        xs.dispatch(None, '//event/myevent')
161
162        self.assertEquals(1, len(called))
163
164
165    def test_buildProtocolStoresFactory(self):
166        """
167        The protocol factory is saved in the protocol.
168        """
169        xs = self.factory.buildProtocol(None)
170        self.assertIdentical(self.factory, xs.factory)
171
172
173
174class FakeReactor(object):
175
176    implements(IReactorTime)
177    def __init__(self):
178        self.clock = task.Clock()
179        self.callLater = self.clock.callLater
180        self.getDelayedCalls = self.clock.getDelayedCalls
181
182
183
184class IQTest(unittest.TestCase):
185    """
186    Tests for L{IQ}.
187    """
188
189    def setUp(self):
190        self.reactor = FakeReactor()
191        self.clock = self.reactor.clock
192
193
194    def testRequestTimingOutEventDispatcher(self):
195        """
196        Test that an iq request with a defined timeout times out.
197        """
198        from twisted.words.xish import utility
199        output = []
200        xs = utility.EventDispatcher()
201        xs.send = output.append
202
203        self.iq = IQ(xs, reactor=self.reactor)
204        self.iq.timeout = 60
205        d = self.iq.send()
206        self.assertFailure(d, xmlstream.TimeoutError)
207
208        self.clock.pump([1, 60])
209        self.assertFalse(self.reactor.getDelayedCalls())
210        self.assertFalse(xs.iqDeferreds)
211        return d
212
213
214
215class NamedConstantTests(unittest.TestCase):
216    """
217    Tests for the L{twisted.python.constants.NamedConstant} class which is used
218    to represent individual values.
219    """
220    def setUp(self):
221        """
222        Create a dummy container into which constants can be placed.
223        """
224        class foo(Names):
225            pass
226        self.container = foo
227
228
229    def test_name(self):
230        """
231        The C{name} attribute of a L{NamedConstant} refers to the value passed
232        for the C{name} parameter to C{_realize}.
233        """
234        name = NamedConstant()
235        name._realize(self.container, "bar", None)
236        self.assertEqual("bar", name.name)
237
238
239    def test_representation(self):
240        """
241        The string representation of an instance of L{NamedConstant} includes
242        the container the instances belongs to as well as the instance's name.
243        """
244        name = NamedConstant()
245        name._realize(self.container, "bar", None)
246        self.assertEqual("<foo=bar>", repr(name))
247
248
249    def test_equality(self):
250        """
251        A L{NamedConstant} instance compares equal to itself.
252        """
253        name = NamedConstant()
254        name._realize(self.container, "bar", None)
255        self.assertTrue(name == name)
256        self.assertFalse(name != name)
257
258
259    def test_nonequality(self):
260        """
261        Two different L{NamedConstant} instances do not compare equal to each
262        other.
263        """
264        first = NamedConstant()
265        first._realize(self.container, "bar", None)
266        second = NamedConstant()
267        second._realize(self.container, "bar", None)
268        self.assertFalse(first == second)
269        self.assertTrue(first != second)
270
271
272    def test_hash(self):
273        """
274        Because two different L{NamedConstant} instances do not compare as equal
275        to each other, they also have different hashes to avoid collisions when
276        added to a C{dict} or C{set}.
277        """
278        first = NamedConstant()
279        first._realize(self.container, "bar", None)
280        second = NamedConstant()
281        second._realize(self.container, "bar", None)
282        self.assertNotEqual(hash(first), hash(second))
283
284
285
286class _ConstantsTestsMixin(object):
287    """
288    Mixin defining test helpers common to multiple types of constants
289    collections.
290    """
291    def _notInstantiableTest(self, name, cls):
292        """
293        Assert that an attempt to instantiate the constants class raises
294        C{TypeError}.
295
296        @param name: A C{str} giving the name of the constants collection.
297        @param cls: The constants class to test.
298        """
299        exc = self.assertRaises(TypeError, cls)
300        self.assertEqual(name + " may not be instantiated.", str(exc))
301
302
303
304class NamesTests(unittest.TestCase, _ConstantsTestsMixin):
305    """
306    Tests for L{twisted.python.constants.Names}, a base class for containers of
307    related constaints.
308    """
309    def setUp(self):
310        """
311        Create a fresh new L{Names} subclass for each unit test to use.  Since
312        L{Names} is stateful, re-using the same subclass across test methods
313        makes exercising all of the implementation code paths difficult.
314        """
315        class METHOD(Names):
316            """
317            A container for some named constants to use in unit tests for
318            L{Names}.
319            """
320            GET = NamedConstant()
321            PUT = NamedConstant()
322            POST = NamedConstant()
323            DELETE = NamedConstant()
324
325        self.METHOD = METHOD
326
327
328    def test_notInstantiable(self):
329        """
330        A subclass of L{Names} raises C{TypeError} if an attempt is made to
331        instantiate it.
332        """
333        self._notInstantiableTest("METHOD", self.METHOD)
334
335
336    def test_symbolicAttributes(self):
337        """
338        Each name associated with a L{NamedConstant} instance in the definition
339        of a L{Names} subclass is available as an attribute on the resulting
340        class.
341        """
342        self.assertTrue(hasattr(self.METHOD, "GET"))
343        self.assertTrue(hasattr(self.METHOD, "PUT"))
344        self.assertTrue(hasattr(self.METHOD, "POST"))
345        self.assertTrue(hasattr(self.METHOD, "DELETE"))
346
347
348    def test_withoutOtherAttributes(self):
349        """
350        As usual, names not defined in the class scope of a L{Names}
351        subclass are not available as attributes on the resulting class.
352        """
353        self.assertFalse(hasattr(self.METHOD, "foo"))
354
355
356    def test_representation(self):
357        """
358        The string representation of a constant on a L{Names} subclass includes
359        the name of the L{Names} subclass and the name of the constant itself.
360        """
361        self.assertEqual("<METHOD=GET>", repr(self.METHOD.GET))
362
363
364    def test_lookupByName(self):
365        """
366        Constants can be looked up by name using L{Names.lookupByName}.
367        """
368        method = self.METHOD.lookupByName("GET")
369        self.assertIdentical(self.METHOD.GET, method)
370
371
372    def test_notLookupMissingByName(self):
373        """
374        Names not defined with a L{NamedConstant} instance cannot be looked up
375        using L{Names.lookupByName}.
376        """
377        self.assertRaises(ValueError, self.METHOD.lookupByName, "lookupByName")
378        self.assertRaises(ValueError, self.METHOD.lookupByName, "__init__")
379        self.assertRaises(ValueError, self.METHOD.lookupByName, "foo")
380
381
382    def test_name(self):
383        """
384        The C{name} attribute of one of the named constants gives that
385        constant's name.
386        """
387        self.assertEqual("GET", self.METHOD.GET.name)
388
389
390    def test_attributeIdentity(self):
391        """
392        Repeated access of an attribute associated with a L{NamedConstant} value
393        in a L{Names} subclass results in the same object.
394        """
395        self.assertIdentical(self.METHOD.GET, self.METHOD.GET)
396
397
398    def test_iterconstants(self):
399        """
400        L{Names.iterconstants} returns an iterator over all of the constants
401        defined in the class, in the order they were defined.
402        """
403        constants = list(self.METHOD.iterconstants())
404        self.assertEqual(
405            [self.METHOD.GET, self.METHOD.PUT,
406             self.METHOD.POST, self.METHOD.DELETE],
407            constants)
408
409
410    def test_attributeIterconstantsIdentity(self):
411        """
412        The constants returned from L{Names.iterconstants} are identical to the
413        constants accessible using attributes.
414        """
415        constants = list(self.METHOD.iterconstants())
416        self.assertIdentical(self.METHOD.GET, constants[0])
417        self.assertIdentical(self.METHOD.PUT, constants[1])
418        self.assertIdentical(self.METHOD.POST, constants[2])
419        self.assertIdentical(self.METHOD.DELETE, constants[3])
420
421
422    def test_iterconstantsIdentity(self):
423        """
424        The constants returned from L{Names.iterconstants} are identical on each
425        call to that method.
426        """
427        constants = list(self.METHOD.iterconstants())
428        again = list(self.METHOD.iterconstants())
429        self.assertIdentical(again[0], constants[0])
430        self.assertIdentical(again[1], constants[1])
431        self.assertIdentical(again[2], constants[2])
432        self.assertIdentical(again[3], constants[3])
433
434
435    def test_initializedOnce(self):
436        """
437        L{Names._enumerants} is initialized once and its value re-used on
438        subsequent access.
439        """
440        first = self.METHOD._enumerants
441        self.METHOD.GET # Side-effects!
442        second = self.METHOD._enumerants
443        self.assertIdentical(first, second)
444
445
446
447class ValuesTests(unittest.TestCase, _ConstantsTestsMixin):
448    """
449    Tests for L{twisted.python.constants.Names}, a base class for containers of
450    related constaints with arbitrary values.
451    """
452    def setUp(self):
453        """
454        Create a fresh new L{Values} subclass for each unit test to use.  Since
455        L{Values} is stateful, re-using the same subclass across test methods
456        makes exercising all of the implementation code paths difficult.
457        """
458        class STATUS(Values):
459            OK = ValueConstant("200")
460            NOT_FOUND = ValueConstant("404")
461
462        self.STATUS = STATUS
463
464
465    def test_notInstantiable(self):
466        """
467        A subclass of L{Values} raises C{TypeError} if an attempt is made to
468        instantiate it.
469        """
470        self._notInstantiableTest("STATUS", self.STATUS)
471
472
473    def test_symbolicAttributes(self):
474        """
475        Each name associated with a L{ValueConstant} instance in the definition
476        of a L{Values} subclass is available as an attribute on the resulting
477        class.
478        """
479        self.assertTrue(hasattr(self.STATUS, "OK"))
480        self.assertTrue(hasattr(self.STATUS, "NOT_FOUND"))
481
482
483    def test_withoutOtherAttributes(self):
484        """
485        As usual, names not defined in the class scope of a L{Values}
486        subclass are not available as attributes on the resulting class.
487        """
488        self.assertFalse(hasattr(self.STATUS, "foo"))
489
490
491    def test_representation(self):
492        """
493        The string representation of a constant on a L{Values} subclass includes
494        the name of the L{Values} subclass and the name of the constant itself.
495        """
496        self.assertEqual("<STATUS=OK>", repr(self.STATUS.OK))
497
498
499    def test_lookupByName(self):
500        """
501        Constants can be looked up by name using L{Values.lookupByName}.
502        """
503        method = self.STATUS.lookupByName("OK")
504        self.assertIdentical(self.STATUS.OK, method)
505
506
507    def test_notLookupMissingByName(self):
508        """
509        Names not defined with a L{ValueConstant} instance cannot be looked up
510        using L{Values.lookupByName}.
511        """
512        self.assertRaises(ValueError, self.STATUS.lookupByName, "lookupByName")
513        self.assertRaises(ValueError, self.STATUS.lookupByName, "__init__")
514        self.assertRaises(ValueError, self.STATUS.lookupByName, "foo")
515
516
517    def test_lookupByValue(self):
518        """
519        Constants can be looked up by their associated value, defined by the
520        argument passed to L{ValueConstant}, using L{Values.lookupByValue}.
521        """
522        status = self.STATUS.lookupByValue("200")
523        self.assertIdentical(self.STATUS.OK, status)
524
525
526    def test_lookupDuplicateByValue(self):
527        """
528        If more than one constant is associated with a particular value,
529        L{Values.lookupByValue} returns whichever of them is defined first.
530        """
531        class TRANSPORT_MESSAGE(Values):
532            """
533            Message types supported by an SSH transport.
534            """
535            KEX_DH_GEX_REQUEST_OLD = ValueConstant(30)
536            KEXDH_INIT = ValueConstant(30)
537
538        self.assertIdentical(
539            TRANSPORT_MESSAGE.lookupByValue(30),
540            TRANSPORT_MESSAGE.KEX_DH_GEX_REQUEST_OLD)
541
542
543    def test_notLookupMissingByValue(self):
544        """
545        L{Values.lookupByValue} raises L{ValueError} when called with a value
546        with which no constant is associated.
547        """
548        self.assertRaises(ValueError, self.STATUS.lookupByValue, "OK")
549        self.assertRaises(ValueError, self.STATUS.lookupByValue, 200)
550        self.assertRaises(ValueError, self.STATUS.lookupByValue, "200.1")
551
552
553    def test_name(self):
554        """
555        The C{name} attribute of one of the constants gives that constant's
556        name.
557        """
558        self.assertEqual("OK", self.STATUS.OK.name)
559
560
561    def test_attributeIdentity(self):
562        """
563        Repeated access of an attribute associated with a L{ValueConstant} value
564        in a L{Values} subclass results in the same object.
565        """
566        self.assertIdentical(self.STATUS.OK, self.STATUS.OK)
567
568
569    def test_iterconstants(self):
570        """
571        L{Values.iterconstants} returns an iterator over all of the constants
572        defined in the class, in the order they were defined.
573        """
574        constants = list(self.STATUS.iterconstants())
575        self.assertEqual(
576            [self.STATUS.OK, self.STATUS.NOT_FOUND],
577            constants)
578
579
580    def test_attributeIterconstantsIdentity(self):
581        """
582        The constants returned from L{Values.iterconstants} are identical to the
583        constants accessible using attributes.
584        """
585        constants = list(self.STATUS.iterconstants())
586        self.assertIdentical(self.STATUS.OK, constants[0])
587        self.assertIdentical(self.STATUS.NOT_FOUND, constants[1])
588
589
590    def test_iterconstantsIdentity(self):
591        """
592        The constants returned from L{Values.iterconstants} are identical on
593        each call to that method.
594        """
595        constants = list(self.STATUS.iterconstants())
596        again = list(self.STATUS.iterconstants())
597        self.assertIdentical(again[0], constants[0])
598        self.assertIdentical(again[1], constants[1])
599
600
601    def test_initializedOnce(self):
602        """
603        L{Values._enumerants} is initialized once and its value re-used on
604        subsequent access.
605        """
606        first = self.STATUS._enumerants
607        self.STATUS.OK # Side-effects!
608        second = self.STATUS._enumerants
609        self.assertIdentical(first, second)
Note: See TracBrowser for help on using the repository browser.