Skip to content

Commit

Permalink
Merge pull request pytest-dev#6521 from blueyed/harden-nose-raises
Browse files Browse the repository at this point in the history
tests: improve test for `nose.raises`
  • Loading branch information
blueyed committed Jan 23, 2020
2 parents 863bab5 + 9dcdea5 commit 6b13379
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/_pytest/compat.py
Expand Up @@ -307,7 +307,7 @@ def get_real_method(obj, holder):
return obj


def getfslineno(obj):
def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, "place_as"):
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/config/argparsing.py
Expand Up @@ -82,8 +82,8 @@ def parse(self, args, namespace=None):

self.optparser = self._getparser()
try_argcomplete(self.optparser)
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(args, namespace=namespace)
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(strargs, namespace=namespace)

def _getparser(self) -> "MyOptionParser":
from _pytest._argcomplete import filescompleter
Expand Down Expand Up @@ -124,8 +124,8 @@ def parse_known_and_unknown_args(
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return optparser.parse_known_args(args, namespace=namespace)
strargs = [str(x) if isinstance(x, py.path.local) else x for x in args]
return optparser.parse_known_args(strargs, namespace=namespace)

def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
Expand Down
7 changes: 5 additions & 2 deletions src/_pytest/config/findpaths.py
@@ -1,6 +1,9 @@
import os
from typing import Any
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple

import py

Expand Down Expand Up @@ -60,7 +63,7 @@ def getcfg(args, config=None):
return None, None, None


def get_common_ancestor(paths):
def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local:
common_ancestor = None
for path in paths:
if not path.exists():
Expand Down Expand Up @@ -113,7 +116,7 @@ def determine_setup(
args: List[str],
rootdir_cmd_arg: Optional[str] = None,
config: Optional["Config"] = None,
):
) -> Tuple[py.path.local, Optional[str], Any]:
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/doctest.py
Expand Up @@ -308,7 +308,7 @@ def repr_failure(self, excinfo):
else:
return super().repr_failure(excinfo)

def reportinfo(self) -> Tuple[str, int, str]:
def reportinfo(self) -> Tuple[py.path.local, int, str]:
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name


Expand Down
11 changes: 7 additions & 4 deletions src/_pytest/fixtures.py
Expand Up @@ -351,7 +351,7 @@ def __init__(self, pyfuncitem):
self.fixturename = None
#: Scope string, one of "function", "class", "module", "session"
self.scope = "function"
self._fixture_defs = {} # argname -> FixtureDef
self._fixture_defs = {} # type: Dict[str, FixtureDef]
fixtureinfo = pyfuncitem._fixtureinfo
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
self._arg2index = {}
Expand Down Expand Up @@ -426,7 +426,8 @@ def module(self):
@scopeproperty()
def fspath(self) -> py.path.local:
""" the file system path of the test module which collected this test. """
return self._pyfuncitem.fspath
# TODO: Remove ignore once _pyfuncitem is properly typed.
return self._pyfuncitem.fspath # type: ignore

@property
def keywords(self):
Expand Down Expand Up @@ -549,7 +550,9 @@ def _compute_fixture_value(self, fixturedef):
source_lineno = frameinfo.lineno
source_path = py.path.local(source_path)
if source_path.relto(funcitem.config.rootdir):
source_path = source_path.relto(funcitem.config.rootdir)
source_path_str = source_path.relto(funcitem.config.rootdir)
else:
source_path_str = str(source_path)
msg = (
"The requested fixture has no parameter defined for test:\n"
" {}\n\n"
Expand All @@ -558,7 +561,7 @@ def _compute_fixture_value(self, fixturedef):
funcitem.nodeid,
fixturedef.argname,
getlocation(fixturedef.func, funcitem.config.rootdir),
source_path,
source_path_str,
source_lineno,
)
)
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/main.py
Expand Up @@ -376,9 +376,9 @@ class Failed(Exception):

@attr.s
class _bestrelpath_cache(dict):
path = attr.ib()
path = attr.ib(type=py.path.local)

