test_logcontext.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import twisted.python.failure
  2. from twisted.internet import defer, reactor
  3. from synapse.logging.context import (
  4. SENTINEL_CONTEXT,
  5. LoggingContext,
  6. PreserveLoggingContext,
  7. current_context,
  8. make_deferred_yieldable,
  9. nested_logging_context,
  10. run_in_background,
  11. )
  12. from synapse.util import Clock
  13. from .. import unittest
  14. class LoggingContextTestCase(unittest.TestCase):
  15. def _check_test_key(self, value):
  16. self.assertEquals(current_context().name, value)
  17. def test_with_context(self):
  18. with LoggingContext("test"):
  19. self._check_test_key("test")
  20. @defer.inlineCallbacks
  21. def test_sleep(self):
  22. clock = Clock(reactor)
  23. @defer.inlineCallbacks
  24. def competing_callback():
  25. with LoggingContext("competing"):
  26. yield clock.sleep(0)
  27. self._check_test_key("competing")
  28. reactor.callLater(0, competing_callback)
  29. with LoggingContext("one"):
  30. yield clock.sleep(0)
  31. self._check_test_key("one")
  32. def _test_run_in_background(self, function):
  33. sentinel_context = current_context()
  34. callback_completed = [False]
  35. with LoggingContext("one"):
  36. # fire off function, but don't wait on it.
  37. d2 = run_in_background(function)
  38. def cb(res):
  39. callback_completed[0] = True
  40. return res
  41. d2.addCallback(cb)
  42. self._check_test_key("one")
  43. # now wait for the function under test to have run, and check that
  44. # the logcontext is left in a sane state.
  45. d2 = defer.Deferred()
  46. def check_logcontext():
  47. if not callback_completed[0]:
  48. reactor.callLater(0.01, check_logcontext)
  49. return
  50. # make sure that the context was reset before it got thrown back
  51. # into the reactor
  52. try:
  53. self.assertIs(current_context(), sentinel_context)
  54. d2.callback(None)
  55. except BaseException:
  56. d2.errback(twisted.python.failure.Failure())
  57. reactor.callLater(0.01, check_logcontext)
  58. # test is done once d2 finishes
  59. return d2
  60. def test_run_in_background_with_blocking_fn(self):
  61. @defer.inlineCallbacks
  62. def blocking_function():
  63. yield Clock(reactor).sleep(0)
  64. return self._test_run_in_background(blocking_function)
  65. def test_run_in_background_with_non_blocking_fn(self):
  66. @defer.inlineCallbacks
  67. def nonblocking_function():
  68. with PreserveLoggingContext():
  69. yield defer.succeed(None)
  70. return self._test_run_in_background(nonblocking_function)
  71. def test_run_in_background_with_chained_deferred(self):
  72. # a function which returns a deferred which looks like it has been
  73. # called, but is actually paused
  74. def testfunc():
  75. return make_deferred_yieldable(_chained_deferred_function())
  76. return self._test_run_in_background(testfunc)
  77. def test_run_in_background_with_coroutine(self):
  78. async def testfunc():
  79. self._check_test_key("one")
  80. d = Clock(reactor).sleep(0)
  81. self.assertIs(current_context(), SENTINEL_CONTEXT)
  82. await d
  83. self._check_test_key("one")
  84. return self._test_run_in_background(testfunc)
  85. def test_run_in_background_with_nonblocking_coroutine(self):
  86. async def testfunc():
  87. self._check_test_key("one")
  88. return self._test_run_in_background(testfunc)
  89. @defer.inlineCallbacks
  90. def test_make_deferred_yieldable(self):
  91. # a function which returns an incomplete deferred, but doesn't follow
  92. # the synapse rules.
  93. def blocking_function():
  94. d = defer.Deferred()
  95. reactor.callLater(0, d.callback, None)
  96. return d
  97. sentinel_context = current_context()
  98. with LoggingContext("one"):
  99. d1 = make_deferred_yieldable(blocking_function())
  100. # make sure that the context was reset by make_deferred_yieldable
  101. self.assertIs(current_context(), sentinel_context)
  102. yield d1
  103. # now it should be restored
  104. self._check_test_key("one")
  105. @defer.inlineCallbacks
  106. def test_make_deferred_yieldable_with_chained_deferreds(self):
  107. sentinel_context = current_context()
  108. with LoggingContext("one"):
  109. d1 = make_deferred_yieldable(_chained_deferred_function())
  110. # make sure that the context was reset by make_deferred_yieldable
  111. self.assertIs(current_context(), sentinel_context)
  112. yield d1
  113. # now it should be restored
  114. self._check_test_key("one")
  115. @defer.inlineCallbacks
  116. def test_make_deferred_yieldable_on_non_deferred(self):
  117. """Check that make_deferred_yieldable does the right thing when its
  118. argument isn't actually a deferred"""
  119. with LoggingContext("one"):
  120. d1 = make_deferred_yieldable("bum")
  121. self._check_test_key("one")
  122. r = yield d1
  123. self.assertEqual(r, "bum")
  124. self._check_test_key("one")
  125. def test_nested_logging_context(self):
  126. with LoggingContext("foo"):
  127. nested_context = nested_logging_context(suffix="bar")
  128. self.assertEqual(nested_context.name, "foo-bar")
  129. @defer.inlineCallbacks
  130. def test_make_deferred_yieldable_with_await(self):
  131. # an async function which returns an incomplete coroutine, but doesn't
  132. # follow the synapse rules.
  133. async def blocking_function():
  134. d = defer.Deferred()
  135. reactor.callLater(0, d.callback, None)
  136. await d
  137. sentinel_context = current_context()
  138. with LoggingContext("one"):
  139. d1 = make_deferred_yieldable(blocking_function())
  140. # make sure that the context was reset by make_deferred_yieldable
  141. self.assertIs(current_context(), sentinel_context)
  142. yield d1
  143. # now it should be restored
  144. self._check_test_key("one")
  145. # a function which returns a deferred which has been "called", but
  146. # which had a function which returned another incomplete deferred on
  147. # its callback list, so won't yet call any other new callbacks.
  148. def _chained_deferred_function():
  149. d = defer.succeed(None)
  150. def cb(res):
  151. d2 = defer.Deferred()
  152. reactor.callLater(0, d2.callback, res)
  153. return d2
  154. d.addCallback(cb)
  155. return d