图片解析应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
5.3 KiB

  1. # Protocol Buffers - Google's data interchange format
  2. # Copyright 2008 Google Inc. All rights reserved.
  3. # https://developers.google.com/protocol-buffers/
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions are
  7. # met:
  8. #
  9. # * Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # * Redistributions in binary form must reproduce the above
  12. # copyright notice, this list of conditions and the following disclaimer
  13. # in the documentation and/or other materials provided with the
  14. # distribution.
  15. # * Neither the name of Google Inc. nor the names of its
  16. # contributors may be used to endorse or promote products derived from
  17. # this software without specific prior written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. """A subclass of unittest.TestCase which checks for reference leaks.
  31. To use:
  32. - Use testing_refleak.BaseTestCase instead of unittest.TestCase
  33. - Configure and compile Python with --with-pydebug
  34. If sys.gettotalrefcount() is not available (because Python was built without
  35. the Py_DEBUG option), then this module is a no-op and tests will run normally.
  36. """
  37. import copyreg
  38. import gc
  39. import sys
  40. import unittest
  41. class LocalTestResult(unittest.TestResult):
  42. """A TestResult which forwards events to a parent object, except for Skips."""
  43. def __init__(self, parent_result):
  44. unittest.TestResult.__init__(self)
  45. self.parent_result = parent_result
  46. def addError(self, test, error):
  47. self.parent_result.addError(test, error)
  48. def addFailure(self, test, error):
  49. self.parent_result.addFailure(test, error)
  50. def addSkip(self, test, reason):
  51. pass
  52. class ReferenceLeakCheckerMixin(object):
  53. """A mixin class for TestCase, which checks reference counts."""
  54. NB_RUNS = 3
  55. def run(self, result=None):
  56. testMethod = getattr(self, self._testMethodName)
  57. expecting_failure_method = getattr(testMethod, "__unittest_expecting_failure__", False)
  58. expecting_failure_class = getattr(self, "__unittest_expecting_failure__", False)
  59. if expecting_failure_class or expecting_failure_method:
  60. return
  61. # python_message.py registers all Message classes to some pickle global
  62. # registry, which makes the classes immortal.
  63. # We save a copy of this registry, and reset it before we could references.
  64. self._saved_pickle_registry = copyreg.dispatch_table.copy()
  65. # Run the test twice, to warm up the instance attributes.
  66. super(ReferenceLeakCheckerMixin, self).run(result=result)
  67. super(ReferenceLeakCheckerMixin, self).run(result=result)
  68. oldrefcount = 0
  69. local_result = LocalTestResult(result)
  70. num_flakes = 0
  71. refcount_deltas = []
  72. while len(refcount_deltas) < self.NB_RUNS:
  73. oldrefcount = self._getRefcounts()
  74. super(ReferenceLeakCheckerMixin, self).run(result=local_result)
  75. newrefcount = self._getRefcounts()
  76. # If the GC was able to collect some objects after the call to run() that
  77. # it could not collect before the call, then the counts won't match.
  78. if newrefcount < oldrefcount and num_flakes < 2:
  79. # This result is (probably) a flake -- garbage collectors aren't very
  80. # predictable, but a lower ending refcount is the opposite of the
  81. # failure we are testing for. If the result is repeatable, then we will
  82. # eventually report it, but not after trying to eliminate it.
  83. num_flakes += 1
  84. continue
  85. num_flakes = 0
  86. refcount_deltas.append(newrefcount - oldrefcount)
  87. print(refcount_deltas, self)
  88. try:
  89. self.assertEqual(refcount_deltas, [0] * self.NB_RUNS)
  90. except Exception: # pylint: disable=broad-except
  91. result.addError(self, sys.exc_info())
  92. def _getRefcounts(self):
  93. copyreg.dispatch_table.clear()
  94. copyreg.dispatch_table.update(self._saved_pickle_registry)
  95. # It is sometimes necessary to gc.collect() multiple times, to ensure
  96. # that all objects can be collected.
  97. gc.collect()
  98. gc.collect()
  99. gc.collect()
  100. return sys.gettotalrefcount()
  101. if hasattr(sys, 'gettotalrefcount'):
  102. def TestCase(test_class):
  103. new_bases = (ReferenceLeakCheckerMixin,) + test_class.__bases__
  104. new_class = type(test_class)(
  105. test_class.__name__, new_bases, dict(test_class.__dict__))
  106. return new_class
  107. SkipReferenceLeakChecker = unittest.skip
  108. else:
  109. # When PyDEBUG is not enabled, run the tests normally.
  110. def TestCase(test_class):
  111. return test_class
  112. def SkipReferenceLeakChecker(reason):
  113. del reason # Don't skip, so don't need a reason.
  114. def Same(func):
  115. return func
  116. return Same