PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/unit/test_singleton.py

http://ultra-finance.googlecode.com/
Python | 271 lines | 205 code | 16 blank | 50 comment | 1 complexity | a9ddc1fb239bf25bfc1c8639445b9e9b MD5 | raw file
  1. '''
  2. Created on May 6, 2011
  3. @author: ppa
  4. By Gary Robinson, grobinson@flyfi.com
  5. '''
  6. import unittest
  7. import time
  8. from ultrafinance.designPattern.singleton import *
  9. class testSingleton(unittest.TestCase):
  10. def testReturnsSameObject(self):
  11. """
  12. Demonstrates normal use -- just call getInstance and it returns a singleton instance
  13. """
  14. class A(Singleton):
  15. def __init__(self):
  16. super(A, self).__init__()
  17. a1 = A.getInstance()
  18. a2 = A.getInstance()
  19. self.assertEquals(id(a1), id(a2))
  20. def testInstantiateWithMultiArgConstructor(self):
  21. """
  22. If the singleton needs args to construct, include them in the first
  23. call to get instances.
  24. """
  25. class B(Singleton):
  26. def __init__(self, arg1, arg2):
  27. super(B, self).__init__()
  28. self.arg1 = arg1
  29. self.arg2 = arg2
  30. b1 = B.getInstance('arg1 value', 'arg2 value')
  31. b2 = B.getInstance()
  32. self.assertEquals(b1.arg1, 'arg1 value')
  33. self.assertEquals(b1.arg2, 'arg2 value')
  34. self.assertEquals(id(b1), id(b2))
  35. def testInstantiateWithKeywordArg(self):
  36. class B(Singleton):
  37. def __init__(self, arg1 = 5):
  38. super(B, self).__init__()
  39. self.arg1 = arg1
  40. b1 = B.getInstance('arg1 value')
  41. b2 = B.getInstance()
  42. self.assertEquals(b1.arg1, 'arg1 value')
  43. self.assertEquals(id(b1), id(b2))
  44. def testTryToInstantiateWithoutNeededArgs(self):
  45. class B(Singleton):
  46. def __init__(self, arg1, arg2):
  47. super(B, self).__init__()
  48. self.arg1 = arg1
  49. self.arg2 = arg2
  50. self.assertRaises(SingletonException, B.getInstance)
  51. def testPassTypeErrorIfAllArgsThere(self):
  52. """
  53. Make sure the test for capturing missing args doesn't interfere with a normal TypeError.
  54. """
  55. class B(Singleton):
  56. def __init__(self, arg1, arg2):
  57. super(B, self).__init__()
  58. self.arg1 = arg1
  59. self.arg2 = arg2
  60. raise TypeError, 'some type error'
  61. self.assertRaises(TypeError, B.getInstance, 1, 2)
  62. def testTryToInstantiateWithoutGetInstance(self):
  63. """
  64. Demonstrates that singletons can ONLY be instantiated through
  65. getInstance, as long as they call Singleton.__init__ during construction.
  66. If this check is not required, you don't need to call Singleton.__init__().
  67. """
  68. class A(Singleton):
  69. def __init__(self):
  70. super(A, self).__init__()
  71. self.assertRaises(SingletonException, A)
  72. def testDontAllowNew(self):
  73. def instantiatedAnIllegalClass():
  74. class A(Singleton):
  75. def __init__(self):
  76. super(A, self).__init__()
  77. def __new__(metaclass, strName, tupBases, dct):
  78. return super(MetaSingleton, metaclass).__new__(metaclass, strName, tupBases, dct)
  79. self.assertRaises(SingletonException, instantiatedAnIllegalClass)
  80. def testDontAllowArgsAfterConstruction(self):
  81. class B(Singleton):
  82. def __init__(self, arg1, arg2):
  83. super(B, self).__init__()
  84. self.arg1 = arg1
  85. self.arg2 = arg2
  86. B.getInstance('arg1 value', 'arg2 value')
  87. self.assertRaises(SingletonException, B, 'arg1 value', 'arg2 value')
  88. def test_forgetClassInstanceReferenceForTesting(self):
  89. class A(Singleton):
  90. def __init__(self):
  91. super(A, self).__init__()
  92. class B(A):
  93. def __init__(self):
  94. super(B, self).__init__()
  95. # check that changing the class after forgetting the instance produces
  96. # an instance of the new class
  97. a = A.getInstance()
  98. assert a.__class__.__name__ == 'A'
  99. A._forgetClassInstanceReferenceForTesting()
  100. b = B.getInstance()
  101. assert b.__class__.__name__ == 'B'
  102. # check that invoking the 'forget' on a subclass still deletes the instance
  103. B._forgetClassInstanceReferenceForTesting()
  104. a = A.getInstance()
  105. B._forgetClassInstanceReferenceForTesting()
  106. b = B.getInstance()
  107. assert b.__class__.__name__ == 'B'
  108. def test_forgetAllSingletons(self):
  109. # Should work if there are no singletons
  110. forgetAllSingletons()
  111. class A(Singleton):
  112. ciInitCount = 0
  113. def __init__(self):
  114. super(A, self).__init__()
  115. A.ciInitCount += 1
  116. A.getInstance()
  117. self.assertEqual(A.ciInitCount, 1)
  118. A.getInstance()
  119. self.assertEqual(A.ciInitCount, 1)
  120. forgetAllSingletons()
  121. A.getInstance()
  122. self.assertEqual(A.ciInitCount, 2)
  123. def test_threadedCreation(self):
  124. # Check that only one Singleton is created even if multiple
  125. # threads try at the same time. If fails, would see assert in _addSingleton
  126. class Test_Singleton(Singleton):
  127. def __init__(self):
  128. super(Test_Singleton, self).__init__()
  129. class Test_SingletonThread(threading.Thread):
  130. def __init__(self, fTargetTime):
  131. super(Test_SingletonThread, self).__init__()
  132. self._fTargetTime = fTargetTime
  133. self._eException = None
  134. def run(self):
  135. try:
  136. fSleepTime = self._fTargetTime - time.time()
  137. if fSleepTime > 0:
  138. time.sleep(fSleepTime)
  139. Test_Singleton.getInstance()
  140. except Exception, e:
  141. self._eException = e
  142. fTargetTime = time.time() + 0.1
  143. lstThreads = []
  144. for _ in xrange(100):
  145. t = Test_SingletonThread(fTargetTime)
  146. t.start()
  147. lstThreads.append(t)
  148. eException = None
  149. for t in lstThreads:
  150. t.join()
  151. if t._eException and not eException:
  152. eException = t._eException
  153. if eException:
  154. raise eException
  155. def testNoInit(self):
  156. """
  157. Demonstrates use with a class not defining __init__
  158. """
  159. class A(Singleton):
  160. pass
  161. #INTENTIONALLY UNDEFINED:
  162. #def __init__(self):
  163. # super(A, self).__init__()
  164. A.getInstance() #Make sure no exception is raised
  165. def testMultipleGetInstancesWithArgs(self):
  166. class A(Singleton):
  167. ignoreSubsequent = True
  168. def __init__(self, a, b = 1):
  169. pass
  170. a1 = A.getInstance(1)
  171. a2 = A.getInstance(2) # ignores the second call because of ignoreSubsequent
  172. class B(Singleton):
  173. def __init__(self, a, b = 1):
  174. pass
  175. b1 = B.getInstance(1)
  176. self.assertRaises(SingletonException, B.getInstance, 2) # No ignoreSubsequent included
  177. class C(Singleton):
  178. def __init__(self, a = 1):
  179. pass
  180. c1 = C.getInstance(a = 1)
  181. self.assertRaises(SingletonException, C.getInstance, a = 2) # No ignoreSubsequent included
  182. def testInheritance(self):
  183. """
  184. It's sometimes said that you can't subclass a singleton (see, for instance,
  185. http://steve.yegge.googlepages.com/singleton-considered-stupid point e). This
  186. test shows that at least rudimentary subclassing works fine for us.
  187. """
  188. class A(Singleton):
  189. def setX(self, x):
  190. self.x = x
  191. def setZ(self, z):
  192. raise NotImplementedError
  193. class B(A):
  194. def setX(self, x):
  195. self.x = -x
  196. def setY(self, y):
  197. self.y = y
  198. a = A.getInstance()
  199. a.setX(5)
  200. b = B.getInstance()
  201. b.setX(5)
  202. b.setY(50)
  203. self.assertEqual((a.x, b.x, b.y), (5, -5, 50))
  204. self.assertRaises(AttributeError, eval, 'a.setY', {}, locals())
  205. self.assertRaises(NotImplementedError, b.setZ, 500)