Merge branch 'features' into unittest-debug

This commit is contained in:
Bruno Oliveira
2019-12-03 10:52:53 -03:00
committed by GitHub
140 changed files with 2151 additions and 1287 deletions
-1
View File
@@ -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
View File
@@ -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")]
+22 -66
View File
@@ -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
View File
@@ -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)
+47 -1
View File
@@ -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__
+25 -3
View 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)
+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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 *",
]
)
+1 -1
View File
@@ -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):
+35 -7
View File
@@ -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,",
" }",
]
+1 -1
View File
@@ -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
View File
@@ -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
-2
View File
@@ -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
View File
@@ -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
+21
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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
+6 -2
View File
@@ -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
View File
@@ -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"))
+1 -1
View File
@@ -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
View File
@@ -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(
"""
-54
View File
@@ -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)
+3 -3
View File
@@ -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):
+91
View File
@@ -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
+1 -1
View File
@@ -115,7 +115,7 @@ class TestEvaluator:
)
def test_skipif_class(self, testdir):
item, = testdir.getitems(
(item,) = testdir.getitems(
"""
import pytest
class TestClass(object):
+27 -1
View File
@@ -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):
+1 -1
View File
@@ -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()
+1 -1
View 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):
+158
View File
@@ -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*",
]
)