fix issue27 - --collectonly and -k keyword selection now work together.

internally, collectonly and terminal reporting has been unified.
This commit is contained in:
holger krekel 2011-03-06 18:32:00 +01:00
parent 18e784c9c9
commit c552b58dc5
8 changed files with 91 additions and 117 deletions

View File

@ -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

View File

@ -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,11 +270,44 @@ 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()
@ -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

View File

@ -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

View File

@ -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'],

View File

@ -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")

View File

@ -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()

View File

@ -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*",

View File

@ -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())