288 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
| 
 | |
| """ Rest reporting stuff
 | |
| """
 | |
| 
 | |
| import py
 | |
| import sys
 | |
| from StringIO import StringIO
 | |
| from py.__.test.reporter import AbstractReporter
 | |
| from py.__.test import event
 | |
| from py.__.rest.rst import *
 | |
| 
 | |
| class RestReporter(AbstractReporter):
 | |
|     linkwriter = None
 | |
| 
 | |
|     def __init__(self, config, hosts):
 | |
|         super(RestReporter, self).__init__(config, hosts)
 | |
|         self.rest = Rest()
 | |
|         self.traceback_num = 0
 | |
|         self.failed = dict([(host, 0) for host in hosts])
 | |
|         self.skipped = dict([(host, 0) for host in hosts])
 | |
|         self.passed = dict([(host, 0) for host in hosts])
 | |
|     
 | |
|     def get_linkwriter(self):
 | |
|         if self.linkwriter is None:
 | |
|             try:
 | |
|                 self.linkwriter = self.config.getvalue('linkwriter')
 | |
|             except KeyError:
 | |
|                 print >>sys.stderr, ('no linkwriter configured, using default '
 | |
|                                      'one')
 | |
|                 self.linkwriter = RelLinkWriter()
 | |
|         return self.linkwriter
 | |
|         
 | |
|     def report_unknown(self, what):
 | |
|         if self.config.option.verbose:
 | |
|             self.add_rest(Paragraph("Unknown report: %s" % what))
 | |
| 
 | |
|     def gethost(self, item):
 | |
|         if item.channel:
 | |
|             return item.channel.gateway.host
 | |
|         return self.hosts[0]
 | |
| 
 | |
|     def report_SendItem(self, item):
 | |
|         address = self.gethost(item).hostname
 | |
|         if self.config.option.verbose:
 | |
|             self.add_rest(Paragraph('sending item %s to %s' % (item.item,
 | |
|                                                                address)))
 | |
| 
 | |
|     def report_HostRSyncing(self, item):
 | |
|         self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10],
 | |
|                                                         item.host.relpath)))
 | |
| 
 | |
|     def _host_ready(self, item):
 | |
|         self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],)))
 | |
| 
 | |
|     def report_TestStarted(self, event):
 | |
|         txt = "Running tests on hosts: %s" % ", ".join([i.hostname for i in
 | |
|                                                         event.hosts])
 | |
|         self.add_rest(Title(txt, abovechar='=', belowchar='='))
 | |
|         self.timestart = event.timestart
 | |
| 
 | |
|     def report_TestTestrunFinish(self, item):
 | |
|         self.timeend = item.timeend
 | |
|         self.summary()
 | |
|         return len(self.failed_tests_outcome) > 0
 | |
| 
 | |
|     def report_ImmediateFailure(self, item):
 | |
|         pass
 | |
| 
 | |
|     def report_HostGatewayReady(self, item):
 | |
|         self.to_rsync[item.host] = len(item.roots)
 | |
| 
 | |
|     def report_ItemStart(self, event):
 | |
|         item = event.item
 | |
|         if isinstance(item, py.test.collect.Module):
 | |
|             lgt = len(list(item._tryiter()))
 | |
|             lns = item.listnames()[1:]
 | |
|             name = "/".join(lns)
 | |
|             link = self.get_linkwriter().get_link(self.get_rootpath(item),
 | |
|                                                   item.fspath)
 | |
|             if link:
 | |
|                 name = Link(name, link)
 | |
|             txt = 'Testing module %s (%d items)' % (name, lgt)
 | |
|             self.add_rest(Title('Testing module', name, '(%d items)' % (lgt,),
 | |
|                                 belowchar='-'))
 | |
|     
 | |
|     def get_rootpath(self, item):
 | |
|         root = item.parent
 | |
|         while root.parent is not None:
 | |
|             root = root.parent
 | |
|         return root.fspath
 | |
| 
 | |
|     def print_summary(self, total, skipped_str, failed_str):
 | |
|         self.skips()
 | |
|         self.failures()
 | |
| 
 | |
|         txt = "%d tests run%s%s in %.2fs (rsync: %.2f)" % \
 | |
|             (total, skipped_str, failed_str, self.timeend - self.timestart,
 | |
|              self.timersync - self.timestart)
 | |
|         self.add_rest(Title(txt, belowchar='-'))
 | |
| 
 | |
|         # since we're rendering each item, the links haven't been rendered
 | |
|         # yet
 | |
