refine lsof/FD leakage testing and rework test setup and some of pytest own tests. Note that the actual diff to non-test code is small. Also remove some redundant tests (introduced by a copy-paste-error apparently in test_mark.py).

This commit is contained in:
holger krekel 2011-11-07 18:08:41 +00:00
parent 077c468589
commit a2f4a11301
19 changed files with 191 additions and 305 deletions

View File

@ -1,7 +1,7 @@
Changes between 2.1.3 and [next version] Changes between 2.1.3 and [next version]
---------------------------------------- ----------------------------------------
- fix pytest's own test suite to not leak FDs - fix and cleanup pytest's own test suite to not leak FDs
- fix issue83: link to generated funcarg list - fix issue83: link to generated funcarg list
- fix issue74: pyarg module names are now checked against imp.find_module false positives - fix issue74: pyarg module names are now checked against imp.find_module false positives

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.1.4.dev2' __version__ = '2.1.4.dev3'

View File

@ -11,20 +11,23 @@ def pytest_addoption(parser):
group._addoption('-s', action="store_const", const="no", dest="capture", group._addoption('-s', action="store_const", const="no", dest="capture",
help="shortcut for --capture=no.") help="shortcut for --capture=no.")
@pytest.mark.tryfirst
def pytest_cmdline_parse(pluginmanager, args):
# we want to perform capturing already for plugin/conftest loading
if '-s' in args or "--capture=no" in args:
method = "no"
elif hasattr(os, 'dup') and '--capture=sys' not in args:
method = "fd"
else:
method = "sys"
capman = CaptureManager(method)
pluginmanager.register(capman, "capturemanager")
def addouterr(rep, outerr): def addouterr(rep, outerr):
for secname, content in zip(["out", "err"], outerr): for secname, content in zip(["out", "err"], outerr):
if content: if content:
rep.sections.append(("Captured std%s" % secname, content)) rep.sections.append(("Captured std%s" % secname, content))
def pytest_unconfigure(config):
# registered in config.py during early conftest.py loading
capman = config.pluginmanager.getplugin('capturemanager')
while capman._method2capture:
name, cap = capman._method2capture.popitem()
# XXX logging module may wants to close it itself on process exit
# otherwise we could do finalization here and call "reset()".
cap.suspend()
class NoCapture: class NoCapture:
def startall(self): def startall(self):
pass pass
@ -36,8 +39,9 @@ class NoCapture:
return "", "" return "", ""
class CaptureManager: class CaptureManager:
def __init__(self): def __init__(self, defaultmethod=None):
self._method2capture = {} self._method2capture = {}
self._defaultmethod = defaultmethod
def _maketempfile(self): def _maketempfile(self):
f = py.std.tempfile.TemporaryFile() f = py.std.tempfile.TemporaryFile()
@ -62,14 +66,6 @@ class CaptureManager:
else: else:
raise ValueError("unknown capturing method: %r" % method) raise ValueError("unknown capturing method: %r" % method)
def _getmethod_preoptionparse(self, args):
if '-s' in args or "--capture=no" in args:
return "no"
elif hasattr(os, 'dup') and '--capture=sys' not in args:
return "fd"
else:
return "sys"
def _getmethod(self, config, fspath): def _getmethod(self, config, fspath):
if config.option.capture: if config.option.capture:
method = config.option.capture method = config.option.capture
@ -82,16 +78,22 @@ class CaptureManager:
method = "sys" method = "sys"
return method return method
def reset_capturings(self):
for name, cap in self._method2capture.items():
cap.reset()
def resumecapture_item(self, item): def resumecapture_item(self, item):
method = self._getmethod(item.config, item.fspath) method = self._getmethod(item.config, item.fspath)
if not hasattr(item, 'outerr'): if not hasattr(item, 'outerr'):
item.outerr = ('', '') # we accumulate outerr on the item item.outerr = ('', '') # we accumulate outerr on the item
return self.resumecapture(method) return self.resumecapture(method)
def resumecapture(self, method): def resumecapture(self, method=None):
if hasattr(self, '_capturing'): if hasattr(self, '_capturing'):
raise ValueError("cannot resume, already capturing with %r" % raise ValueError("cannot resume, already capturing with %r" %
(self._capturing,)) (self._capturing,))
if method is None:
method = self._defaultmethod
cap = self._method2capture.get(method) cap = self._method2capture.get(method)
self._capturing = method self._capturing = method
if cap is None: if cap is None:

View File

@ -11,8 +11,12 @@ def pytest_cmdline_parse(pluginmanager, args):
return config return config
def pytest_unconfigure(config): def pytest_unconfigure(config):
for func in config._cleanup: while 1:
func() try:
fin = config._cleanup.pop()
except IndexError:
break
fin()
class Parser: class Parser:
""" Parser for command line arguments. """ """ Parser for command line arguments. """
@ -254,11 +258,14 @@ class Config(object):
self.hook = self.pluginmanager.hook self.hook = self.pluginmanager.hook
self._inicache = {} self._inicache = {}
self._cleanup = [] self._cleanup = []
@classmethod @classmethod
def fromdictargs(cls, option_dict, args): def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """ """ constructor useable for subprocesses. """
config = cls() config = cls()
# XXX slightly crude way to initialize capturing
import _pytest.capture
_pytest.capture.pytest_cmdline_parse(config.pluginmanager, args)
config._preparse(args, addopts=False) config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict) config.option.__dict__.update(option_dict)
for x in config.option.plugins: for x in config.option.plugins:
@ -283,11 +290,10 @@ class Config(object):
def _setinitialconftest(self, args): def _setinitialconftest(self, args):
# capture output during conftest init (#issue93) # capture output during conftest init (#issue93)
from _pytest.capture import CaptureManager # XXX introduce load_conftest hook to avoid needing to know
capman = CaptureManager() # about capturing plugin here
self.pluginmanager.register(capman, 'capturemanager') capman = self.pluginmanager.getplugin("capturemanager")
# will be unregistered in capture.py's unconfigure() capman.resumecapture()
capman.resumecapture(capman._getmethod_preoptionparse(args))
try: try:
try: try:
self._conftest.setinitial(args) self._conftest.setinitial(args)

View File

@ -431,10 +431,7 @@ _preinit = []
def _preloadplugins(): def _preloadplugins():
_preinit.append(PluginManager(load=True)) _preinit.append(PluginManager(load=True))
def main(args=None, plugins=None): def _prepareconfig(args=None, plugins=None):
""" returned exit code integer, after an in-process testing run
with the given command line arguments, preloading an optional list
of passed in plugin objects. """
if args is None: if args is None:
args = sys.argv[1:] args = sys.argv[1:]
elif isinstance(args, py.path.local): elif isinstance(args, py.path.local):
@ -448,13 +445,19 @@ def main(args=None, plugins=None):
else: # subsequent calls to main will create a fresh instance else: # subsequent calls to main will create a fresh instance
_pluginmanager = PluginManager(load=True) _pluginmanager = PluginManager(load=True)
hook = _pluginmanager.hook hook = _pluginmanager.hook
if plugins:
for plugin in plugins:
_pluginmanager.register(plugin)
return hook.pytest_cmdline_parse(
pluginmanager=_pluginmanager, args=args)
def main(args=None, plugins=None):
""" returned exit code integer, after an in-process testing run
with the given command line arguments, preloading an optional list
of passed in plugin objects. """
try: try:
if plugins: config = _prepareconfig(args, plugins)
for plugin in plugins: exitstatus = config.hook.pytest_cmdline_main(config=config)
_pluginmanager.register(plugin)
config = hook.pytest_cmdline_parse(
pluginmanager=_pluginmanager, args=args)
exitstatus = hook.pytest_cmdline_main(config=config)
except UsageError: except UsageError:
e = sys.exc_info()[1] e = sys.exc_info()[1]
sys.stderr.write("ERROR: %s\n" %(e.args[0],)) sys.stderr.write("ERROR: %s\n" %(e.args[0],))

View File

@ -56,6 +56,7 @@ def pytest_cmdline_main(config):
elif config.option.help: elif config.option.help:
config.pluginmanager.do_configure(config) config.pluginmanager.do_configure(config)
showhelp(config) showhelp(config)
config.pluginmanager.do_unconfigure(config)
return 0 return 0
def showhelp(config): def showhelp(config):
@ -113,7 +114,7 @@ def pytest_report_header(config):
verinfo = getpluginversioninfo(config) verinfo = getpluginversioninfo(config)
if verinfo: if verinfo:
lines.extend(verinfo) lines.extend(verinfo)
if config.option.traceconfig: if config.option.traceconfig:
lines.append("active plugins:") lines.append("active plugins:")
plugins = [] plugins = []

View File

@ -50,7 +50,7 @@ def pytest_addoption(parser):
def pytest_namespace(): def pytest_namespace():
collect = dict(Item=Item, Collector=Collector, File=File, Session=Session) collect = dict(Item=Item, Collector=Collector, File=File, Session=Session)
return dict(collect=collect) return dict(collect=collect)
def pytest_configure(config): def pytest_configure(config):
py.test.config = config # compatibiltiy py.test.config = config # compatibiltiy
if config.option.exitfirst: if config.option.exitfirst:
@ -134,7 +134,7 @@ def compatproperty(name):
return getattr(pytest, name) return getattr(pytest, name)
return property(fget, None, None, return property(fget, None, None,
"deprecated attribute %r, use pytest.%s" % (name,name)) "deprecated attribute %r, use pytest.%s" % (name,name))
class Node(object): 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."""
@ -145,13 +145,13 @@ class Node(object):
#: the parent collector node. #: the parent collector node.
self.parent = parent self.parent = parent
#: the test config object #: the test config object
self.config = config or parent.config self.config = config or parent.config
#: the collection this node is part of #: the collection this node is part of
self.session = session or parent.session self.session = session or parent.session
#: filesystem path where this node was collected from #: filesystem path where this node was collected from
self.fspath = getattr(parent, 'fspath', None) self.fspath = getattr(parent, 'fspath', None)
self.ihook = self.session.gethookproxy(self.fspath) self.ihook = self.session.gethookproxy(self.fspath)
@ -488,7 +488,7 @@ class Session(FSCollector):
else: else:
if fd is not None: if fd is not None:
fd.close() fd.close()
if type_[2] != imp.PKG_DIRECTORY: if type_[2] != imp.PKG_DIRECTORY:
path = [os.path.dirname(mod)] path = [os.path.dirname(mod)]
else: else:
@ -511,7 +511,7 @@ class Session(FSCollector):
raise pytest.UsageError(msg + arg) raise pytest.UsageError(msg + arg)
parts[0] = path parts[0] = path
return parts return parts
def matchnodes(self, matching, names): def matchnodes(self, matching, names):
self.trace("matchnodes", matching, names) self.trace("matchnodes", matching, names)
self.trace.root.indent += 1 self.trace.root.indent += 1

View File

@ -314,16 +314,6 @@ class TmpTestdir:
result.extend(session.genitems(colitem)) result.extend(session.genitems(colitem))
return result return result
def inline_genitems(self, *args):
#config = self.parseconfig(*args)
config = self.parseconfigure(*args)
rec = self.getreportrecorder(config)
session = Session(config)
config.hook.pytest_sessionstart(session=session)
session.perform_collect()
config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
return session.items, rec
def runitem(self, source): def runitem(self, source):
# used from runner functional tests # used from runner functional tests
item = self.getitem(source) item = self.getitem(source)
@ -347,70 +337,52 @@ class TmpTestdir:
assert len(reports) == 1, reports assert len(reports) == 1, reports
return reports[0] return reports[0]
def inline_run(self, *args): def inline_genitems(self, *args):
args = ("-s", ) + args # otherwise FD leakage return self.inprocess_run(list(args) + ['--collectonly'])
config = self.parseconfig(*args)
reprec = self.getreportrecorder(config)
#config.pluginmanager.do_configure(config)
config.hook.pytest_cmdline_main(config=config)
#config.pluginmanager.do_unconfigure(config)
return reprec
def config_preparse(self): def inline_run(self, *args):
config = self.Config() items, rec = self.inprocess_run(args)
for plugin in self.plugins: return rec
if isinstance(plugin, str):
config.pluginmanager.import_plugin(plugin) def inprocess_run(self, args, plugins=None):
else: rec = []
if isinstance(plugin, dict): items = []
plugin = PseudoPlugin(plugin) class Collect:
if not config.pluginmanager.isregistered(plugin): def pytest_configure(x, config):
config.pluginmanager.register(plugin) rec.append(self.getreportrecorder(config))
return config def pytest_itemcollected(self, item):
items.append(item)
if not plugins:
plugins = []
plugins.append(Collect())
self.pytestmain(list(args), plugins=[Collect()])
assert len(rec) == 1
return items, rec[0]
def parseconfig(self, *args): def parseconfig(self, *args):
if not args: args = map(str, args)
args = (self.tmpdir,)
config = self.config_preparse()
args = list(args)
for x in args: for x in args:
if str(x).startswith('--basetemp'): if str(x).startswith('--basetemp'):
break break
else: else:
args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp')) args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
config.parse(args) import _pytest.core
config = _pytest.core._prepareconfig(args, self.plugins)
# the in-process pytest invocation needs to avoid leaking FDs
# so we register a "reset_capturings" callmon the capturing manager
# and make sure it gets called
config._cleanup.append(
config.pluginmanager.getplugin("capturemanager").reset_capturings)
import _pytest.config
self.request.addfinalizer(
lambda: _pytest.config.pytest_unconfigure(config))
return config return config
def reparseconfig(self, args=None):
""" this is used from tests that want to re-invoke parse(). """
if not args:
args = [self.tmpdir]
oldconfig = getattr(py.test, 'config', None)
try:
c = py.test.config = self.Config()
c.basetemp = py.path.local.make_numbered_dir(prefix="reparse",
keep=0, rootdir=self.tmpdir, lock_timeout=None)
c.parse(args)
c.pluginmanager.do_configure(c)
self.request.addfinalizer(lambda: c.pluginmanager.do_unconfigure(c))
return c
finally:
py.test.config = oldconfig
def parseconfigure(self, *args): def parseconfigure(self, *args):
config = self.parseconfig(*args) config = self.parseconfig(*args)
config.pluginmanager.do_configure(config) config.pluginmanager.do_configure(config)
self.request.addfinalizer(lambda: self.request.addfinalizer(lambda:
config.pluginmanager.do_unconfigure(config)) config.pluginmanager.do_unconfigure(config))
# XXX we need to additionally reset FDs to prevent pen FDs
# during our test suite. see also capture.py's unconfigure XXX
# comment about logging
def finalize_capman():
capman = config.pluginmanager.getplugin('capturemanager')
while capman._method2capture:
name, cap = capman._method2capture.popitem()
cap.reset()
self.request.addfinalizer(finalize_capman)
return config return config
def getitem(self, source, funcname="test_func"): def getitem(self, source, funcname="test_func"):
@ -430,7 +402,6 @@ class TmpTestdir:
self.makepyfile(__init__ = "#") self.makepyfile(__init__ = "#")
self.config = config = self.parseconfigure(path, *configargs) self.config = config = self.parseconfigure(path, *configargs)
node = self.getnode(config, path) node = self.getnode(config, path)
#config.pluginmanager.do_unconfigure(config)
return node return node
def collect_by_name(self, modcol, name): def collect_by_name(self, modcol, name):
@ -447,9 +418,16 @@ class TmpTestdir:
return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
def pytestmain(self, *args, **kwargs): def pytestmain(self, *args, **kwargs):
ret = pytest.main(*args, **kwargs) class ResetCapturing:
if ret == 2: @pytest.mark.trylast
raise KeyboardInterrupt() def pytest_unconfigure(self, config):
capman = config.pluginmanager.getplugin("capturemanager")
capman.reset_capturings()
plugins = kwargs.setdefault("plugins", [])
rc = ResetCapturing()
plugins.append(rc)
return pytest.main(*args, **kwargs)
def run(self, *cmdargs): def run(self, *cmdargs):
return self._run(*cmdargs) return self._run(*cmdargs)
@ -550,10 +528,6 @@ def getdecoded(out):
return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % ( return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
py.io.saferepr(out),) py.io.saferepr(out),)
class PseudoPlugin:
def __init__(self, vars):
self.__dict__.update(vars)
class ReportRecorder(object): class ReportRecorder(object):
def __init__(self, hook): def __init__(self, hook):
self.hook = hook self.hook = hook

View File

@ -43,7 +43,8 @@ def pytest_configure(config):
pass pass
else: else:
stdout = os.fdopen(newfd, stdout.mode, 1) stdout = os.fdopen(newfd, stdout.mode, 1)
config._toclose = stdout config._cleanup.append(lambda: stdout.close())
reporter = TerminalReporter(config, stdout) reporter = TerminalReporter(config, stdout)
config.pluginmanager.register(reporter, 'terminalreporter') config.pluginmanager.register(reporter, 'terminalreporter')
if config.option.debug or config.option.traceconfig: if config.option.debug or config.option.traceconfig:
@ -52,11 +53,6 @@ def pytest_configure(config):
reporter.write_line("[traceconfig] " + msg) reporter.write_line("[traceconfig] " + msg)
config.trace.root.setprocessor("pytest:config", mywriter) config.trace.root.setprocessor("pytest:config", mywriter)
def pytest_unconfigure(config):
if hasattr(config, '_toclose'):
#print "closing", config._toclose, config._toclose.fileno()
config._toclose.close()
def getreportopt(config): def getreportopt(config):
reportopts = "" reportopts = ""
optvalue = config.option.report optvalue = config.option.report

View File

@ -24,7 +24,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.1.4.dev2', version='2.1.4.dev3',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],

View File

@ -13,6 +13,12 @@ class TestGeneralUsage:
'*ERROR: hello' '*ERROR: hello'
]) ])
def test_root_conftest_syntax_error(self, testdir):
p = testdir.makepyfile(conftest="raise SyntaxError\n")
result = testdir.runpytest()
result.stderr.fnmatch_lines(["*raise SyntaxError*"])
assert result.ret != 0
def test_early_hook_error_issue38_1(self, testdir): def test_early_hook_error_issue38_1(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""
def pytest_sessionstart(): def pytest_sessionstart():
@ -354,24 +360,24 @@ class TestInvocationVariants:
def test_equivalence_pytest_pytest(self): def test_equivalence_pytest_pytest(self):
assert pytest.main == py.test.cmdline.main assert pytest.main == py.test.cmdline.main
def test_invoke_with_string(self, capsys): def test_invoke_with_string(self, testdir, capsys):
retcode = pytest.main("-h") retcode = testdir.pytestmain("-h")
assert not retcode assert not retcode
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert "--help" in out assert "--help" in out
pytest.raises(ValueError, lambda: pytest.main(retcode)) pytest.raises(ValueError, lambda: pytest.main(0))
def test_invoke_with_path(self, testdir, capsys): def test_invoke_with_path(self, testdir, capsys):
retcode = testdir.pytestmain(testdir.tmpdir) retcode = testdir.pytestmain(testdir.tmpdir)
assert not retcode assert not retcode
out, err = capsys.readouterr() out, err = capsys.readouterr()
def test_invoke_plugin_api(self, capsys): def test_invoke_plugin_api(self, testdir, capsys):
class MyPlugin: class MyPlugin:
def pytest_addoption(self, parser): def pytest_addoption(self, parser):
parser.addoption("--myopt") parser.addoption("--myopt")
pytest.main(["-h"], plugins=[MyPlugin()]) testdir.pytestmain(["-h"], plugins=[MyPlugin()])
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert "--myopt" in out assert "--myopt" in out

View File

@ -18,7 +18,7 @@ def pytest_configure(config):
except py.process.cmdexec.Error: except py.process.cmdexec.Error:
pass pass
else: else:
config._numfiles = getopenfiles(out) config._numfiles = len(getopenfiles(out))
#def pytest_report_header(): #def pytest_report_header():
# return "pid: %s" % os.getpid() # return "pid: %s" % os.getpid()
@ -26,23 +26,31 @@ def pytest_configure(config):
def getopenfiles(out): def getopenfiles(out):
def isopen(line): def isopen(line):
return ("REG" in line or "CHR" in line) and ( return ("REG" in line or "CHR" in line) and (
"deleted" not in line and 'mem' not in line) "deleted" not in line and 'mem' not in line and "txt" not in line)
return len([x for x in out.split("\n") if isopen(x)]) return [x for x in out.split("\n") if isopen(x)]
def pytest_unconfigure(config, __multicall__): def check_open_files(config):
if not hasattr(config, '_numfiles'):
return
__multicall__.execute()
out2 = py.process.cmdexec("lsof -p %d" % pid) out2 = py.process.cmdexec("lsof -p %d" % pid)
len2 = getopenfiles(out2) lines2 = getopenfiles(out2)
assert len2 < config._numfiles + 15, out2 if len(lines2) > config._numfiles + 1:
error = []
error.append("***** %s FD leackage detected" %
(len(lines2)-config._numfiles))
error.extend(lines2)
error.append(error[0])
# update numfile so that the overall test run continuess
config._numfiles = len(lines2)
raise AssertionError("\n".join(error))
def pytest_runtest_setup(item): def pytest_runtest_setup(item):
item._oldir = py.path.local() item._oldir = py.path.local()
def pytest_runtest_teardown(item): def pytest_runtest_teardown(item, __multicall__):
item._oldir.chdir() item._oldir.chdir()
if hasattr(item.config, '_numfiles'):
x = __multicall__.execute()
check_open_files(item.config)
return x
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
multi = getattr(metafunc.function, 'multi', None) multi = getattr(metafunc.function, 'multi', None)

View File

@ -16,7 +16,6 @@ class TestCaptureManager:
def test_configure_per_fspath(self, testdir): def test_configure_per_fspath(self, testdir):
config = testdir.parseconfig(testdir.tmpdir) config = testdir.parseconfig(testdir.tmpdir)
assert config.getvalue("capture") is None
capman = CaptureManager() capman = CaptureManager()
hasfd = hasattr(os, 'dup') hasfd = hasattr(os, 'dup')
if hasfd: if hasfd:
@ -53,6 +52,7 @@ class TestCaptureManager:
capman.resumecapture(method) capman.resumecapture(method)
out, err = capman.suspendcapture() out, err = capman.suspendcapture()
assert not out and not err assert not out and not err
capman.reset_capturings()
finally: finally:
capouter.reset() capouter.reset()
@ -60,20 +60,23 @@ class TestCaptureManager:
def test_juggle_capturings(self, testdir): def test_juggle_capturings(self, testdir):
capouter = py.io.StdCaptureFD() capouter = py.io.StdCaptureFD()
try: try:
config = testdir.parseconfig(testdir.tmpdir) #config = testdir.parseconfig(testdir.tmpdir)
capman = CaptureManager() capman = CaptureManager()
capman.resumecapture("fd") try:
pytest.raises(ValueError, 'capman.resumecapture("fd")') capman.resumecapture("fd")
pytest.raises(ValueError, 'capman.resumecapture("sys")') pytest.raises(ValueError, 'capman.resumecapture("fd")')
os.write(1, "hello\n".encode('ascii')) pytest.raises(ValueError, 'capman.resumecapture("sys")')
out, err = capman.suspendcapture() os.write(1, "hello\n".encode('ascii'))
assert out == "hello\n" out, err = capman.suspendcapture()
capman.resumecapture("sys") assert out == "hello\n"
os.write(1, "hello\n".encode('ascii')) capman.resumecapture("sys")
py.builtin.print_("world", file=sys.stderr) os.write(1, "hello\n".encode('ascii'))
out, err = capman.suspendcapture() py.builtin.print_("world", file=sys.stderr)
assert not out out, err = capman.suspendcapture()
assert err == "world\n" assert not out
assert err == "world\n"
finally:
capman.reset_capturings()
finally: finally:
capouter.reset() capouter.reset()

View File

@ -313,7 +313,8 @@ class TestSession:
def test_collect_topdir(self, testdir): def test_collect_topdir(self, testdir):
p = testdir.makepyfile("def test_func(): pass") p = testdir.makepyfile("def test_func(): pass")
id = "::".join([p.basename, "test_func"]) id = "::".join([p.basename, "test_func"])
config = testdir.parseconfigure(id) # XXX migrate to inline_genitems? (see below)
config = testdir.parseconfig(id)
topdir = testdir.tmpdir topdir = testdir.tmpdir
rcol = Session(config) rcol = Session(config)
assert topdir == rcol.fspath assert topdir == rcol.fspath
@ -328,15 +329,9 @@ class TestSession:
def test_collect_protocol_single_function(self, testdir): def test_collect_protocol_single_function(self, testdir):
p = testdir.makepyfile("def test_func(): pass") p = testdir.makepyfile("def test_func(): pass")
id = "::".join([p.basename, "test_func"]) id = "::".join([p.basename, "test_func"])
config = testdir.parseconfigure(id)
topdir = testdir.tmpdir topdir = testdir.tmpdir
rcol = Session(config) items, hookrec = testdir.inline_genitems(id)
assert topdir == rcol.fspath item, = items
hookrec = testdir.getreportrecorder(config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 1
item = items[0]
assert item.name == "test_func" assert item.name == "test_func"
newid = item.nodeid newid = item.nodeid
assert newid == id assert newid == id
@ -363,10 +358,7 @@ class TestSession:
p.basename + "::TestClass::()", p.basename + "::TestClass::()",
normid, normid,
]: ]:
config = testdir.parseconfigure(id) items, hookrec = testdir.inline_genitems(id)
rcol = Session(config=config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 1 assert len(items) == 1
assert items[0].name == "test_method" assert items[0].name == "test_method"
newid = items[0].nodeid newid = items[0].nodeid
@ -388,11 +380,7 @@ class TestSession:
""" % p.basename) """ % p.basename)
id = p.basename id = p.basename
config = testdir.parseconfigure(id) items, hookrec = testdir.inline_genitems(id)
rcol = Session(config)
hookrec = testdir.getreportrecorder(config)
rcol.perform_collect()
items = rcol.items
py.std.pprint.pprint(hookrec.hookrecorder.calls) py.std.pprint.pprint(hookrec.hookrecorder.calls)
assert len(items) == 2 assert len(items) == 2
hookrec.hookrecorder.contains([ hookrec.hookrecorder.contains([
@ -413,11 +401,8 @@ class TestSession:
aaa = testdir.mkpydir("aaa") aaa = testdir.mkpydir("aaa")
test_aaa = aaa.join("test_aaa.py") test_aaa = aaa.join("test_aaa.py")
p.move(test_aaa) p.move(test_aaa)
config = testdir.parseconfigure()
rcol = Session(config) items, hookrec = testdir.inline_genitems()
hookrec = testdir.getreportrecorder(config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 1 assert len(items) == 1
py.std.pprint.pprint(hookrec.hookrecorder.calls) py.std.pprint.pprint(hookrec.hookrecorder.calls)
hookrec.hookrecorder.contains([ hookrec.hookrecorder.contains([
@ -437,11 +422,8 @@ class TestSession:
p.move(test_bbb) p.move(test_bbb)
id = "." id = "."
config = testdir.parseconfigure(id)
rcol = Session(config) items, hookrec = testdir.inline_genitems(id)
hookrec = testdir.getreportrecorder(config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 2 assert len(items) == 2
py.std.pprint.pprint(hookrec.hookrecorder.calls) py.std.pprint.pprint(hookrec.hookrecorder.calls)
hookrec.hookrecorder.contains([ hookrec.hookrecorder.contains([
@ -455,19 +437,13 @@ class TestSession:
def test_serialization_byid(self, testdir): def test_serialization_byid(self, testdir):
p = testdir.makepyfile("def test_func(): pass") p = testdir.makepyfile("def test_func(): pass")
config = testdir.parseconfigure() items, hookrec = testdir.inline_genitems()
rcol = Session(config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 1 assert len(items) == 1
item, = items item, = items
rcol.config.pluginmanager.unregister(name="session") items2, hookrec = testdir.inline_genitems(item.nodeid)
newcol = Session(config) item2, = items2
item2, = newcol.perform_collect([item.nodeid], genitems=False)
assert item2.name == item.name assert item2.name == item.name
assert item2.fspath == item.fspath assert item2.fspath == item.fspath
item2b, = newcol.perform_collect([item.nodeid], genitems=False)
assert item2b == item2
def test_find_byid_without_instance_parents(self, testdir): def test_find_byid_without_instance_parents(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
@ -476,10 +452,7 @@ class TestSession:
pass pass
""") """)
arg = p.basename + ("::TestClass::test_method") arg = p.basename + ("::TestClass::test_method")
config = testdir.parseconfigure(arg) items, hookrec = testdir.inline_genitems(arg)
rcol = Session(config)
rcol.perform_collect()
items = rcol.items
assert len(items) == 1 assert len(items) == 1
item, = items item, = items
assert item.nodeid.endswith("TestClass::()::test_method") assert item.nodeid.endswith("TestClass::()::test_method")
@ -487,7 +460,7 @@ class TestSession:
class Test_getinitialnodes: class Test_getinitialnodes:
def test_global_file(self, testdir, tmpdir): def test_global_file(self, testdir, tmpdir):
x = tmpdir.ensure("x.py") x = tmpdir.ensure("x.py")
config = testdir.reparseconfig([x]) config = testdir.parseconfigure(x)
col = testdir.getnode(config, x) col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module) assert isinstance(col, pytest.Module)
assert col.name == 'x.py' assert col.name == 'x.py'
@ -502,7 +475,7 @@ class Test_getinitialnodes:
subdir = tmpdir.join("subdir") subdir = tmpdir.join("subdir")
x = subdir.ensure("x.py") x = subdir.ensure("x.py")
subdir.ensure("__init__.py") subdir.ensure("__init__.py")
config = testdir.reparseconfig([x]) config = testdir.parseconfigure(x)
col = testdir.getnode(config, x) col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module) assert isinstance(col, pytest.Module)
assert col.name == 'subdir/x.py' assert col.name == 'subdir/x.py'
@ -528,12 +501,6 @@ class Test_genitems:
assert hash(i) != hash(j) assert hash(i) != hash(j)
assert i != 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")
pytest.raises(SyntaxError, testdir.inline_genitems, p.dirpath())
def test_example_items1(self, testdir): def test_example_items1(self, testdir):
p = testdir.makepyfile(''' p = testdir.makepyfile('''
def testone(): def testone():
@ -597,6 +564,6 @@ def test_matchnodes_two_collections_same_file(testdir):
res.stdout.fnmatch_lines([ res.stdout.fnmatch_lines([
"*1 passed*", "*1 passed*",
]) ])

View File

@ -1,9 +1,9 @@
import py, pytest import py, pytest
from _pytest.config import getcfg, Config from _pytest.config import getcfg
class TestParseIni: class TestParseIni:
def test_getcfg_and_config(self, tmpdir): def test_getcfg_and_config(self, testdir, tmpdir):
sub = tmpdir.mkdir("sub") sub = tmpdir.mkdir("sub")
sub.chdir() sub.chdir()
tmpdir.join("setup.cfg").write(py.code.Source(""" tmpdir.join("setup.cfg").write(py.code.Source("""
@ -12,25 +12,23 @@ class TestParseIni:
""")) """))
cfg = getcfg([sub], ["setup.cfg"]) cfg = getcfg([sub], ["setup.cfg"])
assert cfg['name'] == "value" assert cfg['name'] == "value"
config = Config() config = testdir.parseconfigure(sub)
config._preparse([sub])
assert config.inicfg['name'] == 'value' assert config.inicfg['name'] == 'value'
def test_getcfg_empty_path(self, tmpdir): def test_getcfg_empty_path(self, tmpdir):
cfg = getcfg([''], ['setup.cfg']) #happens on py.test "" cfg = getcfg([''], ['setup.cfg']) #happens on py.test ""
def test_append_parse_args(self, tmpdir): def test_append_parse_args(self, testdir, tmpdir):
tmpdir.join("setup.cfg").write(py.code.Source(""" tmpdir.join("setup.cfg").write(py.code.Source("""
[pytest] [pytest]
addopts = --verbose addopts = --verbose
""")) """))
config = Config() config = testdir.parseconfig(tmpdir)
config.parse([tmpdir])
assert config.option.verbose assert config.option.verbose
config = Config() #config = testdir.Config()
args = [tmpdir,] #args = [tmpdir,]
config._preparse(args, addopts=False) #config._preparse(args, addopts=False)
assert len(args) == 1 #assert len(args) == 1
def test_tox_ini_wrong_version(self, testdir): def test_tox_ini_wrong_version(self, testdir):
p = testdir.makefile('.ini', tox=""" p = testdir.makefile('.ini', tox="""
@ -49,8 +47,7 @@ class TestParseIni:
[pytest] [pytest]
minversion = 1.0 minversion = 1.0
""")) """))
config = Config() config = testdir.parseconfig()
config.parse([testdir.tmpdir])
assert config.getini("minversion") == "1.0" assert config.getini("minversion") == "1.0"
def test_toxini_before_lower_pytestini(self, testdir): def test_toxini_before_lower_pytestini(self, testdir):
@ -63,8 +60,7 @@ class TestParseIni:
[pytest] [pytest]
minversion = 1.5 minversion = 1.5
""")) """))
config = Config() config = testdir.parseconfigure(sub)
config.parse([sub])
assert config.getini("minversion") == "2.0" assert config.getini("minversion") == "2.0"
@pytest.mark.xfail(reason="probably not needed") @pytest.mark.xfail(reason="probably not needed")
@ -77,10 +73,10 @@ class TestParseIni:
""") """)
result = testdir.runpytest("--confcutdir=.") result = testdir.runpytest("--confcutdir=.")
assert result.ret == 0 assert result.ret == 0
class TestConfigCmdlineParsing: class TestConfigCmdlineParsing:
def test_parsing_again_fails(self, testdir): def test_parsing_again_fails(self, testdir):
config = testdir.reparseconfig([testdir.tmpdir]) config = testdir.parseconfig()
pytest.raises(AssertionError, "config.parse([])") pytest.raises(AssertionError, "config.parse([])")
@ -101,7 +97,7 @@ class TestConfigAPI:
assert config.getvalue("x") == 1 assert config.getvalue("x") == 1
assert config.getvalue("x", o.join('sub')) == 2 assert config.getvalue("x", o.join('sub')) == 2
pytest.raises(KeyError, "config.getvalue('y')") pytest.raises(KeyError, "config.getvalue('y')")
config = testdir.reparseconfig([str(o.join('sub'))]) config = testdir.parseconfigure(str(o.join('sub')))
assert config.getvalue("x") == 2 assert config.getvalue("x") == 2
assert config.getvalue("y") == 3 assert config.getvalue("y") == 3
assert config.getvalue("x", o) == 1 assert config.getvalue("x", o) == 1
@ -127,18 +123,18 @@ class TestConfigAPI:
def test_config_overwrite(self, testdir): def test_config_overwrite(self, testdir):
o = testdir.tmpdir o = testdir.tmpdir
o.ensure("conftest.py").write("x=1") o.ensure("conftest.py").write("x=1")
config = testdir.reparseconfig([str(o)]) config = testdir.parseconfig(str(o))
assert config.getvalue('x') == 1 assert config.getvalue('x') == 1
config.option.x = 2 config.option.x = 2
assert config.getvalue('x') == 2 assert config.getvalue('x') == 2
config = testdir.reparseconfig([str(o)]) config = testdir.parseconfig([str(o)])
assert config.getvalue('x') == 1 assert config.getvalue('x') == 1
def test_getconftest_pathlist(self, testdir, tmpdir): def test_getconftest_pathlist(self, testdir, tmpdir):
somepath = tmpdir.join("x", "y", "z") somepath = tmpdir.join("x", "y", "z")
p = tmpdir.join("conftest.py") p = tmpdir.join("conftest.py")
p.write("pathlist = ['.', %r]" % str(somepath)) p.write("pathlist = ['.', %r]" % str(somepath))
config = testdir.reparseconfig([p]) config = testdir.parseconfigure(p)
assert config._getconftest_pathlist('notexist') is None assert config._getconftest_pathlist('notexist') is None
pl = config._getconftest_pathlist('pathlist') pl = config._getconftest_pathlist('pathlist')
print(pl) print(pl)

View File

@ -332,17 +332,6 @@ class TestPytestPluginInteractions:
"*did not find*sys*" "*did not find*sys*"
]) ])
def test_do_option_conftestplugin(self, testdir):
p = testdir.makepyfile("""
def pytest_addoption(parser):
parser.addoption('--test123', action="store_true")
""")
config = testdir.Config()
config._conftest.importconftest(p)
print(config.pluginmanager.getplugins())
config.parse([])
assert not config.option.test123
def test_namespace_early_from_import(self, testdir): def test_namespace_early_from_import(self, testdir):
p = testdir.makepyfile(""" p = testdir.makepyfile("""
from pytest import Item from pytest import Item
@ -370,9 +359,7 @@ class TestPytestPluginInteractions:
]) ])
def test_do_option_postinitialize(self, testdir): def test_do_option_postinitialize(self, testdir):
config = testdir.Config() config = testdir.parseconfigure()
config.parse([])
config.pluginmanager.do_configure(config=config)
assert not hasattr(config.option, 'test123') assert not hasattr(config.option, 'test123')
p = testdir.makepyfile(""" p = testdir.makepyfile("""
def pytest_addoption(parser): def pytest_addoption(parser):
@ -640,7 +627,7 @@ class TestTracer:
log2("seen") log2("seen")
tags, args = l2[0] tags, args = l2[0]
assert args == ("seen",) assert args == ("seen",)
def test_setmyprocessor(self): def test_setmyprocessor(self):
from _pytest.core import TagTracer from _pytest.core import TagTracer

View File

@ -189,58 +189,6 @@ class TestFunctional:
]) ])
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")
pytest.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)
class TestKeywordSelection: class TestKeywordSelection:
def test_select_simple(self, testdir): def test_select_simple(self, testdir):
file_test = testdir.makepyfile(""" file_test = testdir.makepyfile("""

View File

@ -257,7 +257,7 @@ class TestFunction:
assert hasattr(modcol.obj, 'test_func') assert hasattr(modcol.obj, 'test_func')
def test_function_equality(self, testdir, tmpdir): def test_function_equality(self, testdir, tmpdir):
config = testdir.reparseconfig() config = testdir.parseconfigure()
session = testdir.Session(config) session = testdir.Session(config)
f1 = pytest.Function(name="name", config=config, f1 = pytest.Function(name="name", config=config,
args=(1,), callobj=isinstance, session=session) args=(1,), callobj=isinstance, session=session)
@ -279,7 +279,7 @@ class TestFunction:
assert not f1 != f1_b assert not f1 != f1_b
def test_function_equality_with_callspec(self, testdir, tmpdir): def test_function_equality_with_callspec(self, testdir, tmpdir):
config = testdir.reparseconfig() config = testdir.parseconfigure()
class callspec1: class callspec1:
param = 1 param = 1
funcargs = {} funcargs = {}
@ -783,7 +783,7 @@ class TestRequestCachedSetup:
req2 = funcargs.FuncargRequest(item2) req2 = funcargs.FuncargRequest(item2)
ret2 = req2.cached_setup(setup, scope="class") ret2 = req2.cached_setup(setup, scope="class")
assert ret2 == "hello" assert ret2 == "hello"
req3 = funcargs.FuncargRequest(item3) req3 = funcargs.FuncargRequest(item3)
ret3a = req3.cached_setup(setup, scope="class") ret3a = req3.cached_setup(setup, scope="class")
ret3b = req3.cached_setup(setup, scope="class") ret3b = req3.cached_setup(setup, scope="class")
@ -1320,7 +1320,7 @@ def test_customized_python_discovery(testdir):
"*CheckMyApp*", "*CheckMyApp*",
"*check_meth*", "*check_meth*",
]) ])
result = testdir.runpytest() result = testdir.runpytest()
assert result.ret == 0 assert result.ret == 0
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@ -1354,7 +1354,7 @@ def test_customize_through_attributes(testdir):
Function = MyFunction Function = MyFunction
class MyClass(pytest.Class): class MyClass(pytest.Class):
Instance = MyInstance Instance = MyInstance
def pytest_pycollect_makeitem(collector, name, obj): def pytest_pycollect_makeitem(collector, name, obj):
if name.startswith("MyTestClass"): if name.startswith("MyTestClass"):
return MyClass(name, parent=collector) return MyClass(name, parent=collector)

View File

@ -54,17 +54,6 @@ class TestConfigTmpdir:
assert b2.check() assert b2.check()
assert not h.check() assert not h.check()
def test_reparse(self, testdir):
config2 = testdir.reparseconfig([])
config3 = testdir.reparseconfig([])
assert config2.basetemp != config3.basetemp
assert not config2.basetemp.relto(config3.basetemp)
assert not config3.basetemp.relto(config2.basetemp)
def test_reparse_filename_too_long(self, testdir):
config = testdir.reparseconfig(["--basetemp=%s" % ("123"*300)])
def test_basetemp(testdir): def test_basetemp(testdir):
mytemp = testdir.tmpdir.mkdir("mytemp") mytemp = testdir.tmpdir.mkdir("mytemp")
p = testdir.makepyfile(""" p = testdir.makepyfile("""