coverage.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. # coding=utf-8
  2. #
  3. # This file is part of Hypothesis, which may be found at
  4. # https://github.com/HypothesisWorks/hypothesis-python
  5. #
  6. # Most of this work is copyright (C) 2013-2018 David R. MacIver
  7. # (david@drmaciver.com), but it contains contributions by others. See
  8. # CONTRIBUTING.rst for a full list of people who may hold copyright, and
  9. # consult the git log if you need to determine who owns an individual
  10. # contribution.
  11. #
  12. # This Source Code Form is subject to the terms of the Mozilla Public License,
  13. # v. 2.0. If a copy of the MPL was not distributed with this file, You can
  14. # obtain one at http://mozilla.org/MPL/2.0/.
  15. #
  16. # END HEADER
  17. from __future__ import division, print_function, absolute_import
  18. import os
  19. import sys
  20. import json
  21. from contextlib import contextmanager
  22. from hypothesis.internal.reflection import proxies
  23. """
  24. This module implements a custom coverage system that records conditions and
  25. then validates that every condition has been seen to be both True and False
  26. during the execution of our tests.
  27. The only thing we use it for at present is our argument validation functions,
  28. where we assert that every validation function has been seen to both pass and
  29. fail in the course of testing.
  30. When not running with a magic environment variable set, this module disables
  31. itself and has essentially no overhead.
  32. """
  33. pretty_file_name_cache = {}
  34. def pretty_file_name(f):
  35. try:
  36. return pretty_file_name_cache[f]
  37. except KeyError:
  38. pass
  39. parts = f.split(os.path.sep)
  40. parts = parts[parts.index('hypothesis'):]
  41. result = os.path.sep.join(parts)
  42. pretty_file_name_cache[f] = result
  43. return result
  44. IN_COVERAGE_TESTS = os.getenv('HYPOTHESIS_INTERNAL_COVERAGE') == 'true'
  45. if IN_COVERAGE_TESTS:
  46. log = open('branch-check', 'w')
  47. written = set()
  48. def record_branch(name, value):
  49. key = (name, value)
  50. if key in written:
  51. return
  52. written.add(key)
  53. log.write(
  54. json.dumps({'name': name, 'value': value})
  55. )
  56. log.write('\n')
  57. log.flush()
  58. description_stack = []
  59. @contextmanager
  60. def check_block(name, depth):
  61. # We add an extra two callers to the stack: One for the contextmanager
  62. # function, one for our actual caller, so we want to go two extra
  63. # stack frames up.
  64. caller = sys._getframe(depth + 2)
  65. local_description = '%s at %s:%d' % (
  66. name,
  67. pretty_file_name(caller.f_code.co_filename),
  68. caller.f_lineno,
  69. )
  70. try:
  71. description_stack.append(local_description)
  72. description = ' in '.join(reversed(description_stack)) + ' passed'
  73. yield
  74. record_branch(description, True)
  75. except BaseException:
  76. record_branch(description, False)
  77. raise
  78. finally:
  79. description_stack.pop()
  80. @contextmanager
  81. def check(name):
  82. with check_block(name, 2):
  83. yield
  84. def check_function(f):
  85. @proxies(f)
  86. def accept(*args, **kwargs):
  87. # depth of 2 because of the proxy function calling us.
  88. with check_block(f.__name__, 2):
  89. return f(*args, **kwargs)
  90. return accept
  91. else:
  92. def check_function(f):
  93. return f
  94. @contextmanager
  95. def check(name):
  96. yield
  97. class suppress_tracing(object):
  98. def __enter__(self):
  99. self.__original_trace = sys.gettrace()
  100. sys.settrace(None)
  101. def __exit__(self, exc_type, exc_value, traceback):
  102. sys.settrace(self.__original_trace)