test_logcontext.py 6.9 KB

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