def __missing__(self, path: str) -> str:
def __missing__(self, path: py.path.local) -> str:
r = self.path.bestrelpath(path) # type: str
self[path] = r
return r
Expand Down Expand Up @@ -412,7 +412,7 @@ def __init__(self, config: Config) -> None:

self._bestrelpathcache = _bestrelpath_cache(
config.rootdir
) # type: Dict[str, str]
) # type: Dict[py.path.local, str]

self.config.pluginmanager.register(self, name="session")

Expand All @@ -425,7 +425,7 @@ def __repr__(self):
self.testscollected,
)

def _node_location_to_relpath(self, node_path: str) -> str:
def _node_location_to_relpath(self, node_path: py.path.local) -> str:
# bestrelpath is a quite slow function
return self._bestrelpathcache[node_path]

Expand Down
8 changes: 6 additions & 2 deletions src/_pytest/nodes.py
Expand Up @@ -462,6 +462,10 @@ def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]:
@cached_property
def location(self) -> Tuple[str, Optional[int], str]:
location = self.reportinfo()
fspath = self.session._node_location_to_relpath(location[0])
if isinstance(location[0], py.path.local):
fspath = location[0]
else:
fspath = py.path.local(location[0])
relfspath = self.session._node_location_to_relpath(fspath)
assert type(location[2]) is str
return (fspath, location[1], location[2])
return (relfspath, location[1], location[2])
17 changes: 12 additions & 5 deletions src/_pytest/python.py
Expand Up @@ -12,6 +12,7 @@
from textwrap import dedent
from typing import List
from typing import Tuple
from typing import Union

import py

Expand Down Expand Up @@ -280,15 +281,16 @@ def getmodpath(self, stopatmodule=True, includemodule=False):
parts.reverse()
return ".".join(parts)

def reportinfo(self) -> Tuple[str, int, str]:
def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]:
# XXX caching?
obj = self.obj
compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
if isinstance(compat_co_firstlineno, int):
# nose compatibility
fspath = sys.modules[obj.__module__].__file__
if fspath.endswith(".pyc"):
fspath = fspath[:-1]
file_path = sys.modules[obj.__module__].__file__
if file_path.endswith(".pyc"):
file_path = file_path[:-1]
fspath = file_path # type: Union[py.path.local, str]
lineno = compat_co_firstlineno
else:
fspath, lineno = getfslineno(obj)
Expand Down Expand Up @@ -367,7 +369,12 @@ def collect(self):
if not isinstance(res, list):
res = [res]
values.extend(res)
values.sort(key=lambda item: item.reportinfo()[:2])

def sort_key(item):
fspath, lineno, _ = item.reportinfo()
return (str(fspath), lineno)

values.sort(key=sort_key)
return values

def _makeitem(self, name, obj):
Expand Down
41 changes: 37 additions & 4 deletions testing/test_nose.py
Expand Up @@ -377,15 +377,48 @@ def test_io(self):
result.stdout.fnmatch_lines(["* 1 skipped *"])


def test_issue_6517(testdir):
def test_raises(testdir):
testdir.makepyfile(
"""
from nose.tools import raises
@raises(RuntimeError)
def test_fail_without_tcp():
def test_raises_runtimeerror():
raise RuntimeError
@raises(Exception)
def test_raises_baseexception_not_caught():
raise BaseException
@raises(BaseException)
def test_raises_baseexception_caught():
raise BaseException
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(["* 1 passed *"])
result = testdir.runpytest("-vv")
result.stdout.fnmatch_lines(
[
"test_raises.py::test_raises_runtimeerror PASSED*",
"test_raises.py::test_raises_baseexception_not_caught FAILED*",
"test_raises.py::test_raises_baseexception_caught PASSED*",
"*= FAILURES =*",
"*_ test_raises_baseexception_not_caught _*",
"",
"arg = (), kw = {}",
"",
" def newfunc(*arg, **kw):",
" try:",
"> func(*arg, **kw)",
"",
"*/nose/*: ",
"_ _ *",
"",
" @raises(Exception)",
" def test_raises_baseexception_not_caught():",
"> raise BaseException",
"E BaseException",
"",
"test_raises.py:9: BaseException",
"* 1 failed, 2 passed *",
]
)

0 comments on commit 6b13379

Please sign in to comment.