fix issue27 - --collectonly and -k keyword selection now work together.
internally, collectonly and terminal reporting has been unified.
This commit is contained in:
parent
18e784c9c9
commit
c552b58dc5
|
@ -1,12 +1,12 @@
|
|||
Changes between 2.0.1 and 2.0.2
|
||||
----------------------------------------------
|
||||
|
||||
- fix issue30 - extended xfail/skipif handling and better reporting.
|
||||
- fix issue30 - extended xfail/skipif handling and improved reporting.
|
||||
If you have a syntax error in your skip/xfail
|
||||
expressions you now get nice error reports.
|
||||
|
||||
Also you can now access module globals from xfail/skipif
|
||||
expressions so that this works now::
|
||||
expressions so that this for example works now::
|
||||
|
||||
import mymodule
|
||||
@pytest.mark.skipif("mymodule.__version__[0] == "1")
|
||||
|
@ -24,6 +24,11 @@ Changes between 2.0.1 and 2.0.2
|
|||
test function invocations generated from the pytest_generate_tests
|
||||
hook.
|
||||
|
||||
- fix issue27 - collectonly and keyword-selection (-k) now work together
|
||||
Also, if you do "py.test --collectonly -q" you now get a flat list
|
||||
of test ids that you can use to paste to the py.test commandline
|
||||
in order to execute a particular test.
|
||||
|
||||
- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP
|
||||
Starting with Python3.2 os.symlink may be supported. By requiring
|
||||
a newer py lib version the py.path.local() implementation acknowledges
|
||||
|
|
|
@ -32,22 +32,19 @@ def pytest_addoption(parser):
|
|||
|
||||
def pytest_configure(config):
|
||||
config.option.verbose -= config.option.quiet
|
||||
if config.option.collectonly:
|
||||
reporter = CollectonlyReporter(config)
|
||||
else:
|
||||
# we try hard to make printing resilient against
|
||||
# later changes on FD level.
|
||||
stdout = py.std.sys.stdout
|
||||
if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
|
||||
try:
|
||||
newfd = os.dup(stdout.fileno())
|
||||
#print "got newfd", newfd
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
stdout = os.fdopen(newfd, stdout.mode, 1)
|
||||
config._toclose = stdout
|
||||
reporter = TerminalReporter(config, stdout)
|
||||
# we try hard to make printing resilient against
|
||||
# later changes on FD level.
|
||||
stdout = py.std.sys.stdout
|
||||
if hasattr(os, 'dup') and hasattr(stdout, 'fileno'):
|
||||
try:
|
||||
newfd = os.dup(stdout.fileno())
|
||||
#print "got newfd", newfd
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
stdout = os.fdopen(newfd, stdout.mode, 1)
|
||||
config._toclose = stdout
|
||||
reporter = TerminalReporter(config, stdout)
|
||||
config.pluginmanager.register(reporter, 'terminalreporter')
|
||||
if config.option.debug or config.option.traceconfig:
|
||||
def mywriter(tags, args):
|
||||
|
@ -273,12 +270,45 @@ class TerminalReporter:
|
|||
for line in flatten(lines):
|
||||
self.write_line(line)
|
||||
|
||||
def pytest_collection_finish(self):
|
||||
def pytest_collection_finish(self, session):
|
||||
if self.config.option.collectonly:
|
||||
self._printcollecteditems(session.items)
|
||||
if self.stats.get('failed'):
|
||||
self._tw.sep("!", "collection failures")
|
||||
for rep in self.stats.get('failed'):
|
||||
rep.toterminal(self._tw)
|
||||
return 1
|
||||
return 0
|
||||
if not self.showheader:
|
||||
return
|
||||
#for i, testarg in enumerate(self.config.args):
|
||||
# self.write_line("test path %d: %s" %(i+1, testarg))
|
||||
|
||||
def _printcollecteditems(self, items):
|
||||
# to print out items and their parent collectors
|
||||
# we take care to leave out Instances aka ()
|
||||
# because later versions are going to get rid of them anyway
|
||||
if self.config.option.verbose < 0:
|
||||
for item in items:
|
||||
nodeid = item.nodeid
|
||||
nodeid = nodeid.replace("::()::", "::")
|
||||
self._tw.line(nodeid)
|
||||
return
|
||||
stack = []
|
||||
indent = ""
|
||||
for item in items:
|
||||
needed_collectors = item.listchain()[1:] # strip root node
|
||||
while stack:
|
||||
if stack == needed_collectors[:len(stack)]:
|
||||
break
|
||||
stack.pop()
|
||||
for col in needed_collectors[len(stack):]:
|
||||
stack.append(col)
|
||||
#if col.name == "()":
|
||||
# continue
|
||||
indent = (len(stack)-1) * " "
|
||||
self._tw.line("%s%s" %(indent, col))
|
||||
|
||||
def pytest_sessionfinish(self, exitstatus, __multicall__):
|
||||
__multicall__.execute()
|
||||
self._tw.line("")
|
||||
|
@ -403,52 +433,6 @@ class TerminalReporter:
|
|||
self.write_sep("=", "%d tests deselected by %r" %(
|
||||
len(self.stats['deselected']), self.config.option.keyword), bold=True)
|
||||
|
||||
|
||||
class CollectonlyReporter:
|
||||
INDENT = " "
|
||||
|
||||
def __init__(self, config, out=None):
|
||||
self.config = config
|
||||
if out is None:
|
||||
out = py.std.sys.stdout
|
||||
self._tw = py.io.TerminalWriter(out)
|
||||
self.indent = ""
|
||||
self._failed = []
|
||||
|
||||
def outindent(self, line):
|
||||
self._tw.line(self.indent + str(line))
|
||||
|
||||
def pytest_internalerror(self, excrepr):
|
||||
for line in str(excrepr).split("\n"):
|
||||
self._tw.line("INTERNALERROR> " + line)
|
||||
|
||||
def pytest_collectstart(self, collector):
|
||||
if collector.session != collector:
|
||||
self.outindent(collector)
|
||||
self.indent += self.INDENT
|
||||
|
||||
def pytest_itemcollected(self, item):
|
||||
self.outindent(item)
|
||||
|
||||
def pytest_collectreport(self, report):
|
||||
if not report.passed:
|
||||
if hasattr(report.longrepr, 'reprcrash'):
|
||||
msg = report.longrepr.reprcrash.message
|
||||
else:
|
||||
# XXX unify (we have CollectErrorRepr here)
|
||||
msg = str(report.longrepr[2])
|
||||
self.outindent("!!! %s !!!" % msg)
|
||||
#self.outindent("!!! error !!!")
|
||||
self._failed.append(report)
|
||||
self.indent = self.indent[:-len(self.INDENT)]
|
||||
|
||||
def pytest_collection_finish(self):
|
||||
if self._failed:
|
||||
self._tw.sep("!", "collection failures")
|
||||
for rep in self._failed:
|
||||
rep.toterminal(self._tw)
|
||||
return self._failed and 1 or 0
|
||||
|
||||
def repr_pythonversion(v=None):
|
||||
if v is None:
|
||||
v = sys.version_info
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
unit and functional testing with Python.
|
||||
"""
|
||||
__version__ = '2.0.2.dev4'
|
||||
__version__ = '2.0.2.dev5'
|
||||
__all__ = ['main']
|
||||
|
||||
from _pytest.core import main, UsageError, _preloadplugins
|
||||
|
|
2
setup.py
2
setup.py
|
@ -22,7 +22,7 @@ def main():
|
|||
name='pytest',
|
||||
description='py.test: simple powerful testing with Python',
|
||||
long_description = long_description,
|
||||
version='2.0.2.dev4',
|
||||
version='2.0.2.dev5',
|
||||
url='http://pytest.org',
|
||||
license='MIT license',
|
||||
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
|
||||
|
|
|
@ -89,9 +89,11 @@ class TestGeneralUsage:
|
|||
import pytest
|
||||
class MyFile(pytest.File):
|
||||
def collect(self):
|
||||
return
|
||||
return [MyItem("hello", parent=self)]
|
||||
def pytest_collect_file(path, parent):
|
||||
return MyFile(path, parent)
|
||||
class MyItem(pytest.Item):
|
||||
pass
|
||||
""")
|
||||
p = testdir.makepyfile("def test_hello(): pass")
|
||||
result = testdir.runpytest(p, "--collectonly")
|
||||
|
|
|
@ -94,6 +94,8 @@ class TestCollectFS:
|
|||
tmpdir.ensure(".whatever", 'test_notfound.py')
|
||||
tmpdir.ensure(".bzr", 'test_notfound.py')
|
||||
tmpdir.ensure("normal", 'test_found.py')
|
||||
for x in tmpdir.visit("test_*.py"):
|
||||
x.write("def test_hello(): pass")
|
||||
|
||||
result = testdir.runpytest("--collectonly")
|
||||
s = result.stdout.str()
|
||||
|
|
|
@ -359,8 +359,8 @@ class TestConftestCustomization:
|
|||
if path.basename == "test_xyz.py":
|
||||
return MyModule(path, parent)
|
||||
""")
|
||||
testdir.makepyfile("def some(): pass")
|
||||
testdir.makepyfile(test_xyz="")
|
||||
testdir.makepyfile("def test_some(): pass")
|
||||
testdir.makepyfile(test_xyz="def test_func(): pass")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*<Module*test_pytest*",
|
||||
|
|
|
@ -4,8 +4,7 @@ terminal reporting of the full testing process.
|
|||
import pytest,py
|
||||
import sys
|
||||
|
||||
from _pytest.terminal import TerminalReporter, \
|
||||
CollectonlyReporter, repr_pythonversion, getreportopt
|
||||
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
|
||||
from _pytest import runner
|
||||
|
||||
def basic_run_report(item):
|
||||
|
@ -157,53 +156,35 @@ class TestTerminal:
|
|||
|
||||
|
||||
class TestCollectonly:
|
||||
def test_collectonly_basic(self, testdir, linecomp):
|
||||
modcol = testdir.getmodulecol(configargs=['--collectonly'], source="""
|
||||
def test_collectonly_basic(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
def test_func():
|
||||
pass
|
||||
""")
|
||||
rep = CollectonlyReporter(modcol.config, out=linecomp.stringio)
|
||||
modcol.config.pluginmanager.register(rep)
|
||||
indent = rep.indent
|
||||
rep.config.hook.pytest_collectstart(collector=modcol)
|
||||
linecomp.assert_contains_lines([
|
||||
"<Module 'test_collectonly_basic.py'>"
|
||||
])
|
||||
item = modcol.collect()[0]
|
||||
rep.config.hook.pytest_itemcollected(item=item)
|
||||
linecomp.assert_contains_lines([
|
||||
result = testdir.runpytest("--collectonly",)
|
||||
result.stdout.fnmatch_lines([
|
||||
"<Module 'test_collectonly_basic.py'>",
|
||||
" <Function 'test_func'>",
|
||||
])
|
||||
report = rep.config.hook.pytest_make_collect_report(collector=modcol)
|
||||
rep.config.hook.pytest_collectreport(report=report)
|
||||
assert rep.indent == indent
|
||||
|
||||
def test_collectonly_skipped_module(self, testdir, linecomp):
|
||||
modcol = testdir.getmodulecol(configargs=['--collectonly'], source="""
|
||||
def test_collectonly_skipped_module(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
pytest.skip("nomod")
|
||||
""")
|
||||
rep = CollectonlyReporter(modcol.config, out=linecomp.stringio)
|
||||
modcol.config.pluginmanager.register(rep)
|
||||
cols = list(testdir.genitems([modcol]))
|
||||
assert len(cols) == 0
|
||||
linecomp.assert_contains_lines("""
|
||||
<Module 'test_collectonly_skipped_module.py'>
|
||||
!!! Skipped: nomod !!!
|
||||
pytest.skip("hello")
|
||||
""")
|
||||
result = testdir.runpytest("--collectonly", "-rs")
|
||||
result.stdout.fnmatch_lines([
|
||||
"SKIP*hello*",
|
||||
"*1 skip*",
|
||||
])
|
||||
|
||||
def test_collectonly_failed_module(self, testdir, linecomp):
|
||||
modcol = testdir.getmodulecol(configargs=['--collectonly'], source="""
|
||||
raise ValueError(0)
|
||||
""")
|
||||
rep = CollectonlyReporter(modcol.config, out=linecomp.stringio)
|
||||
modcol.config.pluginmanager.register(rep)
|
||||
cols = list(testdir.genitems([modcol]))
|
||||
assert len(cols) == 0
|
||||
linecomp.assert_contains_lines("""
|
||||
<Module 'test_collectonly_failed_module.py'>
|
||||
!!! ValueError: 0 !!!
|
||||
""")
|
||||
def test_collectonly_failed_module(self, testdir):
|
||||
testdir.makepyfile("""raise ValueError(0)""")
|
||||
result = testdir.runpytest("--collectonly")
|
||||
result.stdout.fnmatch_lines([
|
||||
"*raise ValueError*",
|
||||
"*1 error*",
|
||||
])
|
||||
|
||||
def test_collectonly_fatal(self, testdir):
|
||||
p1 = testdir.makeconftest("""
|
||||
|
@ -228,11 +209,11 @@ class TestCollectonly:
|
|||
stderr = result.stderr.str().strip()
|
||||
#assert stderr.startswith("inserting into sys.path")
|
||||
assert result.ret == 0
|
||||
extra = result.stdout.fnmatch_lines([
|
||||
result.stdout.fnmatch_lines([
|
||||
"*<Module '*.py'>",
|
||||
"* <Function 'test_func1'*>",
|
||||
"* <Class 'TestClass'>",
|
||||
"* <Instance '()'>",
|
||||
#"* <Instance '()'>",
|
||||
"* <Function 'test_method'*>",
|
||||
])
|
||||
|
||||
|
@ -241,11 +222,11 @@ class TestCollectonly:
|
|||
result = testdir.runpytest("--collectonly", p)
|
||||
stderr = result.stderr.str().strip()
|
||||
assert result.ret == 1
|
||||
extra = result.stdout.fnmatch_lines(py.code.Source("""
|
||||
*<Module '*.py'>
|
||||
*ImportError*
|
||||
*!!!*failures*!!!
|
||||
*test_collectonly_error.py:1*
|
||||
result.stdout.fnmatch_lines(py.code.Source("""
|
||||
*ERROR*
|
||||
*import Errlk*
|
||||
*ImportError*
|
||||
*1 error*
|
||||
""").strip())
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue