PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/third_party/pytest/testing/test_recwarn.py

https://gitlab.com/Spagnotti3/wpt
Python | 386 lines | 378 code | 5 blank | 3 comment | 0 complexity | 867c0070236b2a4fd9bcd086ab81074d MD5 | raw file
  1. import re
  2. import warnings
  3. from typing import Optional
  4. import pytest
  5. from _pytest.recwarn import WarningsRecorder
  6. def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None:
  7. warnings.warn("hello")
  8. warn = recwarn.pop()
  9. assert warn.filename == __file__
  10. def test_recwarn_functional(testdir) -> None:
  11. testdir.makepyfile(
  12. """
  13. import warnings
  14. def test_method(recwarn):
  15. warnings.warn("hello")
  16. warn = recwarn.pop()
  17. assert isinstance(warn.message, UserWarning)
  18. """
  19. )
  20. reprec = testdir.inline_run()
  21. reprec.assertoutcome(passed=1)
  22. class TestWarningsRecorderChecker:
  23. def test_recording(self) -> None:
  24. rec = WarningsRecorder()
  25. with rec:
  26. assert not rec.list
  27. warnings.warn_explicit("hello", UserWarning, "xyz", 13)
  28. assert len(rec.list) == 1
  29. warnings.warn(DeprecationWarning("hello"))
  30. assert len(rec.list) == 2
  31. warn = rec.pop()
  32. assert str(warn.message) == "hello"
  33. values = rec.list
  34. rec.clear()
  35. assert len(rec.list) == 0
  36. assert values is rec.list
  37. pytest.raises(AssertionError, rec.pop)
  38. def test_warn_stacklevel(self) -> None:
  39. """#4243"""
  40. rec = WarningsRecorder()
  41. with rec:
  42. warnings.warn("test", DeprecationWarning, 2)
  43. def test_typechecking(self) -> None:
  44. from _pytest.recwarn import WarningsChecker
  45. with pytest.raises(TypeError):
  46. WarningsChecker(5) # type: ignore
  47. with pytest.raises(TypeError):
  48. WarningsChecker(("hi", RuntimeWarning)) # type: ignore
  49. with pytest.raises(TypeError):
  50. WarningsChecker([DeprecationWarning, RuntimeWarning]) # type: ignore
  51. def test_invalid_enter_exit(self) -> None:
  52. # wrap this test in WarningsRecorder to ensure warning state gets reset
  53. with WarningsRecorder():
  54. with pytest.raises(RuntimeError):
  55. rec = WarningsRecorder()
  56. rec.__exit__(None, None, None) # can't exit before entering
  57. with pytest.raises(RuntimeError):
  58. rec = WarningsRecorder()
  59. with rec:
  60. with rec:
  61. pass # can't enter twice
  62. class TestDeprecatedCall:
  63. """test pytest.deprecated_call()"""
  64. def dep(self, i: int, j: Optional[int] = None) -> int:
  65. if i == 0:
  66. warnings.warn("is deprecated", DeprecationWarning, stacklevel=1)
  67. return 42
  68. def dep_explicit(self, i: int) -> None:
  69. if i == 0:
  70. warnings.warn_explicit(
  71. "dep_explicit", category=DeprecationWarning, filename="hello", lineno=3
  72. )
  73. def test_deprecated_call_raises(self) -> None:
  74. with pytest.raises(pytest.fail.Exception, match="No warnings of type"):
  75. pytest.deprecated_call(self.dep, 3, 5)
  76. def test_deprecated_call(self) -> None:
  77. pytest.deprecated_call(self.dep, 0, 5)
  78. def test_deprecated_call_ret(self) -> None:
  79. ret = pytest.deprecated_call(self.dep, 0)
  80. assert ret == 42
  81. def test_deprecated_call_preserves(self) -> None:
  82. # Type ignored because `onceregistry` and `filters` are not
  83. # documented API.
  84. onceregistry = warnings.onceregistry.copy() # type: ignore
  85. filters = warnings.filters[:] # type: ignore
  86. warn = warnings.warn
  87. warn_explicit = warnings.warn_explicit
  88. self.test_deprecated_call_raises()
  89. self.test_deprecated_call()
  90. assert onceregistry == warnings.onceregistry # type: ignore
  91. assert filters == warnings.filters # type: ignore
  92. assert warn is warnings.warn
  93. assert warn_explicit is warnings.warn_explicit
  94. def test_deprecated_explicit_call_raises(self) -> None:
  95. with pytest.raises(pytest.fail.Exception):
  96. pytest.deprecated_call(self.dep_explicit, 3)
  97. def test_deprecated_explicit_call(self) -> None:
  98. pytest.deprecated_call(self.dep_explicit, 0)
  99. pytest.deprecated_call(self.dep_explicit, 0)
  100. @pytest.mark.parametrize("mode", ["context_manager", "call"])
  101. def test_deprecated_call_no_warning(self, mode) -> None:
  102. """Ensure deprecated_call() raises the expected failure when its block/function does
  103. not raise a deprecation warning.
  104. """
  105. def f():
  106. pass
  107. msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)"
  108. with pytest.raises(pytest.fail.Exception, match=msg):
  109. if mode == "call":
  110. pytest.deprecated_call(f)
  111. else:
  112. with pytest.deprecated_call():
  113. f()
  114. @pytest.mark.parametrize(
  115. "warning_type", [PendingDeprecationWarning, DeprecationWarning]
  116. )
  117. @pytest.mark.parametrize("mode", ["context_manager", "call"])
  118. @pytest.mark.parametrize("call_f_first", [True, False])
  119. @pytest.mark.filterwarnings("ignore")
  120. def test_deprecated_call_modes(self, warning_type, mode, call_f_first) -> None:
  121. """Ensure deprecated_call() captures a deprecation warning as expected inside its
  122. block/function.
  123. """
  124. def f():
  125. warnings.warn(warning_type("hi"))
  126. return 10
  127. # ensure deprecated_call() can capture the warning even if it has already been triggered
  128. if call_f_first:
  129. assert f() == 10
  130. if mode == "call":
  131. assert pytest.deprecated_call(f) == 10
  132. else:
  133. with pytest.deprecated_call():
  134. assert f() == 10
  135. @pytest.mark.parametrize("mode", ["context_manager", "call"])
  136. def test_deprecated_call_exception_is_raised(self, mode) -> None:
  137. """If the block of the code being tested by deprecated_call() raises an exception,
  138. it must raise the exception undisturbed.
  139. """
  140. def f():
  141. raise ValueError("some exception")
  142. with pytest.raises(ValueError, match="some exception"):
  143. if mode == "call":
  144. pytest.deprecated_call(f)
  145. else:
  146. with pytest.deprecated_call():
  147. f()
  148. def test_deprecated_call_specificity(self) -> None:
  149. other_warnings = [
  150. Warning,
  151. UserWarning,
  152. SyntaxWarning,
  153. RuntimeWarning,
  154. FutureWarning,
  155. ImportWarning,
  156. UnicodeWarning,
  157. ]
  158. for warning in other_warnings:
  159. def f():
  160. warnings.warn(warning("hi"))
  161. with pytest.raises(pytest.fail.Exception):
  162. pytest.deprecated_call(f)
  163. with pytest.raises(pytest.fail.Exception):
  164. with pytest.deprecated_call():
  165. f()
  166. def test_deprecated_call_supports_match(self) -> None:
  167. with pytest.deprecated_call(match=r"must be \d+$"):
  168. warnings.warn("value must be 42", DeprecationWarning)
  169. with pytest.raises(pytest.fail.Exception):
  170. with pytest.deprecated_call(match=r"must be \d+$"):
  171. warnings.warn("this is not here", DeprecationWarning)
  172. class TestWarns:
  173. def test_check_callable(self) -> None:
  174. source = "warnings.warn('w1', RuntimeWarning)"
  175. with pytest.raises(TypeError, match=r".* must be callable"):
  176. pytest.warns(RuntimeWarning, source) # type: ignore
  177. def test_several_messages(self) -> None:
  178. # different messages, b/c Python suppresses multiple identical warnings
  179. pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
  180. with pytest.raises(pytest.fail.Exception):
  181. pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
  182. pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
  183. def test_function(self) -> None:
  184. pytest.warns(
  185. SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax"
  186. )
  187. def test_warning_tuple(self) -> None:
  188. pytest.warns(
  189. (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning)
  190. )
  191. pytest.warns(
  192. (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning)
  193. )
  194. pytest.raises(
  195. pytest.fail.Exception,
  196. lambda: pytest.warns(
  197. (RuntimeWarning, SyntaxWarning),
  198. lambda: warnings.warn("w3", UserWarning),
  199. ),
  200. )
  201. def test_as_contextmanager(self) -> None:
  202. with pytest.warns(RuntimeWarning):
  203. warnings.warn("runtime", RuntimeWarning)
  204. with pytest.warns(UserWarning):
  205. warnings.warn("user", UserWarning)
  206. with pytest.raises(pytest.fail.Exception) as excinfo:
  207. with pytest.warns(RuntimeWarning):
  208. warnings.warn("user", UserWarning)
  209. excinfo.match(
  210. r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. "
  211. r"The list of emitted warnings is: \[UserWarning\('user',?\)\]."
  212. )
  213. with pytest.raises(pytest.fail.Exception) as excinfo:
  214. with pytest.warns(UserWarning):
  215. warnings.warn("runtime", RuntimeWarning)
  216. excinfo.match(
  217. r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
  218. r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]."
  219. )
  220. with pytest.raises(pytest.fail.Exception) as excinfo:
  221. with pytest.warns(UserWarning):
  222. pass
  223. excinfo.match(
  224. r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. "
  225. r"The list of emitted warnings is: \[\]."
  226. )
  227. warning_classes = (UserWarning, FutureWarning)
  228. with pytest.raises(pytest.fail.Exception) as excinfo:
  229. with pytest.warns(warning_classes) as warninfo:
  230. warnings.warn("runtime", RuntimeWarning)
  231. warnings.warn("import", ImportWarning)
  232. message_template = (
  233. "DID NOT WARN. No warnings of type {0} was emitted. "
  234. "The list of emitted warnings is: {1}."
  235. )
  236. excinfo.match(
  237. re.escape(
  238. message_template.format(
  239. warning_classes, [each.message for each in warninfo]
  240. )
  241. )
  242. )
  243. def test_record(self) -> None:
  244. with pytest.warns(UserWarning) as record:
  245. warnings.warn("user", UserWarning)
  246. assert len(record) == 1
  247. assert str(record[0].message) == "user"
  248. def test_record_only(self) -> None:
  249. with pytest.warns(None) as record:
  250. warnings.warn("user", UserWarning)
  251. warnings.warn("runtime", RuntimeWarning)
  252. assert len(record) == 2
  253. assert str(record[0].message) == "user"
  254. assert str(record[1].message) == "runtime"
  255. def test_record_by_subclass(self) -> None:
  256. with pytest.warns(Warning) as record:
  257. warnings.warn("user", UserWarning)
  258. warnings.warn("runtime", RuntimeWarning)
  259. assert len(record) == 2
  260. assert str(record[0].message) == "user"
  261. assert str(record[1].message) == "runtime"
  262. class MyUserWarning(UserWarning):
  263. pass
  264. class MyRuntimeWarning(RuntimeWarning):
  265. pass
  266. with pytest.warns((UserWarning, RuntimeWarning)) as record:
  267. warnings.warn("user", MyUserWarning)
  268. warnings.warn("runtime", MyRuntimeWarning)
  269. assert len(record) == 2
  270. assert str(record[0].message) == "user"
  271. assert str(record[1].message) == "runtime"
  272. def test_double_test(self, testdir) -> None:
  273. """If a test is run again, the warning should still be raised"""
  274. testdir.makepyfile(
  275. """
  276. import pytest
  277. import warnings
  278. @pytest.mark.parametrize('run', [1, 2])
  279. def test(run):
  280. with pytest.warns(RuntimeWarning):
  281. warnings.warn("runtime", RuntimeWarning)
  282. """
  283. )
  284. result = testdir.runpytest()
  285. result.stdout.fnmatch_lines(["*2 passed in*"])
  286. def test_match_regex(self) -> None:
  287. with pytest.warns(UserWarning, match=r"must be \d+$"):
  288. warnings.warn("value must be 42", UserWarning)
  289. with pytest.raises(pytest.fail.Exception):
  290. with pytest.warns(UserWarning, match=r"must be \d+$"):
  291. warnings.warn("this is not here", UserWarning)
  292. with pytest.raises(pytest.fail.Exception):
  293. with pytest.warns(FutureWarning, match=r"must be \d+$"):
  294. warnings.warn("value must be 42", UserWarning)
  295. def test_one_from_multiple_warns(self) -> None:
  296. with pytest.warns(UserWarning, match=r"aaa"):
  297. warnings.warn("cccccccccc", UserWarning)
  298. warnings.warn("bbbbbbbbbb", UserWarning)
  299. warnings.warn("aaaaaaaaaa", UserWarning)
  300. def test_none_of_multiple_warns(self) -> None:
  301. with pytest.raises(pytest.fail.Exception):
  302. with pytest.warns(UserWarning, match=r"aaa"):
  303. warnings.warn("bbbbbbbbbb", UserWarning)
  304. warnings.warn("cccccccccc", UserWarning)
  305. @pytest.mark.filterwarnings("ignore")
  306. def test_can_capture_previously_warned(self) -> None:
  307. def f() -> int:
  308. warnings.warn(UserWarning("ohai"))
  309. return 10
  310. assert f() == 10
  311. assert pytest.warns(UserWarning, f) == 10
  312. assert pytest.warns(UserWarning, f) == 10
  313. assert pytest.warns(UserWarning, f) != "10" # type: ignore[comparison-overlap]
  314. def test_warns_context_manager_with_kwargs(self) -> None:
  315. with pytest.raises(TypeError) as excinfo:
  316. with pytest.warns(UserWarning, foo="bar"): # type: ignore
  317. pass
  318. assert "Unexpected keyword arguments" in str(excinfo.value)