Ignore:
Timestamp:
Jan 8, 2012, 9:26:02 AM (9 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Message:

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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wokkel/test/test_compat.py

    r96 r160  
    1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
     1# Copyright (c) Twisted Matrix Laboratories.
    22# Copyright (c) Ralph Meijer.
    33# See LICENSE for details.
     
    1414from twisted.words.xish import utility
    1515from twisted.words.protocols.jabber import xmlstream
     16
    1617from wokkel.compat import BootstrapMixin, IQ, XmlStreamServerFactory
     18from wokkel.compat import NamedConstant, Names, ValueConstant, Values
    1719
    1820class DummyProtocol(protocol.Protocol, utility.EventDispatcher):
     
    208210        self.assertFalse(xs.iqDeferreds)
    209211        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 TracChangeset for help on using the changeset viewer.