test_ok1/py/test/testing/suptest.py

231 lines
7.3 KiB
Python

"""
test support code
makeuniquepyfile(source) generates a per-test-run-unique directory and test_*.py file
for analyzing events an EventSorter instance is returned for both of:
* events_from_cmdline(args): inprocess-run of cmdline invocation
* events_from_session(session): inprocess-run of given session
eventappender(config): for getting all events in a list:
"""
import py
from py.__.test import event
from fnmatch import fnmatch
def eventappender(session):
l = []
def app(ev):
print ev
l.append(ev)
session.bus.subscribe(app)
return l
def initsorter_from_cmdline(args=None):
if args is None:
args = []
config = py.test.config._reparse(args)
session = config.initsession()
sorter = EventSorter(config, session)
return sorter
def getcolitems(config):
return [config.getfsnode(arg) for arg in config.args]
def events_from_cmdline(args=None):
sorter = initsorter_from_cmdline(args)
sorter.session.main(getcolitems(sorter.session.config))
return sorter
def events_from_runsource(source):
source = py.code.Source(source)
tfile = makeuniquepyfile(source)
return events_from_cmdline([tfile])
def events_from_session(session):
sorter = EventSorter(session.config, session)
session.main(getcolitems(session.config))
return sorter
class EventSorter(object):
def __init__(self, config, session=None):
self.config = config
self.session = session
self.cls2events = d = {}
def app(event):
print "[event]", event
for cls in py.std.inspect.getmro(event.__class__):
if cls is not object:
d.setdefault(cls, []).append(event)
session.bus.subscribe(app)
def get(self, cls):
return self.cls2events.get(cls, [])
def listoutcomes(self):
passed = []
skipped = []
failed = []
for ev in self.get(event.ItemTestReport):
if ev.passed:
passed.append(ev)
elif ev.skipped:
skipped.append(ev)
elif ev.failed:
failed.append(ev)
return passed, skipped, failed
def countoutcomes(self):
return map(len, self.listoutcomes())
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 getfailedcollections(self):
l = []
for ev in self.get(event.CollectionReport):
if ev.failed:
l.append(ev)
return l
def getreport(self, inamepart):
""" return a testreport whose dotted import path matches """
l = []
for rep in self.get(event.ItemTestReport):
if inamepart in rep.colitem.listnames():
l.append(rep)
if len(l) != 1:
raise ValueError("did not find exactly one testreport"
"found" + str(l))
return l[0]
counter = py.std.itertools.count().next
def makeuniquepyfile(source):
dirname = "test_%d" %(counter(),)
tmpdir = py.test.ensuretemp(dirname)
p = tmpdir.join(dirname + ".py")
assert not p.check()
p.write(py.code.Source(source))
print "created test file", p
p.dirpath("__init__.py").ensure()
return p
def getItemTestReport(source, tb="long"):
tfile = makeuniquepyfile(source)
sorter = events_from_cmdline([tfile, "--tb=%s" %tb])
# get failure base info
failevents = sorter.get(event.ItemTestReport)
assert len(failevents) == 1
return failevents[0],tfile
def assert_lines_contain_lines(lines1, lines2):
""" assert that lines2 are contained (linearly) in lines1.
return a list of extralines found.
"""
__tracebackhide__ = True
if isinstance(lines2, str):
lines2 = py.code.Source(lines2)
if isinstance(lines2, py.code.Source):
lines2 = lines2.strip().lines
extralines = []
lines1 = lines1[:]
nextline = None
for line in lines2:
nomatchprinted = False
while lines1:
nextline = lines1.pop(0)
if line == nextline:
print "exact match:", repr(line)
break
elif fnmatch(nextline, line):
print "fnmatch:", repr(line)
print " with:", repr(nextline)
break
else:
if not nomatchprinted:
print "nomatch:", repr(line)
nomatchprinted = True
print " and:", repr(nextline)
extralines.append(nextline)
else:
if line != nextline:
#__tracebackhide__ = True
raise AssertionError("expected line not found: %r" % line)
extralines.extend(lines1)
return extralines
# XXX below some code to help with inlining examples
# as source code.
#
class FileCreation(object):
def setup_method(self, method):
self.tmpdir = py.test.ensuretemp("%s_%s" %
(self.__class__.__name__, method.__name__))
def makepyfile(self, **kwargs):
return self._makefile('.py', **kwargs)
def maketxtfile(self, **kwargs):
return self._makefile('.txt', **kwargs)
def _makefile(self, ext, **kwargs):
ret = None
for name, value in kwargs.iteritems():
p = self.tmpdir.join(name).new(ext=ext)
source = py.code.Source(value)
p.write(str(py.code.Source(value)).lstrip())
if ret is None:
ret = p
return ret
def parseconfig(self, *args):
return py.test.config._reparse(list(args))
class InlineCollection(FileCreation):
""" helps to collect and run test functions inlining other test functions. """
def getmodulecol(self, source, configargs=(), withsession=False):
self.tmpdir.ensure("__init__.py")
kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()}
path = self.makepyfile(**kw)
self.config = self.parseconfig(path, *configargs)
if withsession:
self.session = self.config.initsession()
return self.config.getfsnode(path)
def getitems(self, source):
modulecol = self.getmodulecol(source)
return modulecol.collect()
def getitem(self, source, funcname="test_func"):
modulecol = self.getmodulecol(source)
item = modulecol.join(funcname)
assert item is not None, "%r item not found in module:\n%s" %(funcname, source)
return item
def runitem(self, func, funcname="test_func", **runnerargs):
item = self.getitem(func, funcname=funcname)
runner = self.getrunner()
return runner(item, **runnerargs)
class InlineSession(InlineCollection):
def parse_and_run(self, *args):
config = self.parseconfig(*args)
session = config.initsession()
sorter = EventSorter(config, session)
session.main()
return sorter
def popvalue(stringio):
value = stringio.getvalue().rstrip()
stringio.truncate(0)
return value
def assert_stringio_contains_lines(stringio, tomatchlines):
stringio.seek(0)
l = stringio.readlines()
l = map(str.rstrip, l)
assert_lines_contain_lines(l, tomatchlines)