test_observer.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test cases for L{twisted.logger._observer}.
  5. """
  6. from zope.interface.verify import verifyObject, BrokenMethodImplementation
  7. from twisted.trial import unittest
  8. from .._logger import Logger
  9. from .._observer import ILogObserver
  10. from .._observer import LogPublisher
  11. class LogPublisherTests(unittest.TestCase):
  12. """
  13. Tests for L{LogPublisher}.
  14. """
  15. def test_interface(self):
  16. """
  17. L{LogPublisher} is an L{ILogObserver}.
  18. """
  19. publisher = LogPublisher()
  20. try:
  21. verifyObject(ILogObserver, publisher)
  22. except BrokenMethodImplementation as e:
  23. self.fail(e)
  24. def test_observers(self):
  25. """
  26. L{LogPublisher.observers} returns the observers.
  27. """
  28. o1 = lambda e: None
  29. o2 = lambda e: None
  30. publisher = LogPublisher(o1, o2)
  31. self.assertEqual(set((o1, o2)), set(publisher._observers))
  32. def test_addObserver(self):
  33. """
  34. L{LogPublisher.addObserver} adds an observer.
  35. """
  36. o1 = lambda e: None
  37. o2 = lambda e: None
  38. o3 = lambda e: None
  39. publisher = LogPublisher(o1, o2)
  40. publisher.addObserver(o3)
  41. self.assertEqual(set((o1, o2, o3)), set(publisher._observers))
  42. def test_addObserverNotCallable(self):
  43. """
  44. L{LogPublisher.addObserver} refuses to add an observer that's
  45. not callable.
  46. """
  47. publisher = LogPublisher()
  48. self.assertRaises(TypeError, publisher.addObserver, object())
  49. def test_removeObserver(self):
  50. """
  51. L{LogPublisher.removeObserver} removes an observer.
  52. """
  53. o1 = lambda e: None
  54. o2 = lambda e: None
  55. o3 = lambda e: None
  56. publisher = LogPublisher(o1, o2, o3)
  57. publisher.removeObserver(o2)
  58. self.assertEqual(set((o1, o3)), set(publisher._observers))
  59. def test_removeObserverNotRegistered(self):
  60. """
  61. L{LogPublisher.removeObserver} removes an observer that is not
  62. registered.
  63. """
  64. o1 = lambda e: None
  65. o2 = lambda e: None
  66. o3 = lambda e: None
  67. publisher = LogPublisher(o1, o2)
  68. publisher.removeObserver(o3)
  69. self.assertEqual(set((o1, o2)), set(publisher._observers))
  70. def test_fanOut(self):
  71. """
  72. L{LogPublisher} calls its observers.
  73. """
  74. event = dict(foo=1, bar=2)
  75. events1 = []
  76. events2 = []
  77. events3 = []
  78. o1 = lambda e: events1.append(e)
  79. o2 = lambda e: events2.append(e)
  80. o3 = lambda e: events3.append(e)
  81. publisher = LogPublisher(o1, o2, o3)
  82. publisher(event)
  83. self.assertIn(event, events1)
  84. self.assertIn(event, events2)
  85. self.assertIn(event, events3)
  86. def test_observerRaises(self):
  87. """
  88. Observer raises an exception during fan out: a failure is logged, but
  89. not re-raised. Life goes on.
  90. """
  91. event = dict(foo=1, bar=2)
  92. exception = RuntimeError("ARGH! EVIL DEATH!")
  93. events = []
  94. def observer(event):
  95. shouldRaise = not events
  96. events.append(event)
  97. if shouldRaise:
  98. raise exception
  99. collector = []
  100. publisher = LogPublisher(observer, collector.append)
  101. publisher(event)
  102. # Verify that the observer saw my event
  103. self.assertIn(event, events)
  104. # Verify that the observer raised my exception
  105. errors = [
  106. e["log_failure"] for e in collector
  107. if "log_failure" in e
  108. ]
  109. self.assertEqual(len(errors), 1)
  110. self.assertIs(errors[0].value, exception)
  111. # Make sure the exceptional observer does not receive its own error.
  112. self.assertEqual(len(events), 1)
  113. def test_observerRaisesAndLoggerHatesMe(self):
  114. """
  115. Observer raises an exception during fan out and the publisher's Logger
  116. pukes when the failure is reported. The exception does not propagate
  117. back to the caller.
  118. """
  119. event = dict(foo=1, bar=2)
  120. exception = RuntimeError("ARGH! EVIL DEATH!")
  121. def observer(event):
  122. raise RuntimeError("Sad panda")
  123. class GurkLogger(Logger):
  124. def failure(self, *args, **kwargs):
  125. raise exception
  126. publisher = LogPublisher(observer)
  127. publisher.log = GurkLogger()
  128. publisher(event)
  129. # Here, the lack of an exception thus far is a success, of sorts
  130. def test_trace(self):
  131. """
  132. Tracing keeps track of forwarding to observers.
  133. """
  134. event = dict(foo=1, bar=2, log_trace=[])
  135. traces = {}
  136. # Copy trace to a tuple; otherwise, both observers will store the same
  137. # mutable list, and we won't be able to see o1's view distinctly.
  138. o1 = lambda e: traces.setdefault(1, tuple(e["log_trace"]))
  139. o2 = lambda e: traces.setdefault(2, tuple(e["log_trace"]))
  140. publisher = LogPublisher(o1, o2)
  141. publisher(event)
  142. self.assertEqual(traces[1], ((publisher, o1),))
  143. self.assertEqual(traces[2], ((publisher, o1), (publisher, o2)))