test_logcontext.py 6.2 KB

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