Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Most of this work is copyright (C) 2013-2020 David R. MacIver 

5# (david@drmaciver.com), but it contains contributions by others. See 

6# CONTRIBUTING.rst for a full list of people who may hold copyright, and 

7# consult the git log if you need to determine who owns an individual 

8# contribution. 

9# 

10# This Source Code Form is subject to the terms of the Mozilla Public License, 

11# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

12# obtain one at https://mozilla.org/MPL/2.0/. 

13# 

14# END HEADER 

15 

16from distutils.version import LooseVersion 

17 

18import pytest 

19 

20from hypothesis import Verbosity, core, settings 

21from hypothesis.errors import InvalidArgument 

22from hypothesis.internal.detection import is_hypothesis_test 

23from hypothesis.reporting import default as default_reporter, with_reporter 

24from hypothesis.statistics import collector 

25 

26LOAD_PROFILE_OPTION = "--hypothesis-profile" 

27VERBOSITY_OPTION = "--hypothesis-verbosity" 

28PRINT_STATISTICS_OPTION = "--hypothesis-show-statistics" 

29SEED_OPTION = "--hypothesis-seed" 

30 

31 

32class StoringReporter: 

33 def __init__(self, config): 

34 self.config = config 

35 self.results = [] 

36 

37 def __call__(self, msg): 

38 if self.config.getoption("capture", "fd") == "no": 

39 default_reporter(msg) 

40 if not isinstance(msg, str): 

41 msg = repr(msg) 

42 self.results.append(msg) 

43 

44 

45if LooseVersion(pytest.__version__) < "4.3": # pragma: no cover 

46 import warnings 

47 from hypothesis.errors import HypothesisWarning 

48 

49 PYTEST_TOO_OLD_MESSAGE = """ 

50 You are using Pytest version %s. Hypothesis tests work with any test 

51 runner, but our Pytest plugin requires Pytest 4.3 or newer. 

52 Note that the Pytest developers no longer support this version either! 

53 Disabling the Hypothesis pytest plugin... 

54 """ 

55 warnings.warn(PYTEST_TOO_OLD_MESSAGE % (pytest.__version__,), HypothesisWarning) 

56 

57else: 

58 

59 def pytest_addoption(parser): 

60 group = parser.getgroup("hypothesis", "Hypothesis") 

61 group.addoption( 

62 LOAD_PROFILE_OPTION, 

63 action="store", 

64 help="Load in a registered hypothesis.settings profile", 

65 ) 

66 group.addoption( 

67 VERBOSITY_OPTION, 

68 action="store", 

69 choices=[opt.name for opt in Verbosity], 

70 help="Override profile with verbosity setting specified", 

71 ) 

72 group.addoption( 

73 PRINT_STATISTICS_OPTION, 

74 action="store_true", 

75 help="Configure when statistics are printed", 

76 default=False, 

77 ) 

78 group.addoption( 

79 SEED_OPTION, 

80 action="store", 

81 help="Set a seed to use for all Hypothesis tests", 

82 ) 

83 

84 def pytest_report_header(config): 

85 profile = config.getoption(LOAD_PROFILE_OPTION) 

86 if not profile: 

87 profile = settings._current_profile 

88 settings_str = settings.get_profile(profile).show_changed() 

89 if settings_str != "": 

90 settings_str = " -> %s" % (settings_str) 

91 if ( 

92 config.option.verbose >= 1 

93 or settings.default.verbosity >= Verbosity.verbose 

94 ): 

95 return "hypothesis profile %r%s" % (profile, settings_str) 

96 

97 def pytest_configure(config): 

98 core.running_under_pytest = True 

99 profile = config.getoption(LOAD_PROFILE_OPTION) 

100 if profile: 

101 settings.load_profile(profile) 

102 verbosity_name = config.getoption(VERBOSITY_OPTION) 

103 if verbosity_name: 

104 verbosity_value = Verbosity[verbosity_name] 

105 profile_name = "%s-with-%s-verbosity" % ( 

106 settings._current_profile, 

107 verbosity_name, 

108 ) 

