diff options
Diffstat (limited to 'third_party/python/pytest/testing/test_recwarn.py')
-rw-r--r-- | third_party/python/pytest/testing/test_recwarn.py | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/third_party/python/pytest/testing/test_recwarn.py b/third_party/python/pytest/testing/test_recwarn.py new file mode 100644 index 0000000000..a8e2fb8037 --- /dev/null +++ b/third_party/python/pytest/testing/test_recwarn.py @@ -0,0 +1,347 @@ +from __future__ import absolute_import, division, print_function +import warnings +import re + +import pytest +from _pytest.recwarn import WarningsRecorder + + +def test_recwarn_functional(testdir): + reprec = testdir.inline_runsource( + """ + import warnings + def test_method(recwarn): + warnings.warn("hello") + warn = recwarn.pop() + assert isinstance(warn.message, UserWarning) + """ + ) + res = reprec.countoutcomes() + assert tuple(res) == (1, 0, 0), res + + +class TestWarningsRecorderChecker(object): + + def test_recording(self): + rec = WarningsRecorder() + with rec: + assert not rec.list + warnings.warn_explicit("hello", UserWarning, "xyz", 13) + assert len(rec.list) == 1 + warnings.warn(DeprecationWarning("hello")) + assert len(rec.list) == 2 + warn = rec.pop() + assert str(warn.message) == "hello" + values = rec.list + rec.clear() + assert len(rec.list) == 0 + assert values is rec.list + pytest.raises(AssertionError, "rec.pop()") + + def test_typechecking(self): + from _pytest.recwarn import WarningsChecker + + with pytest.raises(TypeError): + WarningsChecker(5) + with pytest.raises(TypeError): + WarningsChecker(("hi", RuntimeWarning)) + with pytest.raises(TypeError): + WarningsChecker([DeprecationWarning, RuntimeWarning]) + + def test_invalid_enter_exit(self): + # wrap this test in WarningsRecorder to ensure warning state gets reset + with WarningsRecorder(): + with pytest.raises(RuntimeError): + rec = WarningsRecorder() + rec.__exit__(None, None, None) # can't exit before entering + + with pytest.raises(RuntimeError): + rec = WarningsRecorder() + with rec: + with rec: + pass # can't enter twice + + +class TestDeprecatedCall(object): + """test pytest.deprecated_call()""" + + def dep(self, i, j=None): + if i == 0: + warnings.warn("is deprecated", DeprecationWarning, stacklevel=1) + return 42 + + def dep_explicit(self, i): + if i == 0: + warnings.warn_explicit( + "dep_explicit", category=DeprecationWarning, filename="hello", lineno=3 + ) + + def test_deprecated_call_raises(self): + with pytest.raises(AssertionError) as excinfo: + pytest.deprecated_call(self.dep, 3, 5) + assert "Did not produce" in str(excinfo) + + def test_deprecated_call(self): + pytest.deprecated_call(self.dep, 0, 5) + + def test_deprecated_call_ret(self): + ret = pytest.deprecated_call(self.dep, 0) + assert ret == 42 + + def test_deprecated_call_preserves(self): + onceregistry = warnings.onceregistry.copy() + filters = warnings.filters[:] + warn = warnings.warn + warn_explicit = warnings.warn_explicit + self.test_deprecated_call_raises() + self.test_deprecated_call() + assert onceregistry == warnings.onceregistry + assert filters == warnings.filters + assert warn is warnings.warn + assert warn_explicit is warnings.warn_explicit + + def test_deprecated_explicit_call_raises(self): + with pytest.raises(AssertionError): + pytest.deprecated_call(self.dep_explicit, 3) + + def test_deprecated_explicit_call(self): + pytest.deprecated_call(self.dep_explicit, 0) + pytest.deprecated_call(self.dep_explicit, 0) + + @pytest.mark.parametrize("mode", ["context_manager", "call"]) + def test_deprecated_call_no_warning(self, mode): + """Ensure deprecated_call() raises the expected failure when its block/function does + not raise a deprecation warning. + """ + + def f(): + pass + + msg = "Did not produce DeprecationWarning or PendingDeprecationWarning" + with pytest.raises(AssertionError, match=msg): + if mode == "call": + pytest.deprecated_call(f) + else: + with pytest.deprecated_call(): + f() + + @pytest.mark.parametrize( + "warning_type", [PendingDeprecationWarning, DeprecationWarning] + ) + @pytest.mark.parametrize("mode", ["context_manager", "call"]) + @pytest.mark.parametrize("call_f_first", [True, False]) + @pytest.mark.filterwarnings("ignore") + def test_deprecated_call_modes(self, warning_type, mode, call_f_first): + """Ensure deprecated_call() captures a deprecation warning as expected inside its + block/function. + """ + + def f(): + warnings.warn(warning_type("hi")) + return 10 + + # ensure deprecated_call() can capture the warning even if it has already been triggered + if call_f_first: + assert f() == 10 + if mode == "call": + assert pytest.deprecated_call(f) == 10 + else: + with pytest.deprecated_call(): + assert f() == 10 + + @pytest.mark.parametrize("mode", ["context_manager", "call"]) + def test_deprecated_call_exception_is_raised(self, mode): + """If the block of the code being tested by deprecated_call() raises an exception, + it must raise the exception undisturbed. + """ + + def f(): + raise ValueError("some exception") + + with pytest.raises(ValueError, match="some exception"): + if mode == "call": + pytest.deprecated_call(f) + else: + with pytest.deprecated_call(): + f() + + def test_deprecated_call_specificity(self): + other_warnings = [ + Warning, + UserWarning, + SyntaxWarning, + RuntimeWarning, + FutureWarning, + ImportWarning, + UnicodeWarning, + ] + for warning in other_warnings: + + def f(): + warnings.warn(warning("hi")) + + with pytest.raises(AssertionError): + pytest.deprecated_call(f) + with pytest.raises(AssertionError): + with pytest.deprecated_call(): + f() + + +class TestWarns(object): + + def test_strings(self): + # different messages, b/c Python suppresses multiple identical warnings + source1 = "warnings.warn('w1', RuntimeWarning)" + source2 = "warnings.warn('w2', RuntimeWarning)" + source3 = "warnings.warn('w3', RuntimeWarning)" + pytest.warns(RuntimeWarning, source1) + pytest.raises(pytest.fail.Exception, lambda: pytest.warns(UserWarning, source2)) + pytest.warns(RuntimeWarning, source3) + + def test_function(self): + pytest.warns( + SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax" + ) + + def test_warning_tuple(self): + pytest.warns( + (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning) + ) + pytest.warns( + (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning) + ) + pytest.raises( + pytest.fail.Exception, + lambda: pytest.warns( + (RuntimeWarning, SyntaxWarning), + lambda: warnings.warn("w3", UserWarning), + ), + ) + + def test_as_contextmanager(self): + with pytest.warns(RuntimeWarning): + warnings.warn("runtime", RuntimeWarning) + + with pytest.warns(UserWarning): + warnings.warn("user", UserWarning) + + with pytest.raises(pytest.fail.Exception) as excinfo: + with pytest.warns(RuntimeWarning): + warnings.warn("user", UserWarning) + excinfo.match( + r"DID NOT WARN. No warnings of type \(.+RuntimeWarning.+,\) was emitted. " + r"The list of emitted warnings is: \[UserWarning\('user',?\)\]." + ) + + with pytest.raises(pytest.fail.Exception) as excinfo: + with pytest.warns(UserWarning): + warnings.warn("runtime", RuntimeWarning) + excinfo.match( + r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. " + r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]." + ) + + with pytest.raises(pytest.fail.Exception) as excinfo: + with pytest.warns(UserWarning): + pass + excinfo.match( + r"DID NOT WARN. No warnings of type \(.+UserWarning.+,\) was emitted. " + r"The list of emitted warnings is: \[\]." + ) + + warning_classes = (UserWarning, FutureWarning) + with pytest.raises(pytest.fail.Exception) as excinfo: + with pytest.warns(warning_classes) as warninfo: + warnings.warn("runtime", RuntimeWarning) + warnings.warn("import", ImportWarning) + + message_template = ( + "DID NOT WARN. No warnings of type {0} was emitted. " + "The list of emitted warnings is: {1}." + ) + excinfo.match( + re.escape( + message_template.format( + warning_classes, [each.message for each in warninfo] + ) + ) + ) + + def test_record(self): + with pytest.warns(UserWarning) as record: + warnings.warn("user", UserWarning) + + assert len(record) == 1 + assert str(record[0].message) == "user" + + def test_record_only(self): + with pytest.warns(None) as record: + warnings.warn("user", UserWarning) + warnings.warn("runtime", RuntimeWarning) + + assert len(record) == 2 + assert str(record[0].message) == "user" + assert str(record[1].message) == "runtime" + + def test_record_by_subclass(self): + with pytest.warns(Warning) as record: + warnings.warn("user", UserWarning) + warnings.warn("runtime", RuntimeWarning) + + assert len(record) == 2 + assert str(record[0].message) == "user" + assert str(record[1].message) == "runtime" + + class MyUserWarning(UserWarning): + pass + + class MyRuntimeWarning(RuntimeWarning): + pass + + with pytest.warns((UserWarning, RuntimeWarning)) as record: + warnings.warn("user", MyUserWarning) + warnings.warn("runtime", MyRuntimeWarning) + + assert len(record) == 2 + assert str(record[0].message) == "user" + assert str(record[1].message) == "runtime" + + def test_double_test(self, testdir): + """If a test is run again, the warning should still be raised""" + testdir.makepyfile( + """ + import pytest + import warnings + + @pytest.mark.parametrize('run', [1, 2]) + def test(run): + with pytest.warns(RuntimeWarning): + warnings.warn("runtime", RuntimeWarning) + """ + ) + result = testdir.runpytest() + result.stdout.fnmatch_lines(["*2 passed in*"]) + + def test_match_regex(self): + with pytest.warns(UserWarning, match=r"must be \d+$"): + warnings.warn("value must be 42", UserWarning) + + with pytest.raises(pytest.fail.Exception): + with pytest.warns(UserWarning, match=r"must be \d+$"): + warnings.warn("this is not here", UserWarning) + + with pytest.raises(pytest.fail.Exception): + with pytest.warns(FutureWarning, match=r"must be \d+$"): + warnings.warn("value must be 42", UserWarning) + + def test_one_from_multiple_warns(self): + with pytest.warns(UserWarning, match=r"aaa"): + warnings.warn("cccccccccc", UserWarning) + warnings.warn("bbbbbbbbbb", UserWarning) + warnings.warn("aaaaaaaaaa", UserWarning) + + def test_none_of_multiple_warns(self): + with pytest.raises(pytest.fail.Exception): + with pytest.warns(UserWarning, match=r"aaa"): + warnings.warn("bbbbbbbbbb", UserWarning) + warnings.warn("cccccccccc", UserWarning) |