|         self.out.write(self.rest.render_links())
 | |
| 
 | |
|     def report_ItemFinish(self, event):
 | |
|         host = self.gethost(event)
 | |
|         if event.outcome.passed:
 | |
|             status = [Strong("PASSED")]
 | |
|             self.passed[host] += 1
 | |
|         elif event.outcome.skipped:
 | |
|             status = [Strong("SKIPPED")]
 | |
|             self.skipped_tests_outcome.append(event)
 | |
|             self.skipped[host] += 1
 | |
|         else:
 | |
|             status = [Strong("FAILED"),
 | |
|                       InternalLink("traceback%d" % self.traceback_num)]
 | |
|             self.traceback_num += 1
 | |
|             self.failed[host] += 1
 | |
|             self.failed_tests_outcome.append(event)
 | |
|             # we'll take care of them later
 | |
|         itempath = self.get_path_from_item(event.item)
 | |
|         status.append(Text(itempath))
 | |
|         hostname = host.hostname
 | |
|         self.add_rest(ListItem(Text("%10s:" % (hostname[:10],)), *status))
 | |
| 
 | |
|     def skips(self):
 | |
|         # XXX hrmph, copied code
 | |
|         texts = {}
 | |
|         for event in self.skipped_tests_outcome:
 | |
|             colitem = event.item
 | |
|             if isinstance(event, event.ItemFinish):
 | |
|                 outcome = event.outcome
 | |
|                 text = outcome.skipped
 | |
|                 itemname = self.get_item_name(event, colitem)
 | |
|             elif isinstance(event, event.DeselectedItem):
 | |
|                 text = str(event.excinfo.value)
 | |
|                 itemname = "/".join(colitem.listnames())
 | |
|             if text not in texts:
 | |
|                 texts[text] = [itemname]
 | |
|             else:
 | |
|                 texts[text].append(itemname)
 | |
|         if texts:
 | |
|             self.add_rest(Title('Reasons for skipped tests:', belowchar='+'))
 | |
|             for text, items in texts.items():
 | |
|                 for item in items:
 | |
|                     self.add_rest(ListItem('%s: %s' % (item, text)))
 | |
| 
 | |
|     def get_host(self, event):
 | |
|         try:
 | |
|             return event.channel.gateway.host
 | |
|         except AttributeError:
 | |
|             return None
 | |
| 
 | |
|     def failures(self):
 | |
|         self.traceback_num = 0
 | |
|         tbstyle = self.config.option.tbstyle
 | |
|         if self.failed_tests_outcome:
 | |
|             self.add_rest(Title('Exceptions:', belowchar='+'))
 | |
|         for i, event in enumerate(self.failed_tests_outcome):
 | |
|             if i > 0:
 | |
|                 self.add_rest(Transition())
 | |
|             if isinstance(event, event.ItemFinish):
 | |
|                 host = self.get_host(event)
 | |
|                 itempath = self.get_path_from_item(event.item)
 | |
|                 root = self.get_rootpath(event.item)
 | |
|                 link = self.get_linkwriter().get_link(root, event.item.fspath)
 | |
|                 t = Title(belowchar='+')
 | |
|                 if link:
 | |
|                     t.add(Link(itempath, link))
 | |
|                 else:
 | |
|                     t.add(Text(itempath))
 | |
|                 if host:
 | |
|                     t.add(Text('on %s' % (host.hostname,)))
 | |
|                 self.add_rest(t)
 | |
|                 if event.outcome.signal:
 | |
|                     self.repr_signal(event.item, event.outcome)
 | |
|                 else:
 | |
|                     self.repr_failure(event.item, event.outcome, tbstyle)
 | |
|             else:
 | |
|                 itempath = self.get_path_from_item(event.item)
 | |
|                 root = self.get_rootpath(event.item)
 | |
|                 link = self.get_linkwriter().get_link(root, event.item.fspath)
 | |
|                 t = Title(abovechar='+', belowchar='+')
 | |
|                 if link:
 | |
|                     t.add(Link(itempath, link))
 | |
|                 else:
 | |
|                     t.add(Text(itempath))
 | |
|                 out = outcome.Outcome(excinfo=event.excinfo)
 | |
|                 self.repr_failure(event.item,
 | |
|                                   outcome.ReprOutcome(out.make_repr()),
 | |
|                                   tbstyle)
 | |
| 
 | |
|     def repr_signal(self, item, outcome):
 | |
|         signal = outcome.signal
 | |
|         self.add_rest(Title('Received signal: %d' % (outcome.signal,),
 | |
|                             abovechar='+', belowchar='+'))
 | |
