[py-svn] r34885 - in py/dist/py/test: . rsession rsession/testing

guido at codespeak.net guido at codespeak.net
Thu Nov 23 11:43:28 CET 2006


Author: guido
Date: Thu Nov 23 11:43:24 2006
New Revision: 34885

Modified:
   py/dist/py/test/defaultconftest.py
   py/dist/py/test/rsession/rest.py
   py/dist/py/test/rsession/testing/test_rest.py
Log:
Added link writer mechanism to create links for paths in the ReST, added code
to display exceptions and signals.


Modified: py/dist/py/test/defaultconftest.py
==============================================================================
--- py/dist/py/test/defaultconftest.py	(original)
+++ py/dist/py/test/defaultconftest.py	Thu Nov 23 11:43:24 2006
@@ -10,6 +10,8 @@
 
 additionalinfo = None
 
+linkwriter = py.test.rest.RelLinkWriter()
+
 def adddefaultoptions():
     Option = py.test.Config.Option
     py.test.Config.addoptions('general options',

Modified: py/dist/py/test/rsession/rest.py
==============================================================================
--- py/dist/py/test/rsession/rest.py	(original)
+++ py/dist/py/test/rsession/rest.py	Thu Nov 23 11:43:24 2006
@@ -6,9 +6,21 @@
 import sys
 from StringIO import StringIO
 from py.__.test.rsession.reporter import AbstractReporter
+from py.__.test.rsession import report
 from py.__.rest.rst import *
 
 class RestReporter(AbstractReporter):
+    linkwriter = None
+
+    def __init__(self, *args, **kwargs):
+        super(RestReporter, self).__init__(*args, **kwargs)
+        self.rest = Rest()
+    
+    def get_linkwriter(self):
+        if self.linkwriter is None:
+            self.linkwriter = self.config.getinitialvalue('linkwriter')
+        return self.linkwriter
+        
     def report_unknown(self, what):
         self.add_rest(Paragraph("Unknown report: %s" % what))
 
@@ -41,10 +53,21 @@
         item = event.item
         if isinstance(item, py.test.collect.Module):
             lgt = len(list(item.tryiter()))
-            lns = item.listnames()
+            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(txt, belowchar='-'))
+            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):
         txt = "%d tests run%s%s in %.2fs (rsync: %.2f)" % \
@@ -52,6 +75,13 @@
              self.timersync - self.timestart)
         self.add_rest(Title(txt, belowchar='-'))
 
+        self.skips()
+        self.failures()
+
+        # since we're rendering each item, the links haven't been rendered
+        # yet
+        self.out.write(self.rest.render_links())
+
     def report_ReceivedItemOutcome(self, event):
         host = self.get_host(event)
         if event.outcome.passed:
@@ -66,20 +96,159 @@
             self.failed[host] += 1
             self.failed_tests_outcome.append(event)
             # we'll take care of them later
-        lns = event.item.listnames()[1:]
-        for i, ln in enumerate(lns):
-            if i > 0 and ln != '()':
-                lns[i] = '/%s' % (ln,)
-        itempath = ''.join(lns)
+        itempath = self.get_path_from_item(event.item)
         self.add_rest(ListItem(Text("%10s:" % (host[:10],)), Strong(status),
                                Text(itempath)))
-        if event.outcome.excinfo:
-            excinfo = event.outcome.excinfo
-            self.add_rest(Paragraph('exception:', Strong(excinfo.typename)))
-            self.add_rest(LiteralBlock(excinfo.value))
-            self.add_rest(LiteralBlock('\n'.join([str(x) for x in
-                                                  excinfo.traceback])))
+
+    def skips(self):
+        # XXX hrmph, copied code
+        texts = {}
+        for event in self.skipped_tests_outcome:
+            colitem = event.item
+            if isinstance(event, report.ReceivedItemOutcome):
+                outcome = event.outcome
+                text = outcome.skipped
+                itemname = self.get_item_name(event, colitem)
+            elif isinstance(event, report.SkippedTryiter):
+                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 failures(self):
+        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, report.ReceivedItemOutcome):
+                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))
+                t.add(Text('on %s' % (host,)))
+                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)
+        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)
+

