split out pytest-xdist related reporting to the plugin
--HG-- branch : trunk
This commit is contained in:
parent
2664230fad
commit
320835d43f
|
@ -66,6 +66,9 @@ Bug fixes / Maintenance
|
||||||
- make initial conftest discovery ignore "--" prefixed arguments
|
- make initial conftest discovery ignore "--" prefixed arguments
|
||||||
- fix resultlog plugin when used in an multicpu/multihost xdist situation
|
- fix resultlog plugin when used in an multicpu/multihost xdist situation
|
||||||
(thanks Jakub Gustak)
|
(thanks Jakub Gustak)
|
||||||
|
- perform distributed testing related reporting in the xdist-plugin
|
||||||
|
rather than having dist-related code in the generic py.test
|
||||||
|
distribution
|
||||||
|
|
||||||
Changes between 1.3.0 and 1.3.1
|
Changes between 1.3.0 and 1.3.1
|
||||||
==================================================
|
==================================================
|
||||||
|
|
|
@ -115,12 +115,25 @@ class CallInfo:
|
||||||
return "<CallInfo when=%r %s>" % (self.when, status)
|
return "<CallInfo when=%r %s>" % (self.when, status)
|
||||||
|
|
||||||
class BaseReport(object):
|
class BaseReport(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.headerlines = []
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
l = ["%s=%s" %(key, value)
|
l = ["%s=%s" %(key, value)
|
||||||
for key, value in self.__dict__.items()]
|
for key, value in self.__dict__.items()]
|
||||||
return "<%s %s>" %(self.__class__.__name__, " ".join(l),)
|
return "<%s %s>" %(self.__class__.__name__, " ".join(l),)
|
||||||
|
|
||||||
|
def _getcrashline(self):
|
||||||
|
try:
|
||||||
|
return self.longrepr.reprcrash
|
||||||
|
except AttributeError:
|
||||||
|
try:
|
||||||
|
return str(self.longrepr)[:50]
|
||||||
|
except AttributeError:
|
||||||
|
return ""
|
||||||
|
|
||||||
def toterminal(self, out):
|
def toterminal(self, out):
|
||||||
|
for line in self.headerlines:
|
||||||
|
out.line(line)
|
||||||
longrepr = self.longrepr
|
longrepr = self.longrepr
|
||||||
if hasattr(longrepr, 'toterminal'):
|
if hasattr(longrepr, 'toterminal'):
|
||||||
longrepr.toterminal(out)
|
longrepr.toterminal(out)
|
||||||
|
@ -129,6 +142,7 @@ class BaseReport(object):
|
||||||
|
|
||||||
class CollectErrorRepr(BaseReport):
|
class CollectErrorRepr(BaseReport):
|
||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
|
super(CollectErrorRepr, self).__init__()
|
||||||
self.longrepr = msg
|
self.longrepr = msg
|
||||||
def toterminal(self, out):
|
def toterminal(self, out):
|
||||||
out.line(str(self.longrepr), red=True)
|
out.line(str(self.longrepr), red=True)
|
||||||
|
@ -137,6 +151,7 @@ class ItemTestReport(BaseReport):
|
||||||
failed = passed = skipped = False
|
failed = passed = skipped = False
|
||||||
|
|
||||||
def __init__(self, item, excinfo=None, when=None):
|
def __init__(self, item, excinfo=None, when=None):
|
||||||
|
super(ItemTestReport, self).__init__()
|
||||||
self.item = item
|
self.item = item
|
||||||
self.when = when
|
self.when = when
|
||||||
if item and when != "setup":
|
if item and when != "setup":
|
||||||
|
@ -189,6 +204,7 @@ class CollectReport(BaseReport):
|
||||||
skipped = failed = passed = False
|
skipped = failed = passed = False
|
||||||
|
|
||||||
def __init__(self, collector, result, excinfo=None):
|
def __init__(self, collector, result, excinfo=None):
|
||||||
|
super(CollectReport, self).__init__()
|
||||||
self.collector = collector
|
self.collector = collector
|
||||||
if not excinfo:
|
if not excinfo:
|
||||||
self.passed = True
|
self.passed = True
|
||||||
|
@ -213,6 +229,7 @@ class TeardownErrorReport(BaseReport):
|
||||||
failed = True
|
failed = True
|
||||||
when = "teardown"
|
when = "teardown"
|
||||||
def __init__(self, excinfo):
|
def __init__(self, excinfo):
|
||||||
|
super(TeardownErrorReport, self).__init__()
|
||||||
self.longrepr = excinfo.getrepr(funcargs=True)
|
self.longrepr = excinfo.getrepr(funcargs=True)
|
||||||
|
|
||||||
class SetupState(object):
|
class SetupState(object):
|
||||||
|
|
|
@ -6,8 +6,6 @@ This is a good source for looking at the various reporting hooks.
|
||||||
import py
|
import py
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
optionalhook = py.test.mark.optionalhook
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup("terminal reporting", "reporting", after="general")
|
group = parser.getgroup("terminal reporting", "reporting", after="general")
|
||||||
group._addoption('-v', '--verbose', action="count",
|
group._addoption('-v', '--verbose', action="count",
|
||||||
|
@ -80,7 +78,6 @@ class TerminalReporter:
|
||||||
file = py.std.sys.stdout
|
file = py.std.sys.stdout
|
||||||
self._tw = py.io.TerminalWriter(file)
|
self._tw = py.io.TerminalWriter(file)
|
||||||
self.currentfspath = None
|
self.currentfspath = None
|
||||||
self.gateway2info = {}
|
|
||||||
self.reportchars = getreportopt(config)
|
self.reportchars = getreportopt(config)
|
||||||
|
|
||||||
def hasopt(self, char):
|
def hasopt(self, char):
|
||||||
|
@ -167,53 +164,6 @@ class TerminalReporter:
|
||||||
# which garbles our output if we use self.write_line
|
# which garbles our output if we use self.write_line
|
||||||
self.write_line(msg)
|
self.write_line(msg)
|
||||||
|
|
||||||
@optionalhook
|
|
||||||
def pytest_gwmanage_newgateway(self, gateway, platinfo):
|
|
||||||
#self.write_line("%s instantiated gateway from spec %r" %(gateway.id, gateway.spec._spec))
|
|
||||||
d = {}
|
|
||||||
d['version'] = repr_pythonversion(platinfo.version_info)
|
|
||||||
d['id'] = gateway.id
|
|
||||||
d['spec'] = gateway.spec._spec
|
|
||||||
d['platform'] = platinfo.platform
|
|
||||||
if self.config.option.verbose:
|
|
||||||
d['extra'] = "- " + platinfo.executable
|
|
||||||
else:
|
|
||||||
d['extra'] = ""
|
|
||||||
d['cwd'] = platinfo.cwd
|
|
||||||
infoline = ("[%(id)s] %(spec)s -- platform %(platform)s, "
|
|
||||||
"Python %(version)s "
|
|
||||||
"cwd: %(cwd)s"
|
|
||||||
"%(extra)s" % d)
|
|
||||||
self.write_line(infoline)
|
|
||||||
self.gateway2info[gateway] = infoline
|
|
||||||
|
|
||||||
@optionalhook
|
|
||||||
def pytest_testnodeready(self, node):
|
|
||||||
self.write_line("[%s] txnode ready to receive tests" %(node.gateway.id,))
|
|
||||||
|
|
||||||
@optionalhook
|
|
||||||
def pytest_testnodedown(self, node, error):
|
|
||||||
if error:
|
|
||||||
self.write_line("[%s] node down, error: %s" %(node.gateway.id, error))
|
|
||||||
|
|
||||||
@optionalhook
|
|
||||||
def pytest_rescheduleitems(self, items):
|
|
||||||
if self.config.option.debug:
|
|
||||||
self.write_sep("!", "RESCHEDULING %s " %(items,))
|
|
||||||
|
|
||||||
@optionalhook
|
|
||||||
def pytest_looponfailinfo(self, failreports, rootdirs):
|
|
||||||
if failreports:
|
|
||||||
self.write_sep("#", "LOOPONFAILING", red=True)
|
|
||||||
for report in failreports:
|
|
||||||
loc = self._getcrashline(report)
|
|
||||||
if loc:
|
|
||||||
self.write_line(loc, red=True)
|
|
||||||
self.write_sep("#", "waiting for changes")
|
|
||||||
for rootdir in rootdirs:
|
|
||||||
self.write_line("### Watching: %s" %(rootdir,), bold=True)
|
|
||||||
|
|
||||||
|
|
||||||
def pytest_trace(self, category, msg):
|
def pytest_trace(self, category, msg):
|
||||||
if self.config.option.debug or \
|
if self.config.option.debug or \
|
||||||
self.config.option.traceconfig and category.find("config") != -1:
|
self.config.option.traceconfig and category.find("config") != -1:
|
||||||
|
@ -223,24 +173,13 @@ class TerminalReporter:
|
||||||
self.stats.setdefault('deselected', []).append(items)
|
self.stats.setdefault('deselected', []).append(items)
|
||||||
|
|
||||||
def pytest_itemstart(self, item, node=None):
|
def pytest_itemstart(self, item, node=None):
|
||||||
if getattr(self.config.option, 'dist', 'no') != "no":
|
if self.config.option.verbose:
|
||||||
# for dist-testing situations itemstart means we
|
line = self._reportinfoline(item)
|
||||||
# queued the item for sending, not interesting (unless debugging)
|
self.write_ensure_prefix(line, "")
|
||||||
if self.config.option.debug:
|
|
||||||
line = self._reportinfoline(item)
|
|
||||||
extra = ""
|
|
||||||
if node:
|
|
||||||
extra = "-> [%s]" % node.gateway.id
|
|
||||||
self.write_ensure_prefix(line, extra)
|
|
||||||
else:
|
else:
|
||||||
if self.config.option.verbose:
|
# ensure that the path is printed before the
|
||||||
line = self._reportinfoline(item)
|
# 1st test of a module starts running
|
||||||
self.write_ensure_prefix(line, "")
|
self.write_fspath_result(self._getfspath(item), "")
|
||||||
else:
|
|
||||||
# ensure that the path is printed before the
|
|
||||||
# 1st test of a module starts running
|
|
||||||
|
|
||||||
self.write_fspath_result(self._getfspath(item), "")
|
|
||||||
|
|
||||||
def pytest__teardown_final_logerror(self, report):
|
def pytest__teardown_final_logerror(self, report):
|
||||||
self.stats.setdefault("error", []).append(report)
|
self.stats.setdefault("error", []).append(report)
|
||||||
|
@ -321,15 +260,6 @@ class TerminalReporter:
|
||||||
else:
|
else:
|
||||||
excrepr.reprcrash.toterminal(self._tw)
|
excrepr.reprcrash.toterminal(self._tw)
|
||||||
|
|
||||||
def _getcrashline(self, report):
|
|
||||||
try:
|
|
||||||
return report.longrepr.reprcrash
|
|
||||||
except AttributeError:
|
|
||||||
try:
|
|
||||||
return str(report.longrepr)[:50]
|
|
||||||
except AttributeError:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def _reportinfoline(self, item):
|
def _reportinfoline(self, item):
|
||||||
collect_fspath = self._getfspath(item)
|
collect_fspath = self._getfspath(item)
|
||||||
fspath, lineno, msg = self._getreportinfo(item)
|
fspath, lineno, msg = self._getreportinfo(item)
|
||||||
|
@ -387,12 +317,11 @@ class TerminalReporter:
|
||||||
self.write_sep("=", "FAILURES")
|
self.write_sep("=", "FAILURES")
|
||||||
for rep in self.stats['failed']:
|
for rep in self.stats['failed']:
|
||||||
if tbstyle == "line":
|
if tbstyle == "line":
|
||||||
line = self._getcrashline(rep)
|
line = rep._getcrashline()
|
||||||
self.write_line(line)
|
self.write_line(line)
|
||||||
else:
|
else:
|
||||||
msg = self._getfailureheadline(rep)
|
msg = self._getfailureheadline(rep)
|
||||||
self.write_sep("_", msg)
|
self.write_sep("_", msg)
|
||||||
self.write_platinfo(rep)
|
|
||||||
rep.toterminal(self._tw)
|
rep.toterminal(self._tw)
|
||||||
|
|
||||||
def summary_errors(self):
|
def summary_errors(self):
|
||||||
|
@ -408,16 +337,8 @@ class TerminalReporter:
|
||||||
elif rep.when == "teardown":
|
elif rep.when == "teardown":
|
||||||
msg = "ERROR at teardown of " + msg
|
msg = "ERROR at teardown of " + msg
|
||||||
self.write_sep("_", msg)
|
self.write_sep("_", msg)
|
||||||
self.write_platinfo(rep)
|
|
||||||
rep.toterminal(self._tw)
|
rep.toterminal(self._tw)
|
||||||
|
|
||||||
def write_platinfo(self, rep):
|
|
||||||
if hasattr(rep, 'node'):
|
|
||||||
self.write_line(self.gateway2info.get(
|
|
||||||
rep.node.gateway,
|
|
||||||
"node %r (platinfo not found? strange)")
|
|
||||||
[:self._tw.fullwidth-1])
|
|
||||||
|
|
||||||
def summary_stats(self):
|
def summary_stats(self):
|
||||||
session_duration = py.std.time.time() - self._sessionstarttime
|
session_duration = py.std.time.time() - self._sessionstarttime
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,21 @@ class BaseFunctionalTests:
|
||||||
assert isinstance(rep.longrepr, ReprExceptionInfo)
|
assert isinstance(rep.longrepr, ReprExceptionInfo)
|
||||||
assert str(rep.shortrepr) == "F"
|
assert str(rep.shortrepr) == "F"
|
||||||
|
|
||||||
|
def test_failfunction_customized_report(self, testdir, LineMatcher):
|
||||||
|
reports = testdir.runitem("""
|
||||||
|
def test_func():
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
rep = reports[1]
|
||||||
|
rep.headerlines += ["hello world"]
|
||||||
|
tr = py.io.TerminalWriter(stringio=True)
|
||||||
|
rep.toterminal(tr)
|
||||||
|
val = tr.stringio.getvalue()
|
||||||
|
LineMatcher(val.split("\n")).fnmatch_lines([
|
||||||
|
"*hello world",
|
||||||
|
"*def test_func():*"
|
||||||
|
])
|
||||||
|
|
||||||
def test_skipfunction(self, testdir):
|
def test_skipfunction(self, testdir):
|
||||||
reports = testdir.runitem("""
|
reports = testdir.runitem("""
|
||||||
import py
|
import py
|
||||||
|
@ -435,3 +450,4 @@ def test_pytest_cmdline_main(testdir):
|
||||||
s = popen.stdout.read()
|
s = popen.stdout.read()
|
||||||
ret = popen.wait()
|
ret = popen.wait()
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
|
||||||
|
|
|
@ -18,36 +18,28 @@ def basic_run_report(item):
|
||||||
return runner.call_and_report(item, "call", log=False)
|
return runner.call_and_report(item, "call", log=False)
|
||||||
|
|
||||||
class Option:
|
class Option:
|
||||||
def __init__(self, verbose=False, dist=None, fulltrace=False):
|
def __init__(self, verbose=False, fulltrace=False):
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.dist = dist
|
|
||||||
self.fulltrace = fulltrace
|
self.fulltrace = fulltrace
|
||||||
def _getcmdargs(self):
|
|
||||||
|
@property
|
||||||
|
def args(self):
|
||||||
l = []
|
l = []
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
l.append('-v')
|
l.append('-v')
|
||||||
if self.dist:
|
|
||||||
l.append('--dist=%s' % self.dist)
|
|
||||||
l.append('--tx=popen')
|
|
||||||
if self.fulltrace:
|
if self.fulltrace:
|
||||||
l.append('--fulltrace')
|
l.append('--fulltrace')
|
||||||
return l
|
return l
|
||||||
def _getcmdstring(self):
|
|
||||||
return " ".join(self._getcmdargs())
|
|
||||||
|
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
if "option" in metafunc.funcargnames:
|
if "option" in metafunc.funcargnames:
|
||||||
metafunc.addcall(id="default", param=Option(verbose=False))
|
metafunc.addcall(id="default",
|
||||||
metafunc.addcall(id="verbose", param=Option(verbose=True))
|
funcargs={'option': Option(verbose=False)})
|
||||||
metafunc.addcall(id="fulltrace", param=Option(fulltrace=True))
|
metafunc.addcall(id="verbose",
|
||||||
if not getattr(metafunc.function, 'nodist', False):
|
funcargs={'option': Option(verbose=True)})
|
||||||
metafunc.addcall(id="verbose-dist",
|
metafunc.addcall(id="fulltrace",
|
||||||
param=Option(dist='each', verbose=True))
|
funcargs={'option': Option(fulltrace=True)})
|
||||||
|
|
||||||
def pytest_funcarg__option(request):
|
|
||||||
if request.param.dist:
|
|
||||||
request.config.pluginmanager.skipifmissing("xdist")
|
|
||||||
return request.param
|
|
||||||
|
|
||||||
class TestTerminal:
|
class TestTerminal:
|
||||||
def test_pass_skip_fail(self, testdir, option):
|
def test_pass_skip_fail(self, testdir, option):
|
||||||
|
@ -60,22 +52,13 @@ class TestTerminal:
|
||||||
def test_func():
|
def test_func():
|
||||||
assert 0
|
assert 0
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(*option._getcmdargs())
|
result = testdir.runpytest(*option.args)
|
||||||
if option.verbose:
|
if option.verbose:
|
||||||
if not option.dist:
|
result.stdout.fnmatch_lines([
|
||||||
result.stdout.fnmatch_lines([
|
"*test_pass_skip_fail.py:2: *test_ok*PASS*",
|
||||||
"*test_pass_skip_fail.py:2: *test_ok*PASS*",
|
"*test_pass_skip_fail.py:4: *test_skip*SKIP*",
|
||||||
"*test_pass_skip_fail.py:4: *test_skip*SKIP*",
|
"*test_pass_skip_fail.py:6: *test_func*FAIL*",
|
||||||
"*test_pass_skip_fail.py:6: *test_func*FAIL*",
|
])
|
||||||
])
|
|
||||||
else:
|
|
||||||
expected = [
|
|
||||||
"*PASS*test_pass_skip_fail.py:2: *test_ok*",
|
|
||||||
"*SKIP*test_pass_skip_fail.py:4: *test_skip*",
|
|
||||||
"*FAIL*test_pass_skip_fail.py:6: *test_func*",
|
|
||||||
]
|
|
||||||
for line in expected:
|
|
||||||
result.stdout.fnmatch_lines([line])
|
|
||||||
else:
|
else:
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*test_pass_skip_fail.py .sF"
|
"*test_pass_skip_fail.py .sF"
|
||||||
|
@ -86,16 +69,6 @@ class TestTerminal:
|
||||||
"E assert 0",
|
"E assert 0",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_collect_fail(self, testdir, option):
|
|
||||||
p = testdir.makepyfile("import xyz\n")
|
|
||||||
result = testdir.runpytest(*option._getcmdargs())
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*test_collect_fail.py E*",
|
|
||||||
"> import xyz",
|
|
||||||
"E ImportError: No module named xyz",
|
|
||||||
"*1 error*",
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_internalerror(self, testdir, linecomp):
|
def test_internalerror(self, testdir, linecomp):
|
||||||
modcol = testdir.getmodulecol("def test_one(): pass")
|
modcol = testdir.getmodulecol("def test_one(): pass")
|
||||||
rep = TerminalReporter(modcol.config, file=linecomp.stringio)
|
rep = TerminalReporter(modcol.config, file=linecomp.stringio)
|
||||||
|
@ -132,75 +105,6 @@ class TestTerminal:
|
||||||
id = tr.gettestid(method)
|
id = tr.gettestid(method)
|
||||||
assert id.endswith("test_testid.py::TestClass::test_method")
|
assert id.endswith("test_testid.py::TestClass::test_method")
|
||||||
|
|
||||||
def test_looponfailreport(self, testdir, linecomp):
|
|
||||||
modcol = testdir.getmodulecol("""
|
|
||||||
import py
|
|
||||||
def test_fail():
|
|
||||||
assert 0
|
|
||||||
def test_fail2():
|
|
||||||
raise ValueError()
|
|
||||||
@py.test.mark.xfail
|
|
||||||
def test_xfail():
|
|
||||||
assert 0
|
|
||||||
@py.test.mark.xfail
|
|
||||||
def test_xpass():
|
|
||||||
assert 1
|
|
||||||
""")
|
|
||||||
rep = TerminalReporter(modcol.config, file=linecomp.stringio)
|
|
||||||
reports = [basic_run_report(x) for x in modcol.collect()]
|
|
||||||
rep.pytest_looponfailinfo(reports, [modcol.config.topdir])
|
|
||||||
linecomp.assert_contains_lines([
|
|
||||||
"*test_looponfailreport.py:3: assert 0",
|
|
||||||
"*test_looponfailreport.py:5: ValueError*",
|
|
||||||
"*waiting*",
|
|
||||||
"*%s*" % (modcol.config.topdir),
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_tb_option(self, testdir, option):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import py
|
|
||||||
def g():
|
|
||||||
raise IndexError
|
|
||||||
def test_func():
|
|
||||||
print (6*7)
|
|
||||||
g() # --calling--
|
|
||||||
""")
|
|
||||||
for tbopt in ["long", "short", "no"]:
|
|
||||||
print('testing --tb=%s...' % tbopt)
|
|
||||||
result = testdir.runpytest('--tb=%s' % tbopt)
|
|
||||||
s = result.stdout.str()
|
|
||||||
if tbopt == "long":
|
|
||||||
assert 'print (6*7)' in s
|
|
||||||
else:
|
|
||||||
assert 'print (6*7)' not in s
|
|
||||||
if tbopt != "no":
|
|
||||||
assert '--calling--' in s
|
|
||||||
assert 'IndexError' in s
|
|
||||||
else:
|
|
||||||
assert 'FAILURES' not in s
|
|
||||||
assert '--calling--' not in s
|
|
||||||
assert 'IndexError' not in s
|
|
||||||
|
|
||||||
def test_tb_crashline(self, testdir, option):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
import py
|
|
||||||
def g():
|
|
||||||
raise IndexError
|
|
||||||
def test_func1():
|
|
||||||
print (6*7)
|
|
||||||
g() # --calling--
|
|
||||||
def test_func2():
|
|
||||||
assert 0, "hello"
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest("--tb=line")
|
|
||||||
bn = p.basename
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*%s:3: IndexError*" % bn,
|
|
||||||
"*%s:8: AssertionError: hello*" % bn,
|
|
||||||
])
|
|
||||||
s = result.stdout.str()
|
|
||||||
assert "def test_func2" not in s
|
|
||||||
|
|
||||||
def test_show_path_before_running_test(self, testdir, linecomp):
|
def test_show_path_before_running_test(self, testdir, linecomp):
|
||||||
item = testdir.getitem("def test_func(): pass")
|
item = testdir.getitem("def test_func(): pass")
|
||||||
tr = TerminalReporter(item.config, file=linecomp.stringio)
|
tr = TerminalReporter(item.config, file=linecomp.stringio)
|
||||||
|
@ -263,22 +167,6 @@ class TestTerminal:
|
||||||
"*test_p2.py <- *test_p1.py:2: TestMore.test_p1*",
|
"*test_p2.py <- *test_p1.py:2: TestMore.test_p1*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_keyboard_interrupt_dist(self, testdir, option):
|
|
||||||
# xxx could be refined to check for return code
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
def test_sleep():
|
|
||||||
import time
|
|
||||||
time.sleep(10)
|
|
||||||
""")
|
|
||||||
child = testdir.spawn_pytest(" ".join(option._getcmdargs()))
|
|
||||||
child.expect(".*test session starts.*")
|
|
||||||
child.kill(2) # keyboard interrupt
|
|
||||||
child.expect(".*KeyboardInterrupt.*")
|
|
||||||
#child.expect(".*seconds.*")
|
|
||||||
child.close()
|
|
||||||
#assert ret == 2
|
|
||||||
|
|
||||||
@py.test.mark.nodist
|
|
||||||
def test_keyboard_interrupt(self, testdir, option):
|
def test_keyboard_interrupt(self, testdir, option):
|
||||||
p = testdir.makepyfile("""
|
p = testdir.makepyfile("""
|
||||||
def test_foobar():
|
def test_foobar():
|
||||||
|
@ -289,7 +177,7 @@ class TestTerminal:
|
||||||
raise KeyboardInterrupt # simulating the user
|
raise KeyboardInterrupt # simulating the user
|
||||||
""")
|
""")
|
||||||
|
|
||||||
result = testdir.runpytest(*option._getcmdargs())
|
result = testdir.runpytest(*option.args)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
" def test_foobar():",
|
" def test_foobar():",
|
||||||
"> assert 0",
|
"> assert 0",
|
||||||
|
@ -302,37 +190,6 @@ class TestTerminal:
|
||||||
])
|
])
|
||||||
result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
|
result.stdout.fnmatch_lines(['*KeyboardInterrupt*'])
|
||||||
|
|
||||||
def test_maxfailures(self, testdir, option):
|
|
||||||
p = testdir.makepyfile("""
|
|
||||||
def test_1():
|
|
||||||
assert 0
|
|
||||||
def test_2():
|
|
||||||
assert 0
|
|
||||||
def test_3():
|
|
||||||
assert 0
|
|
||||||
""")
|
|
||||||
result = testdir.runpytest("--maxfail=2", *option._getcmdargs())
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*def test_1():*",
|
|
||||||
"*def test_2():*",
|
|
||||||
"*!! Interrupted: stopping after 2 failures*!!*",
|
|
||||||
"*2 failed*",
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_pytest_report_header(self, testdir):
|
|
||||||
testdir.makeconftest("""
|
|
||||||
def pytest_report_header(config):
|
|
||||||
return "hello: info"
|
|
||||||
""")
|
|
||||||
testdir.mkdir("a").join("conftest.py").write("""
|
|
||||||
def pytest_report_header(config):
|
|
||||||
return ["line1", "line2"]""")
|
|
||||||
result = testdir.runpytest("a")
|
|
||||||
result.stdout.fnmatch_lines([
|
|
||||||
"*hello: info*",
|
|
||||||
"line1",
|
|
||||||
"line2",
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
class TestCollectonly:
|
class TestCollectonly:
|
||||||
|
@ -691,12 +548,103 @@ def test_trace_reporting(testdir):
|
||||||
])
|
])
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
@py.test.mark.nodist
|
|
||||||
def test_show_funcarg(testdir, option):
|
def test_show_funcarg(testdir, option):
|
||||||
args = option._getcmdargs() + ["--funcargs"]
|
args = option.args + ["--funcargs"]
|
||||||
result = testdir.runpytest(*args)
|
result = testdir.runpytest(*args)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*tmpdir*",
|
"*tmpdir*",
|
||||||
"*temporary directory*",
|
"*temporary directory*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class TestGenericReporting:
|
||||||
|
""" this test class can be subclassed with a different option
|
||||||
|
provider to run e.g. distributed tests.
|
||||||
|
"""
|
||||||
|
def test_collect_fail(self, testdir, option):
|
||||||
|
p = testdir.makepyfile("import xyz\n")
|
||||||
|
result = testdir.runpytest(*option.args)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*test_collect_fail.py E*",
|
||||||
|
"> import xyz",
|
||||||
|
"E ImportError: No module named xyz",
|
||||||
|
"*1 error*",
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_maxfailures(self, testdir, option):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
def test_1():
|
||||||
|
assert 0
|
||||||
|
def test_2():
|
||||||
|
assert 0
|
||||||
|
def test_3():
|
||||||
|
assert 0
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("--maxfail=2", *option.args)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*def test_1():*",
|
||||||
|
"*def test_2():*",
|
||||||
|
"*!! Interrupted: stopping after 2 failures*!!*",
|
||||||
|
"*2 failed*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def test_tb_option(self, testdir, option):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
def g():
|
||||||
|
raise IndexError
|
||||||
|
def test_func():
|
||||||
|
print (6*7)
|
||||||
|
g() # --calling--
|
||||||
|
""")
|
||||||
|
for tbopt in ["long", "short", "no"]:
|
||||||
|
print('testing --tb=%s...' % tbopt)
|
||||||
|
result = testdir.runpytest('--tb=%s' % tbopt)
|
||||||
|
s = result.stdout.str()
|
||||||
|
if tbopt == "long":
|
||||||
|
assert 'print (6*7)' in s
|
||||||
|
else:
|
||||||
|
assert 'print (6*7)' not in s
|
||||||
|
if tbopt != "no":
|
||||||
|
assert '--calling--' in s
|
||||||
|
assert 'IndexError' in s
|
||||||
|
else:
|
||||||
|
assert 'FAILURES' not in s
|
||||||
|
assert '--calling--' not in s
|
||||||
|
assert 'IndexError' not in s
|
||||||
|
|
||||||
|
def test_tb_crashline(self, testdir, option):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
def g():
|
||||||
|
raise IndexError
|
||||||
|
def test_func1():
|
||||||
|
print (6*7)
|
||||||
|
g() # --calling--
|
||||||
|
def test_func2():
|
||||||
|
assert 0, "hello"
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("--tb=line")
|
||||||
|
bn = p.basename
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*%s:3: IndexError*" % bn,
|
||||||
|
"*%s:8: AssertionError: hello*" % bn,
|
||||||
|
])
|
||||||
|
s = result.stdout.str()
|
||||||
|
assert "def test_func2" not in s
|
||||||
|
|
||||||
|
def test_pytest_report_header(self, testdir, option):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
def pytest_report_header(config):
|
||||||
|
return "hello: info"
|
||||||
|
""")
|
||||||
|
testdir.mkdir("a").join("conftest.py").write("""
|
||||||
|
def pytest_report_header(config):
|
||||||
|
return ["line1", "line2"]""")
|
||||||
|
result = testdir.runpytest("a")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*hello: info*",
|
||||||
|
"line1",
|
||||||
|
"line2",
|
||||||
|
])
|
||||||
|
|
|
@ -181,6 +181,24 @@ class TestPrunetraceback:
|
||||||
"*hello world*",
|
"*hello world*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_collect_report_postprocessing(self, testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import not_exists
|
||||||
|
""")
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import py
|
||||||
|
def pytest_make_collect_report(__multicall__):
|
||||||
|
rep = __multicall__.execute()
|
||||||
|
rep.headerlines += ["header1"]
|
||||||
|
return rep
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest(p)
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ERROR collecting*",
|
||||||
|
"*header1*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
class TestCustomConftests:
|
class TestCustomConftests:
|
||||||
def test_ignore_collect_path(self, testdir):
|
def test_ignore_collect_path(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
|
|
10
tox.ini
10
tox.ini
|
@ -1,5 +1,6 @@
|
||||||
[tox]
|
[tox]
|
||||||
distshare={homedir}/.tox/distshare
|
distshare={homedir}/.tox/distshare
|
||||||
|
envlist=py26,py27,py31,py27-xdist,py25,py24
|
||||||
|
|
||||||
[tox:hudson]
|
[tox:hudson]
|
||||||
distshare={toxworkdir}/distshare
|
distshare={toxworkdir}/distshare
|
||||||
|
@ -14,6 +15,15 @@ deps=
|
||||||
pexpect
|
pexpect
|
||||||
[testenv:py27]
|
[testenv:py27]
|
||||||
basepython=python2.7
|
basepython=python2.7
|
||||||
|
[testenv:py27-xdist]
|
||||||
|
basepython=python2.7
|
||||||
|
deps=
|
||||||
|
{distshare}/py-**LATEST**
|
||||||
|
{distshare}/pytest-xdist-**LATEST**
|
||||||
|
commands=
|
||||||
|
py.test -n3 --confcutdir=.. -rfsxX \
|
||||||
|
--junitxml={envlogdir}/junit-{envname}.xml --tools-on-path []
|
||||||
|
|
||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
basepython=python2.6
|
basepython=python2.6
|
||||||
[testenv:doc]
|
[testenv:doc]
|
||||||
|
|
Loading…
Reference in New Issue