majorly refactor collection process
- get rid of py.test.collect.Directory alltogether. - introduce direct node.nodeid attribute - remove now superflous attributes on collect and test reports
This commit is contained in:
parent
f181c70d8e
commit
6dac77433e
|
@ -26,7 +26,8 @@ Changes between 1.3.4 and 2.0.0dev0
|
||||||
output on assertion failures for comparisons and other cases (Floris Bruynooghe)
|
output on assertion failures for comparisons and other cases (Floris Bruynooghe)
|
||||||
- nose-plugin: pass through type-signature failures in setup/teardown
|
- nose-plugin: pass through type-signature failures in setup/teardown
|
||||||
functions instead of not calling them (Ed Singleton)
|
functions instead of not calling them (Ed Singleton)
|
||||||
- major refactoring of internal collection handling
|
- remove py.test.collect.Directory (follows from a major refactoring
|
||||||
|
and simplification of the collection process)
|
||||||
- majorly reduce py.test core code, shift function/python testing to own plugin
|
- majorly reduce py.test core code, shift function/python testing to own plugin
|
||||||
- fix issue88 (finding custom test nodes from command line arg)
|
- fix issue88 (finding custom test nodes from command line arg)
|
||||||
- refine 'tmpdir' creation, will now create basenames better associated
|
- refine 'tmpdir' creation, will now create basenames better associated
|
||||||
|
|
|
@ -5,7 +5,7 @@ see http://pytest.org for documentation and details
|
||||||
|
|
||||||
(c) Holger Krekel and others, 2004-2010
|
(c) Holger Krekel and others, 2004-2010
|
||||||
"""
|
"""
|
||||||
__version__ = '2.0.0.dev18'
|
__version__ = '2.0.0.dev19'
|
||||||
|
|
||||||
__all__ = ['config', 'cmdline']
|
__all__ = ['config', 'cmdline']
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,7 @@ def pytest_runtest_protocol(item):
|
||||||
"""
|
"""
|
||||||
pytest_runtest_protocol.firstresult = True
|
pytest_runtest_protocol.firstresult = True
|
||||||
|
|
||||||
def pytest_runtest_logstart(nodeid, location, fspath):
|
def pytest_runtest_logstart(nodeid, location):
|
||||||
""" signal the start of a test run. """
|
""" signal the start of a test run. """
|
||||||
|
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
|
|
|
@ -67,7 +67,10 @@ class PluginManager(object):
|
||||||
self._hints = []
|
self._hints = []
|
||||||
self.trace = TagTracer().get("pluginmanage")
|
self.trace = TagTracer().get("pluginmanage")
|
||||||
if os.environ.get('PYTEST_DEBUG'):
|
if os.environ.get('PYTEST_DEBUG'):
|
||||||
self.trace.root.setwriter(sys.stderr.write)
|
err = sys.stderr
|
||||||
|
if hasattr(os, 'dup'):
|
||||||
|
err = py.io.dupfile(err)
|
||||||
|
self.trace.root.setwriter(err.write)
|
||||||
self.hook = HookRelay([hookspec], pm=self)
|
self.hook = HookRelay([hookspec], pm=self)
|
||||||
self.register(self)
|
self.register(self)
|
||||||
if load:
|
if load:
|
||||||
|
@ -370,6 +373,7 @@ class HookCaller:
|
||||||
self.hookrelay = hookrelay
|
self.hookrelay = hookrelay
|
||||||
self.name = name
|
self.name = name
|
||||||
self.firstresult = firstresult
|
self.firstresult = firstresult
|
||||||
|
self.trace = self.hookrelay.trace
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<HookCaller %r>" %(self.name,)
|
return "<HookCaller %r>" %(self.name,)
|
||||||
|
@ -380,10 +384,15 @@ class HookCaller:
|
||||||
return mc.execute()
|
return mc.execute()
|
||||||
|
|
||||||
def pcall(self, plugins, **kwargs):
|
def pcall(self, plugins, **kwargs):
|
||||||
self.hookrelay.trace(self.name, kwargs)
|
self.trace(self.name, kwargs)
|
||||||
|
self.trace.root.indent += 1
|
||||||
methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
|
methods = self.hookrelay._pm.listattr(self.name, plugins=plugins)
|
||||||
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
mc = MultiCall(methods, kwargs, firstresult=self.firstresult)
|
||||||
return mc.execute()
|
res = mc.execute()
|
||||||
|
if res:
|
||||||
|
self.trace(res)
|
||||||
|
self.trace.root.indent -= 1
|
||||||
|
return res
|
||||||
|
|
||||||
_preinit = [PluginManager(load=True)] # triggers default plugin importing
|
_preinit = [PluginManager(load=True)] # triggers default plugin importing
|
||||||
|
|
||||||
|
|
|
@ -133,7 +133,11 @@ class CaptureManager:
|
||||||
|
|
||||||
def pytest_make_collect_report(self, __multicall__, collector):
|
def pytest_make_collect_report(self, __multicall__, collector):
|
||||||
method = self._getmethod(collector.config, collector.fspath)
|
method = self._getmethod(collector.config, collector.fspath)
|
||||||
|
try:
|
||||||
self.resumecapture(method)
|
self.resumecapture(method)
|
||||||
|
except ValueError:
|
||||||
|
return # recursive collect, XXX refactor capturing
|
||||||
|
# to allow for more lightweight recursive capturing
|
||||||
try:
|
try:
|
||||||
rep = __multicall__.execute()
|
rep = __multicall__.execute()
|
||||||
finally:
|
finally:
|
||||||
|
|
|
@ -255,7 +255,7 @@ class Config(object):
|
||||||
)
|
)
|
||||||
#: a pluginmanager instance
|
#: a pluginmanager instance
|
||||||
self.pluginmanager = pluginmanager or PluginManager(load=True)
|
self.pluginmanager = pluginmanager or PluginManager(load=True)
|
||||||
self.trace = self.pluginmanager.trace.get("config")
|
self.trace = self.pluginmanager.trace.root.get("config")
|
||||||
self._conftest = Conftest(onimport=self._onimportconftest)
|
self._conftest = Conftest(onimport=self._onimportconftest)
|
||||||
self.hook = self.pluginmanager.hook
|
self.hook = self.pluginmanager.hook
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ def pytest_addoption(parser):
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
config = parent.config
|
config = parent.config
|
||||||
if path.ext == ".py":
|
if path.ext == ".py":
|
||||||
if config.getvalue("doctestmodules"):
|
if config.option.doctestmodules:
|
||||||
return DoctestModule(path, parent)
|
return DoctestModule(path, parent)
|
||||||
elif path.check(fnmatch=config.getvalue("doctestglob")):
|
elif path.check(fnmatch=config.getvalue("doctestglob")):
|
||||||
return DoctestTextfile(path, parent)
|
return DoctestTextfile(path, parent)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
@ -36,7 +37,9 @@ class LogXML(object):
|
||||||
self._durations = {}
|
self._durations = {}
|
||||||
|
|
||||||
def _opentestcase(self, report):
|
def _opentestcase(self, report):
|
||||||
names = report.nodenames
|
names = report.nodeid.split("::")
|
||||||
|
names[0] = names[0].replace(os.sep, '.')
|
||||||
|
names = tuple(names)
|
||||||
d = {'time': self._durations.pop(names, "0")}
|
d = {'time': self._durations.pop(names, "0")}
|
||||||
names = [x.replace(".py", "") for x in names if x != "()"]
|
names = [x.replace(".py", "") for x in names if x != "()"]
|
||||||
classnames = names[:-1]
|
classnames = names[:-1]
|
||||||
|
|
|
@ -105,6 +105,7 @@ class HookRecorder:
|
||||||
return l
|
return l
|
||||||
|
|
||||||
def contains(self, entries):
|
def contains(self, entries):
|
||||||
|
__tracebackhide__ = True
|
||||||
from py.builtin import print_
|
from py.builtin import print_
|
||||||
i = 0
|
i = 0
|
||||||
entries = list(entries)
|
entries = list(entries)
|
||||||
|
@ -123,8 +124,7 @@ class HookRecorder:
|
||||||
break
|
break
|
||||||
print_("NONAMEMATCH", name, "with", call)
|
print_("NONAMEMATCH", name, "with", call)
|
||||||
else:
|
else:
|
||||||
raise AssertionError("could not find %r in %r" %(
|
py.test.fail("could not find %r check %r" % (name, check))
|
||||||
name, self.calls[i:]))
|
|
||||||
|
|
||||||
def popcall(self, name):
|
def popcall(self, name):
|
||||||
for i, call in enumerate(self.calls):
|
for i, call in enumerate(self.calls):
|
||||||
|
@ -278,7 +278,16 @@ class TmpTestdir:
|
||||||
Collection = Collection
|
Collection = Collection
|
||||||
def getnode(self, config, arg):
|
def getnode(self, config, arg):
|
||||||
collection = Collection(config)
|
collection = Collection(config)
|
||||||
return collection.getbyid(collection._normalizearg(arg))[0]
|
assert '::' not in str(arg)
|
||||||
|
p = py.path.local(arg)
|
||||||
|
x = collection.fspath.bestrelpath(p)
|
||||||
|
return collection.perform_collect([x], genitems=False)[0]
|
||||||
|
|
||||||
|
def getpathnode(self, path):
|
||||||
|
config = self.parseconfig(path)
|
||||||
|
collection = Collection(config)
|
||||||
|
x = collection.fspath.bestrelpath(path)
|
||||||
|
return collection.perform_collect([x], genitems=False)[0]
|
||||||
|
|
||||||
def genitems(self, colitems):
|
def genitems(self, colitems):
|
||||||
collection = colitems[0].collection
|
collection = colitems[0].collection
|
||||||
|
@ -291,8 +300,9 @@ class TmpTestdir:
|
||||||
#config = self.parseconfig(*args)
|
#config = self.parseconfig(*args)
|
||||||
config = self.parseconfigure(*args)
|
config = self.parseconfigure(*args)
|
||||||
rec = self.getreportrecorder(config)
|
rec = self.getreportrecorder(config)
|
||||||
items = Collection(config).perform_collect()
|
collection = Collection(config)
|
||||||
return items, rec
|
collection.perform_collect()
|
||||||
|
return collection.items, rec
|
||||||
|
|
||||||
def runitem(self, source):
|
def runitem(self, source):
|
||||||
# used from runner functional tests
|
# used from runner functional tests
|
||||||
|
@ -469,11 +479,12 @@ class TmpTestdir:
|
||||||
p = py.path.local.make_numbered_dir(prefix="runpytest-",
|
p = py.path.local.make_numbered_dir(prefix="runpytest-",
|
||||||
keep=None, rootdir=self.tmpdir)
|
keep=None, rootdir=self.tmpdir)
|
||||||
args = ('--basetemp=%s' % p, ) + args
|
args = ('--basetemp=%s' % p, ) + args
|
||||||
for x in args:
|
#for x in args:
|
||||||
if '--confcutdir' in str(x):
|
# if '--confcutdir' in str(x):
|
||||||
break
|
# break
|
||||||
else:
|
#else:
|
||||||
args = ('--confcutdir=.',) + args
|
# pass
|
||||||
|
# args = ('--confcutdir=.',) + args
|
||||||
plugins = [x for x in self.plugins if isinstance(x, str)]
|
plugins = [x for x in self.plugins if isinstance(x, str)]
|
||||||
if plugins:
|
if plugins:
|
||||||
args = ('-p', plugins[0]) + args
|
args = ('-p', plugins[0]) + args
|
||||||
|
@ -530,7 +541,7 @@ class ReportRecorder(object):
|
||||||
""" return a testreport whose dotted import path matches """
|
""" return a testreport whose dotted import path matches """
|
||||||
l = []
|
l = []
|
||||||
for rep in self.getreports(names=names):
|
for rep in self.getreports(names=names):
|
||||||
if not inamepart or inamepart in rep.nodenames:
|
if not inamepart or inamepart in rep.nodeid.split("::"):
|
||||||
l.append(rep)
|
l.append(rep)
|
||||||
if not l:
|
if not l:
|
||||||
raise ValueError("could not find test report matching %r: no test reports at all!" %
|
raise ValueError("could not find test report matching %r: no test reports at all!" %
|
||||||
|
@ -616,6 +627,8 @@ class LineMatcher:
|
||||||
raise ValueError("line %r not found in output" % line)
|
raise ValueError("line %r not found in output" % line)
|
||||||
|
|
||||||
def fnmatch_lines(self, lines2):
|
def fnmatch_lines(self, lines2):
|
||||||
|
def show(arg1, arg2):
|
||||||
|
py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)
|
||||||
lines2 = self._getlines(lines2)
|
lines2 = self._getlines(lines2)
|
||||||
lines1 = self.lines[:]
|
lines1 = self.lines[:]
|
||||||
nextline = None
|
nextline = None
|
||||||
|
@ -626,17 +639,17 @@ class LineMatcher:
|
||||||
while lines1:
|
while lines1:
|
||||||
nextline = lines1.pop(0)
|
nextline = lines1.pop(0)
|
||||||
if line == nextline:
|
if line == nextline:
|
||||||
print_("exact match:", repr(line))
|
show("exact match:", repr(line))
|
||||||
break
|
break
|
||||||
elif fnmatch(nextline, line):
|
elif fnmatch(nextline, line):
|
||||||
print_("fnmatch:", repr(line))
|
show("fnmatch:", repr(line))
|
||||||
print_(" with:", repr(nextline))
|
show(" with:", repr(nextline))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if not nomatchprinted:
|
if not nomatchprinted:
|
||||||
print_("nomatch:", repr(line))
|
show("nomatch:", repr(line))
|
||||||
nomatchprinted = True
|
nomatchprinted = True
|
||||||
print_(" and:", repr(nextline))
|
show(" and:", repr(nextline))
|
||||||
extralines.append(nextline)
|
extralines.append(nextline)
|
||||||
else:
|
else:
|
||||||
assert line == nextline
|
py.test.fail("remains unmatched: %r, see stderr" % (line,))
|
||||||
|
|
|
@ -48,9 +48,8 @@ def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
ext = path.ext
|
ext = path.ext
|
||||||
pb = path.purebasename
|
pb = path.purebasename
|
||||||
if pb.startswith("test_") or pb.endswith("_test") or \
|
if ext == ".py" and (pb.startswith("test_") or pb.endswith("_test") or
|
||||||
path in parent.collection._argfspaths:
|
parent.collection.isinitpath(path)):
|
||||||
if ext == ".py":
|
|
||||||
return parent.ihook.pytest_pycollect_makemodule(
|
return parent.ihook.pytest_pycollect_makemodule(
|
||||||
path=path, parent=parent)
|
path=path, parent=parent)
|
||||||
|
|
||||||
|
@ -713,11 +712,13 @@ class FuncargRequest:
|
||||||
def showfuncargs(config):
|
def showfuncargs(config):
|
||||||
from pytest.plugin.session import Collection
|
from pytest.plugin.session import Collection
|
||||||
collection = Collection(config)
|
collection = Collection(config)
|
||||||
firstid = collection._normalizearg(config.args[0])
|
collection.perform_collect()
|
||||||
colitem = collection.getbyid(firstid)[0]
|
if collection.items:
|
||||||
|
plugins = getplugins(collection.items[0])
|
||||||
|
else:
|
||||||
|
plugins = getplugins(collection)
|
||||||
curdir = py.path.local()
|
curdir = py.path.local()
|
||||||
tw = py.io.TerminalWriter()
|
tw = py.io.TerminalWriter()
|
||||||
plugins = getplugins(colitem, withpy=True)
|
|
||||||
verbose = config.getvalue("verbose")
|
verbose = config.getvalue("verbose")
|
||||||
for plugin in plugins:
|
for plugin in plugins:
|
||||||
available = []
|
available = []
|
||||||
|
|
|
@ -29,30 +29,12 @@ def pytest_sessionfinish(session, exitstatus):
|
||||||
session.exitstatus = 1
|
session.exitstatus = 1
|
||||||
|
|
||||||
class NodeInfo:
|
class NodeInfo:
|
||||||
def __init__(self, nodeid, nodenames, fspath, location):
|
def __init__(self, location):
|
||||||
self.nodeid = nodeid
|
|
||||||
self.nodenames = nodenames
|
|
||||||
self.fspath = fspath
|
|
||||||
self.location = location
|
self.location = location
|
||||||
|
|
||||||
def getitemnodeinfo(item):
|
|
||||||
try:
|
|
||||||
return item._nodeinfo
|
|
||||||
except AttributeError:
|
|
||||||
location = item.reportinfo()
|
|
||||||
location = (str(location[0]), location[1], str(location[2]))
|
|
||||||
nodenames = tuple(item.listnames())
|
|
||||||
nodeid = item.collection.getid(item)
|
|
||||||
fspath = item.fspath
|
|
||||||
item._nodeinfo = n = NodeInfo(nodeid, nodenames, fspath, location)
|
|
||||||
return n
|
|
||||||
|
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item):
|
||||||
nodeinfo = getitemnodeinfo(item)
|
|
||||||
item.ihook.pytest_runtest_logstart(
|
item.ihook.pytest_runtest_logstart(
|
||||||
nodeid=nodeinfo.nodeid,
|
nodeid=item.nodeid, location=item.location,
|
||||||
location=nodeinfo.location,
|
|
||||||
fspath=str(item.fspath),
|
|
||||||
)
|
)
|
||||||
runtestprotocol(item)
|
runtestprotocol(item)
|
||||||
return True
|
return True
|
||||||
|
@ -142,16 +124,18 @@ class BaseReport(object):
|
||||||
failed = property(lambda x: x.outcome == "failed")
|
failed = property(lambda x: x.outcome == "failed")
|
||||||
skipped = property(lambda x: x.outcome == "skipped")
|
skipped = property(lambda x: x.outcome == "skipped")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fspath(self):
|
||||||
|
return self.nodeid.split("::")[0]
|
||||||
|
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
nodeinfo = getitemnodeinfo(item)
|
|
||||||
when = call.when
|
when = call.when
|
||||||
keywords = dict([(x,1) for x in item.keywords])
|
keywords = dict([(x,1) for x in item.keywords])
|
||||||
excinfo = call.excinfo
|
|
||||||
if not call.excinfo:
|
if not call.excinfo:
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
longrepr = None
|
longrepr = None
|
||||||
else:
|
else:
|
||||||
|
excinfo = call.excinfo
|
||||||
if not isinstance(excinfo, py.code.ExceptionInfo):
|
if not isinstance(excinfo, py.code.ExceptionInfo):
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
longrepr = excinfo
|
longrepr = excinfo
|
||||||
|
@ -164,25 +148,18 @@ def pytest_runtest_makereport(item, call):
|
||||||
longrepr = item.repr_failure(excinfo)
|
longrepr = item.repr_failure(excinfo)
|
||||||
else: # exception in setup or teardown
|
else: # exception in setup or teardown
|
||||||
longrepr = item._repr_failure_py(excinfo)
|
longrepr = item._repr_failure_py(excinfo)
|
||||||
return TestReport(nodeinfo.nodeid, nodeinfo.nodenames,
|
return TestReport(item.nodeid, item.location,
|
||||||
nodeinfo.fspath, nodeinfo.location,
|
|
||||||
keywords, outcome, longrepr, when)
|
keywords, outcome, longrepr, when)
|
||||||
|
|
||||||
class TestReport(BaseReport):
|
class TestReport(BaseReport):
|
||||||
""" Basic test report object (also used for setup and teardown calls if
|
""" Basic test report object (also used for setup and teardown calls if
|
||||||
they fail).
|
they fail).
|
||||||
"""
|
"""
|
||||||
def __init__(self, nodeid, nodenames, fspath, location,
|
def __init__(self, nodeid, location,
|
||||||
keywords, outcome, longrepr, when):
|
keywords, outcome, longrepr, when):
|
||||||
#: normalized collection node id
|
#: normalized collection node id
|
||||||
self.nodeid = nodeid
|
self.nodeid = nodeid
|
||||||
|
|
||||||
#: list of names indicating position in collection tree.
|
|
||||||
self.nodenames = nodenames
|
|
||||||
|
|
||||||
#: the collected path of the file containing the test.
|
|
||||||
self.fspath = fspath # where the test was collected
|
|
||||||
|
|
||||||
#: a (filesystempath, lineno, domaininfo) tuple indicating the
|
#: a (filesystempath, lineno, domaininfo) tuple indicating the
|
||||||
#: actual location of a test item - it might be different from the
|
#: actual location of a test item - it might be different from the
|
||||||
#: collected one e.g. if a method is inherited from a different module.
|
#: collected one e.g. if a method is inherited from a different module.
|
||||||
|
@ -212,39 +189,27 @@ class TeardownErrorReport(BaseReport):
|
||||||
self.longrepr = longrepr
|
self.longrepr = longrepr
|
||||||
|
|
||||||
def pytest_make_collect_report(collector):
|
def pytest_make_collect_report(collector):
|
||||||
result = excinfo = None
|
call = CallInfo(collector._memocollect, "memocollect")
|
||||||
try:
|
|
||||||
result = collector._memocollect()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
excinfo = py.code.ExceptionInfo()
|
|
||||||
nodenames = tuple(collector.listnames())
|
|
||||||
nodeid = collector.collection.getid(collector)
|
|
||||||
fspath = str(collector.fspath)
|
|
||||||
reason = longrepr = None
|
reason = longrepr = None
|
||||||
if not excinfo:
|
if not call.excinfo:
|
||||||
outcome = "passed"
|
outcome = "passed"
|
||||||
else:
|
else:
|
||||||
if excinfo.errisinstance(py.test.skip.Exception):
|
if call.excinfo.errisinstance(py.test.skip.Exception):
|
||||||
outcome = "skipped"
|
outcome = "skipped"
|
||||||
reason = str(excinfo.value)
|
reason = str(call.excinfo.value)
|
||||||
longrepr = collector._repr_failure_py(excinfo, "line")
|
longrepr = collector._repr_failure_py(call.excinfo, "line")
|
||||||
else:
|
else:
|
||||||
outcome = "failed"
|
outcome = "failed"
|
||||||
errorinfo = collector.repr_failure(excinfo)
|
errorinfo = collector.repr_failure(call.excinfo)
|
||||||
if not hasattr(errorinfo, "toterminal"):
|
if not hasattr(errorinfo, "toterminal"):
|
||||||
errorinfo = CollectErrorRepr(errorinfo)
|
errorinfo = CollectErrorRepr(errorinfo)
|
||||||
longrepr = errorinfo
|
longrepr = errorinfo
|
||||||
return CollectReport(nodenames, nodeid, fspath,
|
return CollectReport(collector.nodeid, outcome, longrepr,
|
||||||
outcome, longrepr, result, reason)
|
getattr(call, 'result', None), reason)
|
||||||
|
|
||||||
class CollectReport(BaseReport):
|
class CollectReport(BaseReport):
|
||||||
def __init__(self, nodenames, nodeid, fspath, outcome,
|
def __init__(self, nodeid, outcome, longrepr, result, reason):
|
||||||
longrepr, result, reason):
|
|
||||||
self.nodenames = nodenames
|
|
||||||
self.nodeid = nodeid
|
self.nodeid = nodeid
|
||||||
self.fspath = fspath
|
|
||||||
self.outcome = outcome
|
self.outcome = outcome
|
||||||
self.longrepr = longrepr
|
self.longrepr = longrepr
|
||||||
self.result = result or []
|
self.result = result or []
|
||||||
|
@ -255,7 +220,8 @@ class CollectReport(BaseReport):
|
||||||
return (self.fspath, None, self.fspath)
|
return (self.fspath, None, self.fspath)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<CollectReport %r outcome=%r>" % (self.nodeid, self.outcome)
|
return "<CollectReport %r lenresult=%s outcome=%r>" % (
|
||||||
|
self.nodeid, len(self.result), self.outcome)
|
||||||
|
|
||||||
class CollectErrorRepr(TerminalRepr):
|
class CollectErrorRepr(TerminalRepr):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
|
|
|
@ -14,11 +14,15 @@ EXIT_OK = 0
|
||||||
EXIT_TESTSFAILED = 1
|
EXIT_TESTSFAILED = 1
|
||||||
EXIT_INTERRUPTED = 2
|
EXIT_INTERRUPTED = 2
|
||||||
EXIT_INTERNALERROR = 3
|
EXIT_INTERNALERROR = 3
|
||||||
EXIT_NOHOSTS = 4
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
|
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
|
||||||
type="args", default=('.*', 'CVS', '_darcs', '{arch}'))
|
type="args", default=('.*', 'CVS', '_darcs', '{arch}'))
|
||||||
|
#parser.addini("dirpatterns",
|
||||||
|
# "patterns specifying possible locations of test files",
|
||||||
|
# type="linelist", default=["**/test_*.txt",
|
||||||
|
# "**/test_*.py", "**/*_test.py"]
|
||||||
|
#)
|
||||||
group = parser.getgroup("general", "running and selection options")
|
group = parser.getgroup("general", "running and selection options")
|
||||||
group._addoption('-x', '--exitfirst', action="store_true", default=False,
|
group._addoption('-x', '--exitfirst', action="store_true", default=False,
|
||||||
dest="exitfirst",
|
dest="exitfirst",
|
||||||
|
@ -44,12 +48,11 @@ def pytest_addoption(parser):
|
||||||
|
|
||||||
|
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
return dict(collect=dict(Item=Item, Collector=Collector,
|
return dict(collect=dict(Item=Item, Collector=Collector, File=File))
|
||||||
File=File, Directory=Directory))
|
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
py.test.config = config # compatibiltiy
|
py.test.config = config # compatibiltiy
|
||||||
if config.getvalue("exitfirst"):
|
if config.option.exitfirst:
|
||||||
config.option.maxfail = 1
|
config.option.maxfail = 1
|
||||||
|
|
||||||
def pytest_cmdline_main(config):
|
def pytest_cmdline_main(config):
|
||||||
|
@ -84,8 +87,10 @@ def pytest_cmdline_main(config):
|
||||||
def pytest_collection(session):
|
def pytest_collection(session):
|
||||||
collection = session.collection
|
collection = session.collection
|
||||||
assert not hasattr(collection, 'items')
|
assert not hasattr(collection, 'items')
|
||||||
|
|
||||||
|
collection.perform_collect()
|
||||||
hook = session.config.hook
|
hook = session.config.hook
|
||||||
collection.items = items = collection.perform_collect()
|
items = collection.items
|
||||||
hook.pytest_collection_modifyitems(config=session.config, items=items)
|
hook.pytest_collection_modifyitems(config=session.config, items=items)
|
||||||
hook.pytest_collection_finish(collection=collection)
|
hook.pytest_collection_finish(collection=collection)
|
||||||
return True
|
return True
|
||||||
|
@ -108,18 +113,6 @@ def pytest_ignore_collect(path, config):
|
||||||
ignore_paths.extend([py.path.local(x) for x in excludeopt])
|
ignore_paths.extend([py.path.local(x) for x in excludeopt])
|
||||||
return path in ignore_paths
|
return path in ignore_paths
|
||||||
|
|
||||||
def pytest_collect_directory(path, parent):
|
|
||||||
# check if cmdline specified this dir or a subdir directly
|
|
||||||
for arg in parent.collection._argfspaths:
|
|
||||||
if path == arg or arg.relto(path):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
patterns = parent.config.getini("norecursedirs")
|
|
||||||
for pat in patterns or []:
|
|
||||||
if path.check(fnmatch=pat):
|
|
||||||
return
|
|
||||||
return Directory(path, parent=parent)
|
|
||||||
|
|
||||||
class Session(object):
|
class Session(object):
|
||||||
class Interrupted(KeyboardInterrupt):
|
class Interrupted(KeyboardInterrupt):
|
||||||
""" signals an interrupted test run. """
|
""" signals an interrupted test run. """
|
||||||
|
@ -145,160 +138,17 @@ class Session(object):
|
||||||
self._testsfailed)
|
self._testsfailed)
|
||||||
pytest_collectreport = pytest_runtest_logreport
|
pytest_collectreport = pytest_runtest_logreport
|
||||||
|
|
||||||
class Collection:
|
|
||||||
def __init__(self, config):
|
|
||||||
self.config = config
|
|
||||||
self.topdir = gettopdir(self.config.args)
|
|
||||||
self._argfspaths = [py.path.local(decodearg(x)[0])
|
|
||||||
for x in self.config.args]
|
|
||||||
x = pytest.collect.Directory(fspath=self.topdir,
|
|
||||||
config=config, collection=self)
|
|
||||||
self._topcollector = x.consider_dir(self.topdir)
|
|
||||||
self._topcollector.parent = None
|
|
||||||
|
|
||||||
def _normalizearg(self, arg):
|
|
||||||
return "::".join(self._parsearg(arg))
|
|
||||||
|
|
||||||
def _parsearg(self, arg, base=None):
|
|
||||||
""" return normalized name list for a command line specified id
|
|
||||||
which might be of the form x/y/z::name1::name2
|
|
||||||
and should result into the form x::y::z::name1::name2
|
|
||||||
"""
|
|
||||||
if base is None:
|
|
||||||
base = py.path.local()
|
|
||||||
parts = str(arg).split("::")
|
|
||||||
path = base.join(parts[0], abs=True)
|
|
||||||
if not path.check():
|
|
||||||
raise pytest.UsageError("file not found: %s" %(path,))
|
|
||||||
topdir = self.topdir
|
|
||||||
if path != topdir and not path.relto(topdir):
|
|
||||||
raise pytest.UsageError("path %r is not relative to %r" %
|
|
||||||
(str(path), str(topdir)))
|
|
||||||
topparts = path.relto(topdir).split(path.sep)
|
|
||||||
return topparts + parts[1:]
|
|
||||||
|
|
||||||
def getid(self, node):
|
|
||||||
""" return id for node, relative to topdir. """
|
|
||||||
path = node.fspath
|
|
||||||
chain = [x for x in node.listchain() if x.fspath == path]
|
|
||||||
chain = chain[1:]
|
|
||||||
names = [x.name for x in chain if x.name != "()"]
|
|
||||||
relpath = path.relto(self.topdir)
|
|
||||||
if not relpath:
|
|
||||||
assert path == self.topdir
|
|
||||||
path = ''
|
|
||||||
else:
|
|
||||||
path = relpath
|
|
||||||
if os.sep != "/":
|
|
||||||
path = str(path).replace(os.sep, "/")
|
|
||||||
names.insert(0, path)
|
|
||||||
return "::".join(names)
|
|
||||||
|
|
||||||
def getbyid(self, id):
|
|
||||||
""" return one or more nodes matching the id. """
|
|
||||||
names = [x for x in id.split("::") if x]
|
|
||||||
if names and '/' in names[0]:
|
|
||||||
names[:1] = names[0].split("/")
|
|
||||||
return list(self.matchnodes([self._topcollector], names))
|
|
||||||
|
|
||||||
def perform_collect(self):
|
|
||||||
items = []
|
|
||||||
for arg in self.config.args:
|
|
||||||
names = self._parsearg(arg)
|
|
||||||
try:
|
|
||||||
for node in self.matchnodes([self._topcollector], names):
|
|
||||||
items.extend(self.genitems(node))
|
|
||||||
except NoMatch:
|
|
||||||
raise pytest.UsageError("can't collect: %s" % (arg,))
|
|
||||||
return items
|
|
||||||
|
|
||||||
def matchnodes(self, matching, names):
|
|
||||||
if not matching:
|
|
||||||
return
|
|
||||||
if not names:
|
|
||||||
for x in matching:
|
|
||||||
yield x
|
|
||||||
return
|
|
||||||
name = names[0]
|
|
||||||
names = names[1:]
|
|
||||||
for node in matching:
|
|
||||||
if isinstance(node, pytest.collect.Item):
|
|
||||||
if not name:
|
|
||||||
yield node
|
|
||||||
continue
|
|
||||||
assert isinstance(node, pytest.collect.Collector)
|
|
||||||
node.ihook.pytest_collectstart(collector=node)
|
|
||||||
rep = node.ihook.pytest_make_collect_report(collector=node)
|
|
||||||
#print "matching", rep.result, "against name", name
|
|
||||||
if rep.passed:
|
|
||||||
if not name:
|
|
||||||
for x in rep.result:
|
|
||||||
yield x
|
|
||||||
else:
|
|
||||||
matched = False
|
|
||||||
for x in rep.result:
|
|
||||||
try:
|
|
||||||
if x.name == name or x.fspath.basename == name:
|
|
||||||
for x in self.matchnodes([x], names):
|
|
||||||
yield x
|
|
||||||
matched = True
|
|
||||||
elif x.name == "()": # XXX special Instance() case
|
|
||||||
for x in self.matchnodes([x], [name] + names):
|
|
||||||
yield x
|
|
||||||
matched = True
|
|
||||||
except NoMatch:
|
|
||||||
pass
|
|
||||||
if not matched:
|
|
||||||
node.ihook.pytest_collectreport(report=rep)
|
|
||||||
raise NoMatch(name)
|
|
||||||
node.ihook.pytest_collectreport(report=rep)
|
|
||||||
|
|
||||||
def genitems(self, node):
|
|
||||||
if isinstance(node, pytest.collect.Item):
|
|
||||||
node.ihook.pytest_itemcollected(item=node)
|
|
||||||
yield node
|
|
||||||
else:
|
|
||||||
assert isinstance(node, pytest.collect.Collector)
|
|
||||||
node.ihook.pytest_collectstart(collector=node)
|
|
||||||
rep = node.ihook.pytest_make_collect_report(collector=node)
|
|
||||||
if rep.passed:
|
|
||||||
for subnode in rep.result:
|
|
||||||
for x in self.genitems(subnode):
|
|
||||||
yield x
|
|
||||||
node.ihook.pytest_collectreport(report=rep)
|
|
||||||
|
|
||||||
class NoMatch(Exception):
|
class NoMatch(Exception):
|
||||||
""" raised if matching cannot locate a matching names. """
|
""" raised if matching cannot locate a matching names. """
|
||||||
|
|
||||||
def gettopdir(args):
|
|
||||||
""" return the top directory for the given paths.
|
|
||||||
if the common base dir resides in a python package
|
|
||||||
parent directory of the root package is returned.
|
|
||||||
"""
|
|
||||||
fsargs = [py.path.local(decodearg(arg)[0]) for arg in args]
|
|
||||||
p = fsargs and fsargs[0] or None
|
|
||||||
for x in fsargs[1:]:
|
|
||||||
p = p.common(x)
|
|
||||||
assert p, "cannot determine common basedir of %s" %(fsargs,)
|
|
||||||
pkgdir = p.pypkgpath()
|
|
||||||
if pkgdir is None:
|
|
||||||
if p.check(file=1):
|
|
||||||
p = p.dirpath()
|
|
||||||
return p
|
|
||||||
else:
|
|
||||||
return pkgdir.dirpath()
|
|
||||||
|
|
||||||
def decodearg(arg):
|
|
||||||
arg = str(arg)
|
|
||||||
return arg.split("::")
|
|
||||||
|
|
||||||
class HookProxy:
|
class HookProxy:
|
||||||
def __init__(self, node):
|
def __init__(self, fspath, config):
|
||||||
self.node = node
|
self.fspath = fspath
|
||||||
|
self.config = config
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
hookmethod = getattr(self.node.config.hook, name)
|
hookmethod = getattr(self.config.hook, name)
|
||||||
def call_matching_hooks(**kwargs):
|
def call_matching_hooks(**kwargs):
|
||||||
plugins = self.node.config._getmatchingplugins(self.node.fspath)
|
plugins = self.config._getmatchingplugins(self.fspath)
|
||||||
return hookmethod.pcall(plugins, **kwargs)
|
return hookmethod.pcall(plugins, **kwargs)
|
||||||
return call_matching_hooks
|
return call_matching_hooks
|
||||||
|
|
||||||
|
@ -329,7 +179,7 @@ class Node(object):
|
||||||
|
|
||||||
#: the file where this item is contained/collected from.
|
#: the file where this item is contained/collected from.
|
||||||
self.fspath = getattr(parent, 'fspath', None)
|
self.fspath = getattr(parent, 'fspath', None)
|
||||||
self.ihook = HookProxy(self)
|
self.ihook = self.collection.gethookproxy(self.fspath)
|
||||||
self.keywords = {self.name: True}
|
self.keywords = {self.name: True}
|
||||||
|
|
||||||
Module = compatproperty("Module")
|
Module = compatproperty("Module")
|
||||||
|
@ -339,14 +189,19 @@ class Node(object):
|
||||||
Item = compatproperty("Item")
|
Item = compatproperty("Item")
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if getattr(self.config.option, 'debug', False):
|
return "<%s %r>" %(self.__class__.__name__, getattr(self, 'name', None))
|
||||||
return "<%s %r %0x>" %(self.__class__.__name__,
|
|
||||||
getattr(self, 'name', None), id(self))
|
|
||||||
else:
|
|
||||||
return "<%s %r>" %(self.__class__.__name__,
|
|
||||||
getattr(self, 'name', None))
|
|
||||||
|
|
||||||
# methods for ordering nodes
|
# methods for ordering nodes
|
||||||
|
@property
|
||||||
|
def nodeid(self):
|
||||||
|
try:
|
||||||
|
return self._nodeid
|
||||||
|
except AttributeError:
|
||||||
|
self._nodeid = x = self._makeid()
|
||||||
|
return x
|
||||||
|
|
||||||
|
def _makeid(self):
|
||||||
|
return self.parent.nodeid + "::" + self.name
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, Node):
|
if not isinstance(other, Node):
|
||||||
|
@ -447,7 +302,7 @@ class Collector(Node):
|
||||||
|
|
||||||
def _memocollect(self):
|
def _memocollect(self):
|
||||||
""" internal helper method to cache results of calling collect(). """
|
""" internal helper method to cache results of calling collect(). """
|
||||||
return self._memoizedcall('_collected', self.collect)
|
return self._memoizedcall('_collected', lambda: list(self.collect()))
|
||||||
|
|
||||||
def _prunetraceback(self, excinfo):
|
def _prunetraceback(self, excinfo):
|
||||||
if hasattr(self, 'fspath'):
|
if hasattr(self, 'fspath'):
|
||||||
|
@ -460,55 +315,177 @@ class Collector(Node):
|
||||||
|
|
||||||
class FSCollector(Collector):
|
class FSCollector(Collector):
|
||||||
def __init__(self, fspath, parent=None, config=None, collection=None):
|
def __init__(self, fspath, parent=None, config=None, collection=None):
|
||||||
fspath = py.path.local(fspath)
|
fspath = py.path.local(fspath) # xxx only for test_resultlog.py?
|
||||||
super(FSCollector, self).__init__(fspath.basename,
|
name = parent and fspath.relto(parent.fspath) or fspath.basename
|
||||||
parent, config, collection)
|
super(FSCollector, self).__init__(name, parent, config, collection)
|
||||||
self.fspath = fspath
|
self.fspath = fspath
|
||||||
|
|
||||||
|
def _makeid(self):
|
||||||
|
if self == self.collection:
|
||||||
|
return "."
|
||||||
|
relpath = self.collection.fspath.bestrelpath(self.fspath)
|
||||||
|
if os.sep != "/":
|
||||||
|
relpath = str(path).replace(os.sep, "/")
|
||||||
|
return relpath
|
||||||
|
|
||||||
class File(FSCollector):
|
class File(FSCollector):
|
||||||
""" base class for collecting tests from a file. """
|
""" base class for collecting tests from a file. """
|
||||||
|
|
||||||
class Directory(FSCollector):
|
|
||||||
def collect(self):
|
|
||||||
l = []
|
|
||||||
for path in self.fspath.listdir(sort=True):
|
|
||||||
res = self.consider(path)
|
|
||||||
if res is not None:
|
|
||||||
if isinstance(res, (list, tuple)):
|
|
||||||
l.extend(res)
|
|
||||||
else:
|
|
||||||
l.append(res)
|
|
||||||
return l
|
|
||||||
|
|
||||||
def consider(self, path):
|
|
||||||
if self.ihook.pytest_ignore_collect(path=path, config=self.config):
|
|
||||||
return
|
|
||||||
if path.check(file=1):
|
|
||||||
res = self.consider_file(path)
|
|
||||||
elif path.check(dir=1):
|
|
||||||
res = self.consider_dir(path)
|
|
||||||
else:
|
|
||||||
res = None
|
|
||||||
if isinstance(res, list):
|
|
||||||
# throw out identical results
|
|
||||||
l = []
|
|
||||||
for x in res:
|
|
||||||
if x not in l:
|
|
||||||
assert x.parent == self, (x.parent, self)
|
|
||||||
assert x.fspath == path, (x.fspath, path)
|
|
||||||
l.append(x)
|
|
||||||
res = l
|
|
||||||
return res
|
|
||||||
|
|
||||||
def consider_file(self, path):
|
|
||||||
return self.ihook.pytest_collect_file(path=path, parent=self)
|
|
||||||
|
|
||||||
def consider_dir(self, path):
|
|
||||||
return self.ihook.pytest_collect_directory(path=path, parent=self)
|
|
||||||
|
|
||||||
class Item(Node):
|
class Item(Node):
|
||||||
""" a basic test invocation item. Note that for a single function
|
""" a basic test invocation item. Note that for a single function
|
||||||
there might be multiple test invocation items.
|
there might be multiple test invocation items.
|
||||||
"""
|
"""
|
||||||
def reportinfo(self):
|
def reportinfo(self):
|
||||||
return self.fspath, None, ""
|
return self.fspath, None, ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def location(self):
|
||||||
|
try:
|
||||||
|
return self._location
|
||||||
|
except AttributeError:
|
||||||
|
location = self.reportinfo()
|
||||||
|
location = (str(location[0]), location[1], str(location[2]))
|
||||||
|
self._location = location
|
||||||
|
return location
|
||||||
|
|
||||||
|
class Collection(FSCollector):
|
||||||
|
def __init__(self, config):
|
||||||
|
super(Collection, self).__init__(py.path.local(), parent=None,
|
||||||
|
config=config, collection=self)
|
||||||
|
self.trace = config.trace.root.get("collection")
|
||||||
|
self._norecursepatterns = config.getini("norecursedirs")
|
||||||
|
|
||||||
|
def isinitpath(self, path):
|
||||||
|
return path in self._initialpaths
|
||||||
|
|
||||||
|
def gethookproxy(self, fspath):
|
||||||
|
return HookProxy(fspath, self.config)
|
||||||
|
|
||||||
|
def perform_collect(self, args=None, genitems=True):
|
||||||
|
if args is None:
|
||||||
|
args = self.config.args
|
||||||
|
self.trace("perform_collect", self, args)
|
||||||
|
self.trace.root.indent += 1
|
||||||
|
self._notfound = []
|
||||||
|
self._initialpaths = set()
|
||||||
|
self._initialargs = args
|
||||||
|
for arg in args:
|
||||||
|
parts = self._parsearg(arg)
|
||||||
|
self._initialpaths.add(parts[0])
|
||||||
|
self.ihook.pytest_collectstart(collector=self)
|
||||||
|
rep = self.ihook.pytest_make_collect_report(collector=self)
|
||||||
|
self.ihook.pytest_collectreport(report=rep)
|
||||||
|
self.trace.root.indent -= 1
|
||||||
|
if self._notfound:
|
||||||
|
for arg, exc in self._notfound:
|
||||||
|
line = "no name %r in any of %r" % (exc.args[1], exc.args[0])
|
||||||
|
raise pytest.UsageError("not found: %s\n%s" %(arg, line))
|
||||||
|
if not genitems:
|
||||||
|
return rep.result
|
||||||
|
else:
|
||||||
|
self.items = items = []
|
||||||
|
if rep.passed:
|
||||||
|
for node in rep.result:
|
||||||
|
self.items.extend(self.genitems(node))
|
||||||
|
return items
|
||||||
|
|
||||||
|
def collect(self):
|
||||||
|
for arg in self._initialargs:
|
||||||
|
self.trace("processing arg", arg)
|
||||||
|
self.trace.root.indent += 1
|
||||||
|
try:
|
||||||
|
for x in self._collect(arg):
|
||||||
|
yield x
|
||||||
|
except NoMatch:
|
||||||
|
# we are inside a make_report hook so
|
||||||
|
# we cannot directly pass through the exception
|
||||||
|
self._notfound.append((arg, sys.exc_info()[1]))
|
||||||
|
self.trace.root.indent -= 1
|
||||||
|
break
|
||||||
|
self.trace.root.indent -= 1
|
||||||
|
|
||||||
|
def _collect(self, arg):
|
||||||
|
names = self._parsearg(arg)
|
||||||
|
path = names.pop(0)
|
||||||
|
if path.check(dir=1):
|
||||||
|
assert not names, "invalid arg %r" %(arg,)
|
||||||
|
for path in path.visit(rec=self._recurse, bf=True, sort=True):
|
||||||
|
for x in self._collectfile(path):
|
||||||
|
yield x
|
||||||
|
else:
|
||||||
|
assert path.check(file=1)
|
||||||
|
for x in self.matchnodes(self._collectfile(path), names):
|
||||||
|
yield x
|
||||||
|
|
||||||
|
def _collectfile(self, path):
|
||||||
|
ihook = self.gethookproxy(path)
|
||||||
|
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
||||||
|
return ()
|
||||||
|
return ihook.pytest_collect_file(path=path, parent=self)
|
||||||
|
|
||||||
|
def _recurse(self, path):
|
||||||
|
ihook = self.gethookproxy(path)
|
||||||
|
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
||||||
|
return
|
||||||
|
for pat in self._norecursepatterns:
|
||||||
|
if path.check(fnmatch=pat):
|
||||||
|
return False
|
||||||
|
ihook.pytest_collect_directory(path=path, parent=self)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _parsearg(self, arg):
|
||||||
|
""" return (fspath, names) tuple after checking the file exists. """
|
||||||
|
parts = str(arg).split("::")
|
||||||
|
path = self.fspath.join(parts[0], abs=True)
|
||||||
|
if not path.check():
|
||||||
|
raise pytest.UsageError("file not found: %s" %(path,))
|
||||||
|
parts[0] = path
|
||||||
|
return parts
|
||||||
|
|
||||||
|
def matchnodes(self, matching, names):
|
||||||
|
self.trace("matchnodes", matching, names)
|
||||||
|
self.trace.root.indent += 1
|
||||||
|
nodes = self._matchnodes(matching, names)
|
||||||
|
num = len(nodes)
|
||||||
|
self.trace("matchnodes finished -> ", num, "nodes")
|
||||||
|
self.trace.root.indent -= 1
|
||||||
|
if num == 0:
|
||||||
|
raise NoMatch(matching, names[:1])
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def _matchnodes(self, matching, names):
|
||||||
|
if not matching or not names:
|
||||||
|
return matching
|
||||||
|
name = names[0]
|
||||||
|
assert name
|
||||||
|
nextnames = names[1:]
|
||||||
|
resultnodes = []
|
||||||
|
for node in matching:
|
||||||
|
if isinstance(node, pytest.collect.Item):
|
||||||
|
resultnodes.append(node)
|
||||||
|
continue
|
||||||
|
assert isinstance(node, pytest.collect.Collector)
|
||||||
|
node.ihook.pytest_collectstart(collector=node)
|
||||||
|
rep = node.ihook.pytest_make_collect_report(collector=node)
|
||||||
|
if rep.passed:
|
||||||
|
for x in rep.result:
|
||||||
|
if x.name == name:
|
||||||
|
resultnodes.extend(self.matchnodes([x], nextnames))
|
||||||
|
node.ihook.pytest_collectreport(report=rep)
|
||||||
|
return resultnodes
|
||||||
|
|
||||||
|
def genitems(self, node):
|
||||||
|
self.trace("genitems", node)
|
||||||
|
if isinstance(node, pytest.collect.Item):
|
||||||
|
node.ihook.pytest_itemcollected(item=node)
|
||||||
|
yield node
|
||||||
|
else:
|
||||||
|
assert isinstance(node, pytest.collect.Collector)
|
||||||
|
node.ihook.pytest_collectstart(collector=node)
|
||||||
|
rep = node.ihook.pytest_make_collect_report(collector=node)
|
||||||
|
if rep.passed:
|
||||||
|
for subnode in rep.result:
|
||||||
|
for x in self.genitems(subnode):
|
||||||
|
yield x
|
||||||
|
node.ihook.pytest_collectreport(report=rep)
|
||||||
|
|
||||||
|
|
|
@ -115,10 +115,10 @@ class TerminalReporter:
|
||||||
def write_fspath_result(self, fspath, res):
|
def write_fspath_result(self, fspath, res):
|
||||||
if fspath != self.currentfspath:
|
if fspath != self.currentfspath:
|
||||||
self.currentfspath = fspath
|
self.currentfspath = fspath
|
||||||
fspath = self.curdir.bestrelpath(fspath)
|
#fspath = self.curdir.bestrelpath(fspath)
|
||||||
self._tw.line()
|
self._tw.line()
|
||||||
relpath = self.curdir.bestrelpath(fspath)
|
#relpath = self.curdir.bestrelpath(fspath)
|
||||||
self._tw.write(relpath + " ")
|
self._tw.write(fspath + " ")
|
||||||
self._tw.write(res)
|
self._tw.write(res)
|
||||||
|
|
||||||
def write_ensure_prefix(self, prefix, extra="", **kwargs):
|
def write_ensure_prefix(self, prefix, extra="", **kwargs):
|
||||||
|
@ -163,14 +163,15 @@ class TerminalReporter:
|
||||||
def pytest__teardown_final_logerror(self, report):
|
def pytest__teardown_final_logerror(self, report):
|
||||||
self.stats.setdefault("error", []).append(report)
|
self.stats.setdefault("error", []).append(report)
|
||||||
|
|
||||||
def pytest_runtest_logstart(self, nodeid, location, fspath):
|
def pytest_runtest_logstart(self, nodeid, location):
|
||||||
# ensure that the path is printed before the
|
# ensure that the path is printed before the
|
||||||
# 1st test of a module starts running
|
# 1st test of a module starts running
|
||||||
|
fspath = nodeid.split("::")[0]
|
||||||
if self.showlongtestinfo:
|
if self.showlongtestinfo:
|
||||||
line = self._locationline(fspath, *location)
|
line = self._locationline(fspath, *location)
|
||||||
self.write_ensure_prefix(line, "")
|
self.write_ensure_prefix(line, "")
|
||||||
elif self.showfspath:
|
elif self.showfspath:
|
||||||
self.write_fspath_result(py.path.local(fspath), "")
|
self.write_fspath_result(fspath, "")
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report):
|
||||||
rep = report
|
rep = report
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -22,7 +22,7 @@ def main():
|
||||||
name='pytest',
|
name='pytest',
|
||||||
description='py.test: simple powerful testing with Python',
|
description='py.test: simple powerful testing with Python',
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version='2.0.0.dev18',
|
version='2.0.0.dev19',
|
||||||
url='http://pytest.org',
|
url='http://pytest.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -76,14 +76,13 @@ class TestGeneralUsage:
|
||||||
p1 = testdir.makepyfile("")
|
p1 = testdir.makepyfile("")
|
||||||
p2 = testdir.makefile(".pyc", "123")
|
p2 = testdir.makefile(".pyc", "123")
|
||||||
result = testdir.runpytest(p1, p2)
|
result = testdir.runpytest(p1, p2)
|
||||||
assert result.ret != 0
|
assert result.ret
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*ERROR: can't collect:*%s" %(p2.basename,)
|
"*ERROR: not found:*%s" %(p2.basename,)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@py.test.mark.xfail
|
|
||||||
def test_early_skip(self, testdir):
|
def test_early_skip(self, testdir):
|
||||||
testdir.mkdir("xyz")
|
testdir.mkdir("xyz")
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
|
@ -97,7 +96,6 @@ class TestGeneralUsage:
|
||||||
"*1 skip*"
|
"*1 skip*"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_issue88_initial_file_multinodes(self, testdir):
|
def test_issue88_initial_file_multinodes(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import py
|
import py
|
||||||
|
@ -145,7 +143,7 @@ class TestGeneralUsage:
|
||||||
print (py.__file__)
|
print (py.__file__)
|
||||||
print (py.__path__)
|
print (py.__path__)
|
||||||
os.chdir(os.path.dirname(os.getcwd()))
|
os.chdir(os.path.dirname(os.getcwd()))
|
||||||
print (py.log.Producer)
|
print (py.log)
|
||||||
"""))
|
"""))
|
||||||
result = testdir.runpython(p, prepend=False)
|
result = testdir.runpython(p, prepend=False)
|
||||||
assert not result.ret
|
assert not result.ret
|
||||||
|
@ -211,6 +209,27 @@ class TestGeneralUsage:
|
||||||
assert res.ret == 0
|
assert res.ret == 0
|
||||||
res.stdout.fnmatch_lines(["*1 skipped*"])
|
res.stdout.fnmatch_lines(["*1 skipped*"])
|
||||||
|
|
||||||
|
def test_direct_addressing_selects(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def pytest_generate_tests(metafunc):
|
||||||
|
metafunc.addcall({'i': 1}, id="1")
|
||||||
|
metafunc.addcall({'i': 2}, id="2")
|
||||||
|
def test_func(i):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
res = testdir.runpytest(p.basename + "::" + "test_func[1]")
|
||||||
|
assert res.ret == 0
|
||||||
|
res.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
def test_direct_addressing_notfound(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def test_func():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
res = testdir.runpytest(p.basename + "::" + "test_notfound")
|
||||||
|
assert res.ret
|
||||||
|
res.stderr.fnmatch_lines(["*ERROR*not found*"])
|
||||||
|
|
||||||
class TestInvocationVariants:
|
class TestInvocationVariants:
|
||||||
def test_earlyinit(self, testdir):
|
def test_earlyinit(self, testdir):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
|
|
|
@ -9,11 +9,13 @@ def runandparse(testdir, *args):
|
||||||
return result, xmldoc
|
return result, xmldoc
|
||||||
|
|
||||||
def assert_attr(node, **kwargs):
|
def assert_attr(node, **kwargs):
|
||||||
|
__tracebackhide__ = True
|
||||||
for name, expected in kwargs.items():
|
for name, expected in kwargs.items():
|
||||||
anode = node.getAttributeNode(name)
|
anode = node.getAttributeNode(name)
|
||||||
assert anode, "node %r has no attribute %r" %(node, name)
|
assert anode, "node %r has no attribute %r" %(node, name)
|
||||||
val = anode.value
|
val = anode.value
|
||||||
assert val == str(expected)
|
if val != str(expected):
|
||||||
|
py.test.fail("%r != %r" %(str(val), str(expected)))
|
||||||
|
|
||||||
class TestPython:
|
class TestPython:
|
||||||
def test_summing_simple(self, testdir):
|
def test_summing_simple(self, testdir):
|
||||||
|
@ -50,7 +52,7 @@ class TestPython:
|
||||||
assert_attr(node, errors=1, tests=0)
|
assert_attr(node, errors=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_setup_error.test_setup_error",
|
classname="test_setup_error",
|
||||||
name="test_function")
|
name="test_function")
|
||||||
fnode = tnode.getElementsByTagName("error")[0]
|
fnode = tnode.getElementsByTagName("error")[0]
|
||||||
assert_attr(fnode, message="test setup failure")
|
assert_attr(fnode, message="test setup failure")
|
||||||
|
@ -68,9 +70,21 @@ class TestPython:
|
||||||
assert_attr(node, failures=1)
|
assert_attr(node, failures=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_classname_instance.test_classname_instance.TestClass",
|
classname="test_classname_instance.TestClass",
|
||||||
name="test_method")
|
name="test_method")
|
||||||
|
|
||||||
|
def test_classname_nested_dir(self, testdir):
|
||||||
|
p = testdir.tmpdir.ensure("sub", "test_hello.py")
|
||||||
|
p.write("def test_func(): 0/0")
|
||||||
|
result, dom = runandparse(testdir)
|
||||||
|
assert result.ret
|
||||||
|
node = dom.getElementsByTagName("testsuite")[0]
|
||||||
|
assert_attr(node, failures=1)
|
||||||
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
|
assert_attr(tnode,
|
||||||
|
classname="sub.test_hello",
|
||||||
|
name="test_func")
|
||||||
|
|
||||||
def test_internal_error(self, testdir):
|
def test_internal_error(self, testdir):
|
||||||
testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0")
|
testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0")
|
||||||
testdir.makepyfile("def test_function(): pass")
|
testdir.makepyfile("def test_function(): pass")
|
||||||
|
@ -92,7 +106,7 @@ class TestPython:
|
||||||
assert_attr(node, failures=1, tests=1)
|
assert_attr(node, failures=1, tests=1)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_failure_function.test_failure_function",
|
classname="test_failure_function",
|
||||||
name="test_fail")
|
name="test_fail")
|
||||||
fnode = tnode.getElementsByTagName("failure")[0]
|
fnode = tnode.getElementsByTagName("failure")[0]
|
||||||
assert_attr(fnode, message="test failure")
|
assert_attr(fnode, message="test failure")
|
||||||
|
@ -112,11 +126,11 @@ class TestPython:
|
||||||
assert_attr(node, failures=2, tests=2)
|
assert_attr(node, failures=2, tests=2)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_failure_escape.test_failure_escape",
|
classname="test_failure_escape",
|
||||||
name="test_func[<]")
|
name="test_func[<]")
|
||||||
tnode = node.getElementsByTagName("testcase")[1]
|
tnode = node.getElementsByTagName("testcase")[1]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_failure_escape.test_failure_escape",
|
classname="test_failure_escape",
|
||||||
name="test_func[&]")
|
name="test_func[&]")
|
||||||
|
|
||||||
def test_junit_prefixing(self, testdir):
|
def test_junit_prefixing(self, testdir):
|
||||||
|
@ -133,11 +147,11 @@ class TestPython:
|
||||||
assert_attr(node, failures=1, tests=2)
|
assert_attr(node, failures=1, tests=2)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="xyz.test_junit_prefixing.test_junit_prefixing",
|
classname="xyz.test_junit_prefixing",
|
||||||
name="test_func")
|
name="test_func")
|
||||||
tnode = node.getElementsByTagName("testcase")[1]
|
tnode = node.getElementsByTagName("testcase")[1]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="xyz.test_junit_prefixing.test_junit_prefixing."
|
classname="xyz.test_junit_prefixing."
|
||||||
"TestHello",
|
"TestHello",
|
||||||
name="test_hello")
|
name="test_hello")
|
||||||
|
|
||||||
|
@ -153,7 +167,7 @@ class TestPython:
|
||||||
assert_attr(node, skips=1, tests=0)
|
assert_attr(node, skips=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_xfailure_function.test_xfailure_function",
|
classname="test_xfailure_function",
|
||||||
name="test_xfail")
|
name="test_xfail")
|
||||||
fnode = tnode.getElementsByTagName("skipped")[0]
|
fnode = tnode.getElementsByTagName("skipped")[0]
|
||||||
assert_attr(fnode, message="expected test failure")
|
assert_attr(fnode, message="expected test failure")
|
||||||
|
@ -172,7 +186,7 @@ class TestPython:
|
||||||
assert_attr(node, skips=1, tests=0)
|
assert_attr(node, skips=1, tests=0)
|
||||||
tnode = node.getElementsByTagName("testcase")[0]
|
tnode = node.getElementsByTagName("testcase")[0]
|
||||||
assert_attr(tnode,
|
assert_attr(tnode,
|
||||||
classname="test_xfailure_xpass.test_xfailure_xpass",
|
classname="test_xfailure_xpass",
|
||||||
name="test_xpass")
|
name="test_xpass")
|
||||||
fnode = tnode.getElementsByTagName("skipped")[0]
|
fnode = tnode.getElementsByTagName("skipped")[0]
|
||||||
assert_attr(fnode, message="xfail-marked test passes unexpectedly")
|
assert_attr(fnode, message="xfail-marked test passes unexpectedly")
|
||||||
|
|
|
@ -235,7 +235,7 @@ class TestFunction:
|
||||||
param = 1
|
param = 1
|
||||||
funcargs = {}
|
funcargs = {}
|
||||||
id = "world"
|
id = "world"
|
||||||
collection = object()
|
collection = testdir.Collection(config)
|
||||||
f5 = py.test.collect.Function(name="name", config=config,
|
f5 = py.test.collect.Function(name="name", config=config,
|
||||||
callspec=callspec1, callobj=isinstance, collection=collection)
|
callspec=callspec1, callobj=isinstance, collection=collection)
|
||||||
f5b = py.test.collect.Function(name="name", config=config,
|
f5b = py.test.collect.Function(name="name", config=config,
|
||||||
|
@ -395,8 +395,8 @@ def test_generate_tests_only_done_in_subdir(testdir):
|
||||||
|
|
||||||
def test_modulecol_roundtrip(testdir):
|
def test_modulecol_roundtrip(testdir):
|
||||||
modcol = testdir.getmodulecol("pass", withinit=True)
|
modcol = testdir.getmodulecol("pass", withinit=True)
|
||||||
trail = modcol.collection.getid(modcol)
|
trail = modcol.nodeid
|
||||||
newcol = modcol.collection.getbyid(trail)[0]
|
newcol = modcol.collection.perform_collect([trail], genitems=0)[0]
|
||||||
assert modcol.name == newcol.name
|
assert modcol.name == newcol.name
|
||||||
|
|
||||||
|
|
||||||
|
@ -1058,8 +1058,7 @@ class TestReportInfo:
|
||||||
""")
|
""")
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
runner = item.config.pluginmanager.getplugin("runner")
|
runner = item.config.pluginmanager.getplugin("runner")
|
||||||
nodeinfo = runner.getitemnodeinfo(item)
|
assert item.location == ("ABCDE", 42, "custom")
|
||||||
assert nodeinfo.location == ("ABCDE", 42, "custom")
|
|
||||||
|
|
||||||
def test_func_reportinfo(self, testdir):
|
def test_func_reportinfo(self, testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
|
|
|
@ -257,9 +257,9 @@ class TestCollectionReports:
|
||||||
assert not rep.skipped
|
assert not rep.skipped
|
||||||
assert rep.passed
|
assert rep.passed
|
||||||
locinfo = rep.location
|
locinfo = rep.location
|
||||||
assert locinfo[0] == col.fspath
|
assert locinfo[0] == col.fspath.basename
|
||||||
assert not locinfo[1]
|
assert not locinfo[1]
|
||||||
assert locinfo[2] == col.fspath
|
assert locinfo[2] == col.fspath.basename
|
||||||
res = rep.result
|
res = rep.result
|
||||||
assert len(res) == 2
|
assert len(res) == 2
|
||||||
assert res[0].name == "test_func1"
|
assert res[0].name == "test_func1"
|
||||||
|
|
|
@ -17,13 +17,14 @@ class SessionTests:
|
||||||
assert len(skipped) == 0
|
assert len(skipped) == 0
|
||||||
assert len(passed) == 1
|
assert len(passed) == 1
|
||||||
assert len(failed) == 3
|
assert len(failed) == 3
|
||||||
assert failed[0].nodenames[-1] == "test_one_one"
|
end = lambda x: x.nodeid.split("::")[-1]
|
||||||
assert failed[1].nodenames[-1] == "test_other"
|
assert end(failed[0]) == "test_one_one"
|
||||||
assert failed[2].nodenames[-1] == "test_two"
|
assert end(failed[1]) == "test_other"
|
||||||
|
assert end(failed[2]) == "test_two"
|
||||||
itemstarted = reprec.getcalls("pytest_itemcollected")
|
itemstarted = reprec.getcalls("pytest_itemcollected")
|
||||||
assert len(itemstarted) == 4
|
assert len(itemstarted) == 4
|
||||||
colstarted = reprec.getcalls("pytest_collectstart")
|
colstarted = reprec.getcalls("pytest_collectstart")
|
||||||
assert len(colstarted) == 1 + 1 # XXX ExtraTopCollector
|
assert len(colstarted) == 1 + 1
|
||||||
col = colstarted[1].collector
|
col = colstarted[1].collector
|
||||||
assert isinstance(col, py.test.collect.Module)
|
assert isinstance(col, py.test.collect.Module)
|
||||||
|
|
||||||
|
@ -186,7 +187,7 @@ class TestNewSession(SessionTests):
|
||||||
started = reprec.getcalls("pytest_collectstart")
|
started = reprec.getcalls("pytest_collectstart")
|
||||||
finished = reprec.getreports("pytest_collectreport")
|
finished = reprec.getreports("pytest_collectreport")
|
||||||
assert len(started) == len(finished)
|
assert len(started) == len(finished)
|
||||||
assert len(started) == 8 + 1 # XXX extra TopCollector
|
assert len(started) == 8 # XXX extra TopCollector
|
||||||
colfail = [x for x in finished if x.failed]
|
colfail = [x for x in finished if x.failed]
|
||||||
colskipped = [x for x in finished if x.skipped]
|
colskipped = [x for x in finished if x.skipped]
|
||||||
assert len(colfail) == 1
|
assert len(colfail) == 1
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
"""
|
"""
|
||||||
terminal reporting of the full testing process.
|
terminal reporting of the full testing process.
|
||||||
"""
|
"""
|
||||||
import py
|
import pytest,py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# ===============================================================================
|
|
||||||
# plugin tests
|
|
||||||
#
|
|
||||||
# ===============================================================================
|
|
||||||
|
|
||||||
from pytest.plugin.terminal import TerminalReporter, \
|
from pytest.plugin.terminal import TerminalReporter, \
|
||||||
CollectonlyReporter, repr_pythonversion, getreportopt
|
CollectonlyReporter, repr_pythonversion, getreportopt
|
||||||
from pytest.plugin import runner
|
from pytest.plugin import runner
|
||||||
|
@ -95,9 +90,8 @@ class TestTerminal:
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
tr = TerminalReporter(item.config, file=linecomp.stringio)
|
tr = TerminalReporter(item.config, file=linecomp.stringio)
|
||||||
item.config.pluginmanager.register(tr)
|
item.config.pluginmanager.register(tr)
|
||||||
nodeid = item.collection.getid(item)
|
|
||||||
location = item.reportinfo()
|
location = item.reportinfo()
|
||||||
tr.config.hook.pytest_runtest_logstart(nodeid=nodeid,
|
tr.config.hook.pytest_runtest_logstart(nodeid=item.nodeid,
|
||||||
location=location, fspath=str(item.fspath))
|
location=location, fspath=str(item.fspath))
|
||||||
linecomp.assert_contains_lines([
|
linecomp.assert_contains_lines([
|
||||||
"*test_show_runtest_logstart.py*"
|
"*test_show_runtest_logstart.py*"
|
||||||
|
@ -424,6 +418,7 @@ class TestTerminalFunctional:
|
||||||
"*test_verbose_reporting.py:10: test_gen*FAIL*",
|
"*test_verbose_reporting.py:10: test_gen*FAIL*",
|
||||||
])
|
])
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
|
pytest.xfail("repair xdist")
|
||||||
pytestconfig.pluginmanager.skipifmissing("xdist")
|
pytestconfig.pluginmanager.skipifmissing("xdist")
|
||||||
result = testdir.runpytest(p1, '-v', '-n 1')
|
result = testdir.runpytest(p1, '-v', '-n 1')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
|
|
|
@ -1,288 +0,0 @@
|
||||||
import py
|
|
||||||
|
|
||||||
class TestCollector:
|
|
||||||
def test_collect_versus_item(self):
|
|
||||||
from pytest.collect import Collector, Item
|
|
||||||
assert not issubclass(Collector, Item)
|
|
||||||
assert not issubclass(Item, Collector)
|
|
||||||
|
|
||||||
def test_compat_attributes(self, testdir, recwarn):
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
def test_pass(): pass
|
|
||||||
def test_fail(): assert 0
|
|
||||||
""")
|
|
||||||
recwarn.clear()
|
|
||||||
assert modcol.Module == py.test.collect.Module
|
|
||||||
recwarn.pop(DeprecationWarning)
|
|
||||||
assert modcol.Class == py.test.collect.Class
|
|
||||||
recwarn.pop(DeprecationWarning)
|
|
||||||
assert modcol.Item == py.test.collect.Item
|
|
||||||
recwarn.pop(DeprecationWarning)
|
|
||||||
assert modcol.File == py.test.collect.File
|
|
||||||
recwarn.pop(DeprecationWarning)
|
|
||||||
assert modcol.Function == py.test.collect.Function
|
|
||||||
recwarn.pop(DeprecationWarning)
|
|
||||||
|
|
||||||
def test_check_equality(self, testdir):
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
def test_pass(): pass
|
|
||||||
def test_fail(): assert 0
|
|
||||||
""")
|
|
||||||
fn1 = testdir.collect_by_name(modcol, "test_pass")
|
|
||||||
assert isinstance(fn1, py.test.collect.Function)
|
|
||||||
fn2 = testdir.collect_by_name(modcol, "test_pass")
|
|
||||||
assert isinstance(fn2, py.test.collect.Function)
|
|
||||||
|
|
||||||
assert fn1 == fn2
|
|
||||||
assert fn1 != modcol
|
|
||||||
if py.std.sys.version_info < (3, 0):
|
|
||||||
assert cmp(fn1, fn2) == 0
|
|
||||||
assert hash(fn1) == hash(fn2)
|
|
||||||
|
|
||||||
fn3 = testdir.collect_by_name(modcol, "test_fail")
|
|
||||||
assert isinstance(fn3, py.test.collect.Function)
|
|
||||||
assert not (fn1 == fn3)
|
|
||||||
assert fn1 != fn3
|
|
||||||
|
|
||||||
for fn in fn1,fn2,fn3:
|
|
||||||
assert fn != 3
|
|
||||||
assert fn != modcol
|
|
||||||
assert fn != [1,2,3]
|
|
||||||
assert [1,2,3] != fn
|
|
||||||
assert modcol != fn
|
|
||||||
|
|
||||||
def test_getparent(self, testdir):
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
class TestClass:
|
|
||||||
def test_foo():
|
|
||||||
pass
|
|
||||||
""")
|
|
||||||
cls = testdir.collect_by_name(modcol, "TestClass")
|
|
||||||
fn = testdir.collect_by_name(
|
|
||||||
testdir.collect_by_name(cls, "()"), "test_foo")
|
|
||||||
|
|
||||||
parent = fn.getparent(py.test.collect.Module)
|
|
||||||
assert parent is modcol
|
|
||||||
|
|
||||||
parent = fn.getparent(py.test.collect.Function)
|
|
||||||
assert parent is fn
|
|
||||||
|
|
||||||
parent = fn.getparent(py.test.collect.Class)
|
|
||||||
assert parent is cls
|
|
||||||
|
|
||||||
|
|
||||||
def test_getcustomfile_roundtrip(self, testdir):
|
|
||||||
hello = testdir.makefile(".xxx", hello="world")
|
|
||||||
testdir.makepyfile(conftest="""
|
|
||||||
import py
|
|
||||||
class CustomFile(py.test.collect.File):
|
|
||||||
pass
|
|
||||||
def pytest_collect_file(path, parent):
|
|
||||||
if path.ext == ".xxx":
|
|
||||||
return CustomFile(path, parent=parent)
|
|
||||||
""")
|
|
||||||
config = testdir.parseconfig(hello)
|
|
||||||
node = testdir.getnode(config, hello)
|
|
||||||
assert isinstance(node, py.test.collect.File)
|
|
||||||
assert node.name == "hello.xxx"
|
|
||||||
id = node.collection.getid(node)
|
|
||||||
nodes = node.collection.getbyid(id)
|
|
||||||
assert len(nodes) == 1
|
|
||||||
assert isinstance(nodes[0], py.test.collect.File)
|
|
||||||
|
|
||||||
class TestCollectFS:
|
|
||||||
def test_ignored_certain_directories(self, testdir):
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
tmpdir.ensure("_darcs", 'test_notfound.py')
|
|
||||||
tmpdir.ensure("CVS", 'test_notfound.py')
|
|
||||||
tmpdir.ensure("{arch}", 'test_notfound.py')
|
|
||||||
tmpdir.ensure(".whatever", 'test_notfound.py')
|
|
||||||
tmpdir.ensure(".bzr", 'test_notfound.py')
|
|
||||||
tmpdir.ensure("normal", 'test_found.py')
|
|
||||||
|
|
||||||
result = testdir.runpytest("--collectonly")
|
|
||||||
s = result.stdout.str()
|
|
||||||
assert "test_notfound" not in s
|
|
||||||
assert "test_found" in s
|
|
||||||
|
|
||||||
def test_custom_norecursedirs(self, testdir):
|
|
||||||
testdir.makeini("""
|
|
||||||
[pytest]
|
|
||||||
norecursedirs = mydir xyz*
|
|
||||||
""")
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass")
|
|
||||||
tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0")
|
|
||||||
tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass")
|
|
||||||
rec = testdir.inline_run()
|
|
||||||
rec.assertoutcome(passed=1)
|
|
||||||
rec = testdir.inline_run("xyz123/test_2.py")
|
|
||||||
rec.assertoutcome(failed=1)
|
|
||||||
|
|
||||||
def test_found_certain_testfiles(self, testdir):
|
|
||||||
p1 = testdir.makepyfile(test_found = "pass", found_test="pass")
|
|
||||||
col = testdir.getnode(testdir.parseconfig(p1), p1.dirpath())
|
|
||||||
items = col.collect() # Directory collect returns files sorted by name
|
|
||||||
assert len(items) == 2
|
|
||||||
assert items[1].name == 'test_found.py'
|
|
||||||
assert items[0].name == 'found_test.py'
|
|
||||||
|
|
||||||
def test_directory_file_sorting(self, testdir):
|
|
||||||
p1 = testdir.makepyfile(test_one="hello")
|
|
||||||
p1.dirpath().mkdir("x")
|
|
||||||
p1.dirpath().mkdir("dir1")
|
|
||||||
testdir.makepyfile(test_two="hello")
|
|
||||||
p1.dirpath().mkdir("dir2")
|
|
||||||
config = testdir.parseconfig()
|
|
||||||
col = testdir.getnode(config, p1.dirpath())
|
|
||||||
names = [x.name for x in col.collect()]
|
|
||||||
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
|
||||||
|
|
||||||
class TestCollectPluginHookRelay:
|
|
||||||
def test_pytest_collect_file(self, testdir):
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
wascalled = []
|
|
||||||
class Plugin:
|
|
||||||
def pytest_collect_file(self, path, parent):
|
|
||||||
wascalled.append(path)
|
|
||||||
config = testdir.Config()
|
|
||||||
config.pluginmanager.register(Plugin())
|
|
||||||
config.parse([tmpdir])
|
|
||||||
col = testdir.getnode(config, tmpdir)
|
|
||||||
testdir.makefile(".abc", "xyz")
|
|
||||||
res = col.collect()
|
|
||||||
assert len(wascalled) == 1
|
|
||||||
assert wascalled[0].ext == '.abc'
|
|
||||||
|
|
||||||
def test_pytest_collect_directory(self, testdir):
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
wascalled = []
|
|
||||||
class Plugin:
|
|
||||||
def pytest_collect_directory(self, path, parent):
|
|
||||||
wascalled.append(path.basename)
|
|
||||||
return py.test.collect.Directory(path, parent)
|
|
||||||
testdir.plugins.append(Plugin())
|
|
||||||
testdir.mkdir("hello")
|
|
||||||
testdir.mkdir("world")
|
|
||||||
reprec = testdir.inline_run()
|
|
||||||
assert "hello" in wascalled
|
|
||||||
assert "world" in wascalled
|
|
||||||
# make sure the directories do not get double-appended
|
|
||||||
colreports = reprec.getreports("pytest_collectreport")
|
|
||||||
names = [rep.nodenames[-1] for rep in colreports]
|
|
||||||
assert names.count("hello") == 1
|
|
||||||
|
|
||||||
class TestPrunetraceback:
|
|
||||||
def test_collection_error(self, testdir):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import not_exists
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest(p)
|
|
||||||
assert "__import__" not in result.stdout.str(), "too long traceback"
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*ERROR collecting*",
|
|
||||||
"*mport*not_exists*"
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_custom_repr_failure(self, testdir):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import not_exists
|
|
||||||
""")
|
|
||||||
testdir.makeconftest("""
|
|
||||||
import py
|
|
||||||
def pytest_collect_file(path, parent):
|
|
||||||
return MyFile(path, parent)
|
|
||||||
class MyError(Exception):
|
|
||||||
pass
|
|
||||||
class MyFile(py.test.collect.File):
|
|
||||||
def collect(self):
|
|
||||||
raise MyError()
|
|
||||||
def repr_failure(self, excinfo):
|
|
||||||
if excinfo.errisinstance(MyError):
|
|
||||||
return "hello world"
|
|
||||||
return py.test.collect.File.repr_failure(self, excinfo)
|
|
||||||
""")
|
|
||||||
|
|
||||||
result = testdir.runpytest(p)
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*ERROR collecting*",
|
|
||||||
"*hello world*",
|
|
||||||
])
|
|
||||||
|
|
||||||
@py.test.mark.xfail(reason="other mechanism for adding to reporting needed")
|
|
||||||
def test_collect_report_postprocessing(self, testdir):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import not_exists
|
|
||||||
""")
|
|
||||||
testdir.makeconftest("""
|
|
||||||
import py
|
|
||||||
def pytest_make_collect_report(__multicall__):
|
|
||||||
rep = __multicall__.execute()
|
|
||||||
rep.headerlines += ["header1"]
|
|
||||||
return rep
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest(p)
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*ERROR collecting*",
|
|
||||||
"*header1*",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class TestCustomConftests:
|
|
||||||
def test_ignore_collect_path(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
def pytest_ignore_collect(path, config):
|
|
||||||
return path.basename.startswith("x") or \
|
|
||||||
path.basename == "test_one.py"
|
|
||||||
""")
|
|
||||||
testdir.mkdir("xy123").ensure("test_hello.py").write(
|
|
||||||
"syntax error"
|
|
||||||
)
|
|
||||||
testdir.makepyfile("def test_hello(): pass")
|
|
||||||
testdir.makepyfile(test_one="syntax error")
|
|
||||||
result = testdir.runpytest()
|
|
||||||
assert result.ret == 0
|
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
|
||||||
|
|
||||||
def test_collectignore_exclude_on_option(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
collect_ignore = ['hello', 'test_world.py']
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
parser.addoption("--XX", action="store_true", default=False)
|
|
||||||
def pytest_configure(config):
|
|
||||||
if config.getvalue("XX"):
|
|
||||||
collect_ignore[:] = []
|
|
||||||
""")
|
|
||||||
testdir.mkdir("hello")
|
|
||||||
testdir.makepyfile(test_world="#")
|
|
||||||
reprec = testdir.inline_run(testdir.tmpdir)
|
|
||||||
names = [rep.nodenames[-1]
|
|
||||||
for rep in reprec.getreports("pytest_collectreport")]
|
|
||||||
assert 'hello' not in names
|
|
||||||
assert 'test_world.py' not in names
|
|
||||||
reprec = testdir.inline_run(testdir.tmpdir, "--XX")
|
|
||||||
names = [rep.nodenames[-1]
|
|
||||||
for rep in reprec.getreports("pytest_collectreport")]
|
|
||||||
assert 'hello' in names
|
|
||||||
assert 'test_world.py' in names
|
|
||||||
|
|
||||||
def test_pytest_fs_collect_hooks_are_seen(self, testdir):
|
|
||||||
conf = testdir.makeconftest("""
|
|
||||||
import py
|
|
||||||
class MyDirectory(py.test.collect.Directory):
|
|
||||||
pass
|
|
||||||
class MyModule(py.test.collect.Module):
|
|
||||||
pass
|
|
||||||
def pytest_collect_directory(path, parent):
|
|
||||||
return MyDirectory(path, parent)
|
|
||||||
def pytest_collect_file(path, parent):
|
|
||||||
return MyModule(path, parent)
|
|
||||||
""")
|
|
||||||
sub = testdir.mkdir("sub")
|
|
||||||
p = testdir.makepyfile("def test_x(): pass")
|
|
||||||
result = testdir.runpytest("--collectonly")
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*MyDirectory*",
|
|
||||||
"*MyModule*",
|
|
||||||
"*test_x*"
|
|
||||||
])
|
|
|
@ -1,6 +1,283 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
from pytest.plugin.session import Collection, gettopdir
|
from pytest.plugin.session import Collection
|
||||||
|
|
||||||
|
class TestCollector:
|
||||||
|
def test_collect_versus_item(self):
|
||||||
|
from pytest.collect import Collector, Item
|
||||||
|
assert not issubclass(Collector, Item)
|
||||||
|
assert not issubclass(Item, Collector)
|
||||||
|
|
||||||
|
def test_compat_attributes(self, testdir, recwarn):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
def test_pass(): pass
|
||||||
|
def test_fail(): assert 0
|
||||||
|
""")
|
||||||
|
recwarn.clear()
|
||||||
|
assert modcol.Module == py.test.collect.Module
|
||||||
|
recwarn.pop(DeprecationWarning)
|
||||||
|
assert modcol.Class == py.test.collect.Class
|
||||||
|
recwarn.pop(DeprecationWarning)
|
||||||
|
assert modcol.Item == py.test.collect.Item
|
||||||
|
recwarn.pop(DeprecationWarning)
|
||||||
|
assert modcol.File == py.test.collect.File
|
||||||
|
recwarn.pop(DeprecationWarning)
|
||||||
|
assert modcol.Function == py.test.collect.Function
|
||||||
|
recwarn.pop(DeprecationWarning)
|
||||||
|
|
||||||
|
def test_check_equality(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
def test_pass(): pass
|
||||||
|
def test_fail(): assert 0
|
||||||
|
""")
|
||||||
|
fn1 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
|
assert isinstance(fn1, py.test.collect.Function)
|
||||||
|
fn2 = testdir.collect_by_name(modcol, "test_pass")
|
||||||
|
assert isinstance(fn2, py.test.collect.Function)
|
||||||
|
|
||||||
|
assert fn1 == fn2
|
||||||
|
assert fn1 != modcol
|
||||||
|
if py.std.sys.version_info < (3, 0):
|
||||||
|
assert cmp(fn1, fn2) == 0
|
||||||
|
assert hash(fn1) == hash(fn2)
|
||||||
|
|
||||||
|
fn3 = testdir.collect_by_name(modcol, "test_fail")
|
||||||
|
assert isinstance(fn3, py.test.collect.Function)
|
||||||
|
assert not (fn1 == fn3)
|
||||||
|
assert fn1 != fn3
|
||||||
|
|
||||||
|
for fn in fn1,fn2,fn3:
|
||||||
|
assert fn != 3
|
||||||
|
assert fn != modcol
|
||||||
|
assert fn != [1,2,3]
|
||||||
|
assert [1,2,3] != fn
|
||||||
|
assert modcol != fn
|
||||||
|
|
||||||
|
def test_getparent(self, testdir):
|
||||||
|
modcol = testdir.getmodulecol("""
|
||||||
|
class TestClass:
|
||||||
|
def test_foo():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
cls = testdir.collect_by_name(modcol, "TestClass")
|
||||||
|
fn = testdir.collect_by_name(
|
||||||
|
testdir.collect_by_name(cls, "()"), "test_foo")
|
||||||
|
|
||||||
|
parent = fn.getparent(py.test.collect.Module)
|
||||||
|
assert parent is modcol
|
||||||
|
|
||||||
|
parent = fn.getparent(py.test.collect.Function)
|
||||||
|
assert parent is fn
|
||||||
|
|
||||||
|
parent = fn.getparent(py.test.collect.Class)
|
||||||
|
assert parent is cls
|
||||||
|
|
||||||
|
|
||||||
|
def test_getcustomfile_roundtrip(self, testdir):
|
||||||
|
hello = testdir.makefile(".xxx", hello="world")
|
||||||
|
testdir.makepyfile(conftest="""
|
||||||
|
import py
|
||||||
|
class CustomFile(py.test.collect.File):
|
||||||
|
pass
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".xxx":
|
||||||
|
return CustomFile(path, parent=parent)
|
||||||
|
""")
|
||||||
|
node = testdir.getpathnode(hello)
|
||||||
|
assert isinstance(node, py.test.collect.File)
|
||||||
|
assert node.name == "hello.xxx"
|
||||||
|
nodes = node.collection.perform_collect([node.nodeid], genitems=False)
|
||||||
|
assert len(nodes) == 1
|
||||||
|
assert isinstance(nodes[0], py.test.collect.File)
|
||||||
|
|
||||||
|
class TestCollectFS:
|
||||||
|
def test_ignored_certain_directories(self, testdir):
|
||||||
|
tmpdir = testdir.tmpdir
|
||||||
|
tmpdir.ensure("_darcs", 'test_notfound.py')
|
||||||
|
tmpdir.ensure("CVS", 'test_notfound.py')
|
||||||
|
tmpdir.ensure("{arch}", 'test_notfound.py')
|
||||||
|
tmpdir.ensure(".whatever", 'test_notfound.py')
|
||||||
|
tmpdir.ensure(".bzr", 'test_notfound.py')
|
||||||
|
tmpdir.ensure("normal", 'test_found.py')
|
||||||
|
|
||||||
|
result = testdir.runpytest("--collectonly")
|
||||||
|
s = result.stdout.str()
|
||||||
|
assert "test_notfound" not in s
|
||||||
|
assert "test_found" in s
|
||||||
|
|
||||||
|
def test_custom_norecursedirs(self, testdir):
|
||||||
|
testdir.makeini("""
|
||||||
|
[pytest]
|
||||||
|
norecursedirs = mydir xyz*
|
||||||
|
""")
|
||||||
|
tmpdir = testdir.tmpdir
|
||||||
|
tmpdir.ensure("mydir", "test_hello.py").write("def test_1(): pass")
|
||||||
|
tmpdir.ensure("xyz123", "test_2.py").write("def test_2(): 0/0")
|
||||||
|
tmpdir.ensure("xy", "test_ok.py").write("def test_3(): pass")
|
||||||
|
rec = testdir.inline_run()
|
||||||
|
rec.assertoutcome(passed=1)
|
||||||
|
rec = testdir.inline_run("xyz123/test_2.py")
|
||||||
|
rec.assertoutcome(failed=1)
|
||||||
|
|
||||||
|
class TestCollectPluginHookRelay:
|
||||||
|
def test_pytest_collect_file(self, testdir):
|
||||||
|
wascalled = []
|
||||||
|
class Plugin:
|
||||||
|
def pytest_collect_file(self, path, parent):
|
||||||
|
wascalled.append(path)
|
||||||
|
testdir.makefile(".abc", "xyz")
|
||||||
|
testdir.pytestmain([testdir.tmpdir], plugins=[Plugin()])
|
||||||
|
assert len(wascalled) == 1
|
||||||
|
assert wascalled[0].ext == '.abc'
|
||||||
|
|
||||||
|
def test_pytest_collect_directory(self, testdir):
|
||||||
|
wascalled = []
|
||||||
|
class Plugin:
|
||||||
|
def pytest_collect_directory(self, path, parent):
|
||||||
|
wascalled.append(path.basename)
|
||||||
|
testdir.mkdir("hello")
|
||||||
|
testdir.mkdir("world")
|
||||||
|
testdir.pytestmain(testdir.tmpdir, plugins=[Plugin()])
|
||||||
|
assert "hello" in wascalled
|
||||||
|
assert "world" in wascalled
|
||||||
|
|
||||||
|
class TestPrunetraceback:
|
||||||
|
def test_collection_error(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import not_exists
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
assert "__import__" not in result.stdout.str(), "too long traceback"
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ERROR collecting*",
|
||||||
|
"*mport*not_exists*"
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_custom_repr_failure(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import not_exists
|
||||||
|
""")
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
return MyFile(path, parent)
|
||||||
|
class MyError(Exception):
|
||||||
|
pass
|
||||||
|
class MyFile(py.test.collect.File):
|
||||||
|
def collect(self):
|
||||||
|
raise MyError()
|
||||||
|
def repr_failure(self, excinfo):
|
||||||
|
if excinfo.errisinstance(MyError):
|
||||||
|
return "hello world"
|
||||||
|
return py.test.collect.File.repr_failure(self, excinfo)
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ERROR collecting*",
|
||||||
|
"*hello world*",
|
||||||
|
])
|
||||||
|
|
||||||
|
@py.test.mark.xfail(reason="other mechanism for adding to reporting needed")
|
||||||
|
def test_collect_report_postprocessing(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import not_exists
|
||||||
|
""")
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
def pytest_make_collect_report(__multicall__):
|
||||||
|
rep = __multicall__.execute()
|
||||||
|
rep.headerlines += ["header1"]
|
||||||
|
return rep
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ERROR collecting*",
|
||||||
|
"*header1*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class TestCustomConftests:
|
||||||
|
def test_ignore_collect_path(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
def pytest_ignore_collect(path, config):
|
||||||
|
return path.basename.startswith("x") or \
|
||||||
|
path.basename == "test_one.py"
|
||||||
|
""")
|
||||||
|
testdir.mkdir("xy123").ensure("test_hello.py").write(
|
||||||
|
"syntax error"
|
||||||
|
)
|
||||||
|
testdir.makepyfile("def test_hello(): pass")
|
||||||
|
testdir.makepyfile(test_one="syntax error")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
def test_collectignore_exclude_on_option(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
collect_ignore = ['hello', 'test_world.py']
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
parser.addoption("--XX", action="store_true", default=False)
|
||||||
|
def pytest_configure(config):
|
||||||
|
if config.getvalue("XX"):
|
||||||
|
collect_ignore[:] = []
|
||||||
|
""")
|
||||||
|
testdir.mkdir("hello")
|
||||||
|
testdir.makepyfile(test_world="def test_hello(): pass")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
assert result.ret == 0
|
||||||
|
assert "passed" not in result.stdout.str()
|
||||||
|
result = testdir.runpytest("--XX")
|
||||||
|
assert result.ret == 0
|
||||||
|
assert "passed" in result.stdout.str()
|
||||||
|
|
||||||
|
def test_pytest_fs_collect_hooks_are_seen(self, testdir):
|
||||||
|
conf = testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
class MyModule(py.test.collect.Module):
|
||||||
|
pass
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".py":
|
||||||
|
return MyModule(path, parent)
|
||||||
|
""")
|
||||||
|
sub = testdir.mkdir("sub")
|
||||||
|
p = testdir.makepyfile("def test_x(): pass")
|
||||||
|
result = testdir.runpytest("--collectonly")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*MyModule*",
|
||||||
|
"*test_x*"
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_pytest_collect_file_from_sister_dir(self, testdir):
|
||||||
|
sub1 = testdir.mkpydir("sub1")
|
||||||
|
sub2 = testdir.mkpydir("sub2")
|
||||||
|
conf1 = testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
class MyModule1(py.test.collect.Module):
|
||||||
|
pass
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".py":
|
||||||
|
return MyModule1(path, parent)
|
||||||
|
""")
|
||||||
|
conf1.move(sub1.join(conf1.basename))
|
||||||
|
conf2 = testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
class MyModule2(py.test.collect.Module):
|
||||||
|
pass
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.ext == ".py":
|
||||||
|
return MyModule2(path, parent)
|
||||||
|
""")
|
||||||
|
conf2.move(sub2.join(conf2.basename))
|
||||||
|
p = testdir.makepyfile("def test_x(): pass")
|
||||||
|
p.copy(sub1.join(p.basename))
|
||||||
|
p.copy(sub2.join(p.basename))
|
||||||
|
result = testdir.runpytest("--collectonly")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*MyModule1*",
|
||||||
|
"*MyModule2*",
|
||||||
|
"*test_x*"
|
||||||
|
])
|
||||||
|
|
||||||
class TestCollection:
|
class TestCollection:
|
||||||
def test_parsearg(self, testdir):
|
def test_parsearg(self, testdir):
|
||||||
|
@ -13,16 +290,15 @@ class TestCollection:
|
||||||
subdir.chdir()
|
subdir.chdir()
|
||||||
config = testdir.parseconfig(p.basename)
|
config = testdir.parseconfig(p.basename)
|
||||||
rcol = Collection(config=config)
|
rcol = Collection(config=config)
|
||||||
assert rcol.topdir == testdir.tmpdir
|
assert rcol.fspath == subdir
|
||||||
parts = rcol._parsearg(p.basename)
|
parts = rcol._parsearg(p.basename)
|
||||||
assert parts[0] == "sub"
|
|
||||||
assert parts[1] == p.basename
|
assert parts[0] == target
|
||||||
assert len(parts) == 2
|
assert len(parts) == 1
|
||||||
parts = rcol._parsearg(p.basename + "::test_func")
|
parts = rcol._parsearg(p.basename + "::test_func")
|
||||||
assert parts[0] == "sub"
|
assert parts[0] == target
|
||||||
assert parts[1] == p.basename
|
assert parts[1] == "test_func"
|
||||||
assert parts[2] == "test_func"
|
assert len(parts) == 2
|
||||||
assert len(parts) == 3
|
|
||||||
|
|
||||||
def test_collect_topdir(self, testdir):
|
def test_collect_topdir(self, testdir):
|
||||||
p = testdir.makepyfile("def test_func(): pass")
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
@ -30,14 +306,14 @@ class TestCollection:
|
||||||
config = testdir.parseconfig(id)
|
config = testdir.parseconfig(id)
|
||||||
topdir = testdir.tmpdir
|
topdir = testdir.tmpdir
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
assert topdir == rcol.topdir
|
assert topdir == rcol.fspath
|
||||||
hookrec = testdir.getreportrecorder(config)
|
rootid = rcol.nodeid
|
||||||
items = rcol.perform_collect()
|
#root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
|
||||||
assert len(items) == 1
|
#assert root2 == rcol, rootid
|
||||||
root = items[0].listchain()[0]
|
colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
|
||||||
root_id = rcol.getid(root)
|
assert len(colitems) == 1
|
||||||
root2 = rcol.getbyid(root_id)[0]
|
assert colitems[0].fspath == p
|
||||||
assert root2.fspath == root.fspath
|
|
||||||
|
|
||||||
def test_collect_protocol_single_function(self, testdir):
|
def test_collect_protocol_single_function(self, testdir):
|
||||||
p = testdir.makepyfile("def test_func(): pass")
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
@ -45,13 +321,14 @@ class TestCollection:
|
||||||
config = testdir.parseconfig(id)
|
config = testdir.parseconfig(id)
|
||||||
topdir = testdir.tmpdir
|
topdir = testdir.tmpdir
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
assert topdir == rcol.topdir
|
assert topdir == rcol.fspath
|
||||||
hookrec = testdir.getreportrecorder(config)
|
hookrec = testdir.getreportrecorder(config)
|
||||||
items = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
|
items = rcol.items
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
item = items[0]
|
item = items[0]
|
||||||
assert item.name == "test_func"
|
assert item.name == "test_func"
|
||||||
newid = rcol.getid(item)
|
newid = item.nodeid
|
||||||
assert newid == id
|
assert newid == id
|
||||||
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
hookrec.hookrecorder.contains([
|
hookrec.hookrecorder.contains([
|
||||||
|
@ -60,8 +337,8 @@ class TestCollection:
|
||||||
("pytest_collectstart", "collector.fspath == p"),
|
("pytest_collectstart", "collector.fspath == p"),
|
||||||
("pytest_make_collect_report", "collector.fspath == p"),
|
("pytest_make_collect_report", "collector.fspath == p"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.fspath == p"),
|
("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
|
||||||
("pytest_collectreport", "report.fspath == topdir")
|
("pytest_collectreport", "report.nodeid == '.'")
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collect_protocol_method(self, testdir):
|
def test_collect_protocol_method(self, testdir):
|
||||||
|
@ -70,19 +347,19 @@ class TestCollection:
|
||||||
def test_method(self):
|
def test_method(self):
|
||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
normid = p.basename + "::TestClass::test_method"
|
normid = p.basename + "::TestClass::()::test_method"
|
||||||
for id in [p.basename,
|
for id in [p.basename,
|
||||||
p.basename + "::TestClass",
|
p.basename + "::TestClass",
|
||||||
p.basename + "::TestClass::()",
|
p.basename + "::TestClass::()",
|
||||||
p.basename + "::TestClass::()::test_method",
|
|
||||||
normid,
|
normid,
|
||||||
]:
|
]:
|
||||||
config = testdir.parseconfig(id)
|
config = testdir.parseconfig(id)
|
||||||
rcol = Collection(config=config)
|
rcol = Collection(config=config)
|
||||||
nodes = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
assert len(nodes) == 1
|
items = rcol.items
|
||||||
assert nodes[0].name == "test_method"
|
assert len(items) == 1
|
||||||
newid = rcol.getid(nodes[0])
|
assert items[0].name == "test_method"
|
||||||
|
newid = items[0].nodeid
|
||||||
assert newid == normid
|
assert newid == normid
|
||||||
|
|
||||||
def test_collect_custom_nodes_multi_id(self, testdir):
|
def test_collect_custom_nodes_multi_id(self, testdir):
|
||||||
|
@ -104,20 +381,21 @@ class TestCollection:
|
||||||
config = testdir.parseconfig(id)
|
config = testdir.parseconfig(id)
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
hookrec = testdir.getreportrecorder(config)
|
hookrec = testdir.getreportrecorder(config)
|
||||||
items = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
|
items = rcol.items
|
||||||
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
assert len(items) == 2
|
assert len(items) == 2
|
||||||
hookrec.hookrecorder.contains([
|
hookrec.hookrecorder.contains([
|
||||||
("pytest_collectstart",
|
("pytest_collectstart",
|
||||||
"collector.fspath == collector.collection.topdir"),
|
"collector.fspath == collector.collection.fspath"),
|
||||||
("pytest_collectstart",
|
("pytest_collectstart",
|
||||||
"collector.__class__.__name__ == 'SpecialFile'"),
|
"collector.__class__.__name__ == 'SpecialFile'"),
|
||||||
("pytest_collectstart",
|
("pytest_collectstart",
|
||||||
"collector.__class__.__name__ == 'Module'"),
|
"collector.__class__.__name__ == 'Module'"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.fspath == p"),
|
("pytest_collectreport", "report.nodeid.startswith(p.basename)"),
|
||||||
("pytest_collectreport",
|
#("pytest_collectreport",
|
||||||
"report.fspath == %r" % str(rcol.topdir)),
|
# "report.fspath == %r" % str(rcol.fspath)),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collect_subdir_event_ordering(self, testdir):
|
def test_collect_subdir_event_ordering(self, testdir):
|
||||||
|
@ -128,134 +406,87 @@ class TestCollection:
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
hookrec = testdir.getreportrecorder(config)
|
hookrec = testdir.getreportrecorder(config)
|
||||||
items = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
|
items = rcol.items
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
hookrec.hookrecorder.contains([
|
hookrec.hookrecorder.contains([
|
||||||
("pytest_collectstart", "collector.fspath == aaa"),
|
|
||||||
("pytest_collectstart", "collector.fspath == test_aaa"),
|
("pytest_collectstart", "collector.fspath == test_aaa"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.fspath == test_aaa"),
|
("pytest_collectreport",
|
||||||
("pytest_collectreport", "report.fspath == aaa"),
|
"report.nodeid.startswith('aaa/test_aaa.py')"),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collect_two_commandline_args(self, testdir):
|
def test_collect_two_commandline_args(self, testdir):
|
||||||
p = testdir.makepyfile("def test_func(): pass")
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
aaa = testdir.mkpydir("aaa")
|
aaa = testdir.mkpydir("aaa")
|
||||||
bbb = testdir.mkpydir("bbb")
|
bbb = testdir.mkpydir("bbb")
|
||||||
p.copy(aaa.join("test_aaa.py"))
|
test_aaa = aaa.join("test_aaa.py")
|
||||||
p.move(bbb.join("test_bbb.py"))
|
p.copy(test_aaa)
|
||||||
|
test_bbb = bbb.join("test_bbb.py")
|
||||||
|
p.move(test_bbb)
|
||||||
|
|
||||||
id = "."
|
id = "."
|
||||||
config = testdir.parseconfig(id)
|
config = testdir.parseconfig(id)
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
hookrec = testdir.getreportrecorder(config)
|
hookrec = testdir.getreportrecorder(config)
|
||||||
items = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
|
items = rcol.items
|
||||||
assert len(items) == 2
|
assert len(items) == 2
|
||||||
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
hookrec.hookrecorder.contains([
|
hookrec.hookrecorder.contains([
|
||||||
("pytest_collectstart", "collector.fspath == aaa"),
|
("pytest_collectstart", "collector.fspath == test_aaa"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.fspath == aaa"),
|
("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
|
||||||
("pytest_collectstart", "collector.fspath == bbb"),
|
("pytest_collectstart", "collector.fspath == test_bbb"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.fspath == bbb"),
|
("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_serialization_byid(self, testdir):
|
def test_serialization_byid(self, testdir):
|
||||||
p = testdir.makepyfile("def test_func(): pass")
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
rcol = Collection(config)
|
rcol = Collection(config)
|
||||||
items = rcol.perform_collect()
|
rcol.perform_collect()
|
||||||
|
items = rcol.items
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
item, = items
|
item, = items
|
||||||
id = rcol.getid(item)
|
|
||||||
newcol = Collection(config)
|
newcol = Collection(config)
|
||||||
item2, = newcol.getbyid(id)
|
item2, = newcol.perform_collect([item.nodeid], genitems=False)
|
||||||
assert item2.name == item.name
|
assert item2.name == item.name
|
||||||
assert item2.fspath == item.fspath
|
assert item2.fspath == item.fspath
|
||||||
item2b, = newcol.getbyid(id)
|
item2b, = newcol.perform_collect([item.nodeid], genitems=False)
|
||||||
assert item2b is item2
|
assert item2b == item2
|
||||||
|
|
||||||
class Test_gettopdir:
|
|
||||||
def test_gettopdir(self, testdir):
|
|
||||||
tmp = testdir.tmpdir
|
|
||||||
assert gettopdir([tmp]) == tmp
|
|
||||||
topdir = gettopdir([tmp.join("hello"), tmp.join("world")])
|
|
||||||
assert topdir == tmp
|
|
||||||
somefile = tmp.ensure("somefile.py")
|
|
||||||
assert gettopdir([somefile]) == tmp
|
|
||||||
|
|
||||||
def test_gettopdir_pypkg(self, testdir):
|
|
||||||
tmp = testdir.tmpdir
|
|
||||||
a = tmp.ensure('a', dir=1)
|
|
||||||
b = tmp.ensure('a', 'b', '__init__.py')
|
|
||||||
c = tmp.ensure('a', 'b', 'c.py')
|
|
||||||
Z = tmp.ensure('Z', dir=1)
|
|
||||||
assert gettopdir([c]) == a
|
|
||||||
assert gettopdir([c, Z]) == tmp
|
|
||||||
assert gettopdir(["%s::xyc" % c]) == a
|
|
||||||
assert gettopdir(["%s::xyc::abc" % c]) == a
|
|
||||||
assert gettopdir(["%s::xyc" % c, "%s::abc" % Z]) == tmp
|
|
||||||
|
|
||||||
def getargnode(collection, arg):
|
def getargnode(collection, arg):
|
||||||
return collection.getbyid(collection._normalizearg(str(arg)))[0]
|
argpath = arg.relto(collection.fspath)
|
||||||
|
return collection.perform_collect([argpath], genitems=False)[0]
|
||||||
|
|
||||||
class Test_getinitialnodes:
|
class Test_getinitialnodes:
|
||||||
def test_onedir(self, testdir):
|
|
||||||
config = testdir.reparseconfig([testdir.tmpdir])
|
|
||||||
c = Collection(config)
|
|
||||||
col = getargnode(c, testdir.tmpdir)
|
|
||||||
assert isinstance(col, py.test.collect.Directory)
|
|
||||||
for col in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
t2 = getargnode(c, testdir.tmpdir)
|
|
||||||
assert col == t2
|
|
||||||
|
|
||||||
def test_curdir_and_subdir(self, testdir, tmpdir):
|
|
||||||
a = tmpdir.ensure("a", dir=1)
|
|
||||||
config = testdir.reparseconfig([tmpdir, a])
|
|
||||||
c = Collection(config)
|
|
||||||
|
|
||||||
col1 = getargnode(c, tmpdir)
|
|
||||||
col2 = getargnode(c, a)
|
|
||||||
assert col1.name == tmpdir.basename
|
|
||||||
assert col2.name == 'a'
|
|
||||||
for col in (col1, col2):
|
|
||||||
for subcol in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
def test_global_file(self, testdir, tmpdir):
|
def test_global_file(self, testdir, tmpdir):
|
||||||
x = tmpdir.ensure("x.py")
|
x = tmpdir.ensure("x.py")
|
||||||
config = testdir.reparseconfig([x])
|
config = testdir.reparseconfig([x])
|
||||||
col = getargnode(Collection(config), x)
|
col = testdir.getnode(config, x)
|
||||||
assert isinstance(col, py.test.collect.Module)
|
assert isinstance(col, py.test.collect.Module)
|
||||||
assert col.name == 'x.py'
|
assert col.name == 'x.py'
|
||||||
assert col.parent.name == tmpdir.basename
|
assert col.parent.name == testdir.tmpdir.basename
|
||||||
assert col.parent.parent is None
|
assert col.parent.parent is None
|
||||||
for col in col.listchain():
|
for col in col.listchain():
|
||||||
assert col.config is config
|
assert col.config is config
|
||||||
|
|
||||||
def test_global_dir(self, testdir, tmpdir):
|
def test_pkgfile(self, testdir):
|
||||||
x = tmpdir.ensure("a", dir=1)
|
testdir.chdir()
|
||||||
|
tmpdir = testdir.tmpdir
|
||||||
|
subdir = tmpdir.join("subdir")
|
||||||
|
x = subdir.ensure("x.py")
|
||||||
|
subdir.ensure("__init__.py")
|
||||||
config = testdir.reparseconfig([x])
|
config = testdir.reparseconfig([x])
|
||||||
col = getargnode(Collection(config), x)
|
col = testdir.getnode(config, x)
|
||||||
assert isinstance(col, py.test.collect.Directory)
|
|
||||||
print(col.listchain())
|
|
||||||
assert col.name == 'a'
|
|
||||||
assert col.parent is None
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
def test_pkgfile(self, testdir, tmpdir):
|
|
||||||
tmpdir = tmpdir.join("subdir")
|
|
||||||
x = tmpdir.ensure("x.py")
|
|
||||||
tmpdir.ensure("__init__.py")
|
|
||||||
config = testdir.reparseconfig([x])
|
|
||||||
col = getargnode(Collection(config), x)
|
|
||||||
assert isinstance(col, py.test.collect.Module)
|
assert isinstance(col, py.test.collect.Module)
|
||||||
assert col.name == 'x.py'
|
print col.obj
|
||||||
assert col.parent.name == x.dirpath().basename
|
print col.listchain()
|
||||||
assert col.parent.parent.parent is None
|
assert col.name == 'subdir/x.py'
|
||||||
|
assert col.parent.parent is None
|
||||||
for col in col.listchain():
|
for col in col.listchain():
|
||||||
assert col.config is config
|
assert col.config is config
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class TestConfigAPI:
|
||||||
config.trace.root.setwriter(l.append)
|
config.trace.root.setwriter(l.append)
|
||||||
config.trace("hello")
|
config.trace("hello")
|
||||||
assert len(l) == 1
|
assert len(l) == 1
|
||||||
assert l[0] == "[pytest:config] hello\n"
|
assert l[0] == "[pytest] hello\n"
|
||||||
|
|
||||||
def test_config_getvalue_honours_conftest(self, testdir):
|
def test_config_getvalue_honours_conftest(self, testdir):
|
||||||
testdir.makepyfile(conftest="x=1")
|
testdir.makepyfile(conftest="x=1")
|
||||||
|
|
Loading…
Reference in New Issue