Merge branch 'features' into unittest-debug
This commit is contained in:
@@ -760,7 +760,6 @@ class TestInvocationVariants:
|
||||
result = testdir.runpytest(str(p) + "::test", "--doctest-modules")
|
||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||
|
||||
@pytest.mark.skipif(not hasattr(os, "symlink"), reason="requires symlinks")
|
||||
def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
|
||||
"""
|
||||
test --pyargs option with packages with path containing symlink can
|
||||
|
||||
+29
-28
@@ -1,18 +1,19 @@
|
||||
import sys
|
||||
from types import FrameType
|
||||
from unittest import mock
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
|
||||
|
||||
def test_ne():
|
||||
def test_ne() -> None:
|
||||
code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec"))
|
||||
assert code1 == code1
|
||||
code2 = _pytest._code.Code(compile('foo = "baz"', "", "exec"))
|
||||
assert code2 != code1
|
||||
|
||||
|
||||
def test_code_gives_back_name_for_not_existing_file():
|
||||
def test_code_gives_back_name_for_not_existing_file() -> None:
|
||||
name = "abc-123"
|
||||
co_code = compile("pass\n", name, "exec")
|
||||
assert co_code.co_filename == name
|
||||
@@ -21,68 +22,67 @@ def test_code_gives_back_name_for_not_existing_file():
|
||||
assert code.fullsource is None
|
||||
|
||||
|
||||
def test_code_with_class():
|
||||
def test_code_with_class() -> None:
|
||||
class A:
|
||||
pass
|
||||
|
||||
pytest.raises(TypeError, _pytest._code.Code, A)
|
||||
|
||||
|
||||
def x():
|
||||
def x() -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def test_code_fullsource():
|
||||
def test_code_fullsource() -> None:
|
||||
code = _pytest._code.Code(x)
|
||||
full = code.fullsource
|
||||
assert "test_code_fullsource()" in str(full)
|
||||
|
||||
|
||||
def test_code_source():
|
||||
def test_code_source() -> None:
|
||||
code = _pytest._code.Code(x)
|
||||
src = code.source()
|
||||
expected = """def x():
|
||||
expected = """def x() -> None:
|
||||
raise NotImplementedError()"""
|
||||
assert str(src) == expected
|
||||
|
||||
|
||||
def test_frame_getsourcelineno_myself():
|
||||
def func():
|
||||
def test_frame_getsourcelineno_myself() -> None:
|
||||
def func() -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
f = func()
|
||||
f = _pytest._code.Frame(f)
|
||||
f = _pytest._code.Frame(func())
|
||||
source, lineno = f.code.fullsource, f.lineno
|
||||
assert source is not None
|
||||
assert source[lineno].startswith(" return sys._getframe(0)")
|
||||
|
||||
|
||||
def test_getstatement_empty_fullsource():
|
||||
def func():
|
||||
def test_getstatement_empty_fullsource() -> None:
|
||||
def func() -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
f = func()
|
||||
f = _pytest._code.Frame(f)
|
||||
f = _pytest._code.Frame(func())
|
||||
with mock.patch.object(f.code.__class__, "fullsource", None):
|
||||
assert f.statement == ""
|
||||
|
||||
|
||||
def test_code_from_func():
|
||||
def test_code_from_func() -> None:
|
||||
co = _pytest._code.Code(test_frame_getsourcelineno_myself)
|
||||
assert co.firstlineno
|
||||
assert co.path
|
||||
|
||||
|
||||
def test_unicode_handling():
|
||||
def test_unicode_handling() -> None:
|
||||
value = "ąć".encode()
|
||||
|
||||
def f():
|
||||
def f() -> None:
|
||||
raise Exception(value)
|
||||
|
||||
excinfo = pytest.raises(Exception, f)
|
||||
str(excinfo)
|
||||
|
||||
|
||||
def test_code_getargs():
|
||||
def test_code_getargs() -> None:
|
||||
def f1(x):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -108,26 +108,26 @@ def test_code_getargs():
|
||||
assert c4.getargs(var=True) == ("x", "y", "z")
|
||||
|
||||
|
||||
def test_frame_getargs():
|
||||
def f1(x):
|
||||
def test_frame_getargs() -> None:
|
||||
def f1(x) -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
fr1 = _pytest._code.Frame(f1("a"))
|
||||
assert fr1.getargs(var=True) == [("x", "a")]
|
||||
|
||||
def f2(x, *y):
|
||||
def f2(x, *y) -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
fr2 = _pytest._code.Frame(f2("a", "b", "c"))
|
||||
assert fr2.getargs(var=True) == [("x", "a"), ("y", ("b", "c"))]
|
||||
|
||||
def f3(x, **z):
|
||||
def f3(x, **z) -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
fr3 = _pytest._code.Frame(f3("a", b="c"))
|
||||
assert fr3.getargs(var=True) == [("x", "a"), ("z", {"b": "c"})]
|
||||
|
||||
def f4(x, *y, **z):
|
||||
def f4(x, *y, **z) -> FrameType:
|
||||
return sys._getframe(0)
|
||||
|
||||
fr4 = _pytest._code.Frame(f4("a", "b", c="d"))
|
||||
@@ -135,7 +135,7 @@ def test_frame_getargs():
|
||||
|
||||
|
||||
class TestExceptionInfo:
|
||||
def test_bad_getsource(self):
|
||||
def test_bad_getsource(self) -> None:
|
||||
try:
|
||||
if False:
|
||||
pass
|
||||
@@ -145,13 +145,13 @@ class TestExceptionInfo:
|
||||
exci = _pytest._code.ExceptionInfo.from_current()
|
||||
assert exci.getrepr()
|
||||
|
||||
def test_from_current_with_missing(self):
|
||||
def test_from_current_with_missing(self) -> None:
|
||||
with pytest.raises(AssertionError, match="no current exception"):
|
||||
_pytest._code.ExceptionInfo.from_current()
|
||||
|
||||
|
||||
class TestTracebackEntry:
|
||||
def test_getsource(self):
|
||||
def test_getsource(self) -> None:
|
||||
try:
|
||||
if False:
|
||||
pass
|
||||
@@ -161,12 +161,13 @@ class TestTracebackEntry:
|
||||
exci = _pytest._code.ExceptionInfo.from_current()
|
||||
entry = exci.traceback[0]
|
||||
source = entry.getsource()
|
||||
assert source is not None
|
||||
assert len(source) == 6
|
||||
assert "assert False" in source[5]
|
||||
|
||||
|
||||
class TestReprFuncArgs:
|
||||
def test_not_raise_exception_with_mixed_encoding(self, tw_mock):
|
||||
def test_not_raise_exception_with_mixed_encoding(self, tw_mock) -> None:
|
||||
from _pytest._code.code import ReprFuncArgs
|
||||
|
||||
args = [("unicode_string", "São Paulo"), ("utf8_string", b"S\xc3\xa3o Paulo")]
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import queue
|
||||
import sys
|
||||
import textwrap
|
||||
from typing import Union
|
||||
|
||||
import py
|
||||
|
||||
@@ -59,9 +60,9 @@ def test_excinfo_getstatement():
|
||||
except ValueError:
|
||||
excinfo = _pytest._code.ExceptionInfo.from_current()
|
||||
linenumbers = [
|
||||
_pytest._code.getrawcode(f).co_firstlineno - 1 + 4,
|
||||
_pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
|
||||
_pytest._code.getrawcode(g).co_firstlineno - 1 + 1,
|
||||
f.__code__.co_firstlineno - 1 + 4,
|
||||
f.__code__.co_firstlineno - 1 + 1,
|
||||
g.__code__.co_firstlineno - 1 + 1,
|
||||
]
|
||||
values = list(excinfo.traceback)
|
||||
foundlinenumbers = [x.lineno for x in values]
|
||||
@@ -224,23 +225,25 @@ class TestTraceback_f_g_h:
|
||||
repr = excinfo.getrepr()
|
||||
assert "RuntimeError: hello" in str(repr.reprcrash)
|
||||
|
||||
def test_traceback_no_recursion_index(self):
|
||||
def do_stuff():
|
||||
def test_traceback_no_recursion_index(self) -> None:
|
||||
def do_stuff() -> None:
|
||||
raise RuntimeError
|
||||
|
||||
def reraise_me():
|
||||
def reraise_me() -> None:
|
||||
import sys
|
||||
|
||||
exc, val, tb = sys.exc_info()
|
||||
assert val is not None
|
||||
raise val.with_traceback(tb)
|
||||
|
||||
def f(n):
|
||||
def f(n: int) -> None:
|
||||
try:
|
||||
do_stuff()
|
||||
except: # noqa
|
||||
reraise_me()
|
||||
|
||||
excinfo = pytest.raises(RuntimeError, f, 8)
|
||||
assert excinfo is not None
|
||||
traceback = excinfo.traceback
|
||||
recindex = traceback.recursionindex()
|
||||
assert recindex is None
|
||||
@@ -502,65 +505,18 @@ raise ValueError()
|
||||
assert repr.reprtraceback.reprentries[1].lines[0] == "> ???"
|
||||
assert repr.chain[0][0].reprentries[1].lines[0] == "> ???"
|
||||
|
||||
def test_repr_source_failing_fullsource(self):
|
||||
def test_repr_source_failing_fullsource(self, monkeypatch) -> None:
|
||||
pr = FormattedExcinfo()
|
||||
|
||||
class FakeCode:
|
||||
class raw:
|
||||
co_filename = "?"
|
||||
try:
|
||||
1 / 0
|
||||
except ZeroDivisionError:
|
||||
excinfo = ExceptionInfo.from_current()
|
||||
|
||||
path = "?"
|
||||
firstlineno = 5
|
||||
with monkeypatch.context() as m:
|
||||
m.setattr(_pytest._code.Code, "fullsource", property(lambda self: None))
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
|
||||
def fullsource(self):
|
||||
return None
|
||||
|
||||
fullsource = property(fullsource)
|
||||
|
||||
class FakeFrame:
|
||||
code = FakeCode()
|
||||
f_locals = {}
|
||||
f_globals = {}
|
||||
|
||||
class FakeTracebackEntry(_pytest._code.Traceback.Entry):
|
||||
def __init__(self, tb, excinfo=None):
|
||||
self.lineno = 5 + 3
|
||||
|
||||
@property
|
||||
def frame(self):
|
||||
return FakeFrame()
|
||||
|
||||
class Traceback(_pytest._code.Traceback):
|
||||
Entry = FakeTracebackEntry
|
||||
|
||||
class FakeExcinfo(_pytest._code.ExceptionInfo):
|
||||
typename = "Foo"
|
||||
value = Exception()
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def exconly(self, tryshort):
|
||||
return "EXC"
|
||||
|
||||
def errisinstance(self, cls):
|
||||
return False
|
||||
|
||||
excinfo = FakeExcinfo()
|
||||
|
||||
class FakeRawTB:
|
||||
tb_next = None
|
||||
|
||||
tb = FakeRawTB()
|
||||
excinfo.traceback = Traceback(tb)
|
||||
|
||||
fail = IOError()
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
|
||||
|
||||
fail = py.error.ENOENT # noqa
|
||||
repr = pr.repr_excinfo(excinfo)
|
||||
assert repr.reprtraceback.reprentries[0].lines[0] == "> ???"
|
||||
assert repr.chain[0][0].reprentries[0].lines[0] == "> ???"
|
||||
|
||||
@@ -643,7 +599,6 @@ raise ValueError()
|
||||
assert lines[3] == "E world"
|
||||
assert not lines[4:]
|
||||
|
||||
loc = repr_entry.reprlocals is not None
|
||||
loc = repr_entry.reprfileloc
|
||||
assert loc.path == mod.__file__
|
||||
assert loc.lineno == 3
|
||||
@@ -1333,9 +1288,10 @@ raise ValueError()
|
||||
@pytest.mark.parametrize("style", ["short", "long"])
|
||||
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
||||
def test_repr_traceback_with_unicode(style, encoding):
|
||||
msg = "☹"
|
||||
if encoding is not None:
|
||||
msg = msg.encode(encoding)
|
||||
if encoding is None:
|
||||
msg = "☹" # type: Union[str, bytes]
|
||||
else:
|
||||
msg = "☹".encode(encoding)
|
||||
try:
|
||||
raise RuntimeError(msg)
|
||||
except RuntimeError:
|
||||
|
||||
+107
-92
@@ -4,13 +4,16 @@
|
||||
import ast
|
||||
import inspect
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
from typing import Optional
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
from _pytest._code import Source
|
||||
|
||||
|
||||
def test_source_str_function():
|
||||
def test_source_str_function() -> None:
|
||||
x = Source("3")
|
||||
assert str(x) == "3"
|
||||
|
||||
@@ -25,7 +28,7 @@ def test_source_str_function():
|
||||
assert str(x) == "\n3"
|
||||
|
||||
|
||||
def test_unicode():
|
||||
def test_unicode() -> None:
|
||||
x = Source("4")
|
||||
assert str(x) == "4"
|
||||
co = _pytest._code.compile('"å"', mode="eval")
|
||||
@@ -33,12 +36,12 @@ def test_unicode():
|
||||
assert isinstance(val, str)
|
||||
|
||||
|
||||
def test_source_from_function():
|
||||
def test_source_from_function() -> None:
|
||||
source = _pytest._code.Source(test_source_str_function)
|
||||
assert str(source).startswith("def test_source_str_function():")
|
||||
assert str(source).startswith("def test_source_str_function() -> None:")
|
||||
|
||||
|
||||
def test_source_from_method():
|
||||
def test_source_from_method() -> None:
|
||||
class TestClass:
|
||||
def test_method(self):
|
||||
pass
|
||||
@@ -47,13 +50,13 @@ def test_source_from_method():
|
||||
assert source.lines == ["def test_method(self):", " pass"]
|
||||
|
||||
|
||||
def test_source_from_lines():
|
||||
def test_source_from_lines() -> None:
|
||||
lines = ["a \n", "b\n", "c"]
|
||||
source = _pytest._code.Source(lines)
|
||||
assert source.lines == ["a ", "b", "c"]
|
||||
|
||||
|
||||
def test_source_from_inner_function():
|
||||
def test_source_from_inner_function() -> None:
|
||||
def f():
|
||||
pass
|
||||
|
||||
@@ -63,7 +66,7 @@ def test_source_from_inner_function():
|
||||
assert str(source).startswith("def f():")
|
||||
|
||||
|
||||
def test_source_putaround_simple():
|
||||
def test_source_putaround_simple() -> None:
|
||||
source = Source("raise ValueError")
|
||||
source = source.putaround(
|
||||
"try:",
|
||||
@@ -85,7 +88,7 @@ else:
|
||||
)
|
||||
|
||||
|
||||
def test_source_putaround():
|
||||
def test_source_putaround() -> None:
|
||||
source = Source()
|
||||
source = source.putaround(
|
||||
"""
|
||||
@@ -96,28 +99,29 @@ def test_source_putaround():
|
||||
assert str(source).strip() == "if 1:\n x=1"
|
||||
|
||||
|
||||
def test_source_strips():
|
||||
def test_source_strips() -> None:
|
||||
source = Source("")
|
||||
assert source == Source()
|
||||
assert str(source) == ""
|
||||
assert source.strip() == source
|
||||
|
||||
|
||||
def test_source_strip_multiline():
|
||||
def test_source_strip_multiline() -> None:
|
||||
source = Source()
|
||||
source.lines = ["", " hello", " "]
|
||||
source2 = source.strip()
|
||||
assert source2.lines == [" hello"]
|
||||
|
||||
|
||||
def test_syntaxerror_rerepresentation():
|
||||
def test_syntaxerror_rerepresentation() -> None:
|
||||
ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz")
|
||||
assert ex is not None
|
||||
assert ex.value.lineno == 1
|
||||
assert ex.value.offset in {5, 7} # cpython: 7, pypy3.6 7.1.1: 5
|
||||
assert ex.value.text.strip(), "x x"
|
||||
assert ex.value.text == "xyz xyz\n"
|
||||
|
||||
|
||||
def test_isparseable():
|
||||
def test_isparseable() -> None:
|
||||
assert Source("hello").isparseable()
|
||||
assert Source("if 1:\n pass").isparseable()
|
||||
assert Source(" \nif 1:\n pass").isparseable()
|
||||
@@ -127,7 +131,7 @@ def test_isparseable():
|
||||
|
||||
|
||||
class TestAccesses:
|
||||
def setup_class(self):
|
||||
def setup_class(self) -> None:
|
||||
self.source = Source(
|
||||
"""\
|
||||
def f(x):
|
||||
@@ -137,26 +141,26 @@ class TestAccesses:
|
||||
"""
|
||||
)
|
||||
|
||||
def test_getrange(self):
|
||||
def test_getrange(self) -> None:
|
||||
x = self.source[0:2]
|
||||
assert x.isparseable()
|
||||
assert len(x.lines) == 2
|
||||
assert str(x) == "def f(x):\n pass"
|
||||
|
||||
def test_getline(self):
|
||||
def test_getline(self) -> None:
|
||||
x = self.source[0]
|
||||
assert x == "def f(x):"
|
||||
|
||||
def test_len(self):
|
||||
def test_len(self) -> None:
|
||||
assert len(self.source) == 4
|
||||
|
||||
def test_iter(self):
|
||||
def test_iter(self) -> None:
|
||||
values = [x for x in self.source]
|
||||
assert len(values) == 4
|
||||
|
||||
|
||||
class TestSourceParsingAndCompiling:
|
||||
def setup_class(self):
|
||||
def setup_class(self) -> None:
|
||||
self.source = Source(
|
||||
"""\
|
||||
def f(x):
|
||||
@@ -166,19 +170,19 @@ class TestSourceParsingAndCompiling:
|
||||
"""
|
||||
).strip()
|
||||
|
||||
def test_compile(self):
|
||||
def test_compile(self) -> None:
|
||||
co = _pytest._code.compile("x=3")
|
||||
d = {}
|
||||
d = {} # type: Dict[str, Any]
|
||||
exec(co, d)
|
||||
assert d["x"] == 3
|
||||
|
||||
def test_compile_and_getsource_simple(self):
|
||||
def test_compile_and_getsource_simple(self) -> None:
|
||||
co = _pytest._code.compile("x=3")
|
||||
exec(co)
|
||||
source = _pytest._code.Source(co)
|
||||
assert str(source) == "x=3"
|
||||
|
||||
def test_compile_and_getsource_through_same_function(self):
|
||||
def test_compile_and_getsource_through_same_function(self) -> None:
|
||||
def gensource(source):
|
||||
return _pytest._code.compile(source)
|
||||
|
||||
@@ -199,7 +203,7 @@ class TestSourceParsingAndCompiling:
|
||||
source2 = inspect.getsource(co2)
|
||||
assert "ValueError" in source2
|
||||
|
||||
def test_getstatement(self):
|
||||
def test_getstatement(self) -> None:
|
||||
# print str(self.source)
|
||||
ass = str(self.source[1:])
|
||||
for i in range(1, 4):
|
||||
@@ -208,7 +212,7 @@ class TestSourceParsingAndCompiling:
|
||||
# x = s.deindent()
|
||||
assert str(s) == ass
|
||||
|
||||
def test_getstatementrange_triple_quoted(self):
|
||||
def test_getstatementrange_triple_quoted(self) -> None:
|
||||
# print str(self.source)
|
||||
source = Source(
|
||||
"""hello('''
|
||||
@@ -219,7 +223,7 @@ class TestSourceParsingAndCompiling:
|
||||
s = source.getstatement(1)
|
||||
assert s == str(source)
|
||||
|
||||
def test_getstatementrange_within_constructs(self):
|
||||
def test_getstatementrange_within_constructs(self) -> None:
|
||||
source = Source(
|
||||
"""\
|
||||
try:
|
||||
@@ -241,7 +245,7 @@ class TestSourceParsingAndCompiling:
|
||||
# assert source.getstatementrange(5) == (0, 7)
|
||||
assert source.getstatementrange(6) == (6, 7)
|
||||
|
||||
def test_getstatementrange_bug(self):
|
||||
def test_getstatementrange_bug(self) -> None:
|
||||
source = Source(
|
||||
"""\
|
||||
try:
|
||||
@@ -255,7 +259,7 @@ class TestSourceParsingAndCompiling:
|
||||
assert len(source) == 6
|
||||
assert source.getstatementrange(2) == (1, 4)
|
||||
|
||||
def test_getstatementrange_bug2(self):
|
||||
def test_getstatementrange_bug2(self) -> None:
|
||||
source = Source(
|
||||
"""\
|
||||
assert (
|
||||
@@ -272,7 +276,7 @@ class TestSourceParsingAndCompiling:
|
||||
assert len(source) == 9
|
||||
assert source.getstatementrange(5) == (0, 9)
|
||||
|
||||
def test_getstatementrange_ast_issue58(self):
|
||||
def test_getstatementrange_ast_issue58(self) -> None:
|
||||
source = Source(
|
||||
"""\
|
||||
|
||||
@@ -286,38 +290,44 @@ class TestSourceParsingAndCompiling:
|
||||
assert getstatement(2, source).lines == source.lines[2:3]
|
||||
assert getstatement(3, source).lines == source.lines[3:4]
|
||||
|
||||
def test_getstatementrange_out_of_bounds_py3(self):
|
||||
def test_getstatementrange_out_of_bounds_py3(self) -> None:
|
||||
source = Source("if xxx:\n from .collections import something")
|
||||
r = source.getstatementrange(1)
|
||||
assert r == (1, 2)
|
||||
|
||||
def test_getstatementrange_with_syntaxerror_issue7(self):
|
||||
def test_getstatementrange_with_syntaxerror_issue7(self) -> None:
|
||||
source = Source(":")
|
||||
pytest.raises(SyntaxError, lambda: source.getstatementrange(0))
|
||||
|
||||
def test_compile_to_ast(self):
|
||||
def test_compile_to_ast(self) -> None:
|
||||
source = Source("x = 4")
|
||||
mod = source.compile(flag=ast.PyCF_ONLY_AST)
|
||||
assert isinstance(mod, ast.Module)
|
||||
compile(mod, "<filename>", "exec")
|
||||
|
||||
def test_compile_and_getsource(self):
|
||||
def test_compile_and_getsource(self) -> None:
|
||||
co = self.source.compile()
|
||||
exec(co, globals())
|
||||
f(7)
|
||||
excinfo = pytest.raises(AssertionError, f, 6)
|
||||
f(7) # type: ignore
|
||||
excinfo = pytest.raises(AssertionError, f, 6) # type: ignore
|
||||
assert excinfo is not None
|
||||
frame = excinfo.traceback[-1].frame
|
||||
assert isinstance(frame.code.fullsource, Source)
|
||||
stmt = frame.code.fullsource.getstatement(frame.lineno)
|
||||
assert str(stmt).strip().startswith("assert")
|
||||
|
||||
@pytest.mark.parametrize("name", ["", None, "my"])
|
||||
def test_compilefuncs_and_path_sanity(self, name):
|
||||
def test_compilefuncs_and_path_sanity(self, name: Optional[str]) -> None:
|
||||
def check(comp, name):
|
||||
co = comp(self.source, name)
|
||||
if not name:
|
||||
expected = "codegen %s:%d>" % (mypath, mylineno + 2 + 2)
|
||||
expected = "codegen %s:%d>" % (mypath, mylineno + 2 + 2) # type: ignore
|
||||
else:
|
||||
expected = "codegen %r %s:%d>" % (name, mypath, mylineno + 2 + 2)
|
||||
expected = "codegen %r %s:%d>" % (
|
||||
name,
|
||||
mypath, # type: ignore
|
||||
mylineno + 2 + 2, # type: ignore
|
||||
) # type: ignore
|
||||
fn = co.co_filename
|
||||
assert fn.endswith(expected)
|
||||
|
||||
@@ -332,9 +342,9 @@ class TestSourceParsingAndCompiling:
|
||||
pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode="eval")
|
||||
|
||||
|
||||
def test_getstartingblock_singleline():
|
||||
def test_getstartingblock_singleline() -> None:
|
||||
class A:
|
||||
def __init__(self, *args):
|
||||
def __init__(self, *args) -> None:
|
||||
frame = sys._getframe(1)
|
||||
self.source = _pytest._code.Frame(frame).statement
|
||||
|
||||
@@ -344,22 +354,22 @@ def test_getstartingblock_singleline():
|
||||
assert len(values) == 1
|
||||
|
||||
|
||||
def test_getline_finally():
|
||||
def c():
|
||||
def test_getline_finally() -> None:
|
||||
def c() -> None:
|
||||
pass
|
||||
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
teardown = None
|
||||
try:
|
||||
c(1)
|
||||
c(1) # type: ignore
|
||||
finally:
|
||||
if teardown:
|
||||
teardown()
|
||||
source = excinfo.traceback[-1].statement
|
||||
assert str(source).strip() == "c(1)"
|
||||
assert str(source).strip() == "c(1) # type: ignore"
|
||||
|
||||
|
||||
def test_getfuncsource_dynamic():
|
||||
def test_getfuncsource_dynamic() -> None:
|
||||
source = """
|
||||
def f():
|
||||
raise ValueError
|
||||
@@ -368,11 +378,13 @@ def test_getfuncsource_dynamic():
|
||||
"""
|
||||
co = _pytest._code.compile(source)
|
||||
exec(co, globals())
|
||||
assert str(_pytest._code.Source(f)).strip() == "def f():\n raise ValueError"
|
||||
assert str(_pytest._code.Source(g)).strip() == "def g(): pass"
|
||||
f_source = _pytest._code.Source(f) # type: ignore
|
||||
g_source = _pytest._code.Source(g) # type: ignore
|
||||
assert str(f_source).strip() == "def f():\n raise ValueError"
|
||||
assert str(g_source).strip() == "def g(): pass"
|
||||
|
||||
|
||||
def test_getfuncsource_with_multine_string():
|
||||
def test_getfuncsource_with_multine_string() -> None:
|
||||
def f():
|
||||
c = """while True:
|
||||
pass
|
||||
@@ -387,7 +399,7 @@ def test_getfuncsource_with_multine_string():
|
||||
assert str(_pytest._code.Source(f)) == expected.rstrip()
|
||||
|
||||
|
||||
def test_deindent():
|
||||
def test_deindent() -> None:
|
||||
from _pytest._code.source import deindent as deindent
|
||||
|
||||
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
|
||||
@@ -401,7 +413,7 @@ def test_deindent():
|
||||
assert lines == ["def f():", " def g():", " pass"]
|
||||
|
||||
|
||||
def test_source_of_class_at_eof_without_newline(tmpdir, _sys_snapshot):
|
||||
def test_source_of_class_at_eof_without_newline(tmpdir, _sys_snapshot) -> None:
|
||||
# this test fails because the implicit inspect.getsource(A) below
|
||||
# does not return the "x = 1" last line.
|
||||
source = _pytest._code.Source(
|
||||
@@ -423,7 +435,7 @@ if True:
|
||||
pass
|
||||
|
||||
|
||||
def test_getsource_fallback():
|
||||
def test_getsource_fallback() -> None:
|
||||
from _pytest._code.source import getsource
|
||||
|
||||
expected = """def x():
|
||||
@@ -432,7 +444,7 @@ def test_getsource_fallback():
|
||||
assert src == expected
|
||||
|
||||
|
||||
def test_idem_compile_and_getsource():
|
||||
def test_idem_compile_and_getsource() -> None:
|
||||
from _pytest._code.source import getsource
|
||||
|
||||
expected = "def x(): pass"
|
||||
@@ -441,15 +453,16 @@ def test_idem_compile_and_getsource():
|
||||
assert src == expected
|
||||
|
||||
|
||||
def test_findsource_fallback():
|
||||
def test_findsource_fallback() -> None:
|
||||
from _pytest._code.source import findsource
|
||||
|
||||
src, lineno = findsource(x)
|
||||
assert src is not None
|
||||
assert "test_findsource_simple" in str(src)
|
||||
assert src[lineno] == " def x():"
|
||||
|
||||
|
||||
def test_findsource():
|
||||
def test_findsource() -> None:
|
||||
from _pytest._code.source import findsource
|
||||
|
||||
co = _pytest._code.compile(
|
||||
@@ -460,25 +473,27 @@ def test_findsource():
|
||||
)
|
||||
|
||||
src, lineno = findsource(co)
|
||||
assert src is not None
|
||||
assert "if 1:" in str(src)
|
||||
|
||||
d = {}
|
||||
d = {} # type: Dict[str, Any]
|
||||
eval(co, d)
|
||||
src, lineno = findsource(d["x"])
|
||||
assert src is not None
|
||||
assert "if 1:" in str(src)
|
||||
assert src[lineno] == " def x():"
|
||||
|
||||
|
||||
def test_getfslineno():
|
||||
def test_getfslineno() -> None:
|
||||
from _pytest._code import getfslineno
|
||||
|
||||
def f(x):
|
||||
def f(x) -> None:
|
||||
pass
|
||||
|
||||
fspath, lineno = getfslineno(f)
|
||||
|
||||
assert fspath.basename == "test_source.py"
|
||||
assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource
|
||||
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
|
||||
|
||||
class A:
|
||||
pass
|
||||
@@ -498,40 +513,40 @@ def test_getfslineno():
|
||||
assert getfslineno(B)[1] == -1
|
||||
|
||||
|
||||
def test_code_of_object_instance_with_call():
|
||||
def test_code_of_object_instance_with_call() -> None:
|
||||
class A:
|
||||
pass
|
||||
|
||||
pytest.raises(TypeError, lambda: _pytest._code.Source(A()))
|
||||
|
||||
class WithCall:
|
||||
def __call__(self):
|
||||
def __call__(self) -> None:
|
||||
pass
|
||||
|
||||
code = _pytest._code.Code(WithCall())
|
||||
assert "pass" in str(code.source())
|
||||
|
||||
class Hello:
|
||||
def __call__(self):
|
||||
def __call__(self) -> None:
|
||||
pass
|
||||
|
||||
pytest.raises(TypeError, lambda: _pytest._code.Code(Hello))
|
||||
|
||||
|
||||
def getstatement(lineno, source):
|
||||
def getstatement(lineno: int, source) -> Source:
|
||||
from _pytest._code.source import getstatementrange_ast
|
||||
|
||||
source = _pytest._code.Source(source, deindent=False)
|
||||
ast, start, end = getstatementrange_ast(lineno, source)
|
||||
return source[start:end]
|
||||
src = _pytest._code.Source(source, deindent=False)
|
||||
ast, start, end = getstatementrange_ast(lineno, src)
|
||||
return src[start:end]
|
||||
|
||||
|
||||
def test_oneline():
|
||||
def test_oneline() -> None:
|
||||
source = getstatement(0, "raise ValueError")
|
||||
assert str(source) == "raise ValueError"
|
||||
|
||||
|
||||
def test_comment_and_no_newline_at_end():
|
||||
def test_comment_and_no_newline_at_end() -> None:
|
||||
from _pytest._code.source import getstatementrange_ast
|
||||
|
||||
source = Source(
|
||||
@@ -545,12 +560,12 @@ def test_comment_and_no_newline_at_end():
|
||||
assert end == 2
|
||||
|
||||
|
||||
def test_oneline_and_comment():
|
||||
def test_oneline_and_comment() -> None:
|
||||
source = getstatement(0, "raise ValueError\n#hello")
|
||||
assert str(source) == "raise ValueError"
|
||||
|
||||
|
||||
def test_comments():
|
||||
def test_comments() -> None:
|
||||
source = '''def test():
|
||||
"comment 1"
|
||||
x = 1
|
||||
@@ -576,7 +591,7 @@ comment 4
|
||||
assert str(getstatement(line, source)) == '"""\ncomment 4\n"""'
|
||||
|
||||
|
||||
def test_comment_in_statement():
|
||||
def test_comment_in_statement() -> None:
|
||||
source = """test(foo=1,
|
||||
# comment 1
|
||||
bar=2)
|
||||
@@ -588,17 +603,17 @@ def test_comment_in_statement():
|
||||
)
|
||||
|
||||
|
||||
def test_single_line_else():
|
||||
def test_single_line_else() -> None:
|
||||
source = getstatement(1, "if False: 2\nelse: 3")
|
||||
assert str(source) == "else: 3"
|
||||
|
||||
|
||||
def test_single_line_finally():
|
||||
def test_single_line_finally() -> None:
|
||||
source = getstatement(1, "try: 1\nfinally: 3")
|
||||
assert str(source) == "finally: 3"
|
||||
|
||||
|
||||
def test_issue55():
|
||||
def test_issue55() -> None:
|
||||
source = (
|
||||
"def round_trip(dinp):\n assert 1 == dinp\n"
|
||||
'def test_rt():\n round_trip("""\n""")\n'
|
||||
@@ -607,7 +622,7 @@ def test_issue55():
|
||||
assert str(s) == ' round_trip("""\n""")'
|
||||
|
||||
|
||||
def test_multiline():
|
||||
def test_multiline() -> None:
|
||||
source = getstatement(
|
||||
0,
|
||||
"""\
|
||||
@@ -621,7 +636,7 @@ x = 3
|
||||
|
||||
|
||||
class TestTry:
|
||||
def setup_class(self):
|
||||
def setup_class(self) -> None:
|
||||
self.source = """\
|
||||
try:
|
||||
raise ValueError
|
||||
@@ -631,25 +646,25 @@ else:
|
||||
raise KeyError()
|
||||
"""
|
||||
|
||||
def test_body(self):
|
||||
def test_body(self) -> None:
|
||||
source = getstatement(1, self.source)
|
||||
assert str(source) == " raise ValueError"
|
||||
|
||||
def test_except_line(self):
|
||||
def test_except_line(self) -> None:
|
||||
source = getstatement(2, self.source)
|
||||
assert str(source) == "except Something:"
|
||||
|
||||
def test_except_body(self):
|
||||
def test_except_body(self) -> None:
|
||||
source = getstatement(3, self.source)
|
||||
assert str(source) == " raise IndexError(1)"
|
||||
|
||||
def test_else(self):
|
||||
def test_else(self) -> None:
|
||||
source = getstatement(5, self.source)
|
||||
assert str(source) == " raise KeyError()"
|
||||
|
||||
|
||||
class TestTryFinally:
|
||||
def setup_class(self):
|
||||
def setup_class(self) -> None:
|
||||
self.source = """\
|
||||
try:
|
||||
raise ValueError
|
||||
@@ -657,17 +672,17 @@ finally:
|
||||
raise IndexError(1)
|
||||
"""
|
||||
|
||||
def test_body(self):
|
||||
def test_body(self) -> None:
|
||||
source = getstatement(1, self.source)
|
||||
assert str(source) == " raise ValueError"
|
||||
|
||||
def test_finally(self):
|
||||
def test_finally(self) -> None:
|
||||
source = getstatement(3, self.source)
|
||||
assert str(source) == " raise IndexError(1)"
|
||||
|
||||
|
||||
class TestIf:
|
||||
def setup_class(self):
|
||||
def setup_class(self) -> None:
|
||||
self.source = """\
|
||||
if 1:
|
||||
y = 3
|
||||
@@ -677,24 +692,24 @@ else:
|
||||
y = 7
|
||||
"""
|
||||
|
||||
def test_body(self):
|
||||
def test_body(self) -> None:
|
||||
source = getstatement(1, self.source)
|
||||
assert str(source) == " y = 3"
|
||||
|
||||
def test_elif_clause(self):
|
||||
def test_elif_clause(self) -> None:
|
||||
source = getstatement(2, self.source)
|
||||
assert str(source) == "elif False:"
|
||||
|
||||
def test_elif(self):
|
||||
def test_elif(self) -> None:
|
||||
source = getstatement(3, self.source)
|
||||
assert str(source) == " y = 5"
|
||||
|
||||
def test_else(self):
|
||||
def test_else(self) -> None:
|
||||
source = getstatement(5, self.source)
|
||||
assert str(source) == " y = 7"
|
||||
|
||||
|
||||
def test_semicolon():
|
||||
def test_semicolon() -> None:
|
||||
s = """\
|
||||
hello ; pytest.skip()
|
||||
"""
|
||||
@@ -702,7 +717,7 @@ hello ; pytest.skip()
|
||||
assert str(source) == s.strip()
|
||||
|
||||
|
||||
def test_def_online():
|
||||
def test_def_online() -> None:
|
||||
s = """\
|
||||
def func(): raise ValueError(42)
|
||||
|
||||
@@ -713,7 +728,7 @@ def something():
|
||||
assert str(source) == "def func(): raise ValueError(42)"
|
||||
|
||||
|
||||
def XXX_test_expression_multiline():
|
||||
def XXX_test_expression_multiline() -> None:
|
||||
source = """\
|
||||
something
|
||||
'''
|
||||
@@ -722,7 +737,7 @@ something
|
||||
assert str(result) == "'''\n'''"
|
||||
|
||||
|
||||
def test_getstartingblock_multiline():
|
||||
def test_getstartingblock_multiline() -> None:
|
||||
class A:
|
||||
def __init__(self, *args):
|
||||
frame = sys._getframe(1)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import inspect
|
||||
|
||||
import pytest
|
||||
from _pytest import deprecated
|
||||
from _pytest import nodes
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("default")
|
||||
@@ -16,7 +19,7 @@ def test_resultlog_is_deprecated(testdir):
|
||||
result = testdir.runpytest("--result-log=%s" % testdir.tmpdir.join("result.log"))
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*--result-log is deprecated and scheduled for removal in pytest 6.0*",
|
||||
"*--result-log is deprecated, please try the new pytest-reportlog plugin.",
|
||||
"*See https://docs.pytest.org/en/latest/deprecations.html#result-log-result-log for more information*",
|
||||
]
|
||||
)
|
||||
@@ -44,3 +47,46 @@ def test_external_plugins_integrated(testdir, plugin):
|
||||
|
||||
with pytest.warns(pytest.PytestConfigWarning):
|
||||
testdir.parseconfig("-p", plugin)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("junit_family", [None, "legacy", "xunit2"])
|
||||
def test_warn_about_imminent_junit_family_default_change(testdir, junit_family):
|
||||
"""Show a warning if junit_family is not defined and --junitxml is used (#6179)"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_foo():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
if junit_family:
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
junit_family={junit_family}
|
||||
""".format(
|
||||
junit_family=junit_family
|
||||
)
|
||||
)
|
||||
|
||||
result = testdir.runpytest("--junit-xml=foo.xml")
|
||||
warning_msg = (
|
||||
"*PytestDeprecationWarning: The 'junit_family' default value will change*"
|
||||
)
|
||||
if junit_family:
|
||||
result.stdout.no_fnmatch_line(warning_msg)
|
||||
else:
|
||||
result.stdout.fnmatch_lines([warning_msg])
|
||||
|
||||
|
||||
def test_node_direct_ctor_warning():
|
||||
class MockConfig:
|
||||
pass
|
||||
|
||||
ms = MockConfig()
|
||||
with pytest.warns(
|
||||
DeprecationWarning,
|
||||
match="direct construction of .* has been deprecated, please use .*.from_parent",
|
||||
) as w:
|
||||
nodes.Node(name="test", config=ms, session=ms, nodeid="None")
|
||||
assert w[0].lineno == inspect.currentframe().f_lineno - 1
|
||||
assert w[0].filename == __file__
|
||||
|
||||
@@ -281,10 +281,10 @@ class TestFunction:
|
||||
from _pytest.fixtures import FixtureManager
|
||||
|
||||
config = testdir.parseconfigure()
|
||||
session = testdir.Session(config)
|
||||
session = testdir.Session.from_config(config)
|
||||
session._fixturemanager = FixtureManager(session)
|
||||
|
||||
return pytest.Function(config=config, parent=session, **kwargs)
|
||||
return pytest.Function.from_parent(config=config, parent=session, **kwargs)
|
||||
|
||||
def test_function_equality(self, testdir, tmpdir):
|
||||
def func1():
|
||||
@@ -1024,7 +1024,7 @@ class TestReportInfo:
|
||||
return "ABCDE", 42, "custom"
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "test_func":
|
||||
return MyFunction(name, parent=collector)
|
||||
return MyFunction.from_parent(name=name, parent=collector)
|
||||
"""
|
||||
)
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
@@ -1210,6 +1210,28 @@ def test_syntax_error_with_non_ascii_chars(testdir):
|
||||
result.stdout.fnmatch_lines(["*ERROR collecting*", "*SyntaxError*", "*1 error in*"])
|
||||
|
||||
|
||||
def test_collecterror_with_fulltrace(testdir):
|
||||
testdir.makepyfile("assert 0")
|
||||
result = testdir.runpytest("--fulltrace")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"collected 0 items / 1 error",
|
||||
"",
|
||||
"*= ERRORS =*",
|
||||
"*_ ERROR collecting test_collecterror_with_fulltrace.py _*",
|
||||
"",
|
||||
"*/_pytest/python.py:*: ",
|
||||
"_ _ _ _ _ _ _ _ *",
|
||||
"",
|
||||
"> assert 0",
|
||||
"E assert 0",
|
||||
"",
|
||||
"test_collecterror_with_fulltrace.py:1: AssertionError",
|
||||
"*! Interrupted: 1 error during collection !*",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_skip_duplicates_by_default(testdir):
|
||||
"""Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609)
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ class TestRequestBasic:
|
||||
assert repr(req).find(req.function.__name__) != -1
|
||||
|
||||
def test_request_attributes_method(self, testdir):
|
||||
item, = testdir.getitems(
|
||||
(item,) = testdir.getitems(
|
||||
"""
|
||||
import pytest
|
||||
class TestB(object):
|
||||
@@ -531,7 +531,7 @@ class TestRequestBasic:
|
||||
pass
|
||||
"""
|
||||
)
|
||||
item1, = testdir.genitems([modcol])
|
||||
(item1,) = testdir.genitems([modcol])
|
||||
assert item1.name == "test_method"
|
||||
arg2fixturedefs = fixtures.FixtureRequest(item1)._arg2fixturedefs
|
||||
assert len(arg2fixturedefs) == 1
|
||||
@@ -781,7 +781,7 @@ class TestRequestBasic:
|
||||
|
||||
def test_request_getmodulepath(self, testdir):
|
||||
modcol = testdir.getmodulecol("def test_somefunc(): pass")
|
||||
item, = testdir.genitems([modcol])
|
||||
(item,) = testdir.genitems([modcol])
|
||||
req = fixtures.FixtureRequest(item)
|
||||
assert req.fspath == modcol.fspath
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class TestOEJSKITSpecials:
|
||||
import pytest
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "MyClass":
|
||||
return MyCollector(name, parent=collector)
|
||||
return MyCollector.from_parent(collector, name=name)
|
||||
class MyCollector(pytest.Collector):
|
||||
def reportinfo(self):
|
||||
return self.fspath, 3, "xyz"
|
||||
@@ -40,7 +40,7 @@ class TestOEJSKITSpecials:
|
||||
import pytest
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
if name == "MyClass":
|
||||
return MyCollector(name, parent=collector)
|
||||
return MyCollector.from_parent(collector, name=name)
|
||||
class MyCollector(pytest.Collector):
|
||||
def reportinfo(self):
|
||||
return self.fspath, 3, "xyz"
|
||||
|
||||
+137
-44
@@ -9,10 +9,12 @@ from hypothesis import strategies
|
||||
import pytest
|
||||
from _pytest import fixtures
|
||||
from _pytest import python
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.python import _idval
|
||||
|
||||
|
||||
class TestMetafunc:
|
||||
def Metafunc(self, func, config=None):
|
||||
def Metafunc(self, func, config=None) -> python.Metafunc:
|
||||
# the unit tests of this class check if things work correctly
|
||||
# on the funcarg level, so we don't need a full blown
|
||||
# initialization
|
||||
@@ -23,12 +25,12 @@ class TestMetafunc:
|
||||
self.names_closure = names
|
||||
|
||||
@attr.s
|
||||
class DefinitionMock:
|
||||
class DefinitionMock(python.FunctionDefinition):
|
||||
obj = attr.ib()
|
||||
|
||||
names = fixtures.getfuncargnames(func)
|
||||
fixtureinfo = FixtureInfo(names)
|
||||
definition = DefinitionMock(func)
|
||||
definition = DefinitionMock._create(func)
|
||||
return python.Metafunc(definition, fixtureinfo, config)
|
||||
|
||||
def test_no_funcargs(self, testdir):
|
||||
@@ -61,6 +63,39 @@ class TestMetafunc:
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
|
||||
pytest.raises(ValueError, lambda: metafunc.parametrize("y", [5, 6]))
|
||||
|
||||
with pytest.raises(
|
||||
TypeError, match="^ids must be a callable, sequence or generator$"
|
||||
):
|
||||
metafunc.parametrize("y", [5, 6], ids=42)
|
||||
|
||||
def test_parametrize_error_iterator(self):
|
||||
def func(x):
|
||||
raise NotImplementedError()
|
||||
|
||||
class Exc(Exception):
|
||||
def __repr__(self):
|
||||
return "Exc(from_gen)"
|
||||
|
||||
def gen():
|
||||
yield 0
|
||||
yield None
|
||||
yield Exc()
|
||||
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1, 2], ids=gen())
|
||||
assert [(x.funcargs, x.id) for x in metafunc._calls] == [
|
||||
({"x": 1}, "0"),
|
||||
({"x": 2}, "2"),
|
||||
]
|
||||
with pytest.raises(
|
||||
fail.Exception,
|
||||
match=(
|
||||
r"In func: ids must be list of string/float/int/bool, found:"
|
||||
r" Exc\(from_gen\) \(type: <class .*Exc'>\) at index 2"
|
||||
),
|
||||
):
|
||||
metafunc.parametrize("x", [1, 2, 3], ids=gen())
|
||||
|
||||
def test_parametrize_bad_scope(self, testdir):
|
||||
def func(x):
|
||||
pass
|
||||
@@ -72,6 +107,19 @@ class TestMetafunc:
|
||||
):
|
||||
metafunc.parametrize("x", [1], scope="doggy")
|
||||
|
||||
def test_parametrize_request_name(self, testdir):
|
||||
"""Show proper error when 'request' is used as a parameter name in parametrize (#6183)"""
|
||||
|
||||
def func(request):
|
||||
raise NotImplementedError()
|
||||
|
||||
metafunc = self.Metafunc(func)
|
||||
with pytest.raises(
|
||||
pytest.fail.Exception,
|
||||
match=r"'request' is a reserved name and cannot be used in @pytest.mark.parametrize",
|
||||
):
|
||||
metafunc.parametrize("request", [1])
|
||||
|
||||
def test_find_parametrized_scope(self):
|
||||
"""unittest for _find_parametrized_scope (#3941)"""
|
||||
from _pytest.python import _find_parametrized_scope
|
||||
@@ -154,6 +202,26 @@ class TestMetafunc:
|
||||
("x", "y"), [("abc", "def"), ("ghi", "jkl")], ids=["one"]
|
||||
)
|
||||
|
||||
def test_parametrize_ids_iterator_without_mark(self):
|
||||
import itertools
|
||||
|
||||
def func(x, y):
|
||||
pass
|
||||
|
||||
it = itertools.count()
|
||||
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1, 2], ids=it)
|
||||
metafunc.parametrize("y", [3, 4], ids=it)
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == ["0-2", "0-3", "1-2", "1-3"]
|
||||
|
||||
metafunc = self.Metafunc(func)
|
||||
metafunc.parametrize("x", [1, 2], ids=it)
|
||||
metafunc.parametrize("y", [3, 4], ids=it)
|
||||
ids = [x.id for x in metafunc._calls]
|
||||
assert ids == ["4-6", "4-7", "5-6", "5-7"]
|
||||
|
||||
def test_parametrize_empty_list(self):
|
||||
"""#510"""
|
||||
|
||||
@@ -196,8 +264,6 @@ class TestMetafunc:
|
||||
deadline=400.0
|
||||
) # very close to std deadline and CI boxes are not reliable in CPU power
|
||||
def test_idval_hypothesis(self, value):
|
||||
from _pytest.python import _idval
|
||||
|
||||
escaped = _idval(value, "a", 6, None, item=None, config=None)
|
||||
assert isinstance(escaped, str)
|
||||
escaped.encode("ascii")
|
||||
@@ -208,8 +274,6 @@ class TestMetafunc:
|
||||
escapes if they're not.
|
||||
|
||||
"""
|
||||
from _pytest.python import _idval
|
||||
|
||||
values = [
|
||||
("", ""),
|
||||
("ascii", "ascii"),
|
||||
@@ -229,7 +293,6 @@ class TestMetafunc:
|
||||
disable_test_id_escaping_and_forfeit_all_rights_to_community_support
|
||||
option. (#5294)
|
||||
"""
|
||||
from _pytest.python import _idval
|
||||
|
||||
class MockConfig:
|
||||
def __init__(self, config):
|
||||
@@ -261,8 +324,6 @@ class TestMetafunc:
|
||||
"binary escape", where any byte < 127 is escaped into its hex form.
|
||||
- python3: bytes objects are always escaped using "binary escape".
|
||||
"""
|
||||
from _pytest.python import _idval
|
||||
|
||||
values = [
|
||||
(b"", ""),
|
||||
(b"\xc3\xb4\xff\xe4", "\\xc3\\xb4\\xff\\xe4"),
|
||||
@@ -276,7 +337,6 @@ class TestMetafunc:
|
||||
"""unittest for the expected behavior to obtain ids for parametrized
|
||||
values that are classes or functions: their __name__.
|
||||
"""
|
||||
from _pytest.python import _idval
|
||||
|
||||
class TestClass:
|
||||
pass
|
||||
@@ -521,9 +581,22 @@ class TestMetafunc:
|
||||
@pytest.mark.parametrize("arg", ({1: 2}, {3, 4}), ids=ids)
|
||||
def test(arg):
|
||||
assert arg
|
||||
|
||||
@pytest.mark.parametrize("arg", (1, 2.0, True), ids=ids)
|
||||
def test_int(arg):
|
||||
assert arg
|
||||
"""
|
||||
)
|
||||
assert testdir.runpytest().ret == 0
|
||||
result = testdir.runpytest("-vv", "-s")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_parametrize_ids_returns_non_string.py::test[arg0] PASSED",
|
||||
"test_parametrize_ids_returns_non_string.py::test[arg1] PASSED",
|
||||
"test_parametrize_ids_returns_non_string.py::test_int[1] PASSED",
|
||||
"test_parametrize_ids_returns_non_string.py::test_int[2.0] PASSED",
|
||||
"test_parametrize_ids_returns_non_string.py::test_int[True] PASSED",
|
||||
]
|
||||
)
|
||||
|
||||
def test_idmaker_with_ids(self):
|
||||
from _pytest.python import idmaker
|
||||
@@ -1173,12 +1246,12 @@ class TestMetafuncFunctional:
|
||||
result.stdout.fnmatch_lines(["* 1 skipped *"])
|
||||
|
||||
def test_parametrized_ids_invalid_type(self, testdir):
|
||||
"""Tests parametrized with ids as non-strings (#1857)."""
|
||||
"""Test error with non-strings/non-ints, without generator (#1857)."""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.parametrize("x, expected", [(10, 20), (40, 80)], ids=(None, 2))
|
||||
@pytest.mark.parametrize("x, expected", [(1, 2), (3, 4), (5, 6)], ids=(None, 2, type))
|
||||
def test_ids_numbers(x,expected):
|
||||
assert x * 2 == expected
|
||||
"""
|
||||
@@ -1186,7 +1259,8 @@ class TestMetafuncFunctional:
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*In test_ids_numbers: ids must be list of strings, found: 2 (type: *'int'>)*"
|
||||
"In test_ids_numbers: ids must be list of string/float/int/bool,"
|
||||
" found: <class 'type'> (type: <class 'type'>) at index 2"
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1310,25 +1384,29 @@ class TestMetafuncFunctional:
|
||||
reprec = testdir.runpytest()
|
||||
reprec.assert_outcomes(passed=4)
|
||||
|
||||
@pytest.mark.parametrize("attr", ["parametrise", "parameterize", "parameterise"])
|
||||
def test_parametrize_misspelling(self, testdir, attr):
|
||||
def test_parametrize_misspelling(self, testdir):
|
||||
"""#463"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.{}("x", range(2))
|
||||
@pytest.mark.parametrise("x", range(2))
|
||||
def test_foo(x):
|
||||
pass
|
||||
""".format(
|
||||
attr
|
||||
)
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_foo has '{}' mark, spelling should be 'parametrize'".format(attr),
|
||||
"*1 error in*",
|
||||
"collected 0 items / 1 error",
|
||||
"",
|
||||
"*= ERRORS =*",
|
||||
"*_ ERROR collecting test_parametrize_misspelling.py _*",
|
||||
"test_parametrize_misspelling.py:3: in <module>",
|
||||
' @pytest.mark.parametrise("x", range(2))',
|
||||
"E Failed: Unknown 'parametrise' mark, did you mean 'parametrize'?",
|
||||
"*! Interrupted: 1 error during collection !*",
|
||||
"*= 1 error in *",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1538,27 +1616,6 @@ class TestMarkersWithParametrization:
|
||||
assert len(skipped) == 0
|
||||
assert len(fail) == 0
|
||||
|
||||
@pytest.mark.xfail(reason="is this important to support??")
|
||||
def test_nested_marks(self, testdir):
|
||||
s = """
|
||||
import pytest
|
||||
mastermark = pytest.mark.foo(pytest.mark.bar)
|
||||
|
||||
@pytest.mark.parametrize(("n", "expected"), [
|
||||
(1, 2),
|
||||
mastermark((1, 3)),
|
||||
(2, 3),
|
||||
])
|
||||
def test_increment(n, expected):
|
||||
assert n + 1 == expected
|
||||
"""
|
||||
items = testdir.getitems(s)
|
||||
assert len(items) == 3
|
||||
for mark in ["foo", "bar"]:
|
||||
assert mark not in items[0].keywords
|
||||
assert mark in items[1].keywords
|
||||
assert mark not in items[2].keywords
|
||||
|
||||
def test_simple_xfail(self, testdir):
|
||||
s = """
|
||||
import pytest
|
||||
@@ -1784,3 +1841,39 @@ class TestMarkersWithParametrization:
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.assert_outcomes(passed=1)
|
||||
|
||||
def test_parametrize_iterator(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import itertools
|
||||
import pytest
|
||||
|
||||
id_parametrize = pytest.mark.parametrize(
|
||||
ids=("param%d" % i for i in itertools.count())
|
||||
)
|
||||
|
||||
@id_parametrize('y', ['a', 'b'])
|
||||
def test1(y):
|
||||
pass
|
||||
|
||||
@id_parametrize('y', ['a', 'b'])
|
||||
def test2(y):
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize("a, b", [(1, 2), (3, 4)], ids=itertools.count())
|
||||
def test_converted_to_str(a, b):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-vv", "-s")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_parametrize_iterator.py::test1[param0] PASSED",
|
||||
"test_parametrize_iterator.py::test1[param1] PASSED",
|
||||
"test_parametrize_iterator.py::test2[param0] PASSED",
|
||||
"test_parametrize_iterator.py::test2[param1] PASSED",
|
||||
"test_parametrize_iterator.py::test_converted_to_str[0] PASSED",
|
||||
"test_parametrize_iterator.py::test_converted_to_str[1] PASSED",
|
||||
"*= 6 passed in *",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -205,7 +205,7 @@ class TestRaises:
|
||||
with pytest.raises(AssertionError) as excinfo:
|
||||
with pytest.raises(AssertionError, match="'foo"):
|
||||
raise AssertionError("'bar")
|
||||
msg, = excinfo.value.args
|
||||
(msg,) = excinfo.value.args
|
||||
assert msg == 'Pattern "\'foo" not found in "\'bar"'
|
||||
|
||||
def test_raises_match_wrong_type(self):
|
||||
|
||||
@@ -70,7 +70,14 @@ class TestImportHookInstallation:
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess()
|
||||
result.stdout.fnmatch_lines(["*assert 1 == 0*"])
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"E * AssertionError: ([[][]], [[][]], [[]<TestReport *>[]])*",
|
||||
"E * assert"
|
||||
" {'failed': 1, 'passed': 0, 'skipped': 0} =="
|
||||
" {'failed': 0, 'passed': 1, 'skipped': 0}",
|
||||
]
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize("mode", ["plain", "rewrite"])
|
||||
def test_pytest_plugins_rewrite(self, testdir, mode):
|
||||
@@ -462,6 +469,29 @@ class TestAssert_reprcompare:
|
||||
" ]",
|
||||
]
|
||||
|
||||
def test_list_dont_wrap_strings(self):
|
||||
long_a = "a" * 10
|
||||
l1 = ["a"] + [long_a for _ in range(0, 7)]
|
||||
l2 = ["should not get wrapped"]
|
||||
diff = callequal(l1, l2, verbose=True)
|
||||
assert diff == [
|
||||
"['a', 'aaaaaa...aaaaaaa', ...] == ['should not get wrapped']",
|
||||
"At index 0 diff: 'a' != 'should not get wrapped'",
|
||||
"Left contains 7 more items, first extra item: 'aaaaaaaaaa'",
|
||||
"Full diff:",
|
||||
" [",
|
||||
"+ 'should not get wrapped',",
|
||||
"- 'a',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
"- 'aaaaaaaaaa',",
|
||||
" ]",
|
||||
]
|
||||
|
||||
def test_dict_wrap(self):
|
||||
d1 = {"common": 1, "env": {"env1": 1}}
|
||||
d2 = {"common": 1, "env": {"env1": 1, "env2": 2}}
|
||||
@@ -479,22 +509,20 @@ class TestAssert_reprcompare:
|
||||
]
|
||||
|
||||
long_a = "a" * 80
|
||||
sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped"}}
|
||||
sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 2}}
|
||||
d1 = {"env": {"sub": sub}}
|
||||
d2 = {"env": {"sub": sub}, "new": 1}
|
||||
diff = callequal(d1, d2, verbose=True)
|
||||
assert diff == [
|
||||
"{'env': {'sub...s wrapped'}}}} == {'env': {'sub...}}}, 'new': 1}",
|
||||
"{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}",
|
||||
"Omitting 1 identical items, use -vv to show",
|
||||
"Right contains 1 more item:",
|
||||
"{'new': 1}",
|
||||
"Full diff:",
|
||||
" {",
|
||||
" 'env': {'sub': {'long_a': '" + long_a + "',",
|
||||
" 'sub1': {'long_a': 'substring '",
|
||||
" 'that '",
|
||||
" 'gets '",
|
||||
" 'wrapped'}}},",
|
||||
" 'sub1': {'long_a': 'substring that gets wrapped substring '",
|
||||
" 'that gets wrapped '}}},",
|
||||
"+ 'new': 1,",
|
||||
" }",
|
||||
]
|
||||
|
||||
@@ -9,7 +9,6 @@ import sys
|
||||
import textwrap
|
||||
import zipfile
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
|
||||
import py
|
||||
|
||||
@@ -23,6 +22,7 @@ from _pytest.assertion.rewrite import PYC_TAIL
|
||||
from _pytest.assertion.rewrite import PYTEST_TAG
|
||||
from _pytest.assertion.rewrite import rewrite_asserts
|
||||
from _pytest.main import ExitCode
|
||||
from _pytest.pathlib import Path
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
|
||||
+58
-154
@@ -2,7 +2,6 @@ import os
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import py
|
||||
|
||||
@@ -60,18 +59,13 @@ class TestNewAPI:
|
||||
@pytest.mark.filterwarnings(
|
||||
"ignore:could not create cache path:pytest.PytestWarning"
|
||||
)
|
||||
def test_cache_failure_warns(self, testdir):
|
||||
def test_cache_failure_warns(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")
|
||||
cache_dir = str(testdir.tmpdir.ensure_dir(".pytest_cache"))
|
||||
mode = os.stat(cache_dir)[stat.ST_MODE]
|
||||
testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0)
|
||||
try:
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_error():
|
||||
raise Exception
|
||||
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile("def test_error(): raise Exception")
|
||||
result = testdir.runpytest("-rw")
|
||||
assert result.ret == 1
|
||||
# warnings from nodeids, lastfailed, and stepwise
|
||||
@@ -178,12 +172,7 @@ def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
|
||||
"test_cache_reportheader_external_abspath_abs"
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_hello():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile("def test_hello(): pass")
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
@@ -192,7 +181,6 @@ def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
|
||||
abscache=external_cache
|
||||
)
|
||||
)
|
||||
|
||||
result = testdir.runpytest("-v")
|
||||
result.stdout.fnmatch_lines(
|
||||
["cachedir: {abscache}".format(abscache=external_cache)]
|
||||
@@ -253,36 +241,26 @@ def test_cache_show(testdir):
|
||||
|
||||
class TestLastFailed:
|
||||
def test_lastfailed_usecase(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
monkeypatch.setattr("sys.dont_write_bytecode", True)
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
def test_1():
|
||||
assert 0
|
||||
def test_2():
|
||||
assert 0
|
||||
def test_3():
|
||||
assert 1
|
||||
"""
|
||||
def test_1(): assert 0
|
||||
def test_2(): assert 0
|
||||
def test_3(): assert 1
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result = testdir.runpytest(str(p))
|
||||
result.stdout.fnmatch_lines(["*2 failed*"])
|
||||
p.write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_1():
|
||||
assert 1
|
||||
|
||||
def test_2():
|
||||
assert 1
|
||||
|
||||
def test_3():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
def test_1(): assert 1
|
||||
def test_2(): assert 1
|
||||
def test_3(): assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--lf")
|
||||
result = testdir.runpytest(str(p), "--lf")
|
||||
result.stdout.fnmatch_lines(["*2 passed*1 desel*"])
|
||||
result = testdir.runpytest("--lf")
|
||||
result = testdir.runpytest(str(p), "--lf")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"collected 3 items",
|
||||
@@ -290,7 +268,7 @@ class TestLastFailed:
|
||||
"*1 failed*2 passed*",
|
||||
]
|
||||
)
|
||||
result = testdir.runpytest("--lf", "--cache-clear")
|
||||
result = testdir.runpytest(str(p), "--lf", "--cache-clear")
|
||||
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
||||
|
||||
# Run this again to make sure clear-cache is robust
|
||||
@@ -300,21 +278,9 @@ class TestLastFailed:
|
||||
result.stdout.fnmatch_lines(["*1 failed*2 passed*"])
|
||||
|
||||
def test_failedfirst_order(self, testdir):
|
||||
testdir.tmpdir.join("test_a.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.tmpdir.join("test_b.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile(
|
||||
test_a="def test_always_passes(): pass",
|
||||
test_b="def test_always_fails(): assert 0",
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
# Test order will be collection order; alphabetical
|
||||
@@ -325,16 +291,8 @@ class TestLastFailed:
|
||||
|
||||
def test_lastfailed_failedfirst_order(self, testdir):
|
||||
testdir.makepyfile(
|
||||
**{
|
||||
"test_a.py": """\
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
""",
|
||||
"test_b.py": """\
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
""",
|
||||
}
|
||||
test_a="def test_always_passes(): assert 1",
|
||||
test_b="def test_always_fails(): assert 0",
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
# Test order will be collection order; alphabetical
|
||||
@@ -345,18 +303,13 @@ class TestLastFailed:
|
||||
result.stdout.no_fnmatch_line("*test_a.py*")
|
||||
|
||||
def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
monkeypatch.setattr("sys.dont_write_bytecode", True)
|
||||
testdir.makepyfile(
|
||||
test_a="""\
|
||||
def test_a1():
|
||||
assert 0
|
||||
def test_a2():
|
||||
assert 1
|
||||
""",
|
||||
test_b="""\
|
||||
def test_b1():
|
||||
assert 0
|
||||
test_a="""
|
||||
def test_a1(): assert 0
|
||||
def test_a2(): assert 1
|
||||
""",
|
||||
test_b="def test_b1(): assert 0",
|
||||
)
|
||||
p = testdir.tmpdir.join("test_a.py")
|
||||
p2 = testdir.tmpdir.join("test_b.py")
|
||||
@@ -365,36 +318,19 @@ class TestLastFailed:
|
||||
result.stdout.fnmatch_lines(["*2 failed*"])
|
||||
result = testdir.runpytest("--lf", p2)
|
||||
result.stdout.fnmatch_lines(["*1 failed*"])
|
||||
p2.write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_b1():
|
||||
assert 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
testdir.makepyfile(test_b="def test_b1(): assert 1")
|
||||
result = testdir.runpytest("--lf", p2)
|
||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||
result = testdir.runpytest("--lf", p)
|
||||
result.stdout.fnmatch_lines(["*1 failed*1 desel*"])
|
||||
|
||||
def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
monkeypatch.setattr("sys.dont_write_bytecode", True)
|
||||
testdir.makepyfile(
|
||||
"""\
|
||||
def test_1():
|
||||
assert 0
|
||||
"""
|
||||
"def test_1(): assert 0", test_something="def test_2(): assert 0"
|
||||
)
|
||||
p2 = testdir.tmpdir.join("test_something.py")
|
||||
p2.write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_2():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*2 failed*"])
|
||||
result = testdir.runpytest("--lf", p2)
|
||||
@@ -436,18 +372,14 @@ class TestLastFailed:
|
||||
def test_terminal_report_lastfailed(self, testdir):
|
||||
test_a = testdir.makepyfile(
|
||||
test_a="""
|
||||
def test_a1():
|
||||
pass
|
||||
def test_a2():
|
||||
pass
|
||||
def test_a1(): pass
|
||||
def test_a2(): pass
|
||||
"""
|
||||
)
|
||||
test_b = testdir.makepyfile(
|
||||
test_b="""
|
||||
def test_b1():
|
||||
assert 0
|
||||
def test_b2():
|
||||
assert 0
|
||||
def test_b1(): assert 0
|
||||
def test_b2(): assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -492,10 +424,8 @@ class TestLastFailed:
|
||||
def test_terminal_report_failedfirst(self, testdir):
|
||||
testdir.makepyfile(
|
||||
test_a="""
|
||||
def test_a1():
|
||||
assert 0
|
||||
def test_a2():
|
||||
pass
|
||||
def test_a1(): assert 0
|
||||
def test_a2(): pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -542,7 +472,6 @@ class TestLastFailed:
|
||||
assert list(lastfailed) == ["test_maybe.py::test_hello"]
|
||||
|
||||
def test_lastfailed_failure_subset(self, testdir, monkeypatch):
|
||||
|
||||
testdir.makepyfile(
|
||||
test_maybe="""
|
||||
import os
|
||||
@@ -560,6 +489,7 @@ class TestLastFailed:
|
||||
env = os.environ
|
||||
if '1' == env['FAILIMPORT']:
|
||||
raise ImportError('fail')
|
||||
|
||||
def test_hello():
|
||||
assert '0' == env['FAILTEST']
|
||||
|
||||
@@ -613,8 +543,7 @@ class TestLastFailed:
|
||||
"""
|
||||
import pytest
|
||||
@pytest.mark.xfail
|
||||
def test():
|
||||
assert 0
|
||||
def test(): assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -626,8 +555,7 @@ class TestLastFailed:
|
||||
"""
|
||||
import pytest
|
||||
@pytest.mark.xfail(strict=True)
|
||||
def test():
|
||||
pass
|
||||
def test(): pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -641,8 +569,7 @@ class TestLastFailed:
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
def test():
|
||||
assert 0
|
||||
def test(): assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -655,8 +582,7 @@ class TestLastFailed:
|
||||
"""
|
||||
import pytest
|
||||
@pytest.{mark}
|
||||
def test():
|
||||
assert 0
|
||||
def test(): assert 0
|
||||
""".format(
|
||||
mark=mark
|
||||
)
|
||||
@@ -694,18 +620,14 @@ class TestLastFailed:
|
||||
# 1. initial run
|
||||
test_bar = testdir.makepyfile(
|
||||
test_bar="""
|
||||
def test_bar_1():
|
||||
pass
|
||||
def test_bar_2():
|
||||
assert 0
|
||||
def test_bar_1(): pass
|
||||
def test_bar_2(): assert 0
|
||||
"""
|
||||
)
|
||||
test_foo = testdir.makepyfile(
|
||||
test_foo="""
|
||||
def test_foo_3():
|
||||
pass
|
||||
def test_foo_4():
|
||||
assert 0
|
||||
def test_foo_3(): pass
|
||||
def test_foo_4(): assert 0
|
||||
"""
|
||||
)
|
||||
testdir.runpytest()
|
||||
@@ -717,10 +639,8 @@ class TestLastFailed:
|
||||
# 2. fix test_bar_2, run only test_bar.py
|
||||
testdir.makepyfile(
|
||||
test_bar="""
|
||||
def test_bar_1():
|
||||
pass
|
||||
def test_bar_2():
|
||||
pass
|
||||
def test_bar_1(): pass
|
||||
def test_bar_2(): pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(test_bar)
|
||||
@@ -735,10 +655,8 @@ class TestLastFailed:
|
||||
# 3. fix test_foo_4, run only test_foo.py
|
||||
test_foo = testdir.makepyfile(
|
||||
test_foo="""
|
||||
def test_foo_3():
|
||||
pass
|
||||
def test_foo_4():
|
||||
pass
|
||||
def test_foo_3(): pass
|
||||
def test_foo_4(): pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(test_foo, "--last-failed")
|
||||
@@ -752,10 +670,8 @@ class TestLastFailed:
|
||||
def test_lastfailed_no_failures_behavior_all_passed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_1():
|
||||
assert True
|
||||
def test_2():
|
||||
assert True
|
||||
def test_1(): pass
|
||||
def test_2(): pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -777,10 +693,8 @@ class TestLastFailed:
|
||||
def test_lastfailed_no_failures_behavior_empty_cache(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_1():
|
||||
assert True
|
||||
def test_2():
|
||||
assert False
|
||||
def test_1(): pass
|
||||
def test_2(): assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--lf", "--cache-clear")
|
||||
@@ -1022,22 +936,12 @@ class TestReadme:
|
||||
return readme.is_file()
|
||||
|
||||
def test_readme_passed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile("def test_always_passes(): pass")
|
||||
testdir.runpytest()
|
||||
assert self.check_readme(testdir) is True
|
||||
|
||||
def test_readme_failed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile("def test_always_fails(): assert 0")
|
||||
testdir.runpytest()
|
||||
assert self.check_readme(testdir) is True
|
||||
|
||||
|
||||
@@ -92,8 +92,6 @@ class TestCaptureManager:
|
||||
|
||||
@pytest.mark.parametrize("method", ["fd", "sys"])
|
||||
def test_capturing_unicode(testdir, method):
|
||||
if hasattr(sys, "pypy_version_info") and sys.pypy_version_info < (2, 2):
|
||||
pytest.xfail("does not work on pypy < 2.2")
|
||||
obj = "'b\u00f6y'"
|
||||
testdir.makepyfile(
|
||||
"""\
|
||||
|
||||
+46
-13
@@ -75,7 +75,7 @@ class TestCollector:
|
||||
pass
|
||||
def pytest_collect_file(path, parent):
|
||||
if path.ext == ".xxx":
|
||||
return CustomFile(path, parent=parent)
|
||||
return CustomFile.from_parent(fspath=path, parent=parent)
|
||||
"""
|
||||
)
|
||||
node = testdir.getpathnode(hello)
|
||||
@@ -446,7 +446,7 @@ class TestSession:
|
||||
p.move(target)
|
||||
subdir.chdir()
|
||||
config = testdir.parseconfig(p.basename)
|
||||
rcol = Session(config=config)
|
||||
rcol = Session.from_config(config)
|
||||
assert rcol.fspath == subdir
|
||||
parts = rcol._parsearg(p.basename)
|
||||
|
||||
@@ -463,7 +463,7 @@ class TestSession:
|
||||
# XXX migrate to collectonly? (see below)
|
||||
config = testdir.parseconfig(id)
|
||||
topdir = testdir.tmpdir
|
||||
rcol = Session(config)
|
||||
rcol = Session.from_config(config)
|
||||
assert topdir == rcol.fspath
|
||||
# rootid = rcol.nodeid
|
||||
# root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
|
||||
@@ -486,7 +486,7 @@ class TestSession:
|
||||
p = testdir.makepyfile("def test_func(): pass")
|
||||
id = "::".join([p.basename, "test_func"])
|
||||
items, hookrec = testdir.inline_genitems(id)
|
||||
item, = items
|
||||
(item,) = items
|
||||
assert item.name == "test_func"
|
||||
newid = item.nodeid
|
||||
assert newid == id
|
||||
@@ -605,9 +605,9 @@ class TestSession:
|
||||
testdir.makepyfile("def test_func(): pass")
|
||||
items, hookrec = testdir.inline_genitems()
|
||||
assert len(items) == 1
|
||||
item, = items
|
||||
(item,) = items
|
||||
items2, hookrec = testdir.inline_genitems(item.nodeid)
|
||||
item2, = items2
|
||||
(item2,) = items2
|
||||
assert item2.name == item.name
|
||||
assert item2.fspath == item.fspath
|
||||
|
||||
@@ -622,7 +622,7 @@ class TestSession:
|
||||
arg = p.basename + "::TestClass::test_method"
|
||||
items, hookrec = testdir.inline_genitems(arg)
|
||||
assert len(items) == 1
|
||||
item, = items
|
||||
(item,) = items
|
||||
assert item.nodeid.endswith("TestClass::test_method")
|
||||
# ensure we are reporting the collection of the single test item (#2464)
|
||||
assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"]
|
||||
@@ -685,6 +685,8 @@ class Test_genitems:
|
||||
def test_example_items1(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def testone():
|
||||
pass
|
||||
|
||||
@@ -693,19 +695,24 @@ class Test_genitems:
|
||||
pass
|
||||
|
||||
class TestY(TestX):
|
||||
pass
|
||||
@pytest.mark.parametrize("arg0", [".["])
|
||||
def testmethod_two(self, arg0):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
items, reprec = testdir.inline_genitems(p)
|
||||
assert len(items) == 3
|
||||
assert len(items) == 4
|
||||
assert items[0].name == "testone"
|
||||
assert items[1].name == "testmethod_one"
|
||||
assert items[2].name == "testmethod_one"
|
||||
assert items[3].name == "testmethod_two[.[]"
|
||||
|
||||
# let's also test getmodpath here
|
||||
assert items[0].getmodpath() == "testone"
|
||||
assert items[1].getmodpath() == "TestX.testmethod_one"
|
||||
assert items[2].getmodpath() == "TestY.testmethod_one"
|
||||
# PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189)
|
||||
assert items[3].getmodpath() == "TestY.testmethod_two[.[]"
|
||||
|
||||
s = items[0].getmodpath(stopatmodule=False)
|
||||
assert s.endswith("test_example_items1.testone")
|
||||
@@ -852,11 +859,15 @@ def test_exit_on_collection_with_maxfail_smaller_than_n_errors(testdir):
|
||||
|
||||
res = testdir.runpytest("--maxfail=1")
|
||||
assert res.ret == 1
|
||||
|
||||
res.stdout.fnmatch_lines(
|
||||
["*ERROR collecting test_02_import_error.py*", "*No module named *asdfa*"]
|
||||
[
|
||||
"collected 1 item / 1 error",
|
||||
"*ERROR collecting test_02_import_error.py*",
|
||||
"*No module named *asdfa*",
|
||||
"*! stopping after 1 failures !*",
|
||||
"*= 1 error in *",
|
||||
]
|
||||
)
|
||||
|
||||
res.stdout.no_fnmatch_line("*test_03*")
|
||||
|
||||
|
||||
@@ -869,7 +880,6 @@ def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir):
|
||||
|
||||
res = testdir.runpytest("--maxfail=4")
|
||||
assert res.ret == 2
|
||||
|
||||
res.stdout.fnmatch_lines(
|
||||
[
|
||||
"collected 2 items / 2 errors",
|
||||
@@ -877,6 +887,8 @@ def test_exit_on_collection_with_maxfail_bigger_than_n_errors(testdir):
|
||||
"*No module named *asdfa*",
|
||||
"*ERROR collecting test_03_import_error.py*",
|
||||
"*No module named *asdfa*",
|
||||
"*! Interrupted: 2 errors during collection !*",
|
||||
"*= 2 errors in *",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1257,3 +1269,24 @@ def test_collector_respects_tbstyle(testdir):
|
||||
"*= 1 error in *",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_does_not_eagerly_collect_packages(testdir):
|
||||
testdir.makepyfile("def test(): pass")
|
||||
pydir = testdir.mkpydir("foopkg")
|
||||
pydir.join("__init__.py").write("assert False")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret == ExitCode.OK
|
||||
|
||||
|
||||
def test_does_not_put_src_on_path(testdir):
|
||||
# `src` is not on sys.path so it should not be importable
|
||||
testdir.tmpdir.join("src/nope/__init__.py").ensure()
|
||||
testdir.makepyfile(
|
||||
"import pytest\n"
|
||||
"def test():\n"
|
||||
" with pytest.raises(ImportError):\n"
|
||||
" import nope\n"
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
assert result.ret == ExitCode.OK
|
||||
|
||||
@@ -4,6 +4,7 @@ from functools import wraps
|
||||
|
||||
import pytest
|
||||
from _pytest.compat import _PytestWrapper
|
||||
from _pytest.compat import cached_property
|
||||
from _pytest.compat import get_real_func
|
||||
from _pytest.compat import is_generator
|
||||
from _pytest.compat import safe_getattr
|
||||
@@ -178,3 +179,23 @@ def test_safe_isclass():
|
||||
assert False, "Should be ignored"
|
||||
|
||||
assert safe_isclass(CrappyClass()) is False
|
||||
|
||||
|
||||
def test_cached_property() -> None:
|
||||
ncalls = 0
|
||||
|
||||
class Class:
|
||||
@cached_property
|
||||
def prop(self) -> int:
|
||||
nonlocal ncalls
|
||||
ncalls += 1
|
||||
return ncalls
|
||||
|
||||
c1 = Class()
|
||||
assert ncalls == 0
|
||||
assert c1.prop == 1
|
||||
assert c1.prop == 1
|
||||
c2 = Class()
|
||||
assert ncalls == 1
|
||||
assert c2.prop == 2
|
||||
assert c1.prop == 1
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
@@ -13,6 +12,7 @@ from _pytest.config.findpaths import determine_setup
|
||||
from _pytest.config.findpaths import get_common_ancestor
|
||||
from _pytest.config.findpaths import getcfg
|
||||
from _pytest.main import ExitCode
|
||||
from _pytest.pathlib import Path
|
||||
|
||||
|
||||
class TestParseIni:
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import os
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
import py
|
||||
|
||||
import pytest
|
||||
from _pytest.config import PytestPluginManager
|
||||
from _pytest.main import ExitCode
|
||||
from _pytest.pathlib import Path
|
||||
|
||||
|
||||
def ConftestWithSetinitial(path):
|
||||
|
||||
@@ -22,7 +22,7 @@ def pdb_env(request):
|
||||
if "testdir" in request.fixturenames:
|
||||
# Disable pdb++ with inner tests.
|
||||
testdir = request.getfixturevalue("testdir")
|
||||
testdir._env_run_update["PDBPP_HIJACK_PDB"] = "0"
|
||||
testdir.monkeypatch.setenv("PDBPP_HIJACK_PDB", "0")
|
||||
|
||||
|
||||
def runpdb_and_get_report(testdir, source):
|
||||
@@ -193,7 +193,7 @@ class TestPDB:
|
||||
)
|
||||
child = testdir.spawn_pytest("-rs --pdb %s" % p1)
|
||||
child.expect("Skipping also with pdb active")
|
||||
child.expect_exact("= \x1b[33m\x1b[1m1 skipped\x1b[0m\x1b[33m in")
|
||||
child.expect_exact("= 1 skipped in")
|
||||
child.sendeof()
|
||||
self.flush(child)
|
||||
|
||||
@@ -221,7 +221,7 @@ class TestPDB:
|
||||
child.sendeof()
|
||||
rest = child.read().decode("utf8")
|
||||
assert "Exit: Quitting debugger" in rest
|
||||
assert "= \x1b[31m\x1b[1m1 failed\x1b[0m\x1b[31m in" in rest
|
||||
assert "= 1 failed in" in rest
|
||||
assert "def test_1" not in rest
|
||||
assert "get rekt" not in rest
|
||||
self.flush(child)
|
||||
@@ -506,7 +506,7 @@ class TestPDB:
|
||||
rest = child.read().decode("utf8")
|
||||
|
||||
assert "! _pytest.outcomes.Exit: Quitting debugger !" in rest
|
||||
assert "= \x1b[33mno tests ran\x1b[0m\x1b[33m in" in rest
|
||||
assert "= no tests ran in" in rest
|
||||
assert "BdbQuit" not in rest
|
||||
assert "UNEXPECTED EXCEPTION" not in rest
|
||||
|
||||
@@ -725,7 +725,7 @@ class TestPDB:
|
||||
assert "> PDB continue (IO-capturing resumed) >" in rest
|
||||
else:
|
||||
assert "> PDB continue >" in rest
|
||||
assert "= \x1b[32m\x1b[1m1 passed\x1b[0m\x1b[32m in" in rest
|
||||
assert "= 1 passed in" in rest
|
||||
|
||||
def test_pdb_used_outside_test(self, testdir):
|
||||
p1 = testdir.makepyfile(
|
||||
@@ -1041,7 +1041,7 @@ class TestTraceOption:
|
||||
child.sendline("q")
|
||||
child.expect_exact("Exit: Quitting debugger")
|
||||
rest = child.read().decode("utf8")
|
||||
assert "= \x1b[32m\x1b[1m2 passed\x1b[0m\x1b[32m in" in rest
|
||||
assert "= 2 passed in" in rest
|
||||
assert "reading from stdin while output" not in rest
|
||||
# Only printed once - not on stderr.
|
||||
assert "Exit: Quitting debugger" not in child.before.decode("utf8")
|
||||
@@ -1086,7 +1086,7 @@ class TestTraceOption:
|
||||
child.sendline("c")
|
||||
child.expect_exact("> PDB continue (IO-capturing resumed) >")
|
||||
rest = child.read().decode("utf8")
|
||||
assert "= \x1b[32m\x1b[1m6 passed\x1b[0m\x1b[32m in" in rest
|
||||
assert "= 6 passed in" in rest
|
||||
assert "reading from stdin while output" not in rest
|
||||
# Only printed once - not on stderr.
|
||||
assert "Exit: Quitting debugger" not in child.before.decode("utf8")
|
||||
@@ -1197,7 +1197,7 @@ def test_pdb_suspends_fixture_capturing(testdir, fixture):
|
||||
|
||||
TestPDB.flush(child)
|
||||
assert child.exitstatus == 0
|
||||
assert "= \x1b[32m\x1b[1m1 passed\x1b[0m\x1b[32m in" in rest
|
||||
assert "= 1 passed in" in rest
|
||||
assert "> PDB continue (IO-capturing resumed for fixture %s) >" % (fixture) in rest
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import platform
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from xml.dom import minidom
|
||||
|
||||
import py
|
||||
@@ -9,6 +8,7 @@ import xmlschema
|
||||
|
||||
import pytest
|
||||
from _pytest.junitxml import LogXML
|
||||
from _pytest.pathlib import Path
|
||||
from _pytest.reports import BaseReport
|
||||
|
||||
|
||||
|
||||
@@ -962,7 +962,11 @@ def test_mark_expressions_no_smear(testdir):
|
||||
|
||||
|
||||
def test_addmarker_order():
|
||||
node = Node("Test", config=mock.Mock(), session=mock.Mock(), nodeid="Test")
|
||||
session = mock.Mock()
|
||||
session.own_markers = []
|
||||
session.parent = None
|
||||
session.nodeid = ""
|
||||
node = Node.from_parent(session, name="Test")
|
||||
node.add_marker("foo")
|
||||
node.add_marker("bar")
|
||||
node.add_marker("baz", append=False)
|
||||
@@ -1011,7 +1015,7 @@ def test_markers_from_parametrize(testdir):
|
||||
def test_pytest_param_id_requires_string():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
pytest.param(id=True)
|
||||
msg, = excinfo.value.args
|
||||
(msg,) = excinfo.value.args
|
||||
assert msg == "Expected id to be a string, got <class 'bool'>: True"
|
||||
|
||||
|
||||
|
||||
+34
-37
@@ -12,22 +12,22 @@ from _pytest.config.exceptions import UsageError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def parser():
|
||||
def parser() -> parseopt.Parser:
|
||||
return parseopt.Parser()
|
||||
|
||||
|
||||
class TestParser:
|
||||
def test_no_help_by_default(self):
|
||||
def test_no_help_by_default(self) -> None:
|
||||
parser = parseopt.Parser(usage="xyz")
|
||||
pytest.raises(UsageError, lambda: parser.parse(["-h"]))
|
||||
|
||||
def test_custom_prog(self, parser):
|
||||
def test_custom_prog(self, parser: parseopt.Parser) -> None:
|
||||
"""Custom prog can be set for `argparse.ArgumentParser`."""
|
||||
assert parser._getparser().prog == os.path.basename(sys.argv[0])
|
||||
parser.prog = "custom-prog"
|
||||
assert parser._getparser().prog == "custom-prog"
|
||||
|
||||
def test_argument(self):
|
||||
def test_argument(self) -> None:
|
||||
with pytest.raises(parseopt.ArgumentError):
|
||||
# need a short or long option
|
||||
argument = parseopt.Argument()
|
||||
@@ -45,7 +45,7 @@ class TestParser:
|
||||
"Argument(_short_opts: ['-t'], _long_opts: ['--test'], dest: 'abc')"
|
||||
)
|
||||
|
||||
def test_argument_type(self):
|
||||
def test_argument_type(self) -> None:
|
||||
argument = parseopt.Argument("-t", dest="abc", type=int)
|
||||
assert argument.type is int
|
||||
argument = parseopt.Argument("-t", dest="abc", type=str)
|
||||
@@ -60,7 +60,7 @@ class TestParser:
|
||||
)
|
||||
assert argument.type is str
|
||||
|
||||
def test_argument_processopt(self):
|
||||
def test_argument_processopt(self) -> None:
|
||||
argument = parseopt.Argument("-t", type=int)
|
||||
argument.default = 42
|
||||
argument.dest = "abc"
|
||||
@@ -68,19 +68,19 @@ class TestParser:
|
||||
assert res["default"] == 42
|
||||
assert res["dest"] == "abc"
|
||||
|
||||
def test_group_add_and_get(self, parser):
|
||||
def test_group_add_and_get(self, parser: parseopt.Parser) -> None:
|
||||
group = parser.getgroup("hello", description="desc")
|
||||
assert group.name == "hello"
|
||||
assert group.description == "desc"
|
||||
|
||||
def test_getgroup_simple(self, parser):
|
||||
def test_getgroup_simple(self, parser: parseopt.Parser) -> None:
|
||||
group = parser.getgroup("hello", description="desc")
|
||||
assert group.name == "hello"
|
||||
assert group.description == "desc"
|
||||
group2 = parser.getgroup("hello")
|
||||
assert group2 is group
|
||||
|
||||
def test_group_ordering(self, parser):
|
||||
def test_group_ordering(self, parser: parseopt.Parser) -> None:
|
||||
parser.getgroup("1")
|
||||
parser.getgroup("2")
|
||||
parser.getgroup("3", after="1")
|
||||
@@ -88,20 +88,20 @@ class TestParser:
|
||||
groups_names = [x.name for x in groups]
|
||||
assert groups_names == list("132")
|
||||
|
||||
def test_group_addoption(self):
|
||||
def test_group_addoption(self) -> None:
|
||||
group = parseopt.OptionGroup("hello")
|
||||
group.addoption("--option1", action="store_true")
|
||||
assert len(group.options) == 1
|
||||
assert isinstance(group.options[0], parseopt.Argument)
|
||||
|
||||
def test_group_addoption_conflict(self):
|
||||
def test_group_addoption_conflict(self) -> None:
|
||||
group = parseopt.OptionGroup("hello again")
|
||||
group.addoption("--option1", "--option-1", action="store_true")
|
||||
with pytest.raises(ValueError) as err:
|
||||
group.addoption("--option1", "--option-one", action="store_true")
|
||||
assert str({"--option1"}) in str(err.value)
|
||||
|
||||
def test_group_shortopt_lowercase(self, parser):
|
||||
def test_group_shortopt_lowercase(self, parser: parseopt.Parser) -> None:
|
||||
group = parser.getgroup("hello")
|
||||
with pytest.raises(ValueError):
|
||||
group.addoption("-x", action="store_true")
|
||||
@@ -109,30 +109,30 @@ class TestParser:
|
||||
group._addoption("-x", action="store_true")
|
||||
assert len(group.options) == 1
|
||||
|
||||
def test_parser_addoption(self, parser):
|
||||
def test_parser_addoption(self, parser: parseopt.Parser) -> None:
|
||||
group = parser.getgroup("custom options")
|
||||
assert len(group.options) == 0
|
||||
group.addoption("--option1", action="store_true")
|
||||
assert len(group.options) == 1
|
||||
|
||||
def test_parse(self, parser):
|
||||
def test_parse(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--hello", dest="hello", action="store")
|
||||
args = parser.parse(["--hello", "world"])
|
||||
assert args.hello == "world"
|
||||
assert not getattr(args, parseopt.FILE_OR_DIR)
|
||||
|
||||
def test_parse2(self, parser):
|
||||
def test_parse2(self, parser: parseopt.Parser) -> None:
|
||||
args = parser.parse([py.path.local()])
|
||||
assert getattr(args, parseopt.FILE_OR_DIR)[0] == py.path.local()
|
||||
|
||||
def test_parse_known_args(self, parser):
|
||||
def test_parse_known_args(self, parser: parseopt.Parser) -> None:
|
||||
parser.parse_known_args([py.path.local()])
|
||||
parser.addoption("--hello", action="store_true")
|
||||
ns = parser.parse_known_args(["x", "--y", "--hello", "this"])
|
||||
assert ns.hello
|
||||
assert ns.file_or_dir == ["x"]
|
||||
|
||||
def test_parse_known_and_unknown_args(self, parser):
|
||||
def test_parse_known_and_unknown_args(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--hello", action="store_true")
|
||||
ns, unknown = parser.parse_known_and_unknown_args(
|
||||
["x", "--y", "--hello", "this"]
|
||||
@@ -141,7 +141,7 @@ class TestParser:
|
||||
assert ns.file_or_dir == ["x"]
|
||||
assert unknown == ["--y", "this"]
|
||||
|
||||
def test_parse_will_set_default(self, parser):
|
||||
def test_parse_will_set_default(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--hello", dest="hello", default="x", action="store")
|
||||
option = parser.parse([])
|
||||
assert option.hello == "x"
|
||||
@@ -149,25 +149,22 @@ class TestParser:
|
||||
parser.parse_setoption([], option)
|
||||
assert option.hello == "x"
|
||||
|
||||
def test_parse_setoption(self, parser):
|
||||
def test_parse_setoption(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--hello", dest="hello", action="store")
|
||||
parser.addoption("--world", dest="world", default=42)
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
option = A()
|
||||
option = argparse.Namespace()
|
||||
args = parser.parse_setoption(["--hello", "world"], option)
|
||||
assert option.hello == "world"
|
||||
assert option.world == 42
|
||||
assert not args
|
||||
|
||||
def test_parse_special_destination(self, parser):
|
||||
def test_parse_special_destination(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--ultimate-answer", type=int)
|
||||
args = parser.parse(["--ultimate-answer", "42"])
|
||||
assert args.ultimate_answer == 42
|
||||
|
||||
def test_parse_split_positional_arguments(self, parser):
|
||||
def test_parse_split_positional_arguments(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("-R", action="store_true")
|
||||
parser.addoption("-S", action="store_false")
|
||||
args = parser.parse(["-R", "4", "2", "-S"])
|
||||
@@ -181,7 +178,7 @@ class TestParser:
|
||||
assert args.R is True
|
||||
assert args.S is False
|
||||
|
||||
def test_parse_defaultgetter(self):
|
||||
def test_parse_defaultgetter(self) -> None:
|
||||
def defaultget(option):
|
||||
if not hasattr(option, "type"):
|
||||
return
|
||||
@@ -199,17 +196,17 @@ class TestParser:
|
||||
assert option.this == 42
|
||||
assert option.no is False
|
||||
|
||||
def test_drop_short_helper(self):
|
||||
def test_drop_short_helper(self) -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=parseopt.DropShorterLongHelpFormatter, allow_abbrev=False
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t", "--twoword", "--duo", "--two-word", "--two", help="foo"
|
||||
).map_long_option = {"two": "two-word"}
|
||||
)
|
||||
# throws error on --deux only!
|
||||
parser.add_argument(
|
||||
"-d", "--deuxmots", "--deux-mots", action="store_true", help="foo"
|
||||
).map_long_option = {"deux": "deux-mots"}
|
||||
)
|
||||
parser.add_argument("-s", action="store_true", help="single short")
|
||||
parser.add_argument("--abc", "-a", action="store_true", help="bar")
|
||||
parser.add_argument("--klm", "-k", "--kl-m", action="store_true", help="bar")
|
||||
@@ -221,7 +218,7 @@ class TestParser:
|
||||
)
|
||||
parser.add_argument(
|
||||
"-x", "--exit-on-first", "--exitfirst", action="store_true", help="spam"
|
||||
).map_long_option = {"exitfirst": "exit-on-first"}
|
||||
)
|
||||
parser.add_argument("files_and_dirs", nargs="*")
|
||||
args = parser.parse_args(["-k", "--duo", "hallo", "--exitfirst"])
|
||||
assert args.twoword == "hallo"
|
||||
@@ -236,32 +233,32 @@ class TestParser:
|
||||
args = parser.parse_args(["file", "dir"])
|
||||
assert "|".join(args.files_and_dirs) == "file|dir"
|
||||
|
||||
def test_drop_short_0(self, parser):
|
||||
def test_drop_short_0(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--funcarg", "--func-arg", action="store_true")
|
||||
parser.addoption("--abc-def", "--abc-def", action="store_true")
|
||||
parser.addoption("--klm-hij", action="store_true")
|
||||
with pytest.raises(UsageError):
|
||||
parser.parse(["--funcarg", "--k"])
|
||||
|
||||
def test_drop_short_2(self, parser):
|
||||
def test_drop_short_2(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--func-arg", "--doit", action="store_true")
|
||||
args = parser.parse(["--doit"])
|
||||
assert args.func_arg is True
|
||||
|
||||
def test_drop_short_3(self, parser):
|
||||
def test_drop_short_3(self, parser: parseopt.Parser) -> None:
|
||||
parser.addoption("--func-arg", "--funcarg", "--doit", action="store_true")
|
||||
args = parser.parse(["abcd"])
|
||||
assert args.func_arg is False
|
||||
assert args.file_or_dir == ["abcd"]
|
||||
|
||||
def test_drop_short_help0(self, parser, capsys):
|
||||
def test_drop_short_help0(self, parser: parseopt.Parser, capsys) -> None:
|
||||
parser.addoption("--func-args", "--doit", help="foo", action="store_true")
|
||||
parser.parse([])
|
||||
help = parser.optparser.format_help()
|
||||
assert "--func-args, --doit foo" in help
|
||||
|
||||
# testing would be more helpful with all help generated
|
||||
def test_drop_short_help1(self, parser, capsys):
|
||||
def test_drop_short_help1(self, parser: parseopt.Parser, capsys) -> None:
|
||||
group = parser.getgroup("general")
|
||||
group.addoption("--doit", "--func-args", action="store_true", help="foo")
|
||||
group._addoption(
|
||||
@@ -275,7 +272,7 @@ class TestParser:
|
||||
help = parser.optparser.format_help()
|
||||
assert "-doit, --func-args foo" in help
|
||||
|
||||
def test_multiple_metavar_help(self, parser):
|
||||
def test_multiple_metavar_help(self, parser: parseopt.Parser) -> None:
|
||||
"""
|
||||
Help text for options with a metavar tuple should display help
|
||||
in the form "--preferences=value1 value2 value3" (#2004).
|
||||
@@ -290,7 +287,7 @@ class TestParser:
|
||||
assert "--preferences=value1 value2 value3" in help
|
||||
|
||||
|
||||
def test_argcomplete(testdir, monkeypatch):
|
||||
def test_argcomplete(testdir, monkeypatch) -> None:
|
||||
if not distutils.spawn.find_executable("bash"):
|
||||
pytest.skip("bash not available")
|
||||
script = str(testdir.tmpdir.join("test_argcomplete"))
|
||||
|
||||
@@ -122,7 +122,7 @@ class TestPytestPluginInteractions:
|
||||
def test_hook_proxy(self, testdir):
|
||||
"""Test the gethookproxy function(#2016)"""
|
||||
config = testdir.parseconfig()
|
||||
session = Session(config)
|
||||
session = Session.from_config(config)
|
||||
testdir.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""})
|
||||
|
||||
conftest1 = testdir.tmpdir.join("tests/conftest.py")
|
||||
|
||||
+18
-16
@@ -530,7 +530,7 @@ def test_no_matching(function):
|
||||
]
|
||||
else:
|
||||
assert obtained == [
|
||||
"nomatch: '{}'".format(good_pattern),
|
||||
" nomatch: '{}'".format(good_pattern),
|
||||
" and: 'cachedir: .pytest_cache'",
|
||||
" and: 'collecting ... collected 1 item'",
|
||||
" and: ''",
|
||||
@@ -542,17 +542,23 @@ def test_no_matching(function):
|
||||
func(bad_pattern) # bad pattern does not match any line: passes
|
||||
|
||||
|
||||
def test_pytester_addopts(request, monkeypatch):
|
||||
def test_no_matching_after_match():
|
||||
lm = LineMatcher(["1", "2", "3"])
|
||||
lm.fnmatch_lines(["1", "3"])
|
||||
with pytest.raises(pytest.fail.Exception) as e:
|
||||
lm.no_fnmatch_line("*")
|
||||
assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"]
|
||||
|
||||
|
||||
def test_pytester_addopts_before_testdir(request, monkeypatch):
|
||||
orig = os.environ.get("PYTEST_ADDOPTS", None)
|
||||
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
|
||||
|
||||
testdir = request.getfixturevalue("testdir")
|
||||
|
||||
try:
|
||||
assert "PYTEST_ADDOPTS" not in os.environ
|
||||
finally:
|
||||
testdir.finalize()
|
||||
|
||||
assert os.environ["PYTEST_ADDOPTS"] == "--orig-unused"
|
||||
assert "PYTEST_ADDOPTS" not in os.environ
|
||||
testdir.finalize()
|
||||
assert os.environ.get("PYTEST_ADDOPTS") == "--orig-unused"
|
||||
monkeypatch.undo()
|
||||
assert os.environ.get("PYTEST_ADDOPTS") == orig
|
||||
|
||||
|
||||
def test_run_stdin(testdir):
|
||||
@@ -632,14 +638,10 @@ def test_popen_default_stdin_stderr_and_stdin_None(testdir):
|
||||
|
||||
|
||||
def test_spawn_uses_tmphome(testdir):
|
||||
import os
|
||||
|
||||
tmphome = str(testdir.tmpdir)
|
||||
assert os.environ.get("HOME") == tmphome
|
||||
|
||||
# Does use HOME only during run.
|
||||
assert os.environ.get("HOME") != tmphome
|
||||
|
||||
testdir._env_run_update["CUSTOMENV"] = "42"
|
||||
testdir.monkeypatch.setenv("CUSTOMENV", "42")
|
||||
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from _pytest.reports import BaseReport
|
||||
|
||||
|
||||
def test_basics(testdir, tmp_path, pytestconfig):
|
||||
"""Basic testing of the report log functionality.
|
||||
|
||||
We don't test the test reports extensively because they have been
|
||||
tested already in ``test_reports``.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_ok():
|
||||
pass
|
||||
|
||||
def test_fail():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
|
||||
log_file = tmp_path / "log.json"
|
||||
|
||||
result = testdir.runpytest("--report-log", str(log_file))
|
||||
assert result.ret == pytest.ExitCode.TESTS_FAILED
|
||||
result.stdout.fnmatch_lines(["* generated report log file: {}*".format(log_file)])
|
||||
|
||||
json_objs = [json.loads(x) for x in log_file.read_text().splitlines()]
|
||||
assert len(json_objs) == 10
|
||||
|
||||
# first line should be the session_start
|
||||
session_start = json_objs[0]
|
||||
assert session_start == {
|
||||
"pytest_version": pytest.__version__,
|
||||
"$report_type": "SessionStart",
|
||||
}
|
||||
|
||||
# last line should be the session_finish
|
||||
session_start = json_objs[-1]
|
||||
assert session_start == {
|
||||
"exitstatus": pytest.ExitCode.TESTS_FAILED,
|
||||
"$report_type": "SessionFinish",
|
||||
}
|
||||
|
||||
# rest of the json objects should be unserialized into report objects; we don't test
|
||||
# the actual report object extensively because it has been tested in ``test_reports``
|
||||
# already.
|
||||
pm = pytestconfig.pluginmanager
|
||||
for json_obj in json_objs[1:-1]:
|
||||
rep = pm.hook.pytest_report_from_serializable(
|
||||
config=pytestconfig, data=json_obj
|
||||
)
|
||||
assert isinstance(rep, BaseReport)
|
||||
@@ -900,9 +900,9 @@ def test_store_except_info_on_error():
|
||||
# The next run should clear the exception info stored by the previous run
|
||||
ItemMightRaise.raise_error = False
|
||||
runner.pytest_runtest_call(ItemMightRaise())
|
||||
assert sys.last_type is None
|
||||
assert sys.last_value is None
|
||||
assert sys.last_traceback is None
|
||||
assert not hasattr(sys, "last_type")
|
||||
assert not hasattr(sys, "last_value")
|
||||
assert not hasattr(sys, "last_traceback")
|
||||
|
||||
|
||||
def test_current_test_env_var(testdir, monkeypatch):
|
||||
|
||||
@@ -17,3 +17,94 @@ def test_show_fixtures_and_test(testdir, dummy_yaml_custom_test):
|
||||
result.stdout.fnmatch_lines(
|
||||
["*SETUP F arg*", "*test_arg (fixtures used: arg)", "*TEARDOWN F arg*"]
|
||||
)
|
||||
|
||||
|
||||
def test_show_multi_test_fixture_setup_and_teardown_correctly_simple(testdir):
|
||||
"""
|
||||
Verify that when a fixture lives for longer than a single test, --setup-plan
|
||||
correctly displays the SETUP/TEARDOWN indicators the right number of times.
|
||||
|
||||
As reported in https://github.com/pytest-dev/pytest/issues/2049
|
||||
--setup-plan was showing SETUP/TEARDOWN on every test, even when the fixture
|
||||
should persist through multiple tests.
|
||||
|
||||
(Note that this bug never affected actual test execution, which used the
|
||||
correct fixture lifetimes. It was purely a display bug for --setup-plan, and
|
||||
did not affect the related --setup-show or --setup-only.)
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(scope = 'class')
|
||||
def fix():
|
||||
return object()
|
||||
class TestClass:
|
||||
def test_one(self, fix):
|
||||
assert False
|
||||
def test_two(self, fix):
|
||||
assert False
|
||||
"""
|
||||
)
|
||||
|
||||
result = testdir.runpytest("--setup-plan")
|
||||
assert result.ret == 0
|
||||
|
||||
setup_fragment = "SETUP C fix"
|
||||
setup_count = 0
|
||||
|
||||
teardown_fragment = "TEARDOWN C fix"
|
||||
teardown_count = 0
|
||||
|
||||
for line in result.stdout.lines:
|
||||
if setup_fragment in line:
|
||||
setup_count += 1
|
||||
if teardown_fragment in line:
|
||||
teardown_count += 1
|
||||
|
||||
# before the fix this tests, there would have been a setup/teardown
|
||||
# message for each test, so the counts would each have been 2
|
||||
assert setup_count == 1
|
||||
assert teardown_count == 1
|
||||
|
||||
|
||||
def test_show_multi_test_fixture_setup_and_teardown_same_as_setup_show(testdir):
|
||||
"""
|
||||
Verify that SETUP/TEARDOWN messages match what comes out of --setup-show.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(scope = 'session')
|
||||
def sess():
|
||||
return True
|
||||
@pytest.fixture(scope = 'module')
|
||||
def mod():
|
||||
return True
|
||||
@pytest.fixture(scope = 'class')
|
||||
def cls():
|
||||
return True
|
||||
@pytest.fixture(scope = 'function')
|
||||
def func():
|
||||
return True
|
||||
def test_outside(sess, mod, cls, func):
|
||||
assert True
|
||||
class TestCls:
|
||||
def test_one(self, sess, mod, cls, func):
|
||||
assert True
|
||||
def test_two(self, sess, mod, cls, func):
|
||||
assert True
|
||||
"""
|
||||
)
|
||||
|
||||
plan_result = testdir.runpytest("--setup-plan")
|
||||
show_result = testdir.runpytest("--setup-show")
|
||||
|
||||
# the number and text of these lines should be identical
|
||||
plan_lines = [
|
||||
l for l in plan_result.stdout.lines if "SETUP" in l or "TEARDOWN" in l
|
||||
]
|
||||
show_lines = [
|
||||
l for l in show_result.stdout.lines if "SETUP" in l or "TEARDOWN" in l
|
||||
]
|
||||
|
||||
assert plan_lines == show_lines
|
||||
|
||||
@@ -115,7 +115,7 @@ class TestEvaluator:
|
||||
)
|
||||
|
||||
def test_skipif_class(self, testdir):
|
||||
item, = testdir.getitems(
|
||||
(item,) = testdir.getitems(
|
||||
"""
|
||||
import pytest
|
||||
class TestClass(object):
|
||||
|
||||
@@ -154,6 +154,8 @@ class TestTerminal:
|
||||
"test2.py": "def test_2(): pass",
|
||||
}
|
||||
)
|
||||
# Explicitly test colored output.
|
||||
testdir.monkeypatch.setenv("PY_COLORS", "1")
|
||||
|
||||
child = testdir.spawn_pytest("-v test1.py test2.py")
|
||||
child.expect(r"collecting \.\.\.")
|
||||
@@ -963,7 +965,31 @@ class TestGenericReporting:
|
||||
)
|
||||
result = testdir.runpytest("--maxfail=2", *option.args)
|
||||
result.stdout.fnmatch_lines(
|
||||
["*def test_1():*", "*def test_2():*", "*2 failed*"]
|
||||
[
|
||||
"*def test_1():*",
|
||||
"*def test_2():*",
|
||||
"*! stopping after 2 failures !*",
|
||||
"*2 failed*",
|
||||
]
|
||||
)
|
||||
|
||||
def test_maxfailures_with_interrupted(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test(request):
|
||||
request.session.shouldstop = "session_interrupted"
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--maxfail=1", "-ra")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*= short test summary info =*",
|
||||
"FAILED *",
|
||||
"*! stopping after 1 failures !*",
|
||||
"*! session_interrupted !*",
|
||||
"*= 1 failed in*",
|
||||
]
|
||||
)
|
||||
|
||||
def test_tb_option(self, testdir, option):
|
||||
|
||||
@@ -258,7 +258,7 @@ class TestNumberedDir:
|
||||
registry = []
|
||||
register_cleanup_lock_removal(lock, register=registry.append)
|
||||
|
||||
cleanup_func, = registry
|
||||
(cleanup_func,) = registry
|
||||
|
||||
assert lock.is_file()
|
||||
|
||||
|
||||
@@ -383,7 +383,7 @@ def test_testcase_custom_exception_info(testdir, type):
|
||||
|
||||
|
||||
def test_testcase_totally_incompatible_exception_info(testdir):
|
||||
item, = testdir.getitems(
|
||||
(item,) = testdir.getitems(
|
||||
"""
|
||||
from unittest import TestCase
|
||||
class MyTestCase(TestCase):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
@@ -641,3 +642,160 @@ def test_pytest_configure_warning(testdir, recwarn):
|
||||
assert "INTERNALERROR" not in result.stderr.str()
|
||||
warning = recwarn.pop()
|
||||
assert str(warning.message) == "from pytest_configure"
|
||||
|
||||
|
||||
class TestStackLevel:
|
||||
@pytest.fixture
|
||||
def capwarn(self, testdir):
|
||||
class CapturedWarnings:
|
||||
captured = []
|
||||
|
||||
@classmethod
|
||||
def pytest_warning_captured(cls, warning_message, when, item, location):
|
||||
cls.captured.append((warning_message, location))
|
||||
|
||||
testdir.plugins = [CapturedWarnings()]
|
||||
|
||||
return CapturedWarnings
|
||||
|
||||
def test_issue4445_rewrite(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.assertion.rewrite.py:241
|
||||
"""
|
||||
testdir.makepyfile(some_mod="")
|
||||
conftest = testdir.makeconftest(
|
||||
"""
|
||||
import some_mod
|
||||
import pytest
|
||||
|
||||
pytest.register_assert_rewrite("some_mod")
|
||||
"""
|
||||
)
|
||||
testdir.parseconfig()
|
||||
|
||||
# with stacklevel=5 the warning originates from register_assert_rewrite
|
||||
# function in the created conftest.py
|
||||
assert len(capwarn.captured) == 1
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, lineno, func = location
|
||||
|
||||
assert "Module already imported" in str(warning.message)
|
||||
assert file == str(conftest)
|
||||
assert func == "<module>" # the above conftest.py
|
||||
assert lineno == 4
|
||||
|
||||
def test_issue4445_preparse(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.config.__init__.py:910
|
||||
"""
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import nothing
|
||||
"""
|
||||
)
|
||||
testdir.parseconfig("--help")
|
||||
|
||||
# with stacklevel=2 the warning should originate from config._preparse and is
|
||||
# thrown by an errorneous conftest.py
|
||||
assert len(capwarn.captured) == 1
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, _, func = location
|
||||
|
||||
assert "could not load initial conftests" in str(warning.message)
|
||||
assert "config{sep}__init__.py".format(sep=os.sep) in file
|
||||
assert func == "_preparse"
|
||||
|
||||
def test_issue4445_import_plugin(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.config.__init__.py:585
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
some_plugin="""
|
||||
import pytest
|
||||
pytest.skip("thing", allow_module_level=True)
|
||||
"""
|
||||
)
|
||||
testdir.syspathinsert()
|
||||
testdir.parseconfig("-p", "some_plugin")
|
||||
|
||||
# with stacklevel=2 the warning should originate from
|
||||
# config.PytestPluginManager.import_plugin is thrown by a skipped plugin
|
||||
|
||||
# During config parsing the the pluginargs are checked in a while loop
|
||||
# that as a result of the argument count runs import_plugin twice, hence
|
||||
# two identical warnings are captured (is this intentional?).
|
||||
assert len(capwarn.captured) == 2
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, _, func = location
|
||||
|
||||
assert "skipped plugin 'some_plugin': thing" in str(warning.message)
|
||||
assert "config{sep}__init__.py".format(sep=os.sep) in file
|
||||
assert func == "import_plugin"
|
||||
|
||||
def test_issue4445_resultlog(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.resultlog.py:35
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_dummy():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
# Use parseconfigure() because the warning in resultlog.py is triggered in
|
||||
# the pytest_configure hook
|
||||
testdir.parseconfigure(
|
||||
"--result-log={dir}".format(dir=testdir.tmpdir.join("result.log"))
|
||||
)
|
||||
|
||||
# with stacklevel=2 the warning originates from resultlog.pytest_configure
|
||||
# and is thrown when --result-log is used
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, _, func = location
|
||||
|
||||
assert "--result-log is deprecated" in str(warning.message)
|
||||
assert "resultlog.py" in file
|
||||
assert func == "pytest_configure"
|
||||
|
||||
def test_issue4445_cacheprovider_set(self, testdir, capwarn):
|
||||
"""#4445: Make sure the warning points to a reasonable location
|
||||
See origin of _issue_warning_captured at: _pytest.cacheprovider.py:59
|
||||
"""
|
||||
testdir.tmpdir.join(".pytest_cache").write("something wrong")
|
||||
testdir.runpytest(plugins=[capwarn()])
|
||||
|
||||
# with stacklevel=3 the warning originates from one stacklevel above
|
||||
# _issue_warning_captured in cacheprovider.Cache.set and is thrown
|
||||
# when there are errors during cache folder creation
|
||||
|
||||
# set is called twice (in module stepwise and in cacheprovider) so emits
|
||||
# two warnings when there are errors during cache folder creation. (is this intentional?)
|
||||
assert len(capwarn.captured) == 2
|
||||
warning, location = capwarn.captured.pop()
|
||||
file, lineno, func = location
|
||||
|
||||
assert "could not create cache path" in str(warning.message)
|
||||
assert "cacheprovider.py" in file
|
||||
assert func == "set"
|
||||
|
||||
def test_issue4445_issue5928_mark_generator(self, testdir):
|
||||
"""#4445 and #5928: Make sure the warning from an unknown mark points to
|
||||
the test file where this mark is used.
|
||||
"""
|
||||
testfile = testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.unknown
|
||||
def test_it():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess()
|
||||
# with stacklevel=2 the warning should originate from the above created test file
|
||||
result.stdout.fnmatch_lines_random(
|
||||
[
|
||||
"*{testfile}:3*".format(testfile=str(testfile)),
|
||||
"*Unknown pytest.mark.unknown*",
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user