2008-08-16 23:26:59 +08:00
|
|
|
"""
|
|
|
|
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
|
2008-09-03 03:38:46 +08:00
|
|
|
|
|
|
|
def parseconfig(self, *args):
|
|
|
|
return py.test.config._reparse(list(args))
|
2008-08-16 23:26:59 +08:00
|
|
|
|
|
|
|
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")
|
2008-09-02 16:58:14 +08:00
|
|
|
kw = {"test_" + self.tmpdir.basename: py.code.Source(source).strip()}
|
2008-08-16 23:26:59 +08:00
|
|
|
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)
|
2008-09-02 16:58:14 +08:00
|
|
|
return modulecol.collect()
|
2008-08-16 23:26:59 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2008-09-02 22:31:42 +08:00
|
|
|
class InlineSession(InlineCollection):
|
|
|
|
def parse_and_run(self, *args):
|
|
|
|
config = self.parseconfig(*args)
|
|
|
|
session = config.initsession()
|
|
|
|
sorter = EventSorter(config, session)
|
|
|
|
session.main()
|
|
|
|
return sorter
|
2008-08-16 23:26:59 +08:00
|
|
|
|
|
|
|
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)
|