test_logcontext.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  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.assertEqual(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. def test_nested_logging_context(self):
  116. with LoggingContext("foo"):
  117. nested_context = nested_logging_context(suffix="bar")
  118. self.assertEqual(nested_context.name, "foo-bar")
  119. # a function which returns a deferred which has been "called", but
  120. # which had a function which returned another incomplete deferred on
  121. # its callback list, so won't yet call any other new callbacks.
  122. def _chained_deferred_function():
  123. d = defer.succeed(None)
  124. def cb(res):
  125. d2 = defer.Deferred()
  126. reactor.callLater(0, d2.callback, res)
  127. return d2
  128. d.addCallback(cb)
  129. return d