|         if outcome.stdout.strip():
 | |
|             self.add_rest(Paragraph('Captured process stdout:'))
 | |
|             self.add_rest(LiteralBlock(outcome.stdout))
 | |
|         if outcome.stderr.strip():
 | |
|             self.add_rest(Paragraph('Captured process stderr:'))
 | |
|             self.add_rest(LiteralBlock(outcome.stderr))
 | |
| 
 | |
|     def repr_failure(self, item, outcome, style):
 | |
|         excinfo = outcome.excinfo
 | |
|         traceback = excinfo.traceback
 | |
|         if not traceback:
 | |
|             self.add_rest(Paragraph('empty traceback from item %r' % (item,)))
 | |
|             return
 | |
|         self.repr_traceback(item, excinfo, traceback, style)
 | |
|         if outcome.stdout:
 | |
|             self.add_rest(Title('Captured process stdout:', abovechar='+',
 | |
|                                 belowchar='+'))
 | |
|             self.add_rest(LiteralBlock(outcome.stdout))
 | |
|         if outcome.stderr:
 | |
|             self.add_rest(Title('Captured process stderr:', abovechar='+',
 | |
|                                 belowchar='+'))
 | |
|             self.add_rest(LiteralBlock(outcome.stderr))
 | |
| 
 | |
|     def repr_traceback(self, item, excinfo, traceback, style):
 | |
|         root = self.get_rootpath(item)
 | |
|         self.add_rest(LinkTarget('traceback%d' % self.traceback_num, ""))
 | |
|         self.traceback_num += 1
 | |
|         if style == 'long':
 | |
|             for entry in traceback:
 | |
|                 link = self.get_linkwriter().get_link(root,
 | |
|                                             py.path.local(entry.path))
 | |
|                 if link:
 | |
|                     self.add_rest(Title(Link(entry.path, link),
 | |
|                                         'line %d' % (entry.lineno,),
 | |
|                                         belowchar='+', abovechar='+'))
 | |
|                 else:
 | |
|                     self.add_rest(Title('%s line %d' % (entry.path,
 | |
|                                                         entry.lineno,),
 | |
|                                         belowchar='+', abovechar='+'))
 | |
|                 self.add_rest(LiteralBlock(self.prepare_source(entry.relline,
 | |
|                                                                entry.source)))
 | |
|         elif style == 'short':
 | |
|             text = []
 | |
|             for entry in traceback:
 | |
|                 text.append('%s line %d' % (entry.path, entry.lineno))
 | |
|                 text.append('  %s' % (entry.source.strip(),))
 | |
|             self.add_rest(LiteralBlock('\n'.join(text)))
 | |
|         self.add_rest(Title(excinfo.typename, belowchar='+'))
 | |
|         self.add_rest(LiteralBlock(excinfo.value))
 | |
| 
 | |
|     def prepare_source(self, relline, source):
 | |
|         text = []
 | |
|         for num, line in enumerate(source.split('\n')):
 | |
|             if num == relline:
 | |
|                 text.append('>>> %s' % (line,))
 | |
|             else:
 | |
|                 text.append('    %s' % (line,))
 | |
|         return '\n'.join(text)
 | |
| 
 | |
|     def add_rest(self, item):
 | |
|         self.rest.add(item)
 | |
|         self.out.write('%s\n\n' % (item.text(),))
 | |
| 
 | |
|     def get_path_from_item(self, item):
 | |
|         lns = item.listnames()[1:]
 | |
|         for i, ln in enumerate(lns):
 | |
|             if i > 0 and ln != '()':
 | |
|                 lns[i] = '/%s' % (ln,)
 | |
|         itempath = ''.join(lns)
 | |
|         return itempath
 | |
| 
 | |
| class AbstractLinkWriter(object):
 | |
|     def get_link(self, base, path):
 | |
|         pass
 | |
| 
 | |
| class NoLinkWriter(AbstractLinkWriter):
 | |
|     def get_link(self, base, path):
 | |
|         return ''
 | |
| 
 | |
| class LinkWriter(AbstractLinkWriter):
 | |
|     def __init__(self, baseurl):
 | |
|         self.baseurl = baseurl
 | |
| 
 | |
|     def get_link(self, base, path):
 | |
|         relpath = path.relto(base)
 | |
|         return self.baseurl + relpath
 | |
| 
 | |
| class RelLinkWriter(AbstractLinkWriter):
 | |
|     def get_link(self, base, path):
 | |
|         return path.relto(base)
 | |
| 
 |