Modified: py/dist/py/test/rsession/testing/test_rest.py
==============================================================================
--- py/dist/py/test/rsession/testing/test_rest.py	(original)
+++ py/dist/py/test/rsession/testing/test_rest.py	Thu Nov 23 11:43:24 2006
@@ -4,7 +4,8 @@
 
 import py
 from py.__.test.rsession.testing.test_reporter import AbstractTestReporter
-from py.__.test.rsession.rest import RestReporter
+from py.__.test.rsession import report
+from py.__.test.rsession.rest import RestReporter, NoLinkWriter
 from py.__.rest.rst import *
 
 class RestTestReporter(RestReporter):
@@ -16,6 +17,12 @@
     def __init__(self, **kwargs):
         self.__dict__.update(kwargs)
 
+class FakeOutcome(Container, report.ReceivedItemOutcome):
+    pass
+
+class FakeTryiter(Container, report.SkippedTryiter):
+    pass
+
 class TestRestUnits(object):
     def setup_method(self, method):
         config, args = py.test.Config.parse(["some_sub"])
@@ -24,6 +31,7 @@
                                                                 ['localhost'])
         method.im_func.func_globals['stdout'] = s = py.std.StringIO.StringIO()
         r.out = s # XXX will need to become a real reporter some time perhaps?
+        r.linkwriter = NoLinkWriter()
 
     def test_report_unknown(self):
         reporter.report_unknown('foo')
@@ -63,13 +71,15 @@
     
     def test_report_ItemStart(self):
         class FakeModule(py.test.collect.Module):
-            def __init__(self):
-                pass
+            def __init__(self, parent):
+                self.parent = parent
+                self.fspath = py.path.local('.')
             def tryiter(self):
                 return ['test_foo', 'test_bar']
             def listnames(self):
-                return ['foo', 'bar.py']
-        event = Container(item=FakeModule())
+                return ['package', 'foo', 'bar.py']
+        parent = Container(parent=None, fspath=py.path.local('.'))
+        event = Container(item=FakeModule(parent))
         reporter.report_ItemStart(event)
         assert stdout.getvalue() == """\
 Testing module foo/bar.py (2 items)
@@ -105,31 +115,148 @@
                                      'foo.py/bar()/baz\n\n')
 
     def test_ReceivedItemOutcome_FAILED(self):
-        excinfo = Container(typename='FooError', value='a foo has occurred',
-                            traceback=['  in foo in line 1, in foo:',
-                                       '    bar()',
-                                       '  in bar in line 4, in bar:',
-                                       ('    raise FooError("a foo has '
-                                        'occurred")')])
-        outcome = Container(passed=False, skipped=False, excinfo=excinfo)
+        outcome = Container(passed=False, skipped=False)
         item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'])
         event = Container(channel=False, outcome=outcome, item=item)
         reporter.report_ReceivedItemOutcome(event)
         assert stdout.getvalue() == """\
 * localhost\: **FAILED** foo.py/bar()/baz
 
