refactor all collection related logic
- drop all pickling support (for now) - perform collection completely ahead of test running (no iterativity) - introduce new collection related hooks - shift all keyword-selection code to pytest_keyword plugin - simplify session object - besides: fix issue88 --HG-- branch : trunk
This commit is contained in:
parent
350ebbd9ad
commit
e2683f4538
|
@ -1,3 +1,10 @@
|
||||||
|
|
||||||
|
Changes between 1.3.4 and 1.4.0a1
|
||||||
|
==================================================
|
||||||
|
|
||||||
|
- major refactoring of internal collection handling
|
||||||
|
- fix issue88 (finding custom test nodes from command line arg)
|
||||||
|
|
||||||
Changes between 1.3.3 and 1.3.4
|
Changes between 1.3.3 and 1.3.4
|
||||||
==================================================
|
==================================================
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ dictionary or an import path.
|
||||||
|
|
||||||
(c) Holger Krekel and others, 2004-2010
|
(c) Holger Krekel and others, 2004-2010
|
||||||
"""
|
"""
|
||||||
__version__ = version = "1.3.4"
|
__version__ = version = "1.4.0a1"
|
||||||
|
|
||||||
import py.apipkg
|
import py.apipkg
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ py.apipkg.initpkg(__name__, dict(
|
||||||
'_fillfuncargs' : '._test.funcargs:fillfuncargs',
|
'_fillfuncargs' : '._test.funcargs:fillfuncargs',
|
||||||
},
|
},
|
||||||
'cmdline': {
|
'cmdline': {
|
||||||
'main' : '._test.cmdline:main', # backward compat
|
'main' : '._test.session:main', # backward compat
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,10 @@ def pytest_configure(config):
|
||||||
and all plugins and initial conftest files been loaded.
|
and all plugins and initial conftest files been loaded.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def pytest_cmdline_main(config):
|
||||||
|
""" called for performing the main (cmdline) action. """
|
||||||
|
pytest_cmdline_main.firstresult = True
|
||||||
|
|
||||||
def pytest_unconfigure(config):
|
def pytest_unconfigure(config):
|
||||||
""" called before test process is exited. """
|
""" called before test process is exited. """
|
||||||
|
|
||||||
|
@ -27,6 +31,15 @@ def pytest_unconfigure(config):
|
||||||
# collection hooks
|
# collection hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pytest_log_startcollection(collection):
|
||||||
|
""" called before collection.perform_collection() is called. """
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(collection):
|
||||||
|
""" called to allow filtering and selecting of test items (inplace). """
|
||||||
|
|
||||||
|
def pytest_log_finishcollection(collection):
|
||||||
|
""" called after collection has finished. """
|
||||||
|
|
||||||
def pytest_ignore_collect(path, config):
|
def pytest_ignore_collect(path, config):
|
||||||
""" return true value to prevent considering this path for collection.
|
""" return true value to prevent considering this path for collection.
|
||||||
This hook is consulted for all files and directories prior to considering
|
This hook is consulted for all files and directories prior to considering
|
||||||
|
@ -41,9 +54,13 @@ pytest_collect_directory.firstresult = True
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
""" return Collection node or None for the given path. """
|
""" return Collection node or None for the given path. """
|
||||||
|
|
||||||
|
# logging hooks for collection
|
||||||
def pytest_collectstart(collector):
|
def pytest_collectstart(collector):
|
||||||
""" collector starts collecting. """
|
""" collector starts collecting. """
|
||||||
|
|
||||||
|
def pytest_log_itemcollect(item):
|
||||||
|
""" we just collected a test item. """
|
||||||
|
|
||||||
def pytest_collectreport(report):
|
def pytest_collectreport(report):
|
||||||
""" collector finished collecting. """
|
""" collector finished collecting. """
|
||||||
|
|
||||||
|
@ -54,10 +71,6 @@ def pytest_make_collect_report(collector):
|
||||||
""" perform a collection and return a collection. """
|
""" perform a collection and return a collection. """
|
||||||
pytest_make_collect_report.firstresult = True
|
pytest_make_collect_report.firstresult = True
|
||||||
|
|
||||||
# XXX rename to item_collected()? meaning in distribution context?
|
|
||||||
def pytest_itemstart(item, node=None):
|
|
||||||
""" test item gets collected. """
|
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# Python test function related hooks
|
# Python test function related hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
@ -85,6 +98,9 @@ def pytest_generate_tests(metafunc):
|
||||||
# generic runtest related hooks
|
# generic runtest related hooks
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pytest_itemstart(item, node=None):
|
||||||
|
""" test item starts running. """
|
||||||
|
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item):
|
||||||
""" implement fixture, run and report about the given test item. """
|
""" implement fixture, run and report about the given test item. """
|
||||||
pytest_runtest_protocol.firstresult = True
|
pytest_runtest_protocol.firstresult = True
|
||||||
|
|
|
@ -87,6 +87,28 @@ class HookRecorder:
|
||||||
l.append(call)
|
l.append(call)
|
||||||
return l
|
return l
|
||||||
|
|
||||||
|
def contains(self, entries):
|
||||||
|
from py.builtin import print_
|
||||||
|
i = 0
|
||||||
|
entries = list(entries)
|
||||||
|
backlocals = py.std.sys._getframe(1).f_locals
|
||||||
|
while entries:
|
||||||
|
name, check = entries.pop(0)
|
||||||
|
for ind, call in enumerate(self.calls[i:]):
|
||||||
|
if call._name == name:
|
||||||
|
print_("NAMEMATCH", name, call)
|
||||||
|
if eval(check, backlocals, call.__dict__):
|
||||||
|
print_("CHECKERMATCH", repr(check), "->", call)
|
||||||
|
else:
|
||||||
|
print_("NOCHECKERMATCH", repr(check), "-", call)
|
||||||
|
continue
|
||||||
|
i += ind + 1
|
||||||
|
break
|
||||||
|
print_("NONAMEMATCH", name, "with", call)
|
||||||
|
else:
|
||||||
|
raise AssertionError("could not find %r in %r" %(
|
||||||
|
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):
|
||||||
if call._name == name:
|
if call._name == name:
|
||||||
|
|
|
@ -3,6 +3,22 @@
|
||||||
import sys
|
import sys
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
def pytest_cmdline_main(config):
|
||||||
|
from py._test.session import Session, Collection
|
||||||
|
exitstatus = 0
|
||||||
|
if config.option.showfuncargs:
|
||||||
|
from py._test.funcargs import showfuncargs
|
||||||
|
session = showfuncargs(config)
|
||||||
|
else:
|
||||||
|
collection = Collection(config)
|
||||||
|
# instantiate session already because it
|
||||||
|
# records failures and implements maxfail handling
|
||||||
|
session = Session(config, collection)
|
||||||
|
exitstatus = collection.do_collection()
|
||||||
|
if not exitstatus:
|
||||||
|
exitstatus = session.main()
|
||||||
|
return exitstatus
|
||||||
|
|
||||||
def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
def pytest_pyfunc_call(__multicall__, pyfuncitem):
|
||||||
if not __multicall__.execute():
|
if not __multicall__.execute():
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
|
@ -16,7 +32,7 @@ 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 pb.startswith("test_") or pb.endswith("_test") or \
|
||||||
path in parent.config._argfspaths:
|
path in parent.collection._argfspaths:
|
||||||
if ext == ".py":
|
if ext == ".py":
|
||||||
return parent.ihook.pytest_pycollect_makemodule(
|
return parent.ihook.pytest_pycollect_makemodule(
|
||||||
path=path, parent=parent)
|
path=path, parent=parent)
|
||||||
|
@ -49,7 +65,7 @@ def pytest_collect_directory(path, parent):
|
||||||
# define Directory(dir) already
|
# define Directory(dir) already
|
||||||
if not parent.recfilter(path): # by default special ".cvs", ...
|
if not parent.recfilter(path): # by default special ".cvs", ...
|
||||||
# check if cmdline specified this dir or a subdir directly
|
# check if cmdline specified this dir or a subdir directly
|
||||||
for arg in parent.config._argfspaths:
|
for arg in parent.collection._argfspaths:
|
||||||
if path == arg or arg.relto(path):
|
if path == arg or arg.relto(path):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -68,12 +84,6 @@ def pytest_addoption(parser):
|
||||||
group._addoption('--maxfail', metavar="num",
|
group._addoption('--maxfail', metavar="num",
|
||||||
action="store", type="int", dest="maxfail", default=0,
|
action="store", type="int", dest="maxfail", default=0,
|
||||||
help="exit after first num failures or errors.")
|
help="exit after first num failures or errors.")
|
||||||
group._addoption('-k',
|
|
||||||
action="store", dest="keyword", default='',
|
|
||||||
help="only run test items matching the given "
|
|
||||||
"space separated keywords. precede a keyword with '-' to negate. "
|
|
||||||
"Terminate the expression with ':' to treat a match as a signal "
|
|
||||||
"to run all subsequent tests. ")
|
|
||||||
|
|
||||||
group = parser.getgroup("collect", "collection")
|
group = parser.getgroup("collect", "collection")
|
||||||
group.addoption('--collectonly',
|
group.addoption('--collectonly',
|
||||||
|
@ -91,17 +101,10 @@ def pytest_addoption(parser):
|
||||||
help="base temporary directory for this test run.")
|
help="base temporary directory for this test run.")
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
setsession(config)
|
|
||||||
# compat
|
# compat
|
||||||
if config.getvalue("exitfirst"):
|
if config.getvalue("exitfirst"):
|
||||||
config.option.maxfail = 1
|
config.option.maxfail = 1
|
||||||
|
|
||||||
def setsession(config):
|
|
||||||
val = config.getvalue
|
|
||||||
if val("collectonly"):
|
|
||||||
from py._test.session import Session
|
|
||||||
config.setsessionclass(Session)
|
|
||||||
|
|
||||||
# pycollect related hooks and code, should move to pytest_pycollect.py
|
# pycollect related hooks and code, should move to pytest_pycollect.py
|
||||||
|
|
||||||
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
def pytest_pycollect_makeitem(__multicall__, collector, name, obj):
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
def pytest_addoption(parser):
|
||||||
|
group = parser.getgroup("general")
|
||||||
|
group._addoption('-k',
|
||||||
|
action="store", dest="keyword", default='',
|
||||||
|
help="only run test items matching the given "
|
||||||
|
"space separated keywords. precede a keyword with '-' to negate. "
|
||||||
|
"Terminate the expression with ':' to treat a match as a signal "
|
||||||
|
"to run all subsequent tests. ")
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(collection):
|
||||||
|
config = collection.config
|
||||||
|
keywordexpr = config.option.keyword
|
||||||
|
if not keywordexpr:
|
||||||
|
return
|
||||||
|
selectuntil = False
|
||||||
|
if keywordexpr[-1] == ":":
|
||||||
|
selectuntil = True
|
||||||
|
keywordexpr = keywordexpr[:-1]
|
||||||
|
|
||||||
|
remaining = []
|
||||||
|
deselected = []
|
||||||
|
for colitem in collection.items:
|
||||||
|
if keywordexpr and skipbykeyword(colitem, keywordexpr):
|
||||||
|
deselected.append(colitem)
|
||||||
|
else:
|
||||||
|
remaining.append(colitem)
|
||||||
|
if selectuntil:
|
||||||
|
keywordexpr = None
|
||||||
|
|
||||||
|
if deselected:
|
||||||
|
config.hook.pytest_deselected(items=deselected)
|
||||||
|
collection.items[:] = remaining
|
||||||
|
|
||||||
|
def skipbykeyword(colitem, keywordexpr):
|
||||||
|
""" return True if they given keyword expression means to
|
||||||
|
skip this collector/item.
|
||||||
|
"""
|
||||||
|
if not keywordexpr:
|
||||||
|
return
|
||||||
|
chain = colitem.listchain()
|
||||||
|
for key in filter(None, keywordexpr.split()):
|
||||||
|
eor = key[:1] == '-'
|
||||||
|
if eor:
|
||||||
|
key = key[1:]
|
||||||
|
if not (eor ^ matchonekeyword(key, chain)):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def matchonekeyword(key, chain):
|
||||||
|
elems = key.split(".")
|
||||||
|
# XXX O(n^2), anyone cares?
|
||||||
|
chain = [item.keywords for item in chain if item.keywords]
|
||||||
|
for start, _ in enumerate(chain):
|
||||||
|
if start + len(elems) > len(chain):
|
||||||
|
return False
|
||||||
|
for num, elem in enumerate(elems):
|
||||||
|
for keyword in chain[num + start]:
|
||||||
|
ok = False
|
||||||
|
if elem in keyword:
|
||||||
|
ok = True
|
||||||
|
break
|
||||||
|
if not ok:
|
||||||
|
break
|
||||||
|
if num == len(elems) - 1 and ok:
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -74,10 +74,8 @@ class TmpTestdir:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TmpTestdir %r>" % (self.tmpdir,)
|
return "<TmpTestdir %r>" % (self.tmpdir,)
|
||||||
|
|
||||||
def Config(self, topdir=None):
|
def Config(self):
|
||||||
if topdir is None:
|
return pytestConfig()
|
||||||
topdir = self.tmpdir.dirpath()
|
|
||||||
return pytestConfig(topdir=topdir)
|
|
||||||
|
|
||||||
def finalize(self):
|
def finalize(self):
|
||||||
for p in self._syspathremove:
|
for p in self._syspathremove:
|
||||||
|
@ -149,16 +147,23 @@ class TmpTestdir:
|
||||||
p.ensure("__init__.py")
|
p.ensure("__init__.py")
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
def getnode(self, config, arg):
|
||||||
|
from py._test.session import Collection
|
||||||
|
collection = Collection(config)
|
||||||
|
return collection.getbyid(collection._normalizearg(arg))[0]
|
||||||
|
|
||||||
def genitems(self, colitems):
|
def genitems(self, colitems):
|
||||||
return list(self.session.genitems(colitems))
|
collection = colitems[0].collection
|
||||||
|
result = []
|
||||||
|
collection.genitems(colitems, (), result)
|
||||||
|
return result
|
||||||
|
|
||||||
def inline_genitems(self, *args):
|
def inline_genitems(self, *args):
|
||||||
#config = self.parseconfig(*args)
|
#config = self.parseconfig(*args)
|
||||||
config = self.parseconfig(*args)
|
from py._test.session import Collection
|
||||||
session = config.initsession()
|
config = self.parseconfigure(*args)
|
||||||
rec = self.getreportrecorder(config)
|
rec = self.getreportrecorder(config)
|
||||||
colitems = [config.getnode(arg) for arg in config.args]
|
items = Collection(config).perform_collect()
|
||||||
items = list(session.genitems(colitems))
|
|
||||||
return items, rec
|
return items, rec
|
||||||
|
|
||||||
def runitem(self, source):
|
def runitem(self, source):
|
||||||
|
@ -187,11 +192,9 @@ class TmpTestdir:
|
||||||
def inline_run(self, *args):
|
def inline_run(self, *args):
|
||||||
args = ("-s", ) + args # otherwise FD leakage
|
args = ("-s", ) + args # otherwise FD leakage
|
||||||
config = self.parseconfig(*args)
|
config = self.parseconfig(*args)
|
||||||
config.pluginmanager.do_configure(config)
|
|
||||||
session = config.initsession()
|
|
||||||
reprec = self.getreportrecorder(config)
|
reprec = self.getreportrecorder(config)
|
||||||
colitems = config.getinitialnodes()
|
config.pluginmanager.do_configure(config)
|
||||||
session.main(colitems)
|
config.hook.pytest_cmdline_main(config=config)
|
||||||
config.pluginmanager.do_unconfigure(config)
|
config.pluginmanager.do_unconfigure(config)
|
||||||
return reprec
|
return reprec
|
||||||
|
|
||||||
|
@ -245,29 +248,17 @@ class TmpTestdir:
|
||||||
|
|
||||||
def getitems(self, source):
|
def getitems(self, source):
|
||||||
modcol = self.getmodulecol(source)
|
modcol = self.getmodulecol(source)
|
||||||
return list(modcol.config.initsession().genitems([modcol]))
|
return self.genitems([modcol])
|
||||||
#assert item is not None, "%r item not found in module:\n%s" %(funcname, source)
|
|
||||||
#return item
|
|
||||||
|
|
||||||
def getfscol(self, path, configargs=()):
|
|
||||||
self.config = self.parseconfig(path, *configargs)
|
|
||||||
self.session = self.config.initsession()
|
|
||||||
return self.config.getnode(path)
|
|
||||||
|
|
||||||
def getmodulecol(self, source, configargs=(), withinit=False):
|
def getmodulecol(self, source, configargs=(), withinit=False):
|
||||||
kw = {self.request.function.__name__: py.code.Source(source).strip()}
|
kw = {self.request.function.__name__: py.code.Source(source).strip()}
|
||||||
path = self.makepyfile(**kw)
|
path = self.makepyfile(**kw)
|
||||||
if withinit:
|
if withinit:
|
||||||
self.makepyfile(__init__ = "#")
|
self.makepyfile(__init__ = "#")
|
||||||
self.config = self.parseconfig(path, *configargs)
|
self.config = config = self.parseconfigure(path, *configargs)
|
||||||
self.session = self.config.initsession()
|
node = self.getnode(config, path)
|
||||||
#self.config.pluginmanager.do_configure(config=self.config)
|
#config.pluginmanager.do_unconfigure(config)
|
||||||
# XXX
|
return node
|
||||||
self.config.pluginmanager.import_plugin("runner")
|
|
||||||
plugin = self.config.pluginmanager.getplugin("runner")
|
|
||||||
plugin.pytest_configure(config=self.config)
|
|
||||||
|
|
||||||
return self.config.getnode(path)
|
|
||||||
|
|
||||||
def popen(self, cmdargs, stdout, stderr, **kw):
|
def popen(self, cmdargs, stdout, stderr, **kw):
|
||||||
if not hasattr(py.std, 'subprocess'):
|
if not hasattr(py.std, 'subprocess'):
|
||||||
|
|
|
@ -35,7 +35,6 @@ def pytest_configure(config):
|
||||||
if config.option.collectonly:
|
if config.option.collectonly:
|
||||||
reporter = CollectonlyReporter(config)
|
reporter = CollectonlyReporter(config)
|
||||||
elif config.option.showfuncargs:
|
elif config.option.showfuncargs:
|
||||||
config.setsessionclass(ShowFuncargSession)
|
|
||||||
reporter = None
|
reporter = None
|
||||||
else:
|
else:
|
||||||
reporter = TerminalReporter(config)
|
reporter = TerminalReporter(config)
|
||||||
|
@ -170,7 +169,7 @@ class TerminalReporter:
|
||||||
self.write_line("[%s] %s" %(category, msg))
|
self.write_line("[%s] %s" %(category, msg))
|
||||||
|
|
||||||
def pytest_deselected(self, items):
|
def pytest_deselected(self, items):
|
||||||
self.stats.setdefault('deselected', []).append(items)
|
self.stats.setdefault('deselected', []).extend(items)
|
||||||
|
|
||||||
def pytest_itemstart(self, item, node=None):
|
def pytest_itemstart(self, item, node=None):
|
||||||
if self.config.option.verbose:
|
if self.config.option.verbose:
|
||||||
|
@ -383,21 +382,27 @@ class CollectonlyReporter:
|
||||||
self.outindent(collector)
|
self.outindent(collector)
|
||||||
self.indent += self.INDENT
|
self.indent += self.INDENT
|
||||||
|
|
||||||
def pytest_itemstart(self, item, node=None):
|
def pytest_log_itemcollect(self, item):
|
||||||
self.outindent(item)
|
self.outindent(item)
|
||||||
|
|
||||||
def pytest_collectreport(self, report):
|
def pytest_collectreport(self, report):
|
||||||
if not report.passed:
|
if not report.passed:
|
||||||
self.outindent("!!! %s !!!" % report.longrepr.reprcrash.message)
|
if hasattr(report.longrepr, 'reprcrash'):
|
||||||
|
msg = report.longrepr.reprcrash.message
|
||||||
|
else:
|
||||||
|
# XXX unify (we have CollectErrorRepr here)
|
||||||
|
msg = str(report.longrepr.longrepr)
|
||||||
|
self.outindent("!!! %s !!!" % msg)
|
||||||
|
#self.outindent("!!! error !!!")
|
||||||
self._failed.append(report)
|
self._failed.append(report)
|
||||||
self.indent = self.indent[:-len(self.INDENT)]
|
self.indent = self.indent[:-len(self.INDENT)]
|
||||||
|
|
||||||
def pytest_sessionfinish(self, session, exitstatus):
|
def pytest_log_finishcollection(self):
|
||||||
if self._failed:
|
if self._failed:
|
||||||
self._tw.sep("!", "collection failures")
|
self._tw.sep("!", "collection failures")
|
||||||
for rep in self._failed:
|
for rep in self._failed:
|
||||||
rep.toterminal(self._tw)
|
rep.toterminal(self._tw)
|
||||||
|
return self._failed and 1 or 0
|
||||||
|
|
||||||
def repr_pythonversion(v=None):
|
def repr_pythonversion(v=None):
|
||||||
if v is None:
|
if v is None:
|
||||||
|
@ -415,50 +420,3 @@ def flatten(l):
|
||||||
else:
|
else:
|
||||||
yield x
|
yield x
|
||||||
|
|
||||||
from py._test.session import Session
|
|
||||||
class ShowFuncargSession(Session):
|
|
||||||
def main(self, colitems):
|
|
||||||
self.fspath = py.path.local()
|
|
||||||
self.sessionstarts()
|
|
||||||
try:
|
|
||||||
self.showargs(colitems[0])
|
|
||||||
finally:
|
|
||||||
self.sessionfinishes(exitstatus=1)
|
|
||||||
|
|
||||||
def showargs(self, colitem):
|
|
||||||
tw = py.io.TerminalWriter()
|
|
||||||
from py._test.funcargs import getplugins
|
|
||||||
from py._test.funcargs import FuncargRequest
|
|
||||||
plugins = getplugins(colitem, withpy=True)
|
|
||||||
verbose = self.config.getvalue("verbose")
|
|
||||||
for plugin in plugins:
|
|
||||||
available = []
|
|
||||||
for name, factory in vars(plugin).items():
|
|
||||||
if name.startswith(FuncargRequest._argprefix):
|
|
||||||
name = name[len(FuncargRequest._argprefix):]
|
|
||||||
if name not in available:
|
|
||||||
available.append([name, factory])
|
|
||||||
if available:
|
|
||||||
pluginname = plugin.__name__
|
|
||||||
for name, factory in available:
|
|
||||||
loc = self.getlocation(factory)
|
|
||||||
if verbose:
|
|
||||||
funcargspec = "%s -- %s" %(name, loc,)
|
|
||||||
else:
|
|
||||||
funcargspec = name
|
|
||||||
tw.line(funcargspec, green=True)
|
|
||||||
doc = factory.__doc__ or ""
|
|
||||||
if doc:
|
|
||||||
for line in doc.split("\n"):
|
|
||||||
tw.line(" " + line.strip())
|
|
||||||
else:
|
|
||||||
tw.line(" %s: no docstring available" %(loc,),
|
|
||||||
red=True)
|
|
||||||
|
|
||||||
def getlocation(self, function):
|
|
||||||
import inspect
|
|
||||||
fn = py.path.local(inspect.getfile(function))
|
|
||||||
lineno = py.builtin._getcode(function).co_firstlineno
|
|
||||||
if fn.relto(self.fspath):
|
|
||||||
fn = fn.relto(self.fspath)
|
|
||||||
return "%s:%d" %(fn, lineno+1)
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
import py
|
|
||||||
import sys
|
|
||||||
|
|
||||||
#
|
|
||||||
# main entry point
|
|
||||||
#
|
|
||||||
|
|
||||||
def main(args=None):
|
|
||||||
if args is None:
|
|
||||||
args = sys.argv[1:]
|
|
||||||
config = py.test.config
|
|
||||||
try:
|
|
||||||
config.parse(args)
|
|
||||||
config.pluginmanager.do_configure(config)
|
|
||||||
session = config.initsession()
|
|
||||||
colitems = config.getinitialnodes()
|
|
||||||
exitstatus = session.main(colitems)
|
|
||||||
config.pluginmanager.do_unconfigure(config)
|
|
||||||
except config.Error:
|
|
||||||
e = sys.exc_info()[1]
|
|
||||||
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
|
||||||
exitstatus = 3
|
|
||||||
py.test.config = py.test.config.__class__()
|
|
||||||
return exitstatus
|
|
|
@ -25,47 +25,15 @@ class Node(object):
|
||||||
""" base class for all Nodes in the collection tree.
|
""" base class for all Nodes in the collection tree.
|
||||||
Collector subclasses have children, Items are terminal nodes.
|
Collector subclasses have children, Items are terminal nodes.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, parent=None, config=None):
|
def __init__(self, name, parent=None, config=None, collection=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.config = config or parent.config
|
self.config = config or parent.config
|
||||||
|
self.collection = collection or getattr(parent, 'collection', None)
|
||||||
self.fspath = getattr(parent, 'fspath', None)
|
self.fspath = getattr(parent, 'fspath', None)
|
||||||
self.ihook = HookProxy(self)
|
self.ihook = HookProxy(self)
|
||||||
self.keywords = self.readkeywords()
|
self.keywords = self.readkeywords()
|
||||||
|
|
||||||
def _reraiseunpicklingproblem(self):
|
|
||||||
if hasattr(self, '_unpickle_exc'):
|
|
||||||
py.builtin._reraise(*self._unpickle_exc)
|
|
||||||
|
|
||||||
#
|
|
||||||
# note to myself: Pickling is uh.
|
|
||||||
#
|
|
||||||
def __getstate__(self):
|
|
||||||
return (self.name, self.parent)
|
|
||||||
def __setstate__(self, nameparent):
|
|
||||||
name, parent = nameparent
|
|
||||||
try:
|
|
||||||
colitems = parent._memocollect()
|
|
||||||
for colitem in colitems:
|
|
||||||
if colitem.name == name:
|
|
||||||
# we are a copy that will not be returned
|
|
||||||
# by our parent
|
|
||||||
self.__dict__ = colitem.__dict__
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("item %r not found in parent collection %r" %(
|
|
||||||
name, [x.name for x in colitems]))
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
raise
|
|
||||||
except Exception:
|
|
||||||
# our parent can't collect us but we want unpickling to
|
|
||||||
# otherwise continue - self._reraiseunpicklingproblem() will
|
|
||||||
# reraise the problem
|
|
||||||
self._unpickle_exc = py.std.sys.exc_info()
|
|
||||||
self.name = name
|
|
||||||
self.parent = parent
|
|
||||||
self.config = parent.config
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if getattr(self.config.option, 'debug', False):
|
if getattr(self.config.option, 'debug', False):
|
||||||
return "<%s %r %0x>" %(self.__class__.__name__,
|
return "<%s %r %0x>" %(self.__class__.__name__,
|
||||||
|
@ -79,7 +47,8 @@ class Node(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, Node):
|
if not isinstance(other, Node):
|
||||||
return False
|
return False
|
||||||
return self.name == other.name and self.parent == other.parent
|
return self.__class__ == other.__class__ and \
|
||||||
|
self.name == other.name and self.parent == other.parent
|
||||||
|
|
||||||
def __ne__(self, other):
|
def __ne__(self, other):
|
||||||
return not self == other
|
return not self == other
|
||||||
|
@ -117,7 +86,7 @@ class Node(object):
|
||||||
l = [self]
|
l = [self]
|
||||||
while 1:
|
while 1:
|
||||||
x = l[0]
|
x = l[0]
|
||||||
if x.parent is not None and x.parent.parent is not None:
|
if x.parent is not None: # and x.parent.parent is not None:
|
||||||
l.insert(0, x.parent)
|
l.insert(0, x.parent)
|
||||||
else:
|
else:
|
||||||
return l
|
return l
|
||||||
|
@ -137,39 +106,6 @@ class Node(object):
|
||||||
def _keywords(self):
|
def _keywords(self):
|
||||||
return [self.name]
|
return [self.name]
|
||||||
|
|
||||||
def _skipbykeyword(self, keywordexpr):
|
|
||||||
""" return True if they given keyword expression means to
|
|
||||||
skip this collector/item.
|
|
||||||
"""
|
|
||||||
if not keywordexpr:
|
|
||||||
return
|
|
||||||
chain = self.listchain()
|
|
||||||
for key in filter(None, keywordexpr.split()):
|
|
||||||
eor = key[:1] == '-'
|
|
||||||
if eor:
|
|
||||||
key = key[1:]
|
|
||||||
if not (eor ^ self._matchonekeyword(key, chain)):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _matchonekeyword(self, key, chain):
|
|
||||||
elems = key.split(".")
|
|
||||||
# XXX O(n^2), anyone cares?
|
|
||||||
chain = [item.keywords for item in chain if item.keywords]
|
|
||||||
for start, _ in enumerate(chain):
|
|
||||||
if start + len(elems) > len(chain):
|
|
||||||
return False
|
|
||||||
for num, elem in enumerate(elems):
|
|
||||||
for keyword in chain[num + start]:
|
|
||||||
ok = False
|
|
||||||
if elem in keyword:
|
|
||||||
ok = True
|
|
||||||
break
|
|
||||||
if not ok:
|
|
||||||
break
|
|
||||||
if num == len(elems) - 1 and ok:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _prunetraceback(self, traceback):
|
def _prunetraceback(self, traceback):
|
||||||
return traceback
|
return traceback
|
||||||
|
|
||||||
|
@ -270,19 +206,12 @@ class Collector(Node):
|
||||||
return traceback
|
return traceback
|
||||||
|
|
||||||
class FSCollector(Collector):
|
class FSCollector(Collector):
|
||||||
def __init__(self, fspath, parent=None, config=None):
|
def __init__(self, fspath, parent=None, config=None, collection=None):
|
||||||
fspath = py.path.local(fspath)
|
fspath = py.path.local(fspath)
|
||||||
super(FSCollector, self).__init__(fspath.basename, parent, config=config)
|
super(FSCollector, self).__init__(fspath.basename,
|
||||||
|
parent, config, collection)
|
||||||
self.fspath = fspath
|
self.fspath = fspath
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
# RootCollector.getbynames() inserts a directory which we need
|
|
||||||
# to throw out here for proper re-instantiation
|
|
||||||
if isinstance(self.parent.parent, RootCollector):
|
|
||||||
assert self.parent.fspath == self.parent.parent.fspath, self.parent
|
|
||||||
return (self.name, self.parent.parent) # shortcut
|
|
||||||
return super(Collector, self).__getstate__()
|
|
||||||
|
|
||||||
class File(FSCollector):
|
class File(FSCollector):
|
||||||
""" base class for collecting tests from a file. """
|
""" base class for collecting tests from a file. """
|
||||||
|
|
||||||
|
@ -368,59 +297,3 @@ def warnoldtestrun(function=None):
|
||||||
"item.run() and item.execute()",
|
"item.run() and item.execute()",
|
||||||
stacklevel=2, function=function)
|
stacklevel=2, function=function)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RootCollector(Directory):
|
|
||||||
def __init__(self, config):
|
|
||||||
Directory.__init__(self, config.topdir, parent=None, config=config)
|
|
||||||
self.name = None
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<RootCollector fspath=%r>" %(self.fspath,)
|
|
||||||
|
|
||||||
def getbynames(self, names):
|
|
||||||
current = self.consider(self.config.topdir)
|
|
||||||
while names:
|
|
||||||
name = names.pop(0)
|
|
||||||
if name == ".": # special "identity" name
|
|
||||||
continue
|
|
||||||
l = []
|
|
||||||
for x in current._memocollect():
|
|
||||||
if x.name == name:
|
|
||||||
l.append(x)
|
|
||||||
elif x.fspath == current.fspath.join(name):
|
|
||||||
l.append(x)
|
|
||||||
elif x.name == "()":
|
|
||||||
names.insert(0, name)
|
|
||||||
l.append(x)
|
|
||||||
break
|
|
||||||
if not l:
|
|
||||||
raise ValueError("no node named %r below %r" %(name, current))
|
|
||||||
current = l[0]
|
|
||||||
return current
|
|
||||||
|
|
||||||
def totrail(self, node):
|
|
||||||
chain = node.listchain()
|
|
||||||
names = [self._getrelpath(chain[0].fspath)]
|
|
||||||
names += [x.name for x in chain[1:]]
|
|
||||||
return names
|
|
||||||
|
|
||||||
def fromtrail(self, trail):
|
|
||||||
return self.config._rootcol.getbynames(trail)
|
|
||||||
|
|
||||||
def _getrelpath(self, fspath):
|
|
||||||
topdir = self.config.topdir
|
|
||||||
relpath = fspath.relto(topdir)
|
|
||||||
if not relpath:
|
|
||||||
if fspath == topdir:
|
|
||||||
relpath = "."
|
|
||||||
else:
|
|
||||||
raise ValueError("%r not relative to topdir %s"
|
|
||||||
%(self.fspath, topdir))
|
|
||||||
return relpath
|
|
||||||
|
|
||||||
def __getstate__(self):
|
|
||||||
return self.config
|
|
||||||
|
|
||||||
def __setstate__(self, config):
|
|
||||||
self.__init__(config)
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ import py, os
|
||||||
from py._test.conftesthandle import Conftest
|
from py._test.conftesthandle import Conftest
|
||||||
from py._test.pluginmanager import PluginManager
|
from py._test.pluginmanager import PluginManager
|
||||||
from py._test import parseopt
|
from py._test import parseopt
|
||||||
from py._test.collect import RootCollector
|
|
||||||
|
|
||||||
def ensuretemp(string, dir=1):
|
def ensuretemp(string, dir=1):
|
||||||
""" (deprecated) return temporary directory path with
|
""" (deprecated) return temporary directory path with
|
||||||
|
@ -31,9 +30,8 @@ class Config(object):
|
||||||
basetemp = None
|
basetemp = None
|
||||||
_sessionclass = None
|
_sessionclass = None
|
||||||
|
|
||||||
def __init__(self, topdir=None, option=None):
|
def __init__(self):
|
||||||
self.option = option or CmdOptions()
|
self.option = CmdOptions()
|
||||||
self.topdir = topdir
|
|
||||||
self._parser = parseopt.Parser(
|
self._parser = parseopt.Parser(
|
||||||
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
|
||||||
processopt=self._processopt,
|
processopt=self._processopt,
|
||||||
|
@ -97,39 +95,7 @@ class Config(object):
|
||||||
args = self._parser.parse_setoption(args, self.option)
|
args = self._parser.parse_setoption(args, self.option)
|
||||||
if not args:
|
if not args:
|
||||||
args.append(py.std.os.getcwd())
|
args.append(py.std.os.getcwd())
|
||||||
self.topdir = gettopdir(args)
|
self.args = args
|
||||||
self._rootcol = RootCollector(config=self)
|
|
||||||
self._setargs(args)
|
|
||||||
|
|
||||||
def _setargs(self, args):
|
|
||||||
self.args = list(args)
|
|
||||||
self._argfspaths = [py.path.local(decodearg(x)[0]) for x in args]
|
|
||||||
|
|
||||||
# config objects are usually pickled across system
|
|
||||||
# barriers but they contain filesystem paths.
|
|
||||||
# upon getstate/setstate we take care to do everything
|
|
||||||
# relative to "topdir".
|
|
||||||
def __getstate__(self):
|
|
||||||
l = []
|
|
||||||
for path in self.args:
|
|
||||||
path = py.path.local(path)
|
|
||||||
l.append(path.relto(self.topdir))
|
|
||||||
return l, self.option.__dict__
|
|
||||||
|
|
||||||
def __setstate__(self, repr):
|
|
||||||
# we have to set py.test.config because loading
|
|
||||||
# of conftest files may use it (deprecated)
|
|
||||||
# mainly by py.test.config.addoptions()
|
|
||||||
global config_per_process
|
|
||||||
py.test.config = config_per_process = self
|
|
||||||
args, cmdlineopts = repr
|
|
||||||
cmdlineopts = CmdOptions(**cmdlineopts)
|
|
||||||
# next line will registers default plugins
|
|
||||||
self.__init__(topdir=py.path.local(), option=cmdlineopts)
|
|
||||||
self._rootcol = RootCollector(config=self)
|
|
||||||
args = [str(self.topdir.join(x)) for x in args]
|
|
||||||
self._preparse(args)
|
|
||||||
self._setargs(args)
|
|
||||||
|
|
||||||
def ensuretemp(self, string, dir=True):
|
def ensuretemp(self, string, dir=True):
|
||||||
return self.getbasetemp().ensure(string, dir=dir)
|
return self.getbasetemp().ensure(string, dir=dir)
|
||||||
|
@ -154,27 +120,6 @@ class Config(object):
|
||||||
return py.path.local.make_numbered_dir(prefix=basename,
|
return py.path.local.make_numbered_dir(prefix=basename,
|
||||||
keep=0, rootdir=basetemp, lock_timeout=None)
|
keep=0, rootdir=basetemp, lock_timeout=None)
|
||||||
|
|
||||||
def getinitialnodes(self):
|
|
||||||
return [self.getnode(arg) for arg in self.args]
|
|
||||||
|
|
||||||
def getnode(self, arg):
|
|
||||||
parts = decodearg(arg)
|
|
||||||
path = py.path.local(parts.pop(0))
|
|
||||||
if not path.check():
|
|
||||||
raise self.Error("file not found: %s" %(path,))
|
|
||||||
topdir = self.topdir
|
|
||||||
if path != topdir and not path.relto(topdir):
|
|
||||||
raise self.Error("path %r is not relative to %r" %
|
|
||||||
(str(path), str(topdir)))
|
|
||||||
# assumtion: pytest's fs-collector tree follows the filesystem tree
|
|
||||||
names = list(filter(None, path.relto(topdir).split(path.sep)))
|
|
||||||
names += parts
|
|
||||||
try:
|
|
||||||
return self._rootcol.getbynames(names)
|
|
||||||
except ValueError:
|
|
||||||
e = py.std.sys.exc_info()[1]
|
|
||||||
raise self.Error("can't collect: %s\n%s" % (arg, e.args[0]))
|
|
||||||
|
|
||||||
def _getcollectclass(self, name, path):
|
def _getcollectclass(self, name, path):
|
||||||
try:
|
try:
|
||||||
cls = self._conftest.rget(name, path)
|
cls = self._conftest.rget(name, path)
|
||||||
|
@ -239,48 +184,10 @@ class Config(object):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return self._conftest.rget(name, path)
|
return self._conftest.rget(name, path)
|
||||||
|
|
||||||
def setsessionclass(self, cls):
|
|
||||||
if self._sessionclass is not None:
|
|
||||||
raise ValueError("sessionclass already set to: %r" %(
|
|
||||||
self._sessionclass))
|
|
||||||
self._sessionclass = cls
|
|
||||||
|
|
||||||
def initsession(self):
|
|
||||||
""" return an initialized session object. """
|
|
||||||
cls = self._sessionclass
|
|
||||||
if cls is None:
|
|
||||||
from py._test.session import Session
|
|
||||||
cls = Session
|
|
||||||
session = cls(self)
|
|
||||||
self.trace("instantiated session %r" % session)
|
|
||||||
return session
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# helpers
|
# helpers
|
||||||
#
|
#
|
||||||
|
|
||||||
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("::")
|
|
||||||
|
|
||||||
def onpytestaccess():
|
def onpytestaccess():
|
||||||
# it's enough to have our containing module loaded as
|
# it's enough to have our containing module loaded as
|
||||||
# it initializes a per-process config instance
|
# it initializes a per-process config instance
|
||||||
|
|
|
@ -184,3 +184,45 @@ class FuncargRequest:
|
||||||
msg += "\n available funcargs: %s" %(", ".join(available),)
|
msg += "\n available funcargs: %s" %(", ".join(available),)
|
||||||
msg += "\n use 'py.test --funcargs [testpath]' for help on them."
|
msg += "\n use 'py.test --funcargs [testpath]' for help on them."
|
||||||
raise self.LookupError(msg)
|
raise self.LookupError(msg)
|
||||||
|
|
||||||
|
def showfuncargs(config):
|
||||||
|
from py._test.session import Collection
|
||||||
|
collection = Collection(config)
|
||||||
|
colitem = collection.getinitialnodes()[0]
|
||||||
|
curdir = py.path.local()
|
||||||
|
tw = py.io.TerminalWriter()
|
||||||
|
#from py._test.funcargs import getplugins
|
||||||
|
#from py._test.funcargs import FuncargRequest
|
||||||
|
plugins = getplugins(colitem, withpy=True)
|
||||||
|
verbose = config.getvalue("verbose")
|
||||||
|
for plugin in plugins:
|
||||||
|
available = []
|
||||||
|
for name, factory in vars(plugin).items():
|
||||||
|
if name.startswith(FuncargRequest._argprefix):
|
||||||
|
name = name[len(FuncargRequest._argprefix):]
|
||||||
|
if name not in available:
|
||||||
|
available.append([name, factory])
|
||||||
|
if available:
|
||||||
|
pluginname = plugin.__name__
|
||||||
|
for name, factory in available:
|
||||||
|
loc = getlocation(factory, curdir)
|
||||||
|
if verbose:
|
||||||
|
funcargspec = "%s -- %s" %(name, loc,)
|
||||||
|
else:
|
||||||
|
funcargspec = name
|
||||||
|
tw.line(funcargspec, green=True)
|
||||||
|
doc = factory.__doc__ or ""
|
||||||
|
if doc:
|
||||||
|
for line in doc.split("\n"):
|
||||||
|
tw.line(" " + line.strip())
|
||||||
|
else:
|
||||||
|
tw.line(" %s: no docstring available" %(loc,),
|
||||||
|
red=True)
|
||||||
|
|
||||||
|
def getlocation(function, curdir):
|
||||||
|
import inspect
|
||||||
|
fn = py.path.local(inspect.getfile(function))
|
||||||
|
lineno = py.builtin._getcode(function).co_firstlineno
|
||||||
|
if fn.relto(curdir):
|
||||||
|
fn = fn.relto(curdir)
|
||||||
|
return "%s:%d" %(fn, lineno+1)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from py._plugin import hookspec
|
||||||
default_plugins = (
|
default_plugins = (
|
||||||
"default runner pdb capture mark terminal skipping tmpdir monkeypatch "
|
"default runner pdb capture mark terminal skipping tmpdir monkeypatch "
|
||||||
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
"recwarn pastebin unittest helpconfig nose assertion genscript "
|
||||||
"junitxml doctest").split()
|
"junitxml doctest keyword").split()
|
||||||
|
|
||||||
def check_old_use(mod, modname):
|
def check_old_use(mod, modname):
|
||||||
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
|
clsname = modname[len('pytest_'):].capitalize() + "Plugin"
|
||||||
|
|
|
@ -348,8 +348,9 @@ class Function(FunctionMixin, py.test.collect.Item):
|
||||||
"""
|
"""
|
||||||
_genid = None
|
_genid = None
|
||||||
def __init__(self, name, parent=None, args=None, config=None,
|
def __init__(self, name, parent=None, args=None, config=None,
|
||||||
callspec=None, callobj=_dummy):
|
callspec=None, callobj=_dummy, collection=None):
|
||||||
super(Function, self).__init__(name, parent, config=config)
|
super(Function, self).__init__(name, parent,
|
||||||
|
config=config, collection=collection)
|
||||||
self._args = args
|
self._args = args
|
||||||
if self._isyieldedfunction():
|
if self._isyieldedfunction():
|
||||||
assert not callspec, "yielded functions (deprecated) cannot have funcargs"
|
assert not callspec, "yielded functions (deprecated) cannot have funcargs"
|
||||||
|
|
|
@ -6,6 +6,28 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
import sys
|
||||||
|
|
||||||
|
#
|
||||||
|
# main entry point
|
||||||
|
#
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
if args is None:
|
||||||
|
args = sys.argv[1:]
|
||||||
|
config = py.test.config
|
||||||
|
try:
|
||||||
|
config.parse(args)
|
||||||
|
config.pluginmanager.do_configure(config)
|
||||||
|
exitstatus = config.hook.pytest_cmdline_main(config=config)
|
||||||
|
config.pluginmanager.do_unconfigure(config)
|
||||||
|
except config.Error:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
|
sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||||
|
exitstatus = EXIT_INTERNALERROR
|
||||||
|
py.test.config = py.test.config.__class__()
|
||||||
|
return exitstatus
|
||||||
|
|
||||||
|
|
||||||
# exitcodes for the command line
|
# exitcodes for the command line
|
||||||
EXIT_OK = 0
|
EXIT_OK = 0
|
||||||
|
@ -24,65 +46,13 @@ class Session(object):
|
||||||
""" signals an interrupted test run. """
|
""" signals an interrupted test run. """
|
||||||
__module__ = 'builtins' # for py3
|
__module__ = 'builtins' # for py3
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config, collection):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.pluginmanager = config.pluginmanager # shortcut
|
self.pluginmanager = config.pluginmanager # shortcut
|
||||||
self.pluginmanager.register(self)
|
self.pluginmanager.register(self)
|
||||||
self._testsfailed = 0
|
self._testsfailed = 0
|
||||||
self._nomatch = False
|
|
||||||
self.shouldstop = False
|
self.shouldstop = False
|
||||||
|
self.collection = collection
|
||||||
def genitems(self, colitems, keywordexpr=None):
|
|
||||||
""" yield Items from iterating over the given colitems. """
|
|
||||||
if colitems:
|
|
||||||
colitems = list(colitems)
|
|
||||||
while colitems:
|
|
||||||
next = colitems.pop(0)
|
|
||||||
if isinstance(next, (tuple, list)):
|
|
||||||
colitems[:] = list(next) + colitems
|
|
||||||
continue
|
|
||||||
assert self.pluginmanager is next.config.pluginmanager
|
|
||||||
if isinstance(next, Item):
|
|
||||||
remaining = self.filteritems([next])
|
|
||||||
if remaining:
|
|
||||||
self.config.hook.pytest_itemstart(item=next)
|
|
||||||
yield next
|
|
||||||
else:
|
|
||||||
assert isinstance(next, Collector)
|
|
||||||
self.config.hook.pytest_collectstart(collector=next)
|
|
||||||
rep = self.config.hook.pytest_make_collect_report(collector=next)
|
|
||||||
if rep.passed:
|
|
||||||
for x in self.genitems(rep.result, keywordexpr):
|
|
||||||
yield x
|
|
||||||
self.config.hook.pytest_collectreport(report=rep)
|
|
||||||
if self.shouldstop:
|
|
||||||
raise self.Interrupted(self.shouldstop)
|
|
||||||
|
|
||||||
def filteritems(self, colitems):
|
|
||||||
""" return items to process (some may be deselected)"""
|
|
||||||
keywordexpr = self.config.option.keyword
|
|
||||||
if not keywordexpr or self._nomatch:
|
|
||||||
return colitems
|
|
||||||
if keywordexpr[-1] == ":":
|
|
||||||
keywordexpr = keywordexpr[:-1]
|
|
||||||
remaining = []
|
|
||||||
deselected = []
|
|
||||||
for colitem in colitems:
|
|
||||||
if isinstance(colitem, Item):
|
|
||||||
if colitem._skipbykeyword(keywordexpr):
|
|
||||||
deselected.append(colitem)
|
|
||||||
continue
|
|
||||||
remaining.append(colitem)
|
|
||||||
if deselected:
|
|
||||||
self.config.hook.pytest_deselected(items=deselected)
|
|
||||||
if self.config.option.keyword.endswith(":"):
|
|
||||||
self._nomatch = True
|
|
||||||
return remaining
|
|
||||||
|
|
||||||
def collect(self, colitems):
|
|
||||||
keyword = self.config.option.keyword
|
|
||||||
for x in self.genitems(colitems, keyword):
|
|
||||||
yield x
|
|
||||||
|
|
||||||
def sessionstarts(self):
|
def sessionstarts(self):
|
||||||
""" setup any neccessary resources ahead of the test run. """
|
""" setup any neccessary resources ahead of the test run. """
|
||||||
|
@ -95,6 +65,7 @@ class Session(object):
|
||||||
if maxfail and self._testsfailed >= maxfail:
|
if maxfail and self._testsfailed >= maxfail:
|
||||||
self.shouldstop = "stopping after %d failures" % (
|
self.shouldstop = "stopping after %d failures" % (
|
||||||
self._testsfailed)
|
self._testsfailed)
|
||||||
|
self.collection.shouldstop = self.shouldstop
|
||||||
pytest_collectreport = pytest_runtest_logreport
|
pytest_collectreport = pytest_runtest_logreport
|
||||||
|
|
||||||
def sessionfinishes(self, exitstatus):
|
def sessionfinishes(self, exitstatus):
|
||||||
|
@ -104,13 +75,14 @@ class Session(object):
|
||||||
exitstatus=exitstatus,
|
exitstatus=exitstatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
def main(self, colitems):
|
def main(self):
|
||||||
""" main loop for running tests. """
|
""" main loop for running tests. """
|
||||||
self.shouldstop = False
|
self.shouldstop = False
|
||||||
|
|
||||||
self.sessionstarts()
|
self.sessionstarts()
|
||||||
exitstatus = EXIT_OK
|
exitstatus = EXIT_OK
|
||||||
try:
|
try:
|
||||||
self._mainloop(colitems)
|
self._mainloop()
|
||||||
if self._testsfailed:
|
if self._testsfailed:
|
||||||
exitstatus = EXIT_TESTSFAILED
|
exitstatus = EXIT_TESTSFAILED
|
||||||
self.sessionfinishes(exitstatus=exitstatus)
|
self.sessionfinishes(exitstatus=exitstatus)
|
||||||
|
@ -126,10 +98,165 @@ class Session(object):
|
||||||
self.sessionfinishes(exitstatus=exitstatus)
|
self.sessionfinishes(exitstatus=exitstatus)
|
||||||
return exitstatus
|
return exitstatus
|
||||||
|
|
||||||
def _mainloop(self, colitems):
|
def _mainloop(self):
|
||||||
for item in self.collect(colitems):
|
if self.config.option.collectonly:
|
||||||
if not self.config.option.collectonly:
|
return
|
||||||
item.config.hook.pytest_runtest_protocol(item=item)
|
for item in self.collection.items:
|
||||||
|
item.config.hook.pytest_runtest_protocol(item=item)
|
||||||
if self.shouldstop:
|
if self.shouldstop:
|
||||||
raise self.Interrupted(self.shouldstop)
|
raise self.Interrupted(self.shouldstop)
|
||||||
|
|
||||||
|
|
||||||
|
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 = py.test.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):
|
||||||
|
""" 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
|
||||||
|
"""
|
||||||
|
parts = str(arg).split("::")
|
||||||
|
path = py.path.local(parts[0])
|
||||||
|
if not path.check():
|
||||||
|
raise self.config.Error("file not found: %s" %(path,))
|
||||||
|
topdir = self.topdir
|
||||||
|
if path != topdir and not path.relto(topdir):
|
||||||
|
raise self.config.Error("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, relative=True):
|
||||||
|
""" 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 != "()"]
|
||||||
|
if relative:
|
||||||
|
relpath = path.relto(self.topdir)
|
||||||
|
if relpath:
|
||||||
|
path = relpath
|
||||||
|
names = relpath.split(node.fspath.sep) + names
|
||||||
|
return "::".join(names)
|
||||||
|
|
||||||
|
def getbyid(self, id):
|
||||||
|
""" return one or more nodes matching the id. """
|
||||||
|
matching = [self._topcollector]
|
||||||
|
if not id:
|
||||||
|
return matching
|
||||||
|
names = id.split("::")
|
||||||
|
while names:
|
||||||
|
name = names.pop(0)
|
||||||
|
l = []
|
||||||
|
for current in matching:
|
||||||
|
for x in current._memocollect():
|
||||||
|
if x.name == name:
|
||||||
|
l.append(x)
|
||||||
|
elif x.name == "()":
|
||||||
|
names.insert(0, name)
|
||||||
|
l.append(x)
|
||||||
|
break
|
||||||
|
if not l:
|
||||||
|
raise ValueError("no node named %r below %r" %(name, current))
|
||||||
|
matching = l
|
||||||
|
return matching
|
||||||
|
|
||||||
|
def do_collection(self):
|
||||||
|
assert not hasattr(self, 'items')
|
||||||
|
hook = self.config.hook
|
||||||
|
hook.pytest_log_startcollection(collection=self)
|
||||||
|
try:
|
||||||
|
self.items = self.perform_collect()
|
||||||
|
except self.config.Error:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
self.config.pluginmanager.notify_exception()
|
||||||
|
return EXIT_INTERNALERROR
|
||||||
|
else:
|
||||||
|
hook.pytest_collection_modifyitems(collection=self)
|
||||||
|
res = hook.pytest_log_finishcollection(collection=self)
|
||||||
|
return res and max(res) or 0 # returncode
|
||||||
|
|
||||||
|
def getinitialnodes(self):
|
||||||
|
idlist = [self._normalizearg(arg) for arg in self.config.args]
|
||||||
|
nodes = []
|
||||||
|
for id in idlist:
|
||||||
|
nodes.extend(self.getbyid(id))
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def perform_collect(self):
|
||||||
|
idlist = [self._parsearg(arg) for arg in self.config.args]
|
||||||
|
nodes = []
|
||||||
|
for names in idlist:
|
||||||
|
self.genitems([self._topcollector], names, nodes)
|
||||||
|
return nodes
|
||||||
|
|
||||||
|
def genitems(self, matching, names, result):
|
||||||
|
names = list(names)
|
||||||
|
name = names and names.pop(0) or None
|
||||||
|
for node in matching:
|
||||||
|
if isinstance(node, Item):
|
||||||
|
if name is None:
|
||||||
|
self.config.hook.pytest_log_itemcollect(item=node)
|
||||||
|
result.append(node)
|
||||||
|
else:
|
||||||
|
assert isinstance(node, 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 name:
|
||||||
|
matched = False
|
||||||
|
for subcol in rep.result:
|
||||||
|
if subcol.name != name and subcol.name == "()":
|
||||||
|
names.insert(0, name)
|
||||||
|
name = "()"
|
||||||
|
# see doctests/custom naming XXX
|
||||||
|
if subcol.name == name or subcol.fspath.basename == name:
|
||||||
|
self.genitems([subcol], names, result)
|
||||||
|
matched = True
|
||||||
|
if not matched:
|
||||||
|
raise self.config.Error(
|
||||||
|
"can't collect: %s" % (name,))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.genitems(rep.result, [], result)
|
||||||
|
node.ihook.pytest_collectreport(report=rep)
|
||||||
|
x = getattr(self, 'shouldstop', None)
|
||||||
|
if x:
|
||||||
|
raise self.Interrupted(x)
|
||||||
|
|
||||||
|
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("::")
|
||||||
|
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -30,7 +30,7 @@ def main():
|
||||||
name='py',
|
name='py',
|
||||||
description='py.test and pylib: rapid testing and development utils.',
|
description='py.test and pylib: rapid testing and development utils.',
|
||||||
long_description = long_description,
|
long_description = long_description,
|
||||||
version= '1.3.4',
|
version= '1.4.0a1',
|
||||||
url='http://pylib.org',
|
url='http://pylib.org',
|
||||||
license='MIT license',
|
license='MIT license',
|
||||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||||
|
|
|
@ -72,7 +72,7 @@ class TestGeneralUsage:
|
||||||
result = testdir.runpytest(p1, p2)
|
result = testdir.runpytest(p1, p2)
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*ERROR: can't collect: %s" %(p2,)
|
"*ERROR: can't collect:*%s" %(p2.basename,)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,7 +122,6 @@ class TestGeneralUsage:
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
@py.test.mark.xfail
|
|
||||||
def test_issue88_initial_file_multinodes(self, testdir):
|
def test_issue88_initial_file_multinodes(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import py
|
import py
|
||||||
|
@ -163,3 +162,32 @@ class TestGeneralUsage:
|
||||||
"""))
|
"""))
|
||||||
result = testdir.runpython(p, prepend=False)
|
result = testdir.runpython(p, prepend=False)
|
||||||
assert not result.ret
|
assert not result.ret
|
||||||
|
|
||||||
|
@py.test.mark.xfail(reason="http://bitbucket.org/hpk42/py-trunk/issue/109")
|
||||||
|
def test_sibling_conftest_issue109(self, testdir):
|
||||||
|
"""
|
||||||
|
This test is to make sure that the conftest.py of sibling directories is not loaded
|
||||||
|
if py.test is run for/in one of the siblings directory and those sibling directories
|
||||||
|
are not packaged together with an __init__.py. See bitbucket issue #109.
|
||||||
|
"""
|
||||||
|
for dirname in ['a', 'b']:
|
||||||
|
testdir.tmpdir.ensure(dirname, dir=True)
|
||||||
|
testdir.tmpdir.ensure(dirname, '__init__.py')
|
||||||
|
|
||||||
|
# To create the conftest.py I would like to use testdir.make*-methods
|
||||||
|
# but as far as I have seen they can only create files in testdir.tempdir
|
||||||
|
# Maybe there is a way to explicitly specifiy the directory on which those
|
||||||
|
# methods work or a completely better way to do that?
|
||||||
|
backupTmpDir = testdir.tmpdir
|
||||||
|
testdir.tmpdir = testdir.tmpdir.join(dirname)
|
||||||
|
testdir.makeconftest("""
|
||||||
|
_DIR_NAME = '%s'
|
||||||
|
def pytest_configure(config):
|
||||||
|
if config.args and config.args[0] != _DIR_NAME:
|
||||||
|
raise Exception("py.test run for '" + config.args[0] + "', but '" + _DIR_NAME + "/conftest.py' loaded.")
|
||||||
|
""" % dirname)
|
||||||
|
testdir.tmpdir = backupTmpDir
|
||||||
|
|
||||||
|
for dirname, other_dirname in [('a', 'b'), ('b', 'a')]:
|
||||||
|
result = testdir.runpytest(dirname)
|
||||||
|
assert result.ret == 0, "test_sibling_conftest: py.test run for '%s', but '%s/conftest.py' loaded." % (dirname, other_dirname)
|
||||||
|
|
|
@ -26,6 +26,7 @@ def test_gen(testdir, anypython, standalone):
|
||||||
"*imported from*mypytest"
|
"*imported from*mypytest"
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@py.test.mark.xfail(reason="fix-dist")
|
||||||
def test_rundist(testdir, pytestconfig, standalone):
|
def test_rundist(testdir, pytestconfig, standalone):
|
||||||
pytestconfig.pluginmanager.skipifmissing("xdist")
|
pytestconfig.pluginmanager.skipifmissing("xdist")
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
|
|
@ -5,6 +5,8 @@ def test_reportrecorder(testdir):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
recorder = testdir.getreportrecorder(item.config)
|
recorder = testdir.getreportrecorder(item.config)
|
||||||
assert not recorder.getfailures()
|
assert not recorder.getfailures()
|
||||||
|
|
||||||
|
py.test.xfail("internal reportrecorder tests need refactoring")
|
||||||
class rep:
|
class rep:
|
||||||
excinfo = None
|
excinfo = None
|
||||||
passed = False
|
passed = False
|
||||||
|
|
|
@ -3,10 +3,12 @@ import os
|
||||||
from py._plugin.pytest_resultlog import generic_path, ResultLog, \
|
from py._plugin.pytest_resultlog import generic_path, ResultLog, \
|
||||||
pytest_configure, pytest_unconfigure
|
pytest_configure, pytest_unconfigure
|
||||||
from py._test.collect import Node, Item, FSCollector
|
from py._test.collect import Node, Item, FSCollector
|
||||||
|
from py._test.session import Collection
|
||||||
|
|
||||||
def test_generic_path(testdir):
|
def test_generic_path(testdir):
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
p1 = Node('a', parent=config._rootcol)
|
collection = Collection(config)
|
||||||
|
p1 = Node('a', config=config, collection=collection)
|
||||||
#assert p1.fspath is None
|
#assert p1.fspath is None
|
||||||
p2 = Node('B', parent=p1)
|
p2 = Node('B', parent=p1)
|
||||||
p3 = Node('()', parent = p2)
|
p3 = Node('()', parent = p2)
|
||||||
|
@ -15,7 +17,7 @@ def test_generic_path(testdir):
|
||||||
res = generic_path(item)
|
res = generic_path(item)
|
||||||
assert res == 'a.B().c'
|
assert res == 'a.B().c'
|
||||||
|
|
||||||
p0 = FSCollector('proj/test', parent=config._rootcol)
|
p0 = FSCollector('proj/test', config=config, collection=collection)
|
||||||
p1 = FSCollector('proj/test/a', parent=p0)
|
p1 = FSCollector('proj/test/a', parent=p0)
|
||||||
p2 = Node('B', parent=p1)
|
p2 = Node('B', parent=p1)
|
||||||
p3 = Node('()', parent = p2)
|
p3 = Node('()', parent = p2)
|
||||||
|
|
|
@ -408,8 +408,8 @@ def test_skipped_reasons_functional(testdir):
|
||||||
)
|
)
|
||||||
result = testdir.runpytest('--report=skipped')
|
result = testdir.runpytest('--report=skipped')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*test_one.py ss",
|
|
||||||
"*test_two.py S",
|
"*test_two.py S",
|
||||||
|
"*test_one.py ss",
|
||||||
"*SKIP*3*conftest.py:3: 'test'",
|
"*SKIP*3*conftest.py:3: 'test'",
|
||||||
])
|
])
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
|
@ -206,7 +206,7 @@ class TestCollectonly:
|
||||||
"<Module 'test_collectonly_basic.py'>"
|
"<Module 'test_collectonly_basic.py'>"
|
||||||
])
|
])
|
||||||
item = modcol.join("test_func")
|
item = modcol.join("test_func")
|
||||||
rep.config.hook.pytest_itemstart(item=item)
|
rep.config.hook.pytest_log_itemcollect(item=item)
|
||||||
linecomp.assert_contains_lines([
|
linecomp.assert_contains_lines([
|
||||||
" <Function 'test_func'>",
|
" <Function 'test_func'>",
|
||||||
])
|
])
|
||||||
|
@ -264,13 +264,13 @@ class TestCollectonly:
|
||||||
stderr = result.stderr.str().strip()
|
stderr = result.stderr.str().strip()
|
||||||
#assert stderr.startswith("inserting into sys.path")
|
#assert stderr.startswith("inserting into sys.path")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
extra = result.stdout.fnmatch_lines(py.code.Source("""
|
extra = result.stdout.fnmatch_lines([
|
||||||
<Module '*.py'>
|
"*<Module '*.py'>",
|
||||||
<Function 'test_func1'*>
|
"* <Function 'test_func1'*>",
|
||||||
<Class 'TestClass'>
|
"* <Class 'TestClass'>",
|
||||||
<Instance '()'>
|
"* <Instance '()'>",
|
||||||
<Function 'test_method'*>
|
"* <Function 'test_method'*>",
|
||||||
""").strip())
|
])
|
||||||
|
|
||||||
def test_collectonly_error(self, testdir):
|
def test_collectonly_error(self, testdir):
|
||||||
p = testdir.makepyfile("import Errlkjqweqwe")
|
p = testdir.makepyfile("import Errlkjqweqwe")
|
||||||
|
@ -278,9 +278,9 @@ class TestCollectonly:
|
||||||
stderr = result.stderr.str().strip()
|
stderr = result.stderr.str().strip()
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
extra = result.stdout.fnmatch_lines(py.code.Source("""
|
extra = result.stdout.fnmatch_lines(py.code.Source("""
|
||||||
<Module '*.py'>
|
*<Module '*.py'>
|
||||||
*ImportError*
|
*ImportError*
|
||||||
!!!*failures*!!!
|
*!!!*failures*!!!
|
||||||
*test_collectonly_error.py:1*
|
*test_collectonly_error.py:1*
|
||||||
""").strip())
|
""").strip())
|
||||||
|
|
||||||
|
@ -454,6 +454,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
|
||||||
|
py.test.xfail("fix dist-testing")
|
||||||
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([
|
||||||
|
|
|
@ -59,19 +59,18 @@ class TestCollector:
|
||||||
import py
|
import py
|
||||||
class CustomFile(py.test.collect.File):
|
class CustomFile(py.test.collect.File):
|
||||||
pass
|
pass
|
||||||
class MyDirectory(py.test.collect.Directory):
|
def pytest_collect_file(path, parent):
|
||||||
def collect(self):
|
if path.ext == ".xxx":
|
||||||
return [CustomFile(self.fspath.join("hello.xxx"), parent=self)]
|
return CustomFile(path, parent=parent)
|
||||||
def pytest_collect_directory(path, parent):
|
|
||||||
return MyDirectory(path, parent=parent)
|
|
||||||
""")
|
""")
|
||||||
config = testdir.parseconfig(hello)
|
config = testdir.parseconfig(hello)
|
||||||
node = config.getnode(hello)
|
node = testdir.getnode(config, hello)
|
||||||
assert isinstance(node, py.test.collect.File)
|
assert isinstance(node, py.test.collect.File)
|
||||||
assert node.name == "hello.xxx"
|
assert node.name == "hello.xxx"
|
||||||
names = config._rootcol.totrail(node)
|
id = node.collection.getid(node)
|
||||||
node = config._rootcol.getbynames(names)
|
nodes = node.collection.getbyid(id)
|
||||||
assert isinstance(node, py.test.collect.File)
|
assert len(nodes) == 1
|
||||||
|
assert isinstance(nodes[0], py.test.collect.File)
|
||||||
|
|
||||||
class TestCollectFS:
|
class TestCollectFS:
|
||||||
def test_ignored_certain_directories(self, testdir):
|
def test_ignored_certain_directories(self, testdir):
|
||||||
|
@ -84,7 +83,7 @@ class TestCollectFS:
|
||||||
tmpdir.ensure("normal", 'test_found.py')
|
tmpdir.ensure("normal", 'test_found.py')
|
||||||
tmpdir.ensure('test_found.py')
|
tmpdir.ensure('test_found.py')
|
||||||
|
|
||||||
col = testdir.parseconfig(tmpdir).getnode(tmpdir)
|
col = testdir.getnode(testdir.parseconfig(tmpdir), tmpdir)
|
||||||
items = col.collect()
|
items = col.collect()
|
||||||
names = [x.name for x in items]
|
names = [x.name for x in items]
|
||||||
assert len(items) == 2
|
assert len(items) == 2
|
||||||
|
@ -93,7 +92,7 @@ class TestCollectFS:
|
||||||
|
|
||||||
def test_found_certain_testfiles(self, testdir):
|
def test_found_certain_testfiles(self, testdir):
|
||||||
p1 = testdir.makepyfile(test_found = "pass", found_test="pass")
|
p1 = testdir.makepyfile(test_found = "pass", found_test="pass")
|
||||||
col = testdir.parseconfig(p1).getnode(p1.dirpath())
|
col = testdir.getnode(testdir.parseconfig(p1), p1.dirpath())
|
||||||
items = col.collect() # Directory collect returns files sorted by name
|
items = col.collect() # Directory collect returns files sorted by name
|
||||||
assert len(items) == 2
|
assert len(items) == 2
|
||||||
assert items[1].name == 'test_found.py'
|
assert items[1].name == 'test_found.py'
|
||||||
|
@ -106,7 +105,7 @@ class TestCollectFS:
|
||||||
testdir.makepyfile(test_two="hello")
|
testdir.makepyfile(test_two="hello")
|
||||||
p1.dirpath().mkdir("dir2")
|
p1.dirpath().mkdir("dir2")
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
col = config.getnode(p1.dirpath())
|
col = testdir.getnode(config, p1.dirpath())
|
||||||
names = [x.name for x in col.collect()]
|
names = [x.name for x in col.collect()]
|
||||||
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
|
||||||
|
|
||||||
|
@ -120,7 +119,7 @@ class TestCollectPluginHookRelay:
|
||||||
config = testdir.Config()
|
config = testdir.Config()
|
||||||
config.pluginmanager.register(Plugin())
|
config.pluginmanager.register(Plugin())
|
||||||
config.parse([tmpdir])
|
config.parse([tmpdir])
|
||||||
col = config.getnode(tmpdir)
|
col = testdir.getnode(config, tmpdir)
|
||||||
testdir.makefile(".abc", "xyz")
|
testdir.makefile(".abc", "xyz")
|
||||||
res = col.collect()
|
res = col.collect()
|
||||||
assert len(wascalled) == 1
|
assert len(wascalled) == 1
|
||||||
|
@ -236,7 +235,7 @@ class TestCustomConftests:
|
||||||
assert 'test_world.py' in names
|
assert 'test_world.py' in names
|
||||||
|
|
||||||
def test_pytest_fs_collect_hooks_are_seen(self, testdir):
|
def test_pytest_fs_collect_hooks_are_seen(self, testdir):
|
||||||
testdir.makeconftest("""
|
conf = testdir.makeconftest("""
|
||||||
import py
|
import py
|
||||||
class MyDirectory(py.test.collect.Directory):
|
class MyDirectory(py.test.collect.Directory):
|
||||||
pass
|
pass
|
||||||
|
@ -247,79 +246,11 @@ class TestCustomConftests:
|
||||||
def pytest_collect_file(path, parent):
|
def pytest_collect_file(path, parent):
|
||||||
return MyModule(path, parent)
|
return MyModule(path, parent)
|
||||||
""")
|
""")
|
||||||
testdir.makepyfile("def test_x(): pass")
|
sub = testdir.mkdir("sub")
|
||||||
|
p = testdir.makepyfile("def test_x(): pass")
|
||||||
result = testdir.runpytest("--collectonly")
|
result = testdir.runpytest("--collectonly")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*MyDirectory*",
|
"*MyDirectory*",
|
||||||
"*MyModule*",
|
"*MyModule*",
|
||||||
"*test_x*"
|
"*test_x*"
|
||||||
])
|
])
|
||||||
|
|
||||||
class TestRootCol:
|
|
||||||
def test_totrail_and_back(self, testdir, tmpdir):
|
|
||||||
a = tmpdir.ensure("a", dir=1)
|
|
||||||
tmpdir.ensure("a", "__init__.py")
|
|
||||||
x = tmpdir.ensure("a", "trail.py")
|
|
||||||
config = testdir.reparseconfig([x])
|
|
||||||
col = config.getnode(x)
|
|
||||||
trail = config._rootcol.totrail(col)
|
|
||||||
col2 = config._rootcol.fromtrail(trail)
|
|
||||||
assert col2 == col
|
|
||||||
|
|
||||||
@py.test.mark.xfail(reason="http://bitbucket.org/hpk42/py-trunk/issue/109")
|
|
||||||
def test_sibling_conftest_issue109(self, testdir):
|
|
||||||
"""
|
|
||||||
This test is to make sure that the conftest.py of sibling directories is not loaded
|
|
||||||
if py.test is run for/in one of the siblings directory and those sibling directories
|
|
||||||
are not packaged together with an __init__.py. See bitbucket issue #109.
|
|
||||||
"""
|
|
||||||
for dirname in ['a', 'b']:
|
|
||||||
testdir.tmpdir.ensure(dirname, dir=True)
|
|
||||||
testdir.tmpdir.ensure(dirname, '__init__.py')
|
|
||||||
|
|
||||||
# To create the conftest.py I would like to use testdir.make*-methods
|
|
||||||
# but as far as I have seen they can only create files in testdir.tempdir
|
|
||||||
# Maybe there is a way to explicitly specifiy the directory on which those
|
|
||||||
# methods work or a completely better way to do that?
|
|
||||||
backupTmpDir = testdir.tmpdir
|
|
||||||
testdir.tmpdir = testdir.tmpdir.join(dirname)
|
|
||||||
testdir.makeconftest("""
|
|
||||||
_DIR_NAME = '%s'
|
|
||||||
def pytest_configure(config):
|
|
||||||
if config.args and config.args[0] != _DIR_NAME:
|
|
||||||
raise Exception("py.test run for '" + config.args[0] + "', but '" + _DIR_NAME + "/conftest.py' loaded.")
|
|
||||||
""" % dirname)
|
|
||||||
testdir.tmpdir = backupTmpDir
|
|
||||||
|
|
||||||
for dirname, other_dirname in [('a', 'b'), ('b', 'a')]:
|
|
||||||
result = testdir.runpytest(dirname)
|
|
||||||
assert result.ret == 0, "test_sibling_conftest: py.test run for '%s', but '%s/conftest.py' loaded." % (dirname, other_dirname)
|
|
||||||
|
|
||||||
def test_totrail_topdir_and_beyond(self, testdir, tmpdir):
|
|
||||||
config = testdir.reparseconfig()
|
|
||||||
col = config.getnode(config.topdir)
|
|
||||||
trail = config._rootcol.totrail(col)
|
|
||||||
col2 = config._rootcol.fromtrail(trail)
|
|
||||||
assert col2.fspath == config.topdir
|
|
||||||
assert len(col2.listchain()) == 1
|
|
||||||
py.test.raises(config.Error, "config.getnode(config.topdir.dirpath())")
|
|
||||||
#col3 = config.getnode(config.topdir.dirpath())
|
|
||||||
#py.test.raises(ValueError,
|
|
||||||
# "col3._totrail()")
|
|
||||||
|
|
||||||
def test_argid(self, testdir, tmpdir):
|
|
||||||
cfg = testdir.parseconfig()
|
|
||||||
p = testdir.makepyfile("def test_func(): pass")
|
|
||||||
item = cfg.getnode("%s::test_func" % p)
|
|
||||||
assert item.name == "test_func"
|
|
||||||
|
|
||||||
def test_argid_with_method(self, testdir, tmpdir):
|
|
||||||
cfg = testdir.parseconfig()
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
class TestClass:
|
|
||||||
def test_method(self): pass
|
|
||||||
""")
|
|
||||||
item = cfg.getnode("%s::TestClass::()::test_method" % p)
|
|
||||||
assert item.name == "test_method"
|
|
||||||
item = cfg.getnode("%s::TestClass::test_method" % p)
|
|
||||||
assert item.name == "test_method"
|
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
import py
|
||||||
|
|
||||||
|
from py._test.session import Collection, gettopdir
|
||||||
|
|
||||||
|
class TestCollection:
|
||||||
|
def test_parsearg(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
subdir = testdir.mkdir("sub")
|
||||||
|
subdir.ensure("__init__.py")
|
||||||
|
target = subdir.join(p.basename)
|
||||||
|
p.move(target)
|
||||||
|
testdir.chdir()
|
||||||
|
subdir.chdir()
|
||||||
|
config = testdir.parseconfig(p.basename)
|
||||||
|
rcol = Collection(config=config)
|
||||||
|
assert rcol.topdir == testdir.tmpdir
|
||||||
|
parts = rcol._parsearg(p.basename)
|
||||||
|
assert parts[0] == "sub"
|
||||||
|
assert parts[1] == p.basename
|
||||||
|
assert len(parts) == 2
|
||||||
|
parts = rcol._parsearg(p.basename + "::test_func")
|
||||||
|
assert parts[0] == "sub"
|
||||||
|
assert parts[1] == p.basename
|
||||||
|
assert parts[2] == "test_func"
|
||||||
|
assert len(parts) == 3
|
||||||
|
|
||||||
|
def test_collect_topdir(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
id = "::".join([p.basename, "test_func"])
|
||||||
|
config = testdir.parseconfig(id)
|
||||||
|
topdir = testdir.tmpdir
|
||||||
|
rcol = Collection(config)
|
||||||
|
assert topdir == rcol.topdir
|
||||||
|
hookrec = testdir.getreportrecorder(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
assert len(items) == 1
|
||||||
|
root = items[0].listchain()[0]
|
||||||
|
root_id = rcol.getid(root)
|
||||||
|
root2 = rcol.getbyid(root_id)[0]
|
||||||
|
assert root2.fspath == root.fspath
|
||||||
|
|
||||||
|
def test_collect_protocol_single_function(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
id = "::".join([p.basename, "test_func"])
|
||||||
|
config = testdir.parseconfig(id)
|
||||||
|
topdir = testdir.tmpdir
|
||||||
|
rcol = Collection(config)
|
||||||
|
assert topdir == rcol.topdir
|
||||||
|
hookrec = testdir.getreportrecorder(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
assert len(items) == 1
|
||||||
|
item = items[0]
|
||||||
|
assert item.name == "test_func"
|
||||||
|
newid = rcol.getid(item)
|
||||||
|
assert newid == id
|
||||||
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
|
hookrec.hookrecorder.contains([
|
||||||
|
("pytest_collectstart", "collector.fspath == topdir"),
|
||||||
|
("pytest_make_collect_report", "collector.fspath == topdir"),
|
||||||
|
("pytest_collectstart", "collector.fspath == p"),
|
||||||
|
("pytest_make_collect_report", "collector.fspath == p"),
|
||||||
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == p"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == topdir")
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_collect_protocol_method(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
class TestClass:
|
||||||
|
def test_method(self):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
normid = p.basename + "::TestClass::test_method"
|
||||||
|
for id in [p.basename,
|
||||||
|
p.basename + "::TestClass",
|
||||||
|
p.basename + "::TestClass::()",
|
||||||
|
p.basename + "::TestClass::()::test_method",
|
||||||
|
normid,
|
||||||
|
]:
|
||||||
|
config = testdir.parseconfig(id)
|
||||||
|
rcol = Collection(config=config)
|
||||||
|
nodes = rcol.perform_collect()
|
||||||
|
assert len(nodes) == 1
|
||||||
|
assert nodes[0].name == "test_method"
|
||||||
|
newid = rcol.getid(nodes[0])
|
||||||
|
assert newid == normid
|
||||||
|
|
||||||
|
def test_collect_custom_nodes_multi_id(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
class SpecialItem(py.test.collect.Item):
|
||||||
|
def runtest(self):
|
||||||
|
return # ok
|
||||||
|
class SpecialFile(py.test.collect.File):
|
||||||
|
def collect(self):
|
||||||
|
return [SpecialItem(name="check", parent=self)]
|
||||||
|
def pytest_collect_file(path, parent):
|
||||||
|
if path.basename == %r:
|
||||||
|
return SpecialFile(fspath=path, parent=parent)
|
||||||
|
""" % p.basename)
|
||||||
|
id = p.basename
|
||||||
|
|
||||||
|
config = testdir.parseconfig(id)
|
||||||
|
rcol = Collection(config)
|
||||||
|
hookrec = testdir.getreportrecorder(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
|
assert len(items) == 2
|
||||||
|
hookrec.hookrecorder.contains([
|
||||||
|
("pytest_collectstart",
|
||||||
|
"collector.fspath == collector.collection.topdir"),
|
||||||
|
("pytest_collectstart",
|
||||||
|
"collector.__class__.__name__ == 'SpecialFile'"),
|
||||||
|
("pytest_collectstart",
|
||||||
|
"collector.__class__.__name__ == 'Module'"),
|
||||||
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == p"),
|
||||||
|
("pytest_collectreport",
|
||||||
|
"report.collector.fspath == report.collector.collection.topdir")
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_collect_subdir_event_ordering(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
aaa = testdir.mkpydir("aaa")
|
||||||
|
test_aaa = aaa.join("test_aaa.py")
|
||||||
|
p.move(test_aaa)
|
||||||
|
config = testdir.parseconfig()
|
||||||
|
rcol = Collection(config)
|
||||||
|
hookrec = testdir.getreportrecorder(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
assert len(items) == 1
|
||||||
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
|
hookrec.hookrecorder.contains([
|
||||||
|
("pytest_collectstart", "collector.fspath == aaa"),
|
||||||
|
("pytest_collectstart", "collector.fspath == test_aaa"),
|
||||||
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == test_aaa"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == aaa"),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_collect_two_commandline_args(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
aaa = testdir.mkpydir("aaa")
|
||||||
|
bbb = testdir.mkpydir("bbb")
|
||||||
|
p.copy(aaa.join("test_aaa.py"))
|
||||||
|
p.move(bbb.join("test_bbb.py"))
|
||||||
|
|
||||||
|
id = "."
|
||||||
|
config = testdir.parseconfig(id)
|
||||||
|
rcol = Collection(config)
|
||||||
|
hookrec = testdir.getreportrecorder(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
assert len(items) == 2
|
||||||
|
py.std.pprint.pprint(hookrec.hookrecorder.calls)
|
||||||
|
hookrec.hookrecorder.contains([
|
||||||
|
("pytest_collectstart", "collector.fspath == aaa"),
|
||||||
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == aaa"),
|
||||||
|
("pytest_collectstart", "collector.fspath == bbb"),
|
||||||
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
|
("pytest_collectreport", "report.collector.fspath == bbb"),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_serialization_byid(self, testdir):
|
||||||
|
p = testdir.makepyfile("def test_func(): pass")
|
||||||
|
config = testdir.parseconfig()
|
||||||
|
rcol = Collection(config)
|
||||||
|
items = rcol.perform_collect()
|
||||||
|
assert len(items) == 1
|
||||||
|
item, = items
|
||||||
|
id = rcol.getid(item)
|
||||||
|
newcol = Collection(config)
|
||||||
|
item2, = newcol.getbyid(id)
|
||||||
|
assert item2.name == item.name
|
||||||
|
assert item2.fspath == item.fspath
|
||||||
|
item2b, = newcol.getbyid(id)
|
||||||
|
assert item2b is 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
|
||||||
|
|
||||||
|
class Test_getinitialnodes:
|
||||||
|
def test_onedir(self, testdir):
|
||||||
|
config = testdir.reparseconfig([testdir.tmpdir])
|
||||||
|
colitems = Collection(config).getinitialnodes()
|
||||||
|
assert len(colitems) == 1
|
||||||
|
col = colitems[0]
|
||||||
|
assert isinstance(col, py.test.collect.Directory)
|
||||||
|
for col in col.listchain():
|
||||||
|
assert col.config is config
|
||||||
|
|
||||||
|
def test_twodirs(self, testdir, tmpdir):
|
||||||
|
config = testdir.reparseconfig([tmpdir, tmpdir])
|
||||||
|
colitems = Collection(config).getinitialnodes()
|
||||||
|
assert len(colitems) == 2
|
||||||
|
col1, col2 = colitems
|
||||||
|
assert col1.name == col2.name
|
||||||
|
assert col1.parent == col2.parent
|
||||||
|
|
||||||
|
def test_curdir_and_subdir(self, testdir, tmpdir):
|
||||||
|
a = tmpdir.ensure("a", dir=1)
|
||||||
|
config = testdir.reparseconfig([tmpdir, a])
|
||||||
|
colitems = Collection(config).getinitialnodes()
|
||||||
|
assert len(colitems) == 2
|
||||||
|
col1, col2 = colitems
|
||||||
|
assert col1.name == tmpdir.basename
|
||||||
|
assert col2.name == 'a'
|
||||||
|
for col in colitems:
|
||||||
|
for subcol in col.listchain():
|
||||||
|
assert col.config is config
|
||||||
|
|
||||||
|
def test_global_file(self, testdir, tmpdir):
|
||||||
|
x = tmpdir.ensure("x.py")
|
||||||
|
config = testdir.reparseconfig([x])
|
||||||
|
col, = Collection(config).getinitialnodes()
|
||||||
|
assert isinstance(col, py.test.collect.Module)
|
||||||
|
assert col.name == 'x.py'
|
||||||
|
assert col.parent.name == tmpdir.basename
|
||||||
|
assert col.parent.parent is None
|
||||||
|
for col in col.listchain():
|
||||||
|
assert col.config is config
|
||||||
|
|
||||||
|
def test_global_dir(self, testdir, tmpdir):
|
||||||
|
x = tmpdir.ensure("a", dir=1)
|
||||||
|
config = testdir.reparseconfig([x])
|
||||||
|
col, = Collection(config).getinitialnodes()
|
||||||
|
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, = Collection(config).getinitialnodes()
|
||||||
|
assert isinstance(col, py.test.collect.Module)
|
||||||
|
assert col.name == 'x.py'
|
||||||
|
assert col.parent.name == x.dirpath().basename
|
||||||
|
assert col.parent.parent.parent is None
|
||||||
|
for col in col.listchain():
|
||||||
|
assert col.config is config
|
||||||
|
|
||||||
|
class Test_genitems:
|
||||||
|
def test_check_collect_hashes(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def test_1():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_2():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
p.copy(p.dirpath(p.purebasename + "2" + ".py"))
|
||||||
|
items, reprec = testdir.inline_genitems(p.dirpath())
|
||||||
|
assert len(items) == 4
|
||||||
|
for numi, i in enumerate(items):
|
||||||
|
for numj, j in enumerate(items):
|
||||||
|
if numj != numi:
|
||||||
|
assert hash(i) != hash(j)
|
||||||
|
assert i != j
|
||||||
|
|
||||||
|
def test_root_conftest_syntax_error(self, testdir):
|
||||||
|
# do we want to unify behaviour with
|
||||||
|
# test_subdir_conftest_error?
|
||||||
|
p = testdir.makepyfile(conftest="raise SyntaxError\n")
|
||||||
|
py.test.raises(SyntaxError, testdir.inline_genitems, p.dirpath())
|
||||||
|
|
||||||
|
def test_example_items1(self, testdir):
|
||||||
|
p = testdir.makepyfile('''
|
||||||
|
def testone():
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestX:
|
||||||
|
def testmethod_one(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TestY(TestX):
|
||||||
|
pass
|
||||||
|
''')
|
||||||
|
items, reprec = testdir.inline_genitems(p)
|
||||||
|
assert len(items) == 3
|
||||||
|
assert items[0].name == 'testone'
|
||||||
|
assert items[1].name == 'testmethod_one'
|
||||||
|
assert items[2].name == 'testmethod_one'
|
||||||
|
|
||||||
|
# let's also test getmodpath here
|
||||||
|
assert items[0].getmodpath() == "testone"
|
||||||
|
assert items[1].getmodpath() == "TestX.testmethod_one"
|
||||||
|
assert items[2].getmodpath() == "TestY.testmethod_one"
|
||||||
|
|
||||||
|
s = items[0].getmodpath(stopatmodule=False)
|
||||||
|
assert s.endswith("test_example_items1.testone")
|
||||||
|
print(s)
|
|
@ -1,6 +1,4 @@
|
||||||
import py
|
import py
|
||||||
from py._test.collect import RootCollector
|
|
||||||
|
|
||||||
|
|
||||||
class TestConfigCmdlineParsing:
|
class TestConfigCmdlineParsing:
|
||||||
def test_parser_addoption_default_env(self, testdir, monkeypatch):
|
def test_parser_addoption_default_env(self, testdir, monkeypatch):
|
||||||
|
@ -106,104 +104,6 @@ class TestConfigAPI:
|
||||||
assert pl[0] == tmpdir
|
assert pl[0] == tmpdir
|
||||||
assert pl[1] == somepath
|
assert pl[1] == somepath
|
||||||
|
|
||||||
def test_setsessionclass_and_initsession(self, testdir):
|
|
||||||
config = testdir.Config()
|
|
||||||
class Session1:
|
|
||||||
def __init__(self, config):
|
|
||||||
self.config = config
|
|
||||||
config.setsessionclass(Session1)
|
|
||||||
session = config.initsession()
|
|
||||||
assert isinstance(session, Session1)
|
|
||||||
assert session.config is config
|
|
||||||
py.test.raises(ValueError, "config.setsessionclass(Session1)")
|
|
||||||
|
|
||||||
|
|
||||||
class TestConfigApi_getinitialnodes:
|
|
||||||
def test_onedir(self, testdir):
|
|
||||||
config = testdir.reparseconfig([testdir.tmpdir])
|
|
||||||
colitems = config.getinitialnodes()
|
|
||||||
assert len(colitems) == 1
|
|
||||||
col = colitems[0]
|
|
||||||
assert isinstance(col, py.test.collect.Directory)
|
|
||||||
for col in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
def test_twodirs(self, testdir, tmpdir):
|
|
||||||
config = testdir.reparseconfig([tmpdir, tmpdir])
|
|
||||||
colitems = config.getinitialnodes()
|
|
||||||
assert len(colitems) == 2
|
|
||||||
col1, col2 = colitems
|
|
||||||
assert col1.name == col2.name
|
|
||||||
assert col1.parent == col2.parent
|
|
||||||
|
|
||||||
def test_curdir_and_subdir(self, testdir, tmpdir):
|
|
||||||
a = tmpdir.ensure("a", dir=1)
|
|
||||||
config = testdir.reparseconfig([tmpdir, a])
|
|
||||||
colitems = config.getinitialnodes()
|
|
||||||
assert len(colitems) == 2
|
|
||||||
col1, col2 = colitems
|
|
||||||
assert col1.name == tmpdir.basename
|
|
||||||
assert col2.name == 'a'
|
|
||||||
for col in colitems:
|
|
||||||
for subcol in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
def test_global_file(self, testdir, tmpdir):
|
|
||||||
x = tmpdir.ensure("x.py")
|
|
||||||
config = testdir.reparseconfig([x])
|
|
||||||
col, = config.getinitialnodes()
|
|
||||||
assert isinstance(col, py.test.collect.Module)
|
|
||||||
assert col.name == 'x.py'
|
|
||||||
assert col.parent.name == tmpdir.basename
|
|
||||||
assert isinstance(col.parent.parent, RootCollector)
|
|
||||||
for col in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
def test_global_dir(self, testdir, tmpdir):
|
|
||||||
x = tmpdir.ensure("a", dir=1)
|
|
||||||
config = testdir.reparseconfig([x])
|
|
||||||
col, = config.getinitialnodes()
|
|
||||||
assert isinstance(col, py.test.collect.Directory)
|
|
||||||
print(col.listchain())
|
|
||||||
assert col.name == 'a'
|
|
||||||
assert isinstance(col.parent, RootCollector)
|
|
||||||
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, = config.getinitialnodes()
|
|
||||||
assert isinstance(col, py.test.collect.Module)
|
|
||||||
assert col.name == 'x.py'
|
|
||||||
assert col.parent.name == x.dirpath().basename
|
|
||||||
assert isinstance(col.parent.parent.parent, RootCollector)
|
|
||||||
for col in col.listchain():
|
|
||||||
assert col.config is config
|
|
||||||
|
|
||||||
class TestConfig_gettopdir:
|
|
||||||
def test_gettopdir(self, testdir):
|
|
||||||
from py._test.config import gettopdir
|
|
||||||
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):
|
|
||||||
from py._test.config import gettopdir
|
|
||||||
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 test_options_on_small_file_do_not_blow_up(testdir):
|
def test_options_on_small_file_do_not_blow_up(testdir):
|
||||||
def runfiletest(opts):
|
def runfiletest(opts):
|
||||||
|
@ -247,133 +147,3 @@ def test_preparse_ordering(testdir, monkeypatch):
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
plugin = config.pluginmanager.getplugin("mytestplugin")
|
plugin = config.pluginmanager.getplugin("mytestplugin")
|
||||||
assert plugin.x == 42
|
assert plugin.x == 42
|
||||||
|
|
||||||
|
|
||||||
import pickle
|
|
||||||
class TestConfigPickling:
|
|
||||||
def pytest_funcarg__testdir(self, request):
|
|
||||||
oldconfig = py.test.config
|
|
||||||
print("setting py.test.config to None")
|
|
||||||
py.test.config = None
|
|
||||||
def resetglobals():
|
|
||||||
py.builtin.print_("setting py.test.config to", oldconfig)
|
|
||||||
py.test.config = oldconfig
|
|
||||||
request.addfinalizer(resetglobals)
|
|
||||||
return request.getfuncargvalue("testdir")
|
|
||||||
|
|
||||||
def test_config_getstate_setstate(self, testdir):
|
|
||||||
from py._test.config import Config
|
|
||||||
testdir.makepyfile(__init__="", conftest="x=1; y=2")
|
|
||||||
hello = testdir.makepyfile(hello="")
|
|
||||||
tmp = testdir.tmpdir
|
|
||||||
testdir.chdir()
|
|
||||||
config1 = testdir.parseconfig(hello)
|
|
||||||
config2 = Config()
|
|
||||||
config2.__setstate__(config1.__getstate__())
|
|
||||||
assert config2.topdir == py.path.local()
|
|
||||||
config2_relpaths = [py.path.local(x).relto(config2.topdir)
|
|
||||||
for x in config2.args]
|
|
||||||
config1_relpaths = [py.path.local(x).relto(config1.topdir)
|
|
||||||
for x in config1.args]
|
|
||||||
|
|
||||||
assert config2_relpaths == config1_relpaths
|
|
||||||
for name, value in config1.option.__dict__.items():
|
|
||||||
assert getattr(config2.option, name) == value
|
|
||||||
assert config2.getvalue("x") == 1
|
|
||||||
|
|
||||||
def test_config_pickling_customoption(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
group = parser.getgroup("testing group")
|
|
||||||
group.addoption('-G', '--glong', action="store", default=42,
|
|
||||||
type="int", dest="gdest", help="g value.")
|
|
||||||
""")
|
|
||||||
config = testdir.parseconfig("-G", "11")
|
|
||||||
assert config.option.gdest == 11
|
|
||||||
repr = config.__getstate__()
|
|
||||||
|
|
||||||
config = testdir.Config()
|
|
||||||
py.test.raises(AttributeError, "config.option.gdest")
|
|
||||||
|
|
||||||
config2 = testdir.Config()
|
|
||||||
config2.__setstate__(repr)
|
|
||||||
assert config2.option.gdest == 11
|
|
||||||
|
|
||||||
def test_config_pickling_and_conftest_deprecated(self, testdir):
|
|
||||||
tmp = testdir.tmpdir.ensure("w1", "w2", dir=1)
|
|
||||||
tmp.ensure("__init__.py")
|
|
||||||
tmp.join("conftest.py").write(py.code.Source("""
|
|
||||||
def pytest_addoption(parser):
|
|
||||||
group = parser.getgroup("testing group")
|
|
||||||
group.addoption('-G', '--glong', action="store", default=42,
|
|
||||||
type="int", dest="gdest", help="g value.")
|
|
||||||
"""))
|
|
||||||
config = testdir.parseconfig(tmp, "-G", "11")
|
|
||||||
assert config.option.gdest == 11
|
|
||||||
repr = config.__getstate__()
|
|
||||||
|
|
||||||
config = testdir.Config()
|
|
||||||
py.test.raises(AttributeError, "config.option.gdest")
|
|
||||||
|
|
||||||
config2 = testdir.Config()
|
|
||||||
config2.__setstate__(repr)
|
|
||||||
assert config2.option.gdest == 11
|
|
||||||
|
|
||||||
option = config2.addoptions("testing group",
|
|
||||||
config2.Option('-G', '--glong', action="store", default=42,
|
|
||||||
type="int", dest="gdest", help="g value."))
|
|
||||||
assert option.gdest == 11
|
|
||||||
|
|
||||||
def test_config_picklability(self, testdir):
|
|
||||||
config = testdir.parseconfig()
|
|
||||||
s = pickle.dumps(config)
|
|
||||||
newconfig = pickle.loads(s)
|
|
||||||
assert hasattr(newconfig, "topdir")
|
|
||||||
assert newconfig.topdir == py.path.local()
|
|
||||||
|
|
||||||
def test_collector_implicit_config_pickling(self, testdir):
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
testdir.chdir()
|
|
||||||
testdir.makepyfile(hello="def test_x(): pass")
|
|
||||||
config = testdir.parseconfig(tmpdir)
|
|
||||||
col = config.getnode(config.topdir)
|
|
||||||
io = py.io.BytesIO()
|
|
||||||
pickler = pickle.Pickler(io)
|
|
||||||
pickler.dump(col)
|
|
||||||
io.seek(0)
|
|
||||||
unpickler = pickle.Unpickler(io)
|
|
||||||
col2 = unpickler.load()
|
|
||||||
assert col2.name == col.name
|
|
||||||
assert col2.listnames() == col.listnames()
|
|
||||||
|
|
||||||
def test_config_and_collector_pickling(self, testdir):
|
|
||||||
tmpdir = testdir.tmpdir
|
|
||||||
dir1 = tmpdir.ensure("sourcedir", "somedir", dir=1)
|
|
||||||
config = testdir.parseconfig()
|
|
||||||
assert config.topdir == tmpdir
|
|
||||||
col = config.getnode(dir1.dirpath())
|
|
||||||
col1 = config.getnode(dir1)
|
|
||||||
assert col1.parent == col
|
|
||||||
io = py.io.BytesIO()
|
|
||||||
pickler = pickle.Pickler(io)
|
|
||||||
pickler.dump(col)
|
|
||||||
pickler.dump(col1)
|
|
||||||
pickler.dump(col)
|
|
||||||
io.seek(0)
|
|
||||||
unpickler = pickle.Unpickler(io)
|
|
||||||
newtopdir = tmpdir.ensure("newtopdir", dir=1)
|
|
||||||
newtopdir.mkdir("sourcedir").mkdir("somedir")
|
|
||||||
old = newtopdir.chdir()
|
|
||||||
try:
|
|
||||||
newcol = unpickler.load()
|
|
||||||
newcol2 = unpickler.load()
|
|
||||||
newcol3 = unpickler.load()
|
|
||||||
assert newcol2.config is newcol.config
|
|
||||||
assert newcol2.parent == newcol
|
|
||||||
assert newcol2.config.topdir.realpath() == newtopdir.realpath()
|
|
||||||
newsourcedir = newtopdir.join("sourcedir")
|
|
||||||
assert newcol.fspath.realpath() == newsourcedir.realpath()
|
|
||||||
assert newcol2.fspath.basename == dir1.basename
|
|
||||||
assert newcol2.fspath.relto(newcol2.config.topdir)
|
|
||||||
finally:
|
|
||||||
old.chdir()
|
|
||||||
|
|
|
@ -82,10 +82,10 @@ class TestConftestValueAccessGlobal:
|
||||||
#conftest.lget("b") == 1
|
#conftest.lget("b") == 1
|
||||||
|
|
||||||
def test_value_access_with_confmod(self, basedir):
|
def test_value_access_with_confmod(self, basedir):
|
||||||
topdir = basedir.join("adir", "b")
|
startdir = basedir.join("adir", "b")
|
||||||
topdir.ensure("xx", dir=True)
|
startdir.ensure("xx", dir=True)
|
||||||
conftest = ConftestWithSetinitial(topdir)
|
conftest = ConftestWithSetinitial(startdir)
|
||||||
mod, value = conftest.rget_with_confmod("a", topdir)
|
mod, value = conftest.rget_with_confmod("a", startdir)
|
||||||
assert value == 1.5
|
assert value == 1.5
|
||||||
path = py.path.local(mod.__file__)
|
path = py.path.local(mod.__file__)
|
||||||
assert path.dirpath() == basedir.join("adir", "b")
|
assert path.dirpath() == basedir.join("adir", "b")
|
||||||
|
|
|
@ -49,7 +49,7 @@ class TestCollectDeprecated:
|
||||||
def check2(self): pass
|
def check2(self): pass
|
||||||
"""))
|
"""))
|
||||||
config = testdir.parseconfig(somefile)
|
config = testdir.parseconfig(somefile)
|
||||||
dirnode = config.getnode(somefile.dirpath())
|
dirnode = testdir.getnode(config, somefile.dirpath())
|
||||||
colitems = dirnode.collect()
|
colitems = dirnode.collect()
|
||||||
w = recwarn.pop(DeprecationWarning)
|
w = recwarn.pop(DeprecationWarning)
|
||||||
assert w.filename.find("conftest.py") != -1
|
assert w.filename.find("conftest.py") != -1
|
||||||
|
@ -170,10 +170,13 @@ class TestCollectDeprecated:
|
||||||
if path.basename == "testme.xxx":
|
if path.basename == "testme.xxx":
|
||||||
return Module(path, parent=self)
|
return Module(path, parent=self)
|
||||||
return super(Directory, self).consider_file(path)
|
return super(Directory, self).consider_file(path)
|
||||||
|
#def pytest_collect_file(path, parent):
|
||||||
|
# if path.basename == "testme.xxx":
|
||||||
|
# return Module(path, parent=parent)
|
||||||
""")
|
""")
|
||||||
testme = testdir.makefile('xxx', testme="hello")
|
testme = testdir.makefile('xxx', testme="hello")
|
||||||
config = testdir.parseconfig(testme)
|
config = testdir.parseconfig(testme)
|
||||||
col = config.getnode(testme)
|
col = testdir.getnode(config, testme)
|
||||||
assert col.collect() == []
|
assert col.collect() == []
|
||||||
|
|
||||||
|
|
||||||
|
@ -219,7 +222,7 @@ class TestDisabled:
|
||||||
""")
|
""")
|
||||||
reprec.assertoutcome(skipped=2)
|
reprec.assertoutcome(skipped=2)
|
||||||
|
|
||||||
@py.test.mark.multi(name="Directory Module Class Function".split())
|
@py.test.mark.multi(name="Module Class Function".split())
|
||||||
def test_function_deprecated_run_execute(self, name, testdir, recwarn):
|
def test_function_deprecated_run_execute(self, name, testdir, recwarn):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import py
|
import py
|
||||||
|
@ -235,11 +238,11 @@ class TestDisabled:
|
||||||
""")
|
""")
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
if name == "Directory":
|
if name == "Directory":
|
||||||
config.getnode(testdir.tmpdir)
|
testdir.getnode(config, testdir.tmpdir)
|
||||||
elif name in ("Module", "File"):
|
elif name in ("Module", "File"):
|
||||||
config.getnode(p)
|
testdir.getnode(config, p)
|
||||||
else:
|
else:
|
||||||
fnode = config.getnode(p)
|
fnode = testdir.getnode(config, p)
|
||||||
recwarn.clear()
|
recwarn.clear()
|
||||||
fnode.collect()
|
fnode.collect()
|
||||||
w = recwarn.pop(DeprecationWarning)
|
w = recwarn.pop(DeprecationWarning)
|
||||||
|
@ -278,9 +281,10 @@ def test_conftest_non_python_items(recwarn, testdir):
|
||||||
checkfile = testdir.makefile(ext="xxx", hello="world")
|
checkfile = testdir.makefile(ext="xxx", hello="world")
|
||||||
testdir.makepyfile(x="")
|
testdir.makepyfile(x="")
|
||||||
testdir.maketxtfile(x="")
|
testdir.maketxtfile(x="")
|
||||||
config = testdir.parseconfig()
|
|
||||||
recwarn.clear()
|
recwarn.clear()
|
||||||
dircol = config.getnode(checkfile.dirpath())
|
config = testdir.parseconfig()
|
||||||
|
dircol = testdir.getnode(config, checkfile.dirpath())
|
||||||
|
|
||||||
w = recwarn.pop(DeprecationWarning)
|
w = recwarn.pop(DeprecationWarning)
|
||||||
assert str(w.message).find("conftest.py") != -1
|
assert str(w.message).find("conftest.py") != -1
|
||||||
colitems = dircol.collect()
|
colitems = dircol.collect()
|
||||||
|
@ -288,7 +292,7 @@ def test_conftest_non_python_items(recwarn, testdir):
|
||||||
assert colitems[0].name == "hello.xxx"
|
assert colitems[0].name == "hello.xxx"
|
||||||
assert colitems[0].__class__.__name__ == "CustomItem"
|
assert colitems[0].__class__.__name__ == "CustomItem"
|
||||||
|
|
||||||
item = config.getnode(checkfile)
|
item = testdir.getnode(config, checkfile)
|
||||||
assert item.name == "hello.xxx"
|
assert item.name == "hello.xxx"
|
||||||
assert item.__class__.__name__ == "CustomItem"
|
assert item.__class__.__name__ == "CustomItem"
|
||||||
|
|
||||||
|
@ -321,14 +325,14 @@ def test_extra_python_files_and_functions(testdir, recwarn):
|
||||||
""")
|
""")
|
||||||
# check that directory collects "check_" files
|
# check that directory collects "check_" files
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
col = config.getnode(checkfile.dirpath())
|
col = testdir.getnode(config, checkfile.dirpath())
|
||||||
colitems = col.collect()
|
colitems = col.collect()
|
||||||
assert len(colitems) == 1
|
assert len(colitems) == 1
|
||||||
assert isinstance(colitems[0], py.test.collect.Module)
|
assert isinstance(colitems[0], py.test.collect.Module)
|
||||||
|
|
||||||
# check that module collects "check_" functions and methods
|
# check that module collects "check_" functions and methods
|
||||||
config = testdir.parseconfig(checkfile)
|
config = testdir.parseconfig(checkfile)
|
||||||
col = config.getnode(checkfile)
|
col = testdir.getnode(config, checkfile)
|
||||||
assert isinstance(col, py.test.collect.Module)
|
assert isinstance(col, py.test.collect.Module)
|
||||||
colitems = col.collect()
|
colitems = col.collect()
|
||||||
assert len(colitems) == 2
|
assert len(colitems) == 2
|
||||||
|
|
|
@ -247,10 +247,11 @@ class TestFunction:
|
||||||
param = 1
|
param = 1
|
||||||
funcargs = {}
|
funcargs = {}
|
||||||
id = "world"
|
id = "world"
|
||||||
|
collection = object()
|
||||||
f5 = py.test.collect.Function(name="name", config=config,
|
f5 = py.test.collect.Function(name="name", config=config,
|
||||||
callspec=callspec1, callobj=isinstance)
|
callspec=callspec1, callobj=isinstance, collection=collection)
|
||||||
f5b = py.test.collect.Function(name="name", config=config,
|
f5b = py.test.collect.Function(name="name", config=config,
|
||||||
callspec=callspec2, callobj=isinstance)
|
callspec=callspec2, callobj=isinstance, collection=collection)
|
||||||
assert f5 != f5b
|
assert f5 != f5b
|
||||||
assert not (f5 == f5b)
|
assert not (f5 == f5b)
|
||||||
|
|
||||||
|
@ -459,8 +460,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.config._rootcol.totrail(modcol)
|
trail = modcol.collection.getid(modcol)
|
||||||
newcol = modcol.config._rootcol.fromtrail(trail)
|
newcol = modcol.collection.getbyid(trail)[0]
|
||||||
assert modcol.name == newcol.name
|
assert modcol.name == newcol.name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
import py
|
import py
|
||||||
|
|
||||||
class SessionTests:
|
class SessionTests:
|
||||||
def test_initsession(self, testdir, tmpdir):
|
|
||||||
config = testdir.reparseconfig()
|
|
||||||
session = config.initsession()
|
|
||||||
assert session.config is config
|
|
||||||
|
|
||||||
def test_basic_testitem_events(self, testdir):
|
def test_basic_testitem_events(self, testdir):
|
||||||
tfile = testdir.makepyfile("""
|
tfile = testdir.makepyfile("""
|
||||||
def test_one():
|
def test_one():
|
||||||
|
@ -25,11 +20,11 @@ class SessionTests:
|
||||||
assert failed[0].item.name == "test_one_one"
|
assert failed[0].item.name == "test_one_one"
|
||||||
assert failed[1].item.name == "test_other"
|
assert failed[1].item.name == "test_other"
|
||||||
assert failed[2].item.name == "test_two"
|
assert failed[2].item.name == "test_two"
|
||||||
itemstarted = reprec.getcalls("pytest_itemstart")
|
itemstarted = reprec.getcalls("pytest_log_itemcollect")
|
||||||
assert len(itemstarted) == 4
|
assert len(itemstarted) == 4
|
||||||
colstarted = reprec.getcalls("pytest_collectstart")
|
colstarted = reprec.getcalls("pytest_collectstart")
|
||||||
assert len(colstarted) == 1
|
assert len(colstarted) == 1 + 1 # XXX ExtraTopCollector
|
||||||
col = colstarted[0].collector
|
col = colstarted[1].collector
|
||||||
assert isinstance(col, py.test.collect.Module)
|
assert isinstance(col, py.test.collect.Module)
|
||||||
|
|
||||||
def test_nested_import_error(self, testdir):
|
def test_nested_import_error(self, testdir):
|
||||||
|
@ -183,13 +178,13 @@ class TestNewSession(SessionTests):
|
||||||
)
|
)
|
||||||
reprec = testdir.inline_run('--collectonly', p.dirpath())
|
reprec = testdir.inline_run('--collectonly', p.dirpath())
|
||||||
|
|
||||||
itemstarted = reprec.getcalls("pytest_itemstart")
|
itemstarted = reprec.getcalls("pytest_log_itemcollect")
|
||||||
assert len(itemstarted) == 3
|
assert len(itemstarted) == 3
|
||||||
assert not reprec.getreports("pytest_runtest_logreport")
|
assert not reprec.getreports("pytest_runtest_logreport")
|
||||||
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
|
assert len(started) == 8 + 1 # 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
|
||||||
|
|
Loading…
Reference in New Issue