109 # register_profile creates a new profile, exactly like the current one, 

110 # with the extra values given (in this case 'verbosity') 

111 settings.register_profile(profile_name, verbosity=verbosity_value) 

112 settings.load_profile(profile_name) 

113 seed = config.getoption(SEED_OPTION) 

114 if seed is not None: 

115 try: 

116 seed = int(seed) 

117 except ValueError: 

118 pass 

119 core.global_force_seed = seed 

120 config.addinivalue_line("markers", "hypothesis: Tests which use hypothesis.") 

121 

122 @pytest.hookimpl(hookwrapper=True) 

123 def pytest_runtest_call(item): 

124 if not hasattr(item, "obj"): 

125 yield 

126 elif not is_hypothesis_test(item.obj): 

127 # If @given was not applied, check whether other hypothesis 

128 # decorators were applied, and raise an error if they were. 

129 if getattr(item.obj, "is_hypothesis_strategy_function", False): 

130 raise InvalidArgument( 

131 "%s is a function that returns a Hypothesis strategy, but pytest " 

132 "has collected it as a test function. This is useless as the " 

133 "function body will never be executed. To define a test " 

134 "function, use @given instead of @composite." % (item.nodeid,) 

135 ) 

136 message = "Using `@%s` on a test without `@given` is completely pointless." 

137 for name, attribute in [ 

138 ("example", "hypothesis_explicit_examples"), 

139 ("seed", "_hypothesis_internal_use_seed"), 

140 ("settings", "_hypothesis_internal_settings_applied"), 

141 ("reproduce_example", "_hypothesis_internal_use_reproduce_failure"), 

142 ]: 

143 if hasattr(item.obj, attribute): 

144 raise InvalidArgument(message % (name,)) 

145 yield 

146 else: 

147 if item.get_closest_marker("parametrize") is not None: 

148 # Give every parametrized test invocation a unique database key 

149 key = item.nodeid.encode("utf-8") 

150 item.obj.hypothesis.inner_test._hypothesis_internal_add_digest = key 

151 

152 store = StoringReporter(item.config) 

153 

154 def note_statistics(stats): 

155 lines = [item.nodeid + ":", ""] + stats.get_description() + [""] 

156 item.hypothesis_statistics = lines 

157 

158 with collector.with_value(note_statistics): 

159 with with_reporter(store): 

160 yield 

161 if store.results: 

162 item.hypothesis_report_information = list(store.results) 

163 

164 @pytest.hookimpl(hookwrapper=True) 

165 def pytest_runtest_makereport(item, call): 

166 report = (yield).get_result() 

167 if hasattr(item, "hypothesis_report_information"): 

168 report.sections.append( 

169 ("Hypothesis", "\n".join(item.hypothesis_report_information)) 

170 ) 

171 if hasattr(item, "hypothesis_statistics") and report.when == "teardown": 

172 val = ("hypothesis-stats", item.hypothesis_statistics) 

173 report.user_properties.append(val) 

174 

175 def pytest_terminal_summary(terminalreporter): 

176 if not terminalreporter.config.getoption(PRINT_STATISTICS_OPTION): 

177 return 

178 terminalreporter.section("Hypothesis Statistics") 

179 # terminalreporter.stats is a dict, where the empty string appears to 

180 # always be the key for a list of _pytest.reports.TestReport objects 

181 # (where we stored the statistics data in pytest_runtest_makereport above) 

182 for test_report in terminalreporter.stats.get("", []): 

183 for name, lines in test_report.user_properties: 

184 if name == "hypothesis-stats" and test_report.when == "teardown": 

185 for li in lines: 

186 terminalreporter.write_line(li) 

187 

188 def pytest_collection_modifyitems(items): 

189 for item in items: 

190 if isinstance(item, pytest.Function) and is_hypothesis_test(item.obj): 

191 item.add_marker("hypothesis") 

192 

193 

194def load(): 

195 """Required for `pluggy` to load a plugin from setuptools entrypoints."""