-exception\: **FooError**
+"""
+    
+    def test_skips(self):
+        reporter.skips()
+        assert stdout.getvalue() == ''
+        reporter.skipped_tests_outcome = [
+            FakeOutcome(outcome=Container(skipped='problem X'),
+                        item=Container(listnames=lambda: ['foo', 'bar.py'])),
+            FakeTryiter(excinfo=Container(value='problem Y'),
+                        item=Container(listnames=lambda: ['foo', 'baz.py']))]
+        reporter.skips()
+        assert stdout.getvalue() == """\
+Reasons for skipped tests\:
++++++++++++++++++++++++++++
+
+* foo/bar.py\: problem X
+
+* foo/baz.py\: problem Y
+
+"""
+
+    def test_failures(self):
+        parent = Container(parent=None, fspath=py.path.local('.'))
+        reporter.failed_tests_outcome = [
+            FakeOutcome(
+                outcome=Container(
+                    signal=False,
+                    excinfo=Container(
+                        typename='FooError',
+                        value='A foo has occurred',
+                        traceback=[
+                            Container(
+                                path='foo/bar.py',
+                                lineno=1,
+                                relline=1,
+                                source='foo()',
+                            ),
+                            Container(
+                                path='foo/baz.py',
+                                lineno=4,
+                                relline=1,
+                                source='raise FooError("A foo has occurred")',
+                            ),
+                        ]
+                    ),
+                    stdout='',
+                    stderr='',
+                ),
+                item=Container(
+                    listnames=lambda: ['package', 'foo', 'bar.py',
+                                       'baz', '()'],
+                    parent=parent,
+                    fspath=py.path.local('.'),
+                ),
+                channel=None,
+            ),
+        ]
+        reporter.config.option.tbstyle = 'no'
+        reporter.failures()
+        assert stdout.getvalue() == """\
+Exceptions\:
+++++++++++++
+
+foo/bar.py/baz() on localhost
++++++++++++++++++++++++++++++
+
+FooError
+++++++++
 
 ::
 
-  a foo has occurred
+  A foo has occurred
+
+"""
+
+        reporter.config.option.tbstyle = 'short'
+        stdout.seek(0)
+        stdout.truncate()
+        reporter.failures()
+        assert stdout.getvalue() == """\
+Exceptions\:
+++++++++++++
+
+foo/bar.py/baz() on localhost
++++++++++++++++++++++++++++++
+
+::
+
+  foo/bar.py line 1
+    foo()
+  foo/baz.py line 4
+    raise FooError("A foo has occurred")
+
+FooError
+++++++++
 
 ::
 
-    in foo in line 1, in foo:
-      bar()
-    in bar in line 4, in bar:
-      raise FooError("a foo has occurred")
+  A foo has occurred
+
+"""
+
+        reporter.config.option.tbstyle = 'long'
+        stdout.seek(0)
+        stdout.truncate()
+        reporter.failures()
+        stdout.getvalue() == """\
+Exceptions\:
+++++++++++++
+
+foo/bar.py/baz() on localhost
++++++++++++++++++++++++++++++
+
++++++++++++++++++
+foo/bar.py line 1
++++++++++++++++++
+
+::
+
+      foo()
+
++++++++++++++++++
+foo/baz.py line 4
++++++++++++++++++
+
+::
+
+      raise FooError("A foo has occurred")
+
+FooError
+++++++++
+
+::
+
+  A foo has occurred
 
 """
 
@@ -141,34 +268,16 @@
     
     def test_report_received_item_outcome(self):
         val = self.report_received_item_outcome()
-        expected = """- localhost\\: FAILED py test rsession testing test\\_slave.py funcpass
-- localhost\\: SKIPPED py test rsession testing test\\_slave.py funcpass
-- localhost\\: FAILED py test rsession testing test\\_slave.py funcpass
-- localhost\\: PASSED py test rsession testing test\\_slave.py funcpass
-"""
-        print repr(val)
-        expected_start = """\
+        expected = """\
 * localhost\: **FAILED** py/test/rsession/testing/test\_slave.py/funcpass
 
 * localhost\: **SKIPPED** py/test/rsession/testing/test\_slave.py/funcpass
 
 * localhost\: **FAILED** py/test/rsession/testing/test\_slave.py/funcpass
 
-exception\\: **ZeroDivisionError**
-
-::
-
-  integer division or modulo by zero
-
-::
-
-"""
-
-        expected_end = """
-
 * localhost\: **PASSED** py/test/rsession/testing/test\_slave.py/funcpass
 
 """
-        assert val.startswith(expected_start)
-        assert val.endswith(expected_end)
+        print val
+        assert val == expected
 



More information about the pytest-commit mailing list