2010-11-06 18:38:53 +08:00
|
|
|
""" (disabled by default) support for testing py.test and py.test plugins. """
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2010-11-06 06:37:31 +08:00
|
|
|
import py, pytest
|
2009-06-19 01:02:59 +08:00
|
|
|
import sys, os
|
2012-09-20 16:57:23 +08:00
|
|
|
import codecs
|
2009-12-25 07:24:58 +08:00
|
|
|
import re
|
2009-12-30 18:37:46 +08:00
|
|
|
import time
|
2010-09-28 02:48:30 +08:00
|
|
|
from fnmatch import fnmatch
|
2011-05-27 06:08:44 +08:00
|
|
|
from _pytest.main import Session, EXIT_OK
|
2009-08-29 01:16:15 +08:00
|
|
|
from py.builtin import print_
|
2010-11-13 18:10:45 +08:00
|
|
|
from _pytest.core import HookRelay
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2013-08-02 05:48:40 +08:00
|
|
|
|
|
|
|
def get_public_names(l):
|
|
|
|
"""Only return names from iterator l without a leading underscore."""
|
|
|
|
return [x for x in l if x[0] != "_"]
|
|
|
|
|
|
|
|
|
2010-01-02 03:36:58 +08:00
|
|
|
def pytest_addoption(parser):
|
|
|
|
group = parser.getgroup("pylib")
|
2010-09-28 02:48:30 +08:00
|
|
|
group.addoption('--no-tools-on-path',
|
|
|
|
action="store_true", dest="notoolsonpath", default=False,
|
2010-01-02 03:36:58 +08:00
|
|
|
help=("discover tools on PATH instead of going through py.cmdline.")
|
|
|
|
)
|
|
|
|
|
2010-11-24 10:27:12 +08:00
|
|
|
def pytest_configure(config):
|
|
|
|
# This might be called multiple times. Only take the first.
|
|
|
|
global _pytest_fullpath
|
|
|
|
try:
|
|
|
|
_pytest_fullpath
|
|
|
|
except NameError:
|
|
|
|
_pytest_fullpath = os.path.abspath(pytest.__file__.rstrip("oc"))
|
2011-09-25 13:40:43 +08:00
|
|
|
_pytest_fullpath = _pytest_fullpath.replace("$py.class", ".py")
|
2010-11-24 10:27:12 +08:00
|
|
|
|
2010-10-12 19:10:39 +08:00
|
|
|
def pytest_funcarg___pytest(request):
|
|
|
|
return PytestArg(request)
|
|
|
|
|
|
|
|
class PytestArg:
|
|
|
|
def __init__(self, request):
|
|
|
|
self.request = request
|
|
|
|
|
|
|
|
def gethookrecorder(self, hook):
|
2010-10-13 17:12:27 +08:00
|
|
|
hookrecorder = HookRecorder(hook._pm)
|
2010-10-12 19:10:39 +08:00
|
|
|
hookrecorder.start_recording(hook._hookspecs)
|
|
|
|
self.request.addfinalizer(hookrecorder.finish_recording)
|
|
|
|
return hookrecorder
|
|
|
|
|
|
|
|
class ParsedCall:
|
|
|
|
def __init__(self, name, locals):
|
|
|
|
assert '_name' not in locals
|
|
|
|
self.__dict__.update(locals)
|
|
|
|
self.__dict__.pop('self')
|
|
|
|
self._name = name
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
d = self.__dict__.copy()
|
|
|
|
del d['_name']
|
|
|
|
return "<ParsedCall %r(**%r)>" %(self._name, d)
|
|
|
|
|
|
|
|
class HookRecorder:
|
2010-10-13 17:12:27 +08:00
|
|
|
def __init__(self, pluginmanager):
|
|
|
|
self._pluginmanager = pluginmanager
|
2010-10-12 19:10:39 +08:00
|
|
|
self.calls = []
|
|
|
|
self._recorders = {}
|
|
|
|
|
|
|
|
def start_recording(self, hookspecs):
|
|
|
|
if not isinstance(hookspecs, (list, tuple)):
|
|
|
|
hookspecs = [hookspecs]
|
|
|
|
for hookspec in hookspecs:
|
|
|
|
assert hookspec not in self._recorders
|
|
|
|
class RecordCalls:
|
|
|
|
_recorder = self
|
|
|
|
for name, method in vars(hookspec).items():
|
|
|
|
if name[0] != "_":
|
|
|
|
setattr(RecordCalls, name, self._makecallparser(method))
|
|
|
|
recorder = RecordCalls()
|
|
|
|
self._recorders[hookspec] = recorder
|
2010-10-13 17:12:27 +08:00
|
|
|
self._pluginmanager.register(recorder)
|
|
|
|
self.hook = HookRelay(hookspecs, pm=self._pluginmanager,
|
2010-10-12 19:10:39 +08:00
|
|
|
prefix="pytest_")
|
|
|
|
|
|
|
|
def finish_recording(self):
|
|
|
|
for recorder in self._recorders.values():
|
2013-09-29 04:23:00 +08:00
|
|
|
if self._pluginmanager.isregistered(recorder):
|
|
|
|
self._pluginmanager.unregister(recorder)
|
2010-10-12 19:10:39 +08:00
|
|
|
self._recorders.clear()
|
|
|
|
|
|
|
|
def _makecallparser(self, method):
|
|
|
|
name = method.__name__
|
|
|
|
args, varargs, varkw, default = py.std.inspect.getargspec(method)
|
|
|
|
if not args or args[0] != "self":
|
|
|
|
args.insert(0, 'self')
|
|
|
|
fspec = py.std.inspect.formatargspec(args, varargs, varkw, default)
|
|
|
|
# we use exec because we want to have early type
|
|
|
|
# errors on wrong input arguments, using
|
|
|
|
# *args/**kwargs delays this and gives errors
|
|
|
|
# elsewhere
|
|
|
|
exec (py.code.compile("""
|
|
|
|
def %(name)s%(fspec)s:
|
|
|
|
self._recorder.calls.append(
|
|
|
|
ParsedCall(%(name)r, locals()))
|
|
|
|
""" % locals()))
|
|
|
|
return locals()[name]
|
|
|
|
|
|
|
|
def getcalls(self, names):
|
|
|
|
if isinstance(names, str):
|
|
|
|
names = names.split()
|
|
|
|
for name in names:
|
|
|
|
for cls in self._recorders:
|
|
|
|
if name in vars(cls):
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise ValueError("callname %r not found in %r" %(
|
|
|
|
name, self._recorders.keys()))
|
|
|
|
l = []
|
|
|
|
for call in self.calls:
|
|
|
|
if call._name in names:
|
|
|
|
l.append(call)
|
|
|
|
return l
|
|
|
|
|
|
|
|
def contains(self, entries):
|
2010-11-06 16:58:04 +08:00
|
|
|
__tracebackhide__ = True
|
2010-10-12 19:10:39 +08:00
|
|
|
i = 0
|
|
|
|
entries = list(entries)
|
2010-10-12 21:34:32 +08:00
|
|
|
backlocals = py.std.sys._getframe(1).f_locals
|
2010-10-12 19:10:39 +08:00
|
|
|
while entries:
|
|
|
|
name, check = entries.pop(0)
|
|
|
|
for ind, call in enumerate(self.calls[i:]):
|
|
|
|
if call._name == name:
|
|
|
|
print_("NAMEMATCH", name, call)
|
|
|
|
if eval(check, backlocals, call.__dict__):
|
|
|
|
print_("CHECKERMATCH", repr(check), "->", call)
|
|
|
|
else:
|
|
|
|
print_("NOCHECKERMATCH", repr(check), "-", call)
|
|
|
|
continue
|
|
|
|
i += ind + 1
|
|
|
|
break
|
|
|
|
print_("NONAMEMATCH", name, "with", call)
|
|
|
|
else:
|
2010-11-06 16:58:04 +08:00
|
|
|
py.test.fail("could not find %r check %r" % (name, check))
|
2010-10-12 19:10:39 +08:00
|
|
|
|
|
|
|
def popcall(self, name):
|
2010-11-22 22:20:18 +08:00
|
|
|
__tracebackhide__ = True
|
2010-10-12 19:10:39 +08:00
|
|
|
for i, call in enumerate(self.calls):
|
|
|
|
if call._name == name:
|
|
|
|
del self.calls[i]
|
|
|
|
return call
|
2010-11-22 22:20:18 +08:00
|
|
|
lines = ["could not find call %r, in:" % (name,)]
|
|
|
|
lines.extend([" %s" % str(x) for x in self.calls])
|
|
|
|
py.test.fail("\n".join(lines))
|
2010-10-12 19:10:39 +08:00
|
|
|
|
|
|
|
def getcall(self, name):
|
|
|
|
l = self.getcalls(name)
|
|
|
|
assert len(l) == 1, (name, l)
|
|
|
|
return l[0]
|
|
|
|
|
2009-05-20 01:25:21 +08:00
|
|
|
|
2009-05-19 05:26:16 +08:00
|
|
|
def pytest_funcarg__linecomp(request):
|
|
|
|
return LineComp()
|
2009-04-09 07:33:48 +08:00
|
|
|
|
2009-05-19 05:26:16 +08:00
|
|
|
def pytest_funcarg__LineMatcher(request):
|
|
|
|
return LineMatcher
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-05-19 05:26:16 +08:00
|
|
|
def pytest_funcarg__testdir(request):
|
|
|
|
tmptestdir = TmpTestdir(request)
|
|
|
|
return tmptestdir
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-12-25 07:24:58 +08:00
|
|
|
rex_outcome = re.compile("(\d+) (\w+)")
|
2009-02-27 18:18:27 +08:00
|
|
|
class RunResult:
|
2009-12-30 18:37:46 +08:00
|
|
|
def __init__(self, ret, outlines, errlines, duration):
|
2009-02-27 18:18:27 +08:00
|
|
|
self.ret = ret
|
|
|
|
self.outlines = outlines
|
|
|
|
self.errlines = errlines
|
|
|
|
self.stdout = LineMatcher(outlines)
|
|
|
|
self.stderr = LineMatcher(errlines)
|
2009-12-30 18:37:46 +08:00
|
|
|
self.duration = duration
|
|
|
|
|
2009-12-25 07:24:58 +08:00
|
|
|
def parseoutcomes(self):
|
|
|
|
for line in reversed(self.outlines):
|
|
|
|
if 'seconds' in line:
|
|
|
|
outcomes = rex_outcome.findall(line)
|
|
|
|
if outcomes:
|
|
|
|
d = {}
|
|
|
|
for num, cat in outcomes:
|
|
|
|
d[cat] = int(num)
|
|
|
|
return d
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
class TmpTestdir:
|
2009-04-15 00:30:26 +08:00
|
|
|
def __init__(self, request):
|
|
|
|
self.request = request
|
2010-10-12 21:34:32 +08:00
|
|
|
self.Config = request.config.__class__
|
2009-05-21 15:45:43 +08:00
|
|
|
self._pytest = request.getfuncargvalue("_pytest")
|
2010-07-27 03:15:15 +08:00
|
|
|
# XXX remove duplication with tmpdir plugin
|
2010-11-22 00:43:18 +08:00
|
|
|
basetmp = request.config._tmpdirhandler.ensuretemp("testdir")
|
2009-04-15 01:57:00 +08:00
|
|
|
name = request.function.__name__
|
2009-02-27 18:18:27 +08:00
|
|
|
for i in range(100):
|
|
|
|
try:
|
|
|
|
tmpdir = basetmp.mkdir(name + str(i))
|
|
|
|
except py.error.EEXIST:
|
|
|
|
continue
|
|
|
|
break
|
2013-02-04 03:47:39 +08:00
|
|
|
self.tmpdir = tmpdir
|
2009-02-27 18:18:27 +08:00
|
|
|
self.plugins = []
|
|
|
|
self._syspathremove = []
|
2010-01-02 18:57:42 +08:00
|
|
|
self.chdir() # always chdir
|
2009-04-15 00:30:26 +08:00
|
|
|
self.request.addfinalizer(self.finalize)
|
2009-04-01 01:58:02 +08:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return "<TmpTestdir %r>" % (self.tmpdir,)
|
2009-03-01 21:43:53 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def finalize(self):
|
|
|
|
for p in self._syspathremove:
|
|
|
|
py.std.sys.path.remove(p)
|
|
|
|
if hasattr(self, '_olddir'):
|
|
|
|
self._olddir.chdir()
|
2009-10-29 02:51:20 +08:00
|
|
|
# delete modules that have been loaded from tmpdir
|
|
|
|
for name, mod in list(sys.modules.items()):
|
|
|
|
if mod:
|
|
|
|
fn = getattr(mod, '__file__', None)
|
|
|
|
if fn and fn.startswith(str(self.tmpdir)):
|
|
|
|
del sys.modules[name]
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-05-21 15:45:43 +08:00
|
|
|
def getreportrecorder(self, obj):
|
2009-12-29 19:36:17 +08:00
|
|
|
if hasattr(obj, 'config'):
|
|
|
|
obj = obj.config
|
|
|
|
if hasattr(obj, 'hook'):
|
|
|
|
obj = obj.hook
|
|
|
|
assert hasattr(obj, '_hookspecs'), obj
|
|
|
|
reprec = ReportRecorder(obj)
|
|
|
|
reprec.hookrecorder = self._pytest.gethookrecorder(obj)
|
2009-05-21 15:45:43 +08:00
|
|
|
reprec.hook = reprec.hookrecorder.hook
|
|
|
|
return reprec
|
2009-04-05 03:06:20 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def chdir(self):
|
2009-04-01 01:58:02 +08:00
|
|
|
old = self.tmpdir.chdir()
|
2009-02-27 18:18:27 +08:00
|
|
|
if not hasattr(self, '_olddir'):
|
2010-07-27 03:15:15 +08:00
|
|
|
self._olddir = old
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def _makefile(self, ext, args, kwargs):
|
2009-08-29 01:16:15 +08:00
|
|
|
items = list(kwargs.items())
|
2009-02-27 18:18:27 +08:00
|
|
|
if args:
|
2011-04-12 06:15:56 +08:00
|
|
|
source = py.builtin._totext("\n").join(
|
|
|
|
map(py.builtin._totext, args)) + py.builtin._totext("\n")
|
2009-04-15 01:57:00 +08:00
|
|
|
basename = self.request.function.__name__
|
2009-02-27 18:18:27 +08:00
|
|
|
items.insert(0, (basename, source))
|
|
|
|
ret = None
|
|
|
|
for name, value in items:
|
|
|
|
p = self.tmpdir.join(name).new(ext=ext)
|
2012-09-20 16:57:23 +08:00
|
|
|
source = py.builtin._totext(py.code.Source(value)).strip()
|
|
|
|
content = source.encode("utf-8") # + "\n"
|
|
|
|
#content = content.rstrip() + "\n"
|
|
|
|
p.write(content, "wb")
|
2009-02-27 18:18:27 +08:00
|
|
|
if ret is None:
|
|
|
|
ret = p
|
2010-07-27 03:15:15 +08:00
|
|
|
return ret
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
|
|
|
|
def makefile(self, ext, *args, **kwargs):
|
|
|
|
return self._makefile(ext, args, kwargs)
|
|
|
|
|
|
|
|
def makeconftest(self, source):
|
|
|
|
return self.makepyfile(conftest=source)
|
|
|
|
|
2010-11-01 00:41:58 +08:00
|
|
|
def makeini(self, source):
|
|
|
|
return self.makefile('.ini', tox=source)
|
|
|
|
|
|
|
|
def getinicfg(self, source):
|
|
|
|
p = self.makeini(source)
|
|
|
|
return py.iniconfig.IniConfig(p)['pytest']
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def makepyfile(self, *args, **kwargs):
|
|
|
|
return self._makefile('.py', args, kwargs)
|
|
|
|
|
|
|
|
def maketxtfile(self, *args, **kwargs):
|
|
|
|
return self._makefile('.txt', args, kwargs)
|
|
|
|
|
|
|
|
def syspathinsert(self, path=None):
|
|
|
|
if path is None:
|
|
|
|
path = self.tmpdir
|
|
|
|
py.std.sys.path.insert(0, str(path))
|
|
|
|
self._syspathremove.append(str(path))
|
2010-07-27 03:15:15 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def mkdir(self, name):
|
|
|
|
return self.tmpdir.mkdir(name)
|
2009-07-30 15:52:12 +08:00
|
|
|
|
|
|
|
def mkpydir(self, name):
|
|
|
|
p = self.mkdir(name)
|
|
|
|
p.ensure("__init__.py")
|
|
|
|
return p
|
|
|
|
|
2010-11-07 17:19:58 +08:00
|
|
|
Session = Session
|
2010-09-15 16:30:50 +08:00
|
|
|
def getnode(self, config, arg):
|
2010-11-07 17:19:58 +08:00
|
|
|
session = Session(config)
|
2010-11-06 16:58:04 +08:00
|
|
|
assert '::' not in str(arg)
|
|
|
|
p = py.path.local(arg)
|
2010-11-07 17:19:58 +08:00
|
|
|
x = session.fspath.bestrelpath(p)
|
2011-05-27 06:08:44 +08:00
|
|
|
config.hook.pytest_sessionstart(session=session)
|
|
|
|
res = session.perform_collect([x], genitems=False)[0]
|
|
|
|
config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
|
|
|
|
return res
|
2010-11-06 16:58:04 +08:00
|
|
|
|
|
|
|
def getpathnode(self, path):
|
2011-05-27 07:37:04 +08:00
|
|
|
config = self.parseconfigure(path)
|
2010-11-07 17:19:58 +08:00
|
|
|
session = Session(config)
|
|
|
|
x = session.fspath.bestrelpath(path)
|
2011-05-27 06:08:44 +08:00
|
|
|
config.hook.pytest_sessionstart(session=session)
|
|
|
|
res = session.perform_collect([x], genitems=False)[0]
|
|
|
|
config.hook.pytest_sessionfinish(session=session, exitstatus=EXIT_OK)
|
|
|
|
return res
|
2010-09-15 16:30:50 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def genitems(self, colitems):
|
2010-11-07 17:19:58 +08:00
|
|
|
session = colitems[0].session
|
2010-09-15 16:30:50 +08:00
|
|
|
result = []
|
2010-10-26 05:08:41 +08:00
|
|
|
for colitem in colitems:
|
2010-11-07 17:19:58 +08:00
|
|
|
result.extend(session.genitems(colitem))
|
2010-09-15 16:30:50 +08:00
|
|
|
return result
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-05-23 01:57:21 +08:00
|
|
|
def runitem(self, source):
|
2010-07-27 03:15:15 +08:00
|
|
|
# used from runner functional tests
|
2009-02-27 18:18:27 +08:00
|
|
|
item = self.getitem(source)
|
2010-07-27 03:15:15 +08:00
|
|
|
# the test class where we are called from wants to provide the runner
|
2012-06-25 23:35:33 +08:00
|
|
|
testclassinstance = self.request.instance
|
2009-02-27 18:18:27 +08:00
|
|
|
runner = testclassinstance.getrunner()
|
2009-05-23 01:57:21 +08:00
|
|
|
return runner(item)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def inline_runsource(self, source, *cmdlineargs):
|
|
|
|
p = self.makepyfile(source)
|
|
|
|
l = list(cmdlineargs) + [p]
|
|
|
|
return self.inline_run(*l)
|
|
|
|
|
2009-05-23 01:57:21 +08:00
|
|
|
def inline_runsource1(self, *args):
|
|
|
|
args = list(args)
|
|
|
|
source = args.pop()
|
|
|
|
p = self.makepyfile(source)
|
|
|
|
l = list(args) + [p]
|
|
|
|
reprec = self.inline_run(*l)
|
2009-06-09 00:31:10 +08:00
|
|
|
reports = reprec.getreports("pytest_runtest_logreport")
|
2011-11-09 01:53:46 +08:00
|
|
|
assert len(reports) == 3, reports # setup/call/teardown
|
|
|
|
return reports[1]
|
2009-05-23 01:57:21 +08:00
|
|
|
|
2011-11-08 02:08:41 +08:00
|
|
|
def inline_genitems(self, *args):
|
|
|
|
return self.inprocess_run(list(args) + ['--collectonly'])
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2011-11-08 02:08:41 +08:00
|
|
|
def inline_run(self, *args):
|
|
|
|
items, rec = self.inprocess_run(args)
|
|
|
|
return rec
|
|
|
|
|
|
|
|
def inprocess_run(self, args, plugins=None):
|
|
|
|
rec = []
|
|
|
|
items = []
|
|
|
|
class Collect:
|
|
|
|
def pytest_configure(x, config):
|
|
|
|
rec.append(self.getreportrecorder(config))
|
|
|
|
def pytest_itemcollected(self, item):
|
|
|
|
items.append(item)
|
|
|
|
if not plugins:
|
|
|
|
plugins = []
|
|
|
|
plugins.append(Collect())
|
2013-09-29 04:23:00 +08:00
|
|
|
ret = pytest.main(list(args), plugins=plugins)
|
2011-11-19 00:01:29 +08:00
|
|
|
reprec = rec[0]
|
|
|
|
reprec.ret = ret
|
2011-11-08 02:08:41 +08:00
|
|
|
assert len(rec) == 1
|
2011-11-19 00:01:29 +08:00
|
|
|
return items, reprec
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def parseconfig(self, *args):
|
2011-11-08 02:28:30 +08:00
|
|
|
args = [str(x) for x in args]
|
2010-11-22 00:43:18 +08:00
|
|
|
for x in args:
|
|
|
|
if str(x).startswith('--basetemp'):
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
args.append("--basetemp=%s" % self.tmpdir.dirpath('basetemp'))
|
2013-09-30 19:14:14 +08:00
|
|
|
import _pytest.config
|
|
|
|
config = _pytest.config._prepareconfig(args, self.plugins)
|
2013-09-29 04:23:00 +08:00
|
|
|
# we don't know what the test will do with this half-setup config
|
|
|
|
# object and thus we make sure it gets unconfigured properly in any
|
|
|
|
# case (otherwise capturing could still be active, for example)
|
|
|
|
def ensure_unconfigure():
|
|
|
|
if hasattr(config.pluginmanager, "_config"):
|
|
|
|
config.pluginmanager.do_unconfigure(config)
|
|
|
|
config.pluginmanager.ensure_shutdown()
|
|
|
|
|
|
|
|
self.request.addfinalizer(ensure_unconfigure)
|
2010-07-27 03:15:15 +08:00
|
|
|
return config
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-03-23 01:41:36 +08:00
|
|
|
def parseconfigure(self, *args):
|
|
|
|
config = self.parseconfig(*args)
|
2013-09-30 19:14:14 +08:00
|
|
|
config.do_configure()
|
2010-10-26 05:09:24 +08:00
|
|
|
self.request.addfinalizer(lambda:
|
2013-09-30 19:14:14 +08:00
|
|
|
config.do_unconfigure())
|
2009-03-23 01:41:36 +08:00
|
|
|
return config
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def getitem(self, source, funcname="test_func"):
|
2012-07-20 20:16:28 +08:00
|
|
|
items = self.getitems(source)
|
|
|
|
for item in items:
|
2009-04-05 03:06:20 +08:00
|
|
|
if item.name == funcname:
|
2010-07-27 03:15:15 +08:00
|
|
|
return item
|
2012-07-20 20:16:28 +08:00
|
|
|
assert 0, "%r item not found in module:\n%s\nitems: %s" %(
|
|
|
|
funcname, source, items)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def getitems(self, source):
|
|
|
|
modcol = self.getmodulecol(source)
|
2010-09-15 16:30:50 +08:00
|
|
|
return self.genitems([modcol])
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def getmodulecol(self, source, configargs=(), withinit=False):
|
2009-04-15 01:57:00 +08:00
|
|
|
kw = {self.request.function.__name__: py.code.Source(source).strip()}
|
2009-02-27 18:18:27 +08:00
|
|
|
path = self.makepyfile(**kw)
|
|
|
|
if withinit:
|
|
|
|
self.makepyfile(__init__ = "#")
|
2010-09-15 16:30:50 +08:00
|
|
|
self.config = config = self.parseconfigure(path, *configargs)
|
|
|
|
node = self.getnode(config, path)
|
|
|
|
return node
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2010-11-01 01:01:33 +08:00
|
|
|
def collect_by_name(self, modcol, name):
|
|
|
|
for colitem in modcol._memocollect():
|
|
|
|
if colitem.name == name:
|
|
|
|
return colitem
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def popen(self, cmdargs, stdout, stderr, **kw):
|
2009-04-29 05:49:03 +08:00
|
|
|
env = os.environ.copy()
|
2010-11-14 02:46:28 +08:00
|
|
|
env['PYTHONPATH'] = os.pathsep.join(filter(None, [
|
2009-05-08 03:04:56 +08:00
|
|
|
str(os.getcwd()), env.get('PYTHONPATH', '')]))
|
2009-04-29 05:49:03 +08:00
|
|
|
kw['env'] = env
|
2009-05-08 03:04:56 +08:00
|
|
|
#print "env", env
|
2013-02-04 03:47:39 +08:00
|
|
|
return py.std.subprocess.Popen(cmdargs,
|
|
|
|
stdout=stdout, stderr=stderr, **kw)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def run(self, *cmdargs):
|
2010-01-02 04:54:27 +08:00
|
|
|
return self._run(*cmdargs)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def _run(self, *cmdargs):
|
2009-08-29 21:51:49 +08:00
|
|
|
cmdargs = [str(x) for x in cmdargs]
|
2010-01-02 06:05:00 +08:00
|
|
|
p1 = self.tmpdir.join("stdout")
|
|
|
|
p2 = self.tmpdir.join("stderr")
|
2009-08-29 01:16:15 +08:00
|
|
|
print_("running", cmdargs, "curdir=", py.path.local())
|
2012-09-20 16:57:23 +08:00
|
|
|
f1 = codecs.open(str(p1), "w", encoding="utf8")
|
|
|
|
f2 = codecs.open(str(p2), "w", encoding="utf8")
|
|
|
|
try:
|
|
|
|
now = time.time()
|
|
|
|
popen = self.popen(cmdargs, stdout=f1, stderr=f2,
|
|
|
|
close_fds=(sys.platform != "win32"))
|
|
|
|
ret = popen.wait()
|
|
|
|
finally:
|
|
|
|
f1.close()
|
|
|
|
f2.close()
|
|
|
|
f1 = codecs.open(str(p1), "r", encoding="utf8")
|
|
|
|
f2 = codecs.open(str(p2), "r", encoding="utf8")
|
|
|
|
try:
|
|
|
|
out = f1.read().splitlines()
|
|
|
|
err = f2.read().splitlines()
|
|
|
|
finally:
|
|
|
|
f1.close()
|
|
|
|
f2.close()
|
|
|
|
self._dump_lines(out, sys.stdout)
|
|
|
|
self._dump_lines(err, sys.stderr)
|
2009-12-30 18:37:46 +08:00
|
|
|
return RunResult(ret, out, err, time.time()-now)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2012-09-20 16:57:23 +08:00
|
|
|
def _dump_lines(self, lines, fp):
|
|
|
|
try:
|
|
|
|
for line in lines:
|
|
|
|
py.builtin.print_(line, file=fp)
|
|
|
|
except UnicodeEncodeError:
|
|
|
|
print("couldn't print to %s because of encoding" % (fp,))
|
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def runpybin(self, scriptname, *args):
|
2009-05-06 17:47:48 +08:00
|
|
|
fullargs = self._getpybinargs(scriptname) + args
|
|
|
|
return self.run(*fullargs)
|
|
|
|
|
|
|
|
def _getpybinargs(self, scriptname):
|
2010-09-28 02:48:30 +08:00
|
|
|
if not self.request.config.getvalue("notoolsonpath"):
|
2010-11-13 18:44:58 +08:00
|
|
|
# XXX we rely on script refering to the correct environment
|
|
|
|
# we cannot use "(py.std.sys.executable,script)"
|
|
|
|
# becaue on windows the script is e.g. a py.test.exe
|
2013-10-12 21:39:22 +08:00
|
|
|
return (py.std.sys.executable, _pytest_fullpath,) # noqa
|
2010-01-02 03:36:58 +08:00
|
|
|
else:
|
2010-10-21 04:10:35 +08:00
|
|
|
py.test.skip("cannot run %r with --no-tools-on-path" % scriptname)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2010-08-02 02:43:02 +08:00
|
|
|
def runpython(self, script, prepend=True):
|
|
|
|
if prepend:
|
|
|
|
s = self._getsysprepend()
|
|
|
|
if s:
|
|
|
|
script.write(s + "\n" + script.read())
|
2010-01-11 03:45:37 +08:00
|
|
|
return self.run(sys.executable, script)
|
|
|
|
|
|
|
|
def _getsysprepend(self):
|
2010-09-28 02:48:30 +08:00
|
|
|
if self.request.config.getvalue("notoolsonpath"):
|
2010-04-21 18:50:03 +08:00
|
|
|
s = "import sys;sys.path.insert(0,%r);" % str(py._pydir.dirpath())
|
2010-01-11 03:45:37 +08:00
|
|
|
else:
|
|
|
|
s = ""
|
|
|
|
return s
|
|
|
|
|
|
|
|
def runpython_c(self, command):
|
|
|
|
command = self._getsysprepend() + command
|
|
|
|
return self.run(py.std.sys.executable, "-c", command)
|
2009-07-31 20:21:02 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
def runpytest(self, *args):
|
2010-07-27 03:15:15 +08:00
|
|
|
p = py.path.local.make_numbered_dir(prefix="runpytest-",
|
2009-03-17 18:29:45 +08:00
|
|
|
keep=None, rootdir=self.tmpdir)
|
2010-07-27 03:15:15 +08:00
|
|
|
args = ('--basetemp=%s' % p, ) + args
|
2010-11-06 16:58:04 +08:00
|
|
|
#for x in args:
|
|
|
|
# if '--confcutdir' in str(x):
|
|
|
|
# break
|
|
|
|
#else:
|
|
|
|
# pass
|
|
|
|
# args = ('--confcutdir=.',) + args
|
2009-12-25 07:24:58 +08:00
|
|
|
plugins = [x for x in self.plugins if isinstance(x, str)]
|
|
|
|
if plugins:
|
|
|
|
args = ('-p', plugins[0]) + args
|
2009-02-27 18:18:27 +08:00
|
|
|
return self.runpybin("py.test", *args)
|
|
|
|
|
2009-05-06 17:47:48 +08:00
|
|
|
def spawn_pytest(self, string, expect_timeout=10.0):
|
2010-09-28 02:48:30 +08:00
|
|
|
if self.request.config.getvalue("notoolsonpath"):
|
|
|
|
py.test.skip("--no-tools-on-path prevents running pexpect-spawn tests")
|
2009-05-06 17:47:48 +08:00
|
|
|
basetemp = self.tmpdir.mkdir("pexpect")
|
2010-11-13 18:10:45 +08:00
|
|
|
invoke = " ".join(map(str, self._getpybinargs("py.test")))
|
2009-05-06 17:47:48 +08:00
|
|
|
cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
|
2010-10-06 20:48:24 +08:00
|
|
|
return self.spawn(cmd, expect_timeout=expect_timeout)
|
|
|
|
|
|
|
|
def spawn(self, cmd, expect_timeout=10.0):
|
|
|
|
pexpect = py.test.importorskip("pexpect", "2.4")
|
2010-11-27 23:40:52 +08:00
|
|
|
if hasattr(sys, 'pypy_version_info') and '64' in py.std.platform.machine():
|
|
|
|
pytest.skip("pypy-64 bit not supported")
|
2011-11-12 05:33:45 +08:00
|
|
|
if sys.platform == "darwin":
|
|
|
|
pytest.xfail("pexpect does not work reliably on darwin?!")
|
2012-10-19 21:59:29 +08:00
|
|
|
if sys.platform.startswith("freebsd"):
|
|
|
|
pytest.xfail("pexpect does not work reliably on freebsd")
|
2010-10-06 20:48:24 +08:00
|
|
|
logfile = self.tmpdir.join("spawn.out")
|
|
|
|
child = pexpect.spawn(cmd, logfile=logfile.open("w"))
|
2009-05-06 17:47:48 +08:00
|
|
|
child.timeout = expect_timeout
|
|
|
|
return child
|
|
|
|
|
2010-05-21 22:42:46 +08:00
|
|
|
def getdecoded(out):
|
|
|
|
try:
|
|
|
|
return out.decode("utf-8")
|
|
|
|
except UnicodeDecodeError:
|
|
|
|
return "INTERNAL not-utf8-decodeable, truncated string:\n%s" % (
|
|
|
|
py.io.saferepr(out),)
|
|
|
|
|
2009-05-20 01:25:21 +08:00
|
|
|
class ReportRecorder(object):
|
2009-12-29 19:36:17 +08:00
|
|
|
def __init__(self, hook):
|
|
|
|
self.hook = hook
|
2010-10-13 17:12:27 +08:00
|
|
|
self.pluginmanager = hook._pm
|
|
|
|
self.pluginmanager.register(self)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-04-07 18:48:57 +08:00
|
|
|
def getcall(self, name):
|
2009-05-20 01:25:21 +08:00
|
|
|
return self.hookrecorder.getcall(name)
|
2009-04-07 18:48:57 +08:00
|
|
|
|
2009-05-23 01:56:05 +08:00
|
|
|
def popcall(self, name):
|
|
|
|
return self.hookrecorder.popcall(name)
|
|
|
|
|
2009-05-20 01:25:21 +08:00
|
|
|
def getcalls(self, names):
|
2009-04-07 17:53:01 +08:00
|
|
|
""" return list of ParsedCall instances matching the given eventname. """
|
2009-05-20 01:25:21 +08:00
|
|
|
return self.hookrecorder.getcalls(names)
|
2009-04-03 18:57:34 +08:00
|
|
|
|
2010-07-27 03:15:15 +08:00
|
|
|
# functionality for test reports
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
def getreports(self, names="pytest_runtest_logreport pytest_collectreport"):
|
2009-08-04 18:00:04 +08:00
|
|
|
return [x.report for x in self.getcalls(names)]
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2011-11-09 01:20:56 +08:00
|
|
|
def matchreport(self, inamepart="",
|
|
|
|
names="pytest_runtest_logreport pytest_collectreport", when=None):
|
2009-04-07 18:48:57 +08:00
|
|
|
""" return a testreport whose dotted import path matches """
|
|
|
|
l = []
|
|
|
|
for rep in self.getreports(names=names):
|
2011-11-09 01:20:56 +08:00
|
|
|
try:
|
|
|
|
if not when and rep.when != "call" and rep.passed:
|
|
|
|
# setup/teardown passing reports - let's ignore those
|
|
|
|
continue
|
|
|
|
except AttributeError:
|
|
|
|
pass
|
2010-11-24 23:17:49 +08:00
|
|
|
if when and getattr(rep, 'when', None) != when:
|
|
|
|
continue
|
2010-11-06 16:58:04 +08:00
|
|
|
if not inamepart or inamepart in rep.nodeid.split("::"):
|
2009-04-07 18:48:57 +08:00
|
|
|
l.append(rep)
|
|
|
|
if not l:
|
|
|
|
raise ValueError("could not find test report matching %r: no test reports at all!" %
|
|
|
|
(inamepart,))
|
|
|
|
if len(l) > 1:
|
|
|
|
raise ValueError("found more than one testreport matching %r: %s" %(
|
|
|
|
inamepart, l))
|
|
|
|
return l[0]
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2009-06-09 00:31:10 +08:00
|
|
|
def getfailures(self, names='pytest_runtest_logreport pytest_collectreport'):
|
2009-05-20 01:25:21 +08:00
|
|
|
return [rep for rep in self.getreports(names) if rep.failed]
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def getfailedcollections(self):
|
2009-05-20 01:25:21 +08:00
|
|
|
return self.getfailures('pytest_collectreport')
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def listoutcomes(self):
|
|
|
|
passed = []
|
|
|
|
skipped = []
|
|
|
|
failed = []
|
2013-08-16 17:33:58 +08:00
|
|
|
for rep in self.getreports(
|
|
|
|
"pytest_collectreport pytest_runtest_logreport"):
|
2010-07-27 03:15:15 +08:00
|
|
|
if rep.passed:
|
2013-08-16 17:33:58 +08:00
|
|
|
if getattr(rep, "when", None) == "call":
|
2010-07-27 03:15:15 +08:00
|
|
|
passed.append(rep)
|
|
|
|
elif rep.skipped:
|
|
|
|
skipped.append(rep)
|
2009-04-07 18:48:57 +08:00
|
|
|
elif rep.failed:
|
2010-07-27 03:15:15 +08:00
|
|
|
failed.append(rep)
|
|
|
|
return passed, skipped, failed
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def countoutcomes(self):
|
2009-09-05 03:47:49 +08:00
|
|
|
return [len(x) for x in self.listoutcomes()]
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def assertoutcome(self, passed=0, skipped=0, failed=0):
|
|
|
|
realpassed, realskipped, realfailed = self.listoutcomes()
|
|
|
|
assert passed == len(realpassed)
|
|
|
|
assert skipped == len(realskipped)
|
|
|
|
assert failed == len(realfailed)
|
|
|
|
|
|
|
|
def clear(self):
|
2009-05-20 01:25:21 +08:00
|
|
|
self.hookrecorder.calls[:] = []
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def unregister(self):
|
2010-10-13 17:12:27 +08:00
|
|
|
self.pluginmanager.unregister(self)
|
2009-05-20 01:25:21 +08:00
|
|
|
self.hookrecorder.finish_recording()
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
class LineComp:
|
|
|
|
def __init__(self):
|
2009-08-21 02:47:39 +08:00
|
|
|
self.stringio = py.io.TextIO()
|
2009-02-27 18:18:27 +08:00
|
|
|
|
|
|
|
def assert_contains_lines(self, lines2):
|
2010-07-27 03:15:15 +08:00
|
|
|
""" assert that lines2 are contained (linearly) in lines1.
|
2009-02-27 18:18:27 +08:00
|
|
|
return a list of extralines found.
|
|
|
|
"""
|
|
|
|
__tracebackhide__ = True
|
|
|
|
val = self.stringio.getvalue()
|
2010-04-28 19:22:05 +08:00
|
|
|
self.stringio.truncate(0)
|
|
|
|
self.stringio.seek(0)
|
2009-02-27 18:18:27 +08:00
|
|
|
lines1 = val.split("\n")
|
|
|
|
return LineMatcher(lines1).fnmatch_lines(lines2)
|
2010-07-27 03:15:15 +08:00
|
|
|
|
2009-02-27 18:18:27 +08:00
|
|
|
class LineMatcher:
|
|
|
|
def __init__(self, lines):
|
|
|
|
self.lines = lines
|
|
|
|
|
|
|
|
def str(self):
|
|
|
|
return "\n".join(self.lines)
|
|
|
|
|
2010-09-28 02:48:30 +08:00
|
|
|
def _getlines(self, lines2):
|
2009-02-27 18:18:27 +08:00
|
|
|
if isinstance(lines2, str):
|
|
|
|
lines2 = py.code.Source(lines2)
|
|
|
|
if isinstance(lines2, py.code.Source):
|
|
|
|
lines2 = lines2.strip().lines
|
2010-09-28 02:48:30 +08:00
|
|
|
return lines2
|
|
|
|
|
|
|
|
def fnmatch_lines_random(self, lines2):
|
|
|
|
lines2 = self._getlines(lines2)
|
|
|
|
for line in lines2:
|
|
|
|
for x in self.lines:
|
|
|
|
if line == x or fnmatch(x, line):
|
|
|
|
print_("matched: ", repr(line))
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
raise ValueError("line %r not found in output" % line)
|
2009-02-27 18:18:27 +08:00
|
|
|
|
2013-10-01 18:37:11 +08:00
|
|
|
def get_lines_after(self, fnline):
|
|
|
|
for i, line in enumerate(self.lines):
|
|
|
|
if fnline == line or fnmatch(line, fnline):
|
|
|
|
return self.lines[i+1:]
|
|
|
|
raise ValueError("line %r not found in output" % fnline)
|
|
|
|
|
2010-09-28 02:48:30 +08:00
|
|
|
def fnmatch_lines(self, lines2):
|
2010-11-06 16:58:04 +08:00
|
|
|
def show(arg1, arg2):
|
|
|
|
py.builtin.print_(arg1, arg2, file=py.std.sys.stderr)
|
2010-09-28 02:48:30 +08:00
|
|
|
lines2 = self._getlines(lines2)
|
2009-02-27 18:18:27 +08:00
|
|
|
lines1 = self.lines[:]
|
|
|
|
nextline = None
|
|
|
|
extralines = []
|
2010-04-29 05:51:36 +08:00
|
|
|
__tracebackhide__ = True
|
2009-02-27 18:18:27 +08:00
|
|
|
for line in lines2:
|
|
|
|
nomatchprinted = False
|
|
|
|
while lines1:
|
|
|
|
nextline = lines1.pop(0)
|
|
|
|
if line == nextline:
|
2010-11-06 16:58:04 +08:00
|
|
|
show("exact match:", repr(line))
|
2010-07-27 03:15:15 +08:00
|
|
|
break
|
2009-02-27 18:18:27 +08:00
|
|
|
elif fnmatch(nextline, line):
|
2010-11-06 16:58:04 +08:00
|
|
|
show("fnmatch:", repr(line))
|
|
|
|
show(" with:", repr(nextline))
|
2009-02-27 18:18:27 +08:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
if not nomatchprinted:
|
2010-11-06 16:58:04 +08:00
|
|
|
show("nomatch:", repr(line))
|
2009-02-27 18:18:27 +08:00
|
|
|
nomatchprinted = True
|
2010-11-06 16:58:04 +08:00
|
|
|
show(" and:", repr(nextline))
|
2009-02-27 18:18:27 +08:00
|
|
|
extralines.append(nextline)
|
|
|
|
else:
|
2010-11-06 16:58:04 +08:00
|
|
|
py.test.fail("remains unmatched: %r, see stderr" % (line,))
|