295 lines
11 KiB
Python
295 lines
11 KiB
Python
import py
|
|
|
|
from time import time as now
|
|
from py.__.test.terminal.out import getout
|
|
from py.__.test.representation import Presenter
|
|
from py.__.test.outcome import Skipped, Passed, Failed
|
|
|
|
def getrelpath(source, dest):
|
|
base = source.common(dest)
|
|
if not base:
|
|
return None
|
|
# with posix local paths '/' is always a common base
|
|
relsource = source.relto(base)
|
|
reldest = dest.relto(base)
|
|
n = relsource.count(source.sep)
|
|
target = dest.sep.join(('..', )*n + (reldest, ))
|
|
return target
|
|
|
|
from py.__.test.session import Session
|
|
|
|
class TerminalSession(Session):
|
|
def __init__(self, config, file=None):
|
|
super(TerminalSession, self).__init__(config)
|
|
if file is None:
|
|
file = py.std.sys.stdout
|
|
self._file = file
|
|
self.out = getout(file)
|
|
self._opencollectors = []
|
|
self.presenter = Presenter(self.out, config)
|
|
|
|
# ---------------------
|
|
# PROGRESS information
|
|
# ---------------------
|
|
|
|
def start(self, colitem):
|
|
super(TerminalSession, self).start(colitem)
|
|
if self.config.option.collectonly:
|
|
cols = self._opencollectors
|
|
self.out.line(' ' * len(cols) + repr(colitem))
|
|
cols.append(colitem)
|
|
else:
|
|
cls = getattr(colitem, '__class__', None)
|
|
if cls is None:
|
|
return
|
|
if issubclass(cls, py.test.collect.Module):
|
|
self.start_Module(colitem)
|
|
elif issubclass(cls, py.test.collect.Item):
|
|
self.start_Item(colitem)
|
|
#for typ in py.std.inspect.getmro(cls):
|
|
# meth = getattr(self, 'start_%s' % typ.__name__, None)
|
|
# if meth:
|
|
# meth(colitem)
|
|
# break
|
|
colitem.start = py.std.time.time()
|
|
|
|
def start_Module(self, colitem):
|
|
if self.config.option.verbose == 0:
|
|
abbrev_fn = getrelpath(py.path.local('.xxx.'), colitem.fspath)
|
|
self.out.write('%s' % (abbrev_fn, ))
|
|
else:
|
|
self.out.line()
|
|
self.out.line("+ testmodule: %s" % colitem.fspath)
|
|
|
|
def startiteration(self, colitem, subitems):
|
|
if (isinstance(colitem, py.test.collect.Module)
|
|
and self.config.option.verbose == 0
|
|
and not self.config.option.collectonly):
|
|
try:
|
|
sum = 0
|
|
for sub in subitems:
|
|
sum += len(list(colitem.join(sub)._tryiter()))
|
|
except (SystemExit, KeyboardInterrupt):
|
|
raise
|
|
except:
|
|
self.out.write('[?]')
|
|
else:
|
|
self.out.write('[%d] ' % sum)
|
|
return self.out.line
|
|
|
|
def start_Item(self, colitem):
|
|
if self.config.option.verbose >= 1:
|
|
if isinstance(colitem, py.test.collect.Item):
|
|
realpath, lineno = colitem._getpathlineno()
|
|
location = "%s:%d" % (realpath.basename, lineno+1)
|
|
self.out.write("%-20s %s " % (location, colitem._getmodpath()))
|
|
|
|
def finish(self, colitem, outcome):
|
|
end = now()
|
|
super(TerminalSession, self).finish(colitem, outcome)
|
|
if self.config.option.collectonly:
|
|
cols = self._opencollectors
|
|
last = cols.pop()
|
|
#assert last == colitem, "expected %r, got %r" %(last, colitem)
|
|
return
|
|
colitem.elapsedtime = end - colitem.start
|
|
if self.config.option.usepdb:
|
|
if isinstance(outcome, Failed):
|
|
print "dispatching to ppdb", colitem
|
|
self.repr_failure(colitem, outcome)
|
|
import pdb
|
|
self.out.write('\n%s\n' % (outcome.excinfo.exconly(),))
|
|
pdb.post_mortem(outcome.excinfo._excinfo[2])
|
|
if isinstance(colitem, py.test.collect.Module):
|
|
resultstring = self.repr_progress_module_result(colitem, outcome)
|
|
if resultstring:
|
|
self.out.line(" - " + resultstring)
|
|
if isinstance(colitem, py.test.collect.Item):
|
|
if self.config.option.verbose >= 1:
|
|
resultstring = self.repr_progress_long_result(colitem, outcome)
|
|
resultstring += " (%.2f)" % (colitem.elapsedtime,)
|
|
self.out.line(resultstring)
|
|
else:
|
|
c = self.repr_progress_short_result(colitem, outcome)
|
|
self.out.write(c)
|
|
|
|
|
|
# -------------------
|
|
# HEADER information
|
|
# -------------------
|
|
def header(self, colitems):
|
|
super(TerminalSession, self).header(colitems)
|
|
self.out.sep("=", "test process starts")
|
|
option = self.config.option
|
|
modes = []
|
|
for name in 'looponfailing', 'exitfirst', 'nomagic':
|
|
if getattr(option, name):
|
|
modes.append(name)
|
|
#if self._isremoteoption._fromremote:
|
|
# modes.insert(0, 'child process')
|
|
#else:
|
|
# modes.insert(0, 'inprocess')
|
|
#mode = "/".join(modes)
|
|
#self.out.line("testing-mode: %s" % mode)
|
|
self.out.line("executable: %s (%s)" %
|
|
(py.std.sys.executable, repr_pythonversion()))
|
|
rev = py.__package__.getrev()
|
|
self.out.line("using py lib: %s <rev %s>" % (
|
|
py.path.local(py.__file__).dirpath(), rev))
|
|
|
|
if self.config.option.traceconfig or self.config.option.verbose:
|
|
|
|
for x in colitems:
|
|
self.out.line("test target: %s" %(x.fspath,))
|
|
|
|
conftestmodules = self.config._conftest.getconftestmodules(None)
|
|
for i,x in py.builtin.enumerate(conftestmodules):
|
|
self.out.line("initial conf %d: %s" %(i, x.__file__))
|
|
|
|
#for i, x in py.builtin.enumerate(py.test.config.configpaths):
|
|
# self.out.line("initial testconfig %d: %s" %(i, x))
|
|
#additional = py.test.config.getfirst('additionalinfo')
|
|
#if additional:
|
|
# for key, descr in additional():
|
|
# self.out.line("%s: %s" %(key, descr))
|
|
self.out.line()
|
|
self.starttime = now()
|
|
|
|
# -------------------
|
|
# FOOTER information
|
|
# -------------------
|
|
|
|
def footer(self, colitems):
|
|
super(TerminalSession, self).footer(colitems)
|
|
self.endtime = now()
|
|
self.out.line()
|
|
self.skippedreasons()
|
|
self.failures()
|
|
self.summaryline()
|
|
|
|
# --------------------
|
|
# progress information
|
|
# --------------------
|
|
typemap = {
|
|
Passed: '.',
|
|
Skipped: 's',
|
|
Failed: 'F',
|
|
}
|
|
namemap = {
|
|
Passed: 'ok',
|
|
Skipped: 'SKIP',
|
|
Failed: 'FAIL',
|
|
}
|
|
|
|
def repr_progress_short_result(self, item, outcome):
|
|
for outcometype, char in self.typemap.items():
|
|
if isinstance(outcome, outcometype):
|
|
return char
|
|
else:
|
|
#raise TypeError, "not an Outomce instance: %r" % (outcome,)
|
|
return '?'
|
|
|
|
def repr_progress_long_result(self, item, outcome):
|
|
for outcometype, char in self.namemap.items():
|
|
if isinstance(outcome, outcometype):
|
|
return char
|
|
else:
|
|
#raise TypeError, "not an Outcome instance: %r" % (outcome,)
|
|
return 'UNKNOWN'
|
|
|
|
def repr_progress_module_result(self, item, outcome):
|
|
if isinstance(outcome, Failed):
|
|
return "FAILED TO LOAD MODULE"
|
|
elif isinstance(outcome, Skipped):
|
|
return "skipped"
|
|
elif not isinstance(outcome, (list, Passed)):
|
|
return "?"
|
|
|
|
# --------------------
|
|
# summary information
|
|
# --------------------
|
|
def summaryline(self):
|
|
outlist = []
|
|
sum = 0
|
|
for typ in Passed, Failed, Skipped:
|
|
l = self.getitemoutcomepairs(typ)
|
|
if l:
|
|
outlist.append('%d %s' % (len(l), typ.__name__.lower()))
|
|
sum += len(l)
|
|
elapsed = self.endtime-self.starttime
|
|
status = "%s" % ", ".join(outlist)
|
|
self.out.sep('=', 'tests finished: %s in %4.2f seconds' %
|
|
(status, elapsed))
|
|
|
|
def getlastvisible(self, sourcetraceback):
|
|
traceback = sourcetraceback[:]
|
|
while traceback:
|
|
entry = traceback.pop()
|
|
try:
|
|
x = entry.frame.eval("__tracebackhide__")
|
|
except:
|
|
x = False
|
|
if not x:
|
|
return entry
|
|
else:
|
|
return sourcetraceback[-1]
|
|
|
|
def skippedreasons(self):
|
|
texts = {}
|
|
for colitem, outcome in self.getitemoutcomepairs(Skipped):
|
|
raisingtb = self.getlastvisible(outcome.excinfo.traceback)
|
|
fn = raisingtb.frame.code.path
|
|
lineno = raisingtb.lineno
|
|
d = texts.setdefault(outcome.excinfo.exconly(), {})
|
|
d[(fn,lineno)] = outcome
|
|
|
|
if texts:
|
|
self.out.line()
|
|
self.out.sep('_', 'reasons for skipped tests')
|
|
for text, dict in texts.items():
|
|
for (fn, lineno), outcome in dict.items():
|
|
self.out.line('Skipped in %s:%d' %(fn, lineno+1))
|
|
self.out.line("reason: %s" % text)
|
|
self.out.line()
|
|
|
|
def failures(self):
|
|
if self.config.option.tbstyle == 'no':
|
|
return # skip the detailed failure reports altogether
|
|
l = self.getitemoutcomepairs(Failed)
|
|
if l:
|
|
self.out.sep('_')
|
|
for colitem, outcome in l:
|
|
self.repr_failure(colitem, outcome)
|
|
|
|
def repr_failure(self, item, outcome):
|
|
excinfo = outcome.excinfo
|
|
traceback = excinfo.traceback
|
|
#print "repr_failures sees item", item
|
|
#print "repr_failures sees traceback"
|
|
#py.std.pprint.pprint(traceback)
|
|
if item and not self.config.option.fulltrace:
|
|
path, firstlineno = item._getpathlineno()
|
|
ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
|
|
if ntraceback == traceback:
|
|
ntraceback = ntraceback.cut(path=path)
|
|
traceback = ntraceback.filter()
|
|
if not traceback:
|
|
self.out.line("empty traceback from item %r" % (item,))
|
|
return
|
|
handler = getattr(self.presenter, 'repr_failure_tb%s' % self.config.option.tbstyle)
|
|
handler(item, excinfo, traceback, lambda : self.repr_out_err(item))
|
|
|
|
def repr_out_err(self, colitem):
|
|
for parent in colitem.listchain():
|
|
for name, obj in zip(['out', 'err'], parent._getouterr()):
|
|
if obj:
|
|
self.out.sep("- ", "%s: recorded std%s" % (parent.name, name))
|
|
self.out.line(obj)
|
|
|
|
def repr_pythonversion():
|
|
v = py.std.sys.version_info
|
|
try:
|
|
return "%s.%s.%s-%s-%s" % v
|
|
except ValueError:
|
|
return str(v)
|