Changeset 160:33ef849a77d8
- Timestamp:
- Jan 8, 2012, 9:26:02 AM (11 years ago)
- Branch:
- default
- Location:
- wokkel
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
wokkel/compat.py
r96 r160 3 3 # Copyright (c) Twisted Matrix Laboratories. 4 4 # See LICENSE for details. 5 6 """ 7 Compatibility module to provide backwards compatibility with Twisted features. 8 """ 9 10 __all__ = ['BootstrapMixin', 'XmlStreamServerFactory', 'IQ', 11 'NamedConstant', 'ValueConstant', 'Names', 'Values'] 12 13 from itertools import count 5 14 6 15 from twisted.internet import protocol … … 116 125 # Patch the XmlStream instance so that it has a _callLater 117 126 self._xmlstream._callLater = reactor.callLater 127 128 129 130 _unspecified = object() 131 _constantOrder = count().next 132 133 134 class _Constant(object): 135 """ 136 @ivar _index: A C{int} allocated from a shared counter in order to keep 137 track of the order in which L{_Constant}s are instantiated. 138 139 @ivar name: A C{str} giving the name of this constant; only set once the 140 constant is initialized by L{_ConstantsContainer}. 141 142 @ivar _container: The L{_ConstantsContainer} subclass this constant belongs 143 to; only set once the constant is initialized by that subclass. 144 """ 145 def __init__(self): 146 self._index = _constantOrder() 147 148 149 def __get__(self, oself, cls): 150 """ 151 Ensure this constant has been initialized before returning it. 152 """ 153 cls._initializeEnumerants() 154 return self 155 156 157 def __repr__(self): 158 """ 159 Return text identifying both which constant this is and which collection 160 it belongs to. 161 """ 162 return "<%s=%s>" % (self._container.__name__, self.name) 163 164 165 def _realize(self, container, name, value): 166 """ 167 Complete the initialization of this L{_Constant}. 168 169 @param container: The L{_ConstantsContainer} subclass this constant is 170 part of. 171 172 @param name: The name of this constant in its container. 173 174 @param value: The value of this constant; not used, as named constants 175 have no value apart from their identity. 176 """ 177 self._container = container 178 self.name = name 179 180 181 182 class _EnumerantsInitializer(object): 183 """ 184 L{_EnumerantsInitializer} is a descriptor used to initialize a cache of 185 objects representing named constants for a particular L{_ConstantsContainer} 186 subclass. 187 """ 188 def __get__(self, oself, cls): 189 """ 190 Trigger the initialization of the enumerants cache on C{cls} and then 191 return it. 192 """ 193 cls._initializeEnumerants() 194 return cls._enumerants 195 196 197 198 class _ConstantsContainer(object): 199 """ 200 L{_ConstantsContainer} is a class with attributes used as symbolic 201 constants. It is up to subclasses to specify what kind of constants are 202 allowed. 203 204 @cvar _constantType: Specified by a L{_ConstantsContainer} subclass to 205 specify the type of constants allowed by that subclass. 206 207 @cvar _enumerantsInitialized: A C{bool} tracking whether C{_enumerants} has 208 been initialized yet or not. 209 210 @cvar _enumerants: A C{dict} mapping the names of constants (eg 211 L{NamedConstant} instances) found in the class definition to those 212 instances. This is initialized via the L{_EnumerantsInitializer} 213 descriptor the first time it is accessed. 214 """ 215 _constantType = None 216 217 _enumerantsInitialized = False 218 _enumerants = _EnumerantsInitializer() 219 220 def __new__(cls): 221 """ 222 Classes representing constants containers are not intended to be 223 instantiated. 224 225 The class object itself is used directly. 226 """ 227 raise TypeError("%s may not be instantiated." % (cls.__name__,)) 228 229 230 def _initializeEnumerants(cls): 231 """ 232 Find all of the L{NamedConstant} instances in the definition of C{cls}, 233 initialize them with constant values, and build a mapping from their 234 names to them to attach to C{cls}. 235 """ 236 if not cls._enumerantsInitialized: 237 constants = [] 238 for (name, descriptor) in cls.__dict__.iteritems(): 239 if isinstance(descriptor, cls._constantType): 240 constants.append((descriptor._index, name, descriptor)) 241 enumerants = {} 242 for (index, enumerant, descriptor) in constants: 243 value = cls._constantFactory(enumerant) 244 descriptor._realize(cls, enumerant, value) 245 enumerants[enumerant] = descriptor 246 # Replace the _enumerants descriptor with the result so future 247 # access will go directly to the values. The _enumerantsInitialized 248 # flag is still necessary because NamedConstant.__get__ may also 249 # call this method. 250 cls._enumerants = enumerants 251 cls._enumerantsInitialized = True 252 _initializeEnumerants = classmethod(_initializeEnumerants) 253 254 255 def _constantFactory(cls, name): 256 """ 257 Construct the value for a new constant to add to this container. 258 259 @param name: The name of the constant to create. 260 261 @return: L{NamedConstant} instances have no value apart from identity, 262 so return a meaningless dummy value. 263 """ 264 return _unspecified 265 _constantFactory = classmethod(_constantFactory) 266 267 268 def lookupByName(cls, name): 269 """ 270 Retrieve a constant by its name or raise a C{ValueError} if there is no 271 constant associated with that name. 272 273 @param name: A C{str} giving the name of one of the constants defined by 274 C{cls}. 275 276 @raise ValueError: If C{name} is not the name of one of the constants 277 defined by C{cls}. 278 279 @return: The L{NamedConstant} associated with C{name}. 280 """ 281 if name in cls._enumerants: 282 return getattr(cls, name) 283 raise ValueError(name) 284 lookupByName = classmethod(lookupByName) 285 286 287 def iterconstants(cls): 288 """ 289 Iteration over a L{Names} subclass results in all of the constants it 290 contains. 291 292 @return: an iterator the elements of which are the L{NamedConstant} 293 instances defined in the body of this L{Names} subclass. 294 """ 295 constants = cls._enumerants.values() 296 constants.sort(key=lambda descriptor: descriptor._index) 297 return iter(constants) 298 iterconstants = classmethod(iterconstants) 299 300 301 302 class NamedConstant(_Constant): 303 """ 304 L{NamedConstant} defines an attribute to be a named constant within a 305 collection defined by a L{Names} subclass. 306 307 L{NamedConstant} is only for use in the definition of L{Names} 308 subclasses. Do not instantiate L{NamedConstant} elsewhere and do not 309 subclass it. 310 """ 311 312 313 314 class Names(_ConstantsContainer): 315 """ 316 A L{Names} subclass contains constants which differ only in their names and 317 identities. 318 """ 319 _constantType = NamedConstant 320 321 322 323 class ValueConstant(_Constant): 324 """ 325 L{ValueConstant} defines an attribute to be a named constant within a 326 collection defined by a L{Values} subclass. 327 328 L{ValueConstant} is only for use in the definition of L{Values} subclasses. 329 Do not instantiate L{ValueConstant} elsewhere and do not subclass it. 330 """ 331 def __init__(self, value): 332 _Constant.__init__(self) 333 self.value = value 334 335 336 337 class Values(_ConstantsContainer): 338 """ 339 A L{Values} subclass contains constants which are associated with arbitrary 340 values. 341 """ 342 _constantType = ValueConstant 343 344 def lookupByValue(cls, value): 345 """ 346 Retrieve a constant by its value or raise a C{ValueError} if there is no 347 constant associated with that value. 348 349 @param value: The value of one of the constants defined by C{cls}. 350 351 @raise ValueError: If C{value} is not the value of one of the constants 352 defined by C{cls}. 353 354 @return: The L{ValueConstant} associated with C{value}. 355 """ 356 for constant in cls.iterconstants(): 357 if constant.value == value: 358 return constant 359 raise ValueError(value) 360 lookupByValue = classmethod(lookupByValue) -
wokkel/iwokkel.py
r158 r160 806 806 class IMUCClient(Interface): 807 807 """ 808 Multi-User Chat Client 808 Multi-User Chat Client. 809 809 810 810 A client interface to XEP-045 : http://xmpp.org/extensions/xep-0045.html 811 812 811 """ 813 812 … … 1028 1027 @type sender: L{jid.JID} 1029 1028 """ 1029 1030 1031 class IMUCStatuses(Interface): 1032 """ 1033 Interface for a container of Multi-User Chat status conditions. 1034 """ 1035 1036 def __contains__(key): 1037 """ 1038 Return if a status exists in the container. 1039 """ 1040 1041 1042 def __iter__(): 1043 """ 1044 Return an iterator over the status codes. 1045 """ 1046 1047 1048 def __len__(): 1049 """ 1050 Return the number of status conditions. 1051 """ -
wokkel/muc.py
r156 r160 8 8 9 9 This protocol is specified in 10 U{XEP-0045<http:// www.xmpp.org/extensions/xep-0045.html>}.10 U{XEP-0045<http://xmpp.org/extensions/xep-0045.html>}. 11 11 """ 12 12 from dateutil.tz import tzutc … … 18 18 from twisted.words.xish import domish 19 19 20 from wokkel import data_form, generic, xmppim 20 from wokkel import data_form, generic, iwokkel, xmppim 21 from wokkel.compat import Values, ValueConstant 21 22 from wokkel.delay import Delay, DelayMixin 22 23 from wokkel.subprotocols import XMPPHandler … … 41 42 42 43 DEFER_TIMEOUT = 30 # basic timeout is 30 seconds 44 45 class STATUS_CODE(Values): 46 REALJID_PUBLIC = ValueConstant(100) 47 AFFILIATION_CHANGED = ValueConstant(101) 48 UNAVAILABLE_SHOWN = ValueConstant(102) 49 UNAVAILABLE_NOT_SHOWN = ValueConstant(103) 50 CONFIGURATION_CHANGED = ValueConstant(104) 51 SELF_PRESENCE = ValueConstant(110) 52 LOGGING_ENABLED = ValueConstant(170) 53 LOGGING_DISABLED = ValueConstant(171) 54 NON_ANONYMOUS = ValueConstant(172) 55 SEMI_ANONYMOUS = ValueConstant(173) 56 FULLY_ANONYMOUS = ValueConstant(174) 57 ROOM_CREATED = ValueConstant(201) 58 NICK_ASSIGNED = ValueConstant(210) 59 BANNED = ValueConstant(301) 60 NEW_NICK = ValueConstant(303) 61 KICKED = ValueConstant(307) 62 REMOVED_AFFILIATION = ValueConstant(321) 63 REMOVED_MEMBERSHIP = ValueConstant(322) 64 REMOVED_SHUTDOWN = ValueConstant(332) 65 66 67 class Statuses(set): 68 """ 69 Container of MUC status conditions. 70 71 This is currently implemented as a set of constant values from 72 L{STATUS_CODE}. Instances of this class provide L{IMUCStatuses}, that 73 defines the supported operations. Even though this class currently derives 74 from C{set}, future versions might not. This provides an upgrade path to 75 cater for extensible status conditions, as defined in 76 U{XEP-0306<http://xmpp.org/extensions/xep-0306.html>}. 77 """ 78 implements(iwokkel.IMUCStatuses) 43 79 44 80 … … 354 390 """ 355 391 Availability presence sent from MUC service to client. 392 393 @ivar affiliation: Affiliation of the entity to the room. 394 @type affiliation: C{unicode} 395 396 @ivar role: Role of the entity in the room. 397 @type role: C{unicode} 398 399 @ivar entity: The real JID of the entity this presence is from. 400 @type entity: L{jid.JID} 401 402 @ivar mucStatuses: Set of one or more status codes from L{STATUS_CODE}. 403 See L{Statuses} for usage notes. 404 @type mucStatuses: L{Statuses} 405 406 @ivar nick: The nick name of the entity in the room. 407 @type nick: C{unicode} 356 408 """ 357 409 … … 361 413 nick = None 362 414 363 statusCodes = None415 mucStatuses = None 364 416 365 417 childParsers = {(NS_MUC_USER, 'x'): '_childParser_mucUser'} 366 418 419 def __init__(self, *args, **kwargs): 420 self.mucStatuses = Statuses() 421 xmppim.AvailabilityPresence.__init__(self, *args, **kwargs) 422 423 367 424 def _childParser_mucUser(self, element): 368 statusCodes = set() 369 425 """ 426 Parse the MUC user extension element. 427 """ 370 428 for child in element.elements(): 371 429 if child.uri != NS_MUC_USER: … … 374 432 elif child.name == 'status': 375 433 try: 376 statusCode = int(child.getAttribute('code')) 434 value = int(child.getAttribute('code')) 435 statusCode = STATUS_CODE.lookupByValue(value) 377 436 except (TypeError, ValueError): 378 437 continue 379 438 380 s tatusCodes.add(statusCode)439 self.mucStatuses.add(statusCode) 381 440 382 441 elif child.name == 'item': … … 393 452 # TODO: destroy 394 453 395 if statusCodes:396 self.statusCodes = statusCodes397 454 398 455 -
wokkel/test/test_compat.py
r96 r160 1 # Copyright (c) 2001-2008Twisted Matrix Laboratories.1 # Copyright (c) Twisted Matrix Laboratories. 2 2 # Copyright (c) Ralph Meijer. 3 3 # See LICENSE for details. … … 14 14 from twisted.words.xish import utility 15 15 from twisted.words.protocols.jabber import xmlstream 16 16 17 from wokkel.compat import BootstrapMixin, IQ, XmlStreamServerFactory 18 from wokkel.compat import NamedConstant, Names, ValueConstant, Values 17 19 18 20 class DummyProtocol(protocol.Protocol, utility.EventDispatcher): … … 208 210 self.assertFalse(xs.iqDeferreds) 209 211 return d 212 213 214 215 class 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 286 class _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 304 class 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 447 class 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) -
wokkel/test/test_muc.py
r157 r160 43 43 44 44 45 class StatusCodeTest(unittest.TestCase): 46 """ 47 Tests for L{muc.STATUS_CODE}. 48 """ 49 50 def test_lookupByValue(self): 51 """ 52 The registered MUC status codes map to STATUS_CODE value constants. 53 54 Note: the identifiers used in the dictionary of status codes are 55 borrowed from U{XEP-0306<http://xmpp.org/extensions/xep-0306.html>} 56 that defines Extensible Status Conditions for Multi-User Chat. If this 57 specification is implemented itself, the dictionary could move there. 58 """ 59 codes = { 60 100: 'realjid-public', 61 101: 'affiliation-changed', 62 102: 'unavailable-shown', 63 103: 'unavailable-not-shown', 64 104: 'configuration-changed', 65 110: 'self-presence', 66 170: 'logging-enabled', 67 171: 'logging-disabled', 68 172: 'non-anonymous', 69 173: 'semi-anonymous', 70 174: 'fully-anonymous', 71 201: 'room-created', 72 210: 'nick-assigned', 73 301: 'banned', 74 303: 'new-nick', 75 307: 'kicked', 76 321: 'removed-affiliation', 77 322: 'removed-membership', 78 332: 'removed-shutdown', 79 } 80 81 for code, condition in codes.iteritems(): 82 constantName = condition.replace('-', '_').upper() 83 self.assertEqual(getattr(muc.STATUS_CODE, constantName), 84 muc.STATUS_CODE.lookupByValue(code)) 85 86 87 88 class StatusesTest(unittest.TestCase): 89 """ 90 Tests for L{muc.Statuses}. 91 """ 92 93 def setUp(self): 94 self.mucStatuses = muc.Statuses() 95 self.mucStatuses.add(muc.STATUS_CODE.SELF_PRESENCE) 96 self.mucStatuses.add(muc.STATUS_CODE.ROOM_CREATED) 97 98 99 def test_interface(self): 100 """ 101 Instances of L{Statuses} provide L{iwokkel.IMUCStatuses}. 102 """ 103 verify.verifyObject(iwokkel.IMUCStatuses, self.mucStatuses) 104 105 106 def test_contains(self): 107 """ 108 The status contained are 'in' the container. 109 """ 110 self.assertIn(muc.STATUS_CODE.SELF_PRESENCE, self.mucStatuses) 111 self.assertIn(muc.STATUS_CODE.ROOM_CREATED, self.mucStatuses) 112 self.assertNotIn(muc.STATUS_CODE.NON_ANONYMOUS, self.mucStatuses) 113 114 115 def test_iter(self): 116 """ 117 All statuses can be iterated over. 118 """ 119 statuses = set() 120 for status in self.mucStatuses: 121 statuses.add(status) 122 123 self.assertEqual(set([muc.STATUS_CODE.SELF_PRESENCE, 124 muc.STATUS_CODE.ROOM_CREATED]), statuses) 125 126 127 def test_len(self): 128 """ 129 The number of items in this container is returned by C{__len__}. 130 """ 131 self.assertEqual(2, len(self.mucStatuses)) 132 133 134 45 135 class GroupChatTest(unittest.TestCase): 46 136 """ 47 Tests for {muc.GroupChat}.137 Tests for L{muc.GroupChat}. 48 138 """ 49 139 … … 121 211 122 212 213 123 214 class UserPresenceTest(unittest.TestCase): 124 215 """ … … 126 217 """ 127 218 128 129 def test_toElementUnknownChild(self): 219 def test_fromElementNoUserElement(self): 220 """ 221 Without user element, all associated attributes are None. 222 """ 223 xml = """ 224 <presence from='coven@chat.shakespeare.lit/thirdwitch' 225 id='026B3509-2CCE-4D69-96D6-25F41FFDC408' 226 to='hag66@shakespeare.lit/pda'> 227 </presence> 228 """ 229 230 element = parseXml(xml) 231 presence = muc.UserPresence.fromElement(element) 232 233 self.assertIdentical(None, presence.affiliation) 234 self.assertIdentical(None, presence.role) 235 self.assertIdentical(None, presence.entity) 236 self.assertIdentical(None, presence.nick) 237 self.assertEqual(0, len(presence.mucStatuses)) 238 239 240 def test_fromElementUnknownChild(self): 130 241 """ 131 242 Unknown child elements are ignored. … … 144 255 presence = muc.UserPresence.fromElement(element) 145 256 146 self.assert Identical(None, presence.statusCodes)147 148 149 def test_ toElementStatusOne(self):257 self.assertEqual(0, len(presence.mucStatuses)) 258 259 260 def test_fromElementStatusOne(self): 150 261 """ 151 262 Status codes are extracted. … … 165 276 presence = muc.UserPresence.fromElement(element) 166 277 167 self.assertIn( 110, presence.statusCodes)168 169 170 def test_ toElementStatusMultiple(self):278 self.assertIn(muc.STATUS_CODE.SELF_PRESENCE, presence.mucStatuses) 279 280 281 def test_fromElementStatusMultiple(self): 171 282 """ 172 283 Multiple status codes are all extracted. … … 187 298 presence = muc.UserPresence.fromElement(element) 188 299 189 self.assertIn( 110, presence.statusCodes)190 self.assertIn( 100, presence.statusCodes)191 192 193 def test_ toElementStatusEmpty(self):300 self.assertIn(muc.STATUS_CODE.SELF_PRESENCE, presence.mucStatuses) 301 self.assertIn(muc.STATUS_CODE.REALJID_PUBLIC, presence.mucStatuses) 302 303 304 def test_fromElementStatusEmpty(self): 194 305 """ 195 306 Empty status elements are ignored. … … 209 320 presence = muc.UserPresence.fromElement(element) 210 321 211 self.assert Identical(None, presence.statusCodes)212 213 214 def test_ toElementStatusBad(self):322 self.assertEqual(0, len(presence.mucStatuses)) 323 324 325 def test_fromElementStatusBad(self): 215 326 """ 216 327 Bad status codes are ignored. … … 230 341 presence = muc.UserPresence.fromElement(element) 231 342 232 self.assert Identical(None, presence.statusCodes)233 234 235 def test_ toElementStatusUnknown(self):236 """ 237 Unknown status codes are still recorded in C{statusCodes}.343 self.assertEqual(0, len(presence.mucStatuses)) 344 345 346 def test_fromElementStatusUnknown(self): 347 """ 348 Unknown status codes are not recorded in C{mucStatuses}. 238 349 """ 239 350 xml = """ … … 251 362 presence = muc.UserPresence.fromElement(element) 252 363 253 self.assert In(999, presence.statusCodes)254 255 256 def test_ toElementItem(self):364 self.assertEqual(0, len(presence.mucStatuses)) 365 366 367 def test_fromElementItem(self): 257 368 """ 258 369 Item attributes are parsed properly.
Note: See TracChangeset
for help on using the changeset viewer.