[svn r58316] (iko, pedronis)
move the FileLogSession into the py.lib proper, activated with the option --resultlog (suggestions for a better name are welcome) - added its tests - plus a functional/integration test in test_config in the style of the one for eventlog --HG-- branch : trunk
This commit is contained in:
parent
ef5709d30b
commit
8b3fe55158
|
@ -113,4 +113,7 @@ def adddefaultoptions(config):
|
||||||
Option('', '--session',
|
Option('', '--session',
|
||||||
action="store", dest="session", default=None,
|
action="store", dest="session", default=None,
|
||||||
help="lookup given sessioname in conftest.py files and use it."),
|
help="lookup given sessioname in conftest.py files and use it."),
|
||||||
|
Option('--resultlog', action="store",
|
||||||
|
default=None, dest="resultlog",
|
||||||
|
help="path for machine-readable result log")
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import py
|
||||||
|
from py.__.test import event
|
||||||
|
|
||||||
|
def generic_path(item):
|
||||||
|
chain = item.listchain()
|
||||||
|
gpath = [chain[0].name]
|
||||||
|
fspath = chain[0].fspath
|
||||||
|
fspart = False
|
||||||
|
for node in chain[1:]:
|
||||||
|
newfspath = node.fspath
|
||||||
|
if newfspath == fspath:
|
||||||
|
if fspart:
|
||||||
|
gpath.append(':')
|
||||||
|
fspart = False
|
||||||
|
else:
|
||||||
|
gpath.append('.')
|
||||||
|
else:
|
||||||
|
gpath.append('/')
|
||||||
|
fspart = True
|
||||||
|
name = node.name
|
||||||
|
if name[0] in '([':
|
||||||
|
gpath.pop()
|
||||||
|
gpath.append(name)
|
||||||
|
fspath = newfspath
|
||||||
|
return ''.join(gpath)
|
||||||
|
|
||||||
|
class ResultLog(object):
|
||||||
|
|
||||||
|
def __init__(self, bus, logfile):
|
||||||
|
bus.subscribe(self.log_event_to_file)
|
||||||
|
self.logfile = logfile #open(logpath, 'w') # line buffering ?
|
||||||
|
|
||||||
|
def write_log_entry(self, shortrepr, name, longrepr):
|
||||||
|
print >>self.logfile, "%s %s" % (shortrepr, name)
|
||||||
|
for line in longrepr.splitlines():
|
||||||
|
print >>self.logfile, " %s" % line
|
||||||
|
|
||||||
|
def log_outcome(self, ev):
|
||||||
|
outcome = ev.outcome
|
||||||
|
gpath = generic_path(ev.colitem)
|
||||||
|
self.write_log_entry(outcome.shortrepr, gpath, str(outcome.longrepr))
|
||||||
|
|
||||||
|
def log_event_to_file(self, ev):
|
||||||
|
if isinstance(ev, event.ItemTestReport):
|
||||||
|
self.log_outcome(ev)
|
||||||
|
elif isinstance(ev, event.CollectionReport):
|
||||||
|
if not ev.passed:
|
||||||
|
self.log_outcome(ev)
|
||||||
|
elif isinstance(ev, event.InternalException):
|
||||||
|
path = ev.repr.reprcrash.path # fishing :(
|
||||||
|
self.write_log_entry('!', path, str(ev.repr))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import py
|
||||||
from py.__.test import event, outcome
|
from py.__.test import event, outcome
|
||||||
from py.__.test.event import EventBus
|
from py.__.test.event import EventBus
|
||||||
import py.__.test.custompdb
|
import py.__.test.custompdb
|
||||||
|
from py.__.test.resultlog import ResultLog
|
||||||
|
|
||||||
# used for genitems()
|
# used for genitems()
|
||||||
from py.__.test.outcome import Exit
|
from py.__.test.outcome import Exit
|
||||||
|
@ -34,6 +35,10 @@ class Session(object):
|
||||||
print >>f, ev
|
print >>f, ev
|
||||||
f.flush()
|
f.flush()
|
||||||
self.bus.subscribe(eventwrite)
|
self.bus.subscribe(eventwrite)
|
||||||
|
resultlog = self.config.option.resultlog
|
||||||
|
if resultlog:
|
||||||
|
f = py.path.local(resultlog).open('w')
|
||||||
|
self.resultlog = ResultLog(self.bus, f)
|
||||||
|
|
||||||
def fixoptions(self):
|
def fixoptions(self):
|
||||||
""" check, fix and determine conflicting options. """
|
""" check, fix and determine conflicting options. """
|
||||||
|
|
|
@ -181,6 +181,27 @@ class TestSessionAndOptions(suptest.FileCreation):
|
||||||
s = eventlog.read()
|
s = eventlog.read()
|
||||||
assert s.find("TestrunStart") != -1
|
assert s.find("TestrunStart") != -1
|
||||||
|
|
||||||
|
def test_session_resultlog(self):
|
||||||
|
from py.__.test.collect import Item
|
||||||
|
from py.__.test.runner import OutcomeRepr
|
||||||
|
|
||||||
|
resultlog = self.tmpdir.join("test_session_resultlog")
|
||||||
|
config = py.test.config._reparse([self.tmpdir,
|
||||||
|
'--resultlog=%s' % resultlog])
|
||||||
|
|
||||||
|
session = config.initsession()
|
||||||
|
|
||||||
|
item = Item("a", config=config)
|
||||||
|
outcome = OutcomeRepr('execute', '.', '')
|
||||||
|
rep_ev = event.ItemTestReport(item, passed=outcome)
|
||||||
|
|
||||||
|
session.bus.notify(rep_ev)
|
||||||
|
|
||||||
|
session.resultlog.logfile.flush()
|
||||||
|
|
||||||
|
s = resultlog.read()
|
||||||
|
assert s.find(". a") != -1
|
||||||
|
|
||||||
def test_tracedir_tracer(self):
|
def test_tracedir_tracer(self):
|
||||||
tracedir = self.tmpdir.join("tracedir")
|
tracedir = self.tmpdir.join("tracedir")
|
||||||
config = py.test.config._reparse([self.tmpdir,
|
config = py.test.config._reparse([self.tmpdir,
|
||||||
|
|
|
@ -0,0 +1,184 @@
|
||||||
|
import os, StringIO
|
||||||
|
|
||||||
|
import py
|
||||||
|
|
||||||
|
from py.__.test import resultlog
|
||||||
|
from py.__.test.collect import Node, Item, FSCollector
|
||||||
|
from py.__.test.event import EventBus
|
||||||
|
from py.__.test.event import ItemTestReport, CollectionReport
|
||||||
|
from py.__.test.event import InternalException
|
||||||
|
from py.__.test.runner import OutcomeRepr
|
||||||
|
|
||||||
|
|
||||||
|
class Fake(object):
|
||||||
|
def __init__(self, **kwds):
|
||||||
|
self.__dict__.update(kwds)
|
||||||
|
|
||||||
|
|
||||||
|
def test_generic_path():
|
||||||
|
p1 = Node('a', config='dummy')
|
||||||
|
assert p1.fspath is None
|
||||||
|
p2 = Node('B', parent=p1)
|
||||||
|
p3 = Node('()', parent = p2)
|
||||||
|
item = Item('c', parent = p3)
|
||||||
|
|
||||||
|
res = resultlog.generic_path(item)
|
||||||
|
assert res == 'a.B().c'
|
||||||
|
|
||||||
|
p0 = FSCollector('proj/test', config='dummy')
|
||||||
|
p1 = FSCollector('proj/test/a', parent=p0)
|
||||||
|
p2 = Node('B', parent=p1)
|
||||||
|
p3 = Node('()', parent = p2)
|
||||||
|
p4 = Node('c', parent=p3)
|
||||||
|
item = Item('[1]', parent = p4)
|
||||||
|
|
||||||
|
res = resultlog.generic_path(item)
|
||||||
|
assert res == 'test/a:B().c[1]'
|
||||||
|
|
||||||
|
|
||||||
|
def make_item(*names):
|
||||||
|
node = None
|
||||||
|
config = "dummy"
|
||||||
|
for name in names[:-1]:
|
||||||
|
if '/' in name:
|
||||||
|
node = FSCollector(name, parent=node, config=config)
|
||||||
|
else:
|
||||||
|
node = Node(name, parent=node, config=config)
|
||||||
|
if names[-1] is None:
|
||||||
|
return node
|
||||||
|
return Item(names[-1], parent=node)
|
||||||
|
|
||||||
|
class TestResultLog(object):
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
bus = EventBus()
|
||||||
|
logfile = object()
|
||||||
|
|
||||||
|
reslog = resultlog.ResultLog(bus, logfile)
|
||||||
|
assert len(bus._subscribers) == 1
|
||||||
|
assert reslog.logfile is logfile
|
||||||
|
|
||||||
|
def test_write_log_entry(self):
|
||||||
|
reslog = resultlog.ResultLog(EventBus(), None)
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
reslog.write_log_entry('.', 'name', '')
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
assert entry[-1] == '\n'
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
assert len(entry_lines) == 1
|
||||||
|
assert entry_lines[0] == '. name'
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
reslog.write_log_entry('s', 'name', 'Skipped')
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
assert entry[-1] == '\n'
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
assert len(entry_lines) == 2
|
||||||
|
assert entry_lines[0] == 's name'
|
||||||
|
assert entry_lines[1] == ' Skipped'
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
reslog.write_log_entry('s', 'name', 'Skipped\n')
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
assert entry[-1] == '\n'
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
assert len(entry_lines) == 2
|
||||||
|
assert entry_lines[0] == 's name'
|
||||||
|
assert entry_lines[1] == ' Skipped'
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
longrepr = ' tb1\n tb 2\nE tb3\nSome Error'
|
||||||
|
reslog.write_log_entry('F', 'name', longrepr)
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
assert entry[-1] == '\n'
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
assert len(entry_lines) == 5
|
||||||
|
assert entry_lines[0] == 'F name'
|
||||||
|
assert entry_lines[1:] == [' '+line for line in longrepr.splitlines()]
|
||||||
|
|
||||||
|
def test_log_outcome(self):
|
||||||
|
reslog = resultlog.ResultLog(EventBus(), StringIO.StringIO())
|
||||||
|
|
||||||
|
colitem = make_item('some', 'path', 'a', 'b')
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
the_repr = py.code.ExceptionInfo().getrepr()
|
||||||
|
|
||||||
|
outcome=OutcomeRepr('execute', 'F', the_repr)
|
||||||
|
ev = Fake(colitem=colitem, outcome=outcome)
|
||||||
|
|
||||||
|
reslog.log_outcome(ev)
|
||||||
|
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
|
||||||
|
assert entry_lines[0] == 'F some.path.a.b'
|
||||||
|
assert entry_lines[-1][0] == ' '
|
||||||
|
assert 'ValueError' in entry
|
||||||
|
|
||||||
|
def test_item_test_passed(self):
|
||||||
|
bus = EventBus()
|
||||||
|
reslog = resultlog.ResultLog(bus, StringIO.StringIO())
|
||||||
|
|
||||||
|
colitem = make_item('proj/test', 'proj/test/mod', 'a', 'b')
|
||||||
|
|
||||||
|
outcome=OutcomeRepr('execute', '.', '')
|
||||||
|
rep_ev = ItemTestReport(colitem, passed=outcome)
|
||||||
|
|
||||||
|
bus.notify(rep_ev)
|
||||||
|
|
||||||
|
lines = reslog.logfile.getvalue().splitlines()
|
||||||
|
assert len(lines) == 1
|
||||||
|
line = lines[0]
|
||||||
|
assert line.startswith(". ")
|
||||||
|
assert line[2:] == 'test/mod:a.b'
|
||||||
|
|
||||||
|
def test_collection_report(self):
|
||||||
|
bus = EventBus()
|
||||||
|
reslog = resultlog.ResultLog(bus, None)
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
colitem = make_item('proj/test', 'proj/test/mod', 'A', None)
|
||||||
|
outcome=OutcomeRepr('execute', '', '')
|
||||||
|
rep_ev = CollectionReport(colitem, object(), passed=outcome)
|
||||||
|
|
||||||
|
bus.notify(rep_ev)
|
||||||
|
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
assert not entry
|
||||||
|
|
||||||
|
reslog.logfile = StringIO.StringIO()
|
||||||
|
outcome=OutcomeRepr('execute', 'F', 'Some Error')
|
||||||
|
rep_ev = CollectionReport(colitem, object(), failed=outcome)
|
||||||
|
|
||||||
|
bus.notify(rep_ev)
|
||||||
|
|
||||||
|
lines = reslog.logfile.getvalue().splitlines()
|
||||||
|
assert len(lines) == 2
|
||||||
|
assert lines[0] == 'F test/mod:A'
|
||||||
|
|
||||||
|
def test_internal_exception(self):
|
||||||
|
# they are produced for example by a teardown failing
|
||||||
|
# at the end of the run
|
||||||
|
bus = EventBus()
|
||||||
|
reslog = resultlog.ResultLog(bus, StringIO.StringIO())
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise ValueError
|
||||||
|
except ValueError:
|
||||||
|
excinfo = py.code.ExceptionInfo()
|
||||||
|
|
||||||
|
internal = InternalException(excinfo)
|
||||||
|
|
||||||
|
bus.notify(internal)
|
||||||
|
|
||||||
|
entry = reslog.logfile.getvalue()
|
||||||
|
entry_lines = entry.splitlines()
|
||||||
|
|
||||||
|
assert entry_lines[0].startswith('! ')
|
||||||
|
assert os.path.basename(__file__)[:-1] in entry_lines[0] #.py/.pyc
|
||||||
|
assert entry_lines[-1][0] == ' '
|
||||||
|
assert 'ValueError' in entry
|
Loading…
Reference in New Issue