[pypy-svn] r58178 - in pypy/build/bot2/pypybuildbot: . test

pedronis at codespeak.net pedronis at codespeak.net
Tue Sep 16 16:14:23 CEST 2008


Author: pedronis
Date: Tue Sep 16 16:14:21 2008
New Revision: 58178

Modified:
   pypy/build/bot2/pypybuildbot/summary.py
   pypy/build/bot2/pypybuildbot/test/test_summary.py
Log:
(iko, pedronis)

basic summary with links to tracebacks is now working



Modified: pypy/build/bot2/pypybuildbot/summary.py
==============================================================================
--- pypy/build/bot2/pypybuildbot/summary.py	(original)
+++ pypy/build/bot2/pypybuildbot/summary.py	Tue Sep 16 16:14:21 2008
@@ -1,22 +1,29 @@
+import urllib
+
 import py
 html = py.xml.html
 
 from buildbot.status.web.base import HtmlResource
 
-# xxx caching?
 class RevisionOutcomeSet(object):
 
-    def __init__(self, rev):
+    def __init__(self, rev, key=None):
         self.revision = rev
+        self.key = key
         self._outcomes = {}
         self.failed = set()
         self.skipped = set()
-        # xxx failure tracebacks
+        self.longreprs = {}
 
-    def populate_one(self, name, shortrepr):
-        namekey = name.split(':', 1) # xxx not always the correct thing
-        if namekey[0].endswith('.py'):
-            namekey[0] = namekey[0][:-3].replace('/', '.')
+    def populate_one(self, name, shortrepr, longrepr=None):
+        if shortrepr == '!':
+            namekey = [name, '']
+        else:        
+            namekey = name.split(':', 1)
+            if namekey[0].endswith('.py'):
+                namekey[0] = namekey[0][:-3].replace('/', '.')
+            if len(namekey) == 1:
+                namekey.append('')
 
         namekey = tuple(namekey)
         self._outcomes[namekey] = shortrepr
@@ -27,17 +34,80 @@
         else:
             self.failed.add(namekey)
 
+        if longrepr:
+            self.longreprs[namekey] = longrepr
+
     def populate(self, log):
+        kind = None
+        def add_one():
+            if kind is not None:
+                self.populate_one(name, kind, ''.join(longrepr))        
         for line in log.readlines():
-            kind = line[0]
-            if kind == ' ':
+            first = line[0]
+            if first == ' ':
+                longrepr.append(line[1:])
                 continue
+            add_one()
+            kind = first
             name = line[2:].rstrip()
-            self.populate_one(name, kind)
+            longrepr = []
+        add_one()
 
     def get_outcome(self, namekey):
         return self._outcomes[namekey]
 
+    def get_key_namekey(self, namekey):
+        return (self.key, namekey)
+
+    def get_longrepr(self, namekey):
+        return self.longreprs.get(namekey, '')
+
+class RevisionOutcomeSetCache(object):
+    CACHESIZE = 10
+
+    def __init__(self):
+        self._outcome_sets = {}
+        self._lru = []
+
+    def _load_outcome_set(self, status, key):
+        builderName, buildNumber = key
+        builderStatus = status.getBuilder(builderName)
+        build = builderStatus.getBuild(buildNumber)
+
+        rev = int(build.getProperty("got_revision"))
+        log = None
+        for step in build.getSteps():
+            candLogs = [log for log in step.getLogs()
+                        if log.getName() == "pytestLog"]
+            if candLogs:
+                log = candLogs[0]
+                break
+
+        outcome_set = RevisionOutcomeSet(rev, key) 
+        if log is None or not log.hasContents():
+            outcome_set.populate_one('<run>', '!', "no log from the test run")
+        else:
+            outcome_set.populate(log)
+        return outcome_set
+        
+    def get(self, status, key):
+        try:
+            self._lru.remove(key)
+        except ValueError:
+            pass
+        self._lru.append(key)
+        try:
+            return self._outcome_sets[key]
+        except KeyError:
+            pass
+        if len(self._lru) > self.CACHESIZE:
+            dead_key = self._lru.pop(0)
+            self._outcome_sets.pop(dead_key, None)
+        outcome_set = self._load_outcome_set(status, key)
+        self._outcome_sets[key] = outcome_set
+        return outcome_set
+
+outcome_set_cache = RevisionOutcomeSetCache()
 
 class GatherOutcomeSet(object):
 
@@ -47,7 +117,6 @@
         self._skipped = None
         self.revision = map.values()[0].revision
         
-
     @property
     def failed(self):
         if self._failed is None:
@@ -68,6 +137,12 @@
 
     def get_outcome(self, namekey):
         return self.map[namekey[0]].get_outcome(namekey[1:])
+
+    def get_key_namekey(self, namekey):
+        return self.map[namekey[0]].get_key_namekey(namekey[1:])
+
+    def get_longrepr(self, namekey):
+        return self.map[namekey[0]].get_longrepr(namekey[1:])
          
 # ________________________________________________________________
 
@@ -88,6 +163,17 @@
     def __init__(self):
         self.sections = []
 
+    def make_longrepr_url_for(self, outcome_set, namekey):
+        cachekey, namekey = outcome_set.get_key_namekey(namekey)
+        parms={
+            'builder': cachekey[0],
+            'build': cachekey[1],
+            'mod': namekey[0],
+            'testname': namekey[1]
+            }
+        qs = urllib.urlencode(parms)
+        return "/summary/longrepr?" + qs
+
     def add_section(self, outcome_sets):
         by_rev = sorted((outcome_set.revision, outcome_set) for outcome_set
                          in outcome_sets)
@@ -97,9 +183,9 @@
         for rev, outcome_set in by_rev:
             count_failures = len(outcome_set.failed)
             count_skipped = len(outcome_set.skipped)
-            lines.append("%s %d" % (bars(),rev))
-        lines.append(bars())
-
+            lines.append(["%s %d" % (bars(),rev), "\n"])
+        lines.append([bars(), "\n"])
+        
         failed = set()
         for rev, outcome_set in by_rev:            
             failed.update(outcome_set.failed)
@@ -110,20 +196,72 @@
             line = []
             for rev, outcome_set in by_rev:
                 letter = outcome_set.get_outcome(failure)
-                line.append(" %s" % letter)
+                if outcome_set.get_longrepr(failure):
+                    longrepr_url = self.make_longrepr_url_for(outcome_set,
+                                                              failure)
+                    line.append([" ",html.a(letter, href=longrepr_url)])
+                else:
+                    line.append(" %s" % letter)
             for width, key in zip(colwidths, failure):
                 line.append("  %-*s" % (width, key))
-            lines.append(''.join(line))
+            lines.append(line)
+            lines.append("\n")
         
-        section = html.pre('\n'.join(lines))
+        section = html.pre(lines)
         self.sections.append(section)
 
     def render(self):
         body_html = html.div(self.sections)
         return body_html.unicode()
 
+class LongRepr(HtmlResource):
+
+    def get_namekey(self, request):
+        mod = request.args.get('mod', [])
+        if not mod:
+            mod = None
+        else:
+            mod = mod[0]
+        
+        testname = request.args.get('testname', [])
+        if testname:
+            testname = testname[0]
+        else:
+            testname = ''
+
+        return (mod, testname)
+        
+    def getTitle(self, request):
+        mod, testname = self.get_namekey(request)
+        if mod is None:
+            return "no such test"
+        return "%s %s" % (mod, testname)        
+
+    def body(self, request):
+        builder = request.args.get('builder', [])
+        build = request.args.get('build', [])
+        if not builder or not build:
+            return "no such build"
+        builderName = builder[0]
+        buildNumber = int(build[0])
+
+        outcome_set = outcome_set_cache.get(self.getStatus(request),
+                                            (builderName,
+                                             buildNumber))
+
+        namekey = self.get_namekey(request)
+
+        longrepr = outcome_set.get_longrepr(namekey)
+
+        return html.pre(longrepr).unicode()
+
 
 class Summary(HtmlResource):
+    title="Summary" # xxx
+
+    def __init__(self):
+        HtmlResource.__init__(self)
+        self.putChild('longrepr', LongRepr())
 
     def recentRevisions(self, request):
         # xxx branches
@@ -135,13 +273,10 @@
                 rev = int(build.getProperty("got_revision"))
                 revBuilds = revs.setdefault(rev, {})
                 if builderName not in revBuilds: # pick the most recent or ?
-                    # xxx hack, go through the steps and make sure
-                    # the log is there
-                    log = [log for log in build.getLogs()
-                           if log.getName() == "pytestLog"][0]
-                    outcome_set = RevisionOutcomeSet(rev)
-                    outcome_set.populate(log)
+                    key = (builderName, build.getNumber())
+                    outcome_set = outcome_set_cache.get(status, key)
                     revBuilds[builderName] = outcome_set
+                            
         return revs
                             
     def body(self, request):

Modified: pypy/build/bot2/pypybuildbot/test/test_summary.py
==============================================================================
--- pypy/build/bot2/pypybuildbot/test/test_summary.py	(original)
+++ pypy/build/bot2/pypybuildbot/test/test_summary.py	Tue Sep 16 16:14:21 2008
@@ -4,9 +4,10 @@
 class TestOutcomes(object):
 
     def test_populate(self):
-        rev_outcome_set = summary.RevisionOutcomeSet(50000)
+        rev_outcome_set = summary.RevisionOutcomeSet(50000, ('foo', 40))
 
         assert rev_outcome_set.revision == 50000
+        assert rev_outcome_set.key == ('foo', 40)
 
         log = StringIO("""F a/b.py:test_one
 . a/b.py:test_two
@@ -20,24 +21,119 @@
 
         res = rev_outcome_set.get_outcome(("a.b", "test_one"))
         assert res == 'F'
+        key_namekey = rev_outcome_set.get_key_namekey(("a.b", "test_one"))
+        assert key_namekey == (('foo', 40), ("a.b", "test_one"))
 
         res = rev_outcome_set.get_outcome(("a.b", "test_three"))
         assert res == 's'
+        key_namekey = rev_outcome_set.get_key_namekey(("a.b", "test_three"))
+        assert key_namekey == (('foo', 40), ("a.b", "test_three"))
 
         res = rev_outcome_set.get_outcome(("a.b", "test_two"))
         assert res == '.'
 
+    def test_populate_from_empty(self):
+        rev_outcome_set = summary.RevisionOutcomeSet(0)
+        log = StringIO("")
+        rev_outcome_set.populate(log)
+        
+    def test_populate_longrepr(self):
+        rev_outcome_set = summary.RevisionOutcomeSet(50000)
+        log = StringIO("""F a/b.py:test_one
+ some
+ traceback
+. a/b.py:test_two
+s a/b.py:test_three
+ some skip
+""")
+        
+        rev_outcome_set.populate(log)
+
+        assert len(rev_outcome_set.skipped) == 1
+        assert len(rev_outcome_set.failed) == 1
+
+        assert rev_outcome_set.longreprs == {
+("a.b","test_three"): "some skip\n",
+("a.b", "test_one"): "some\ntraceback\n"
+            }
+
+        res = rev_outcome_set.get_longrepr(("a.b", "test_two"))
+        assert res == ''
+
+        res = rev_outcome_set.get_longrepr(("a.b", "test_one"))
+        assert res == "some\ntraceback\n"
+
+    def test_populate_special(self):
+        rev_outcome_set = summary.RevisionOutcomeSet(50000)
+        log = StringIO("""F a/b.py
+s a/c.py
+! <run>
+! /a/b/c.py:92
+""")
+        
+        rev_outcome_set.populate(log)
+
+        assert rev_outcome_set.failed == set([
+            ("a.b", ''),
+            ("<run>", ''),
+            ("/a/b/c.py:92", '')])
+
+        assert rev_outcome_set.skipped == set([
+            ("a.c", '')])
+        
+
+    def test_RevisionOutcomeSetCache(self):
+        cache = summary.RevisionOutcomeSetCache()
+        cache.CACHESIZE = 3
+        calls = []
+        def load(x, y):
+            calls.append(y)
+            return y
+        
+        cache._load_outcome_set = load
+
+        res = cache.get('status', 'a')
+        assert res == 'a'
+        cache.get('status', 'b')
+        cache.get('status', 'c')
+
+        assert calls == ['a', 'b', 'c']
+
+        cache.get('status', 'a')
+        cache.get('status', 'b')
+        res = cache.get('status', 'c')
+        assert res == 'c'
+        
+        assert calls == ['a', 'b', 'c']
+
+        calls = []
+        res = cache.get('status', 'd')
+        assert res == 'd'
+        assert cache.get('status', 'c') == 'c'
+        assert cache.get('status', 'b') == 'b'        
+        assert calls == ['d']
+
+        res = cache.get('status', 'a')
+        assert res == 'a'
+
+        assert calls == ['d', 'a']
+
     def test_GatherOutcomeSet(self):
-        rev_outcome_set_foo = summary.RevisionOutcomeSet(50000)
+        key_foo = ('foo', 3)
+        rev_outcome_set_foo = summary.RevisionOutcomeSet(50000, key_foo)
         log = StringIO("""F a/b.py:test_one
+ some
+ traceback
 . a/b.py:test_two
 s a/b.py:test_three
 """)
         
         rev_outcome_set_foo.populate(log)
 
-        
-        rev_outcome_set_bar = summary.RevisionOutcomeSet(50000)
+
+        key_bar = ('bar', 7)        
+        rev_outcome_set_bar = summary.RevisionOutcomeSet(50000,
+                                                         key_bar)
         log = StringIO(""". a/b.py:test_one
 . a/b.py:test_two
 s a/b.py:test_three
@@ -70,6 +166,13 @@
                 outcome2 = goutcome.get_outcome((prefix, mod, testname))
                 assert outcome2 == outcome1
 
+                key_namekey1 = d[prefix].get_key_namekey((mod, testname))
+                key_namekey2 = goutcome.get_key_namekey((prefix, mod,
+                                                         testname))
+                assert key_namekey1 == key_namekey2
+
+
+
         goutcome_top = summary.GatherOutcomeSet({'sub': goutcome})
 
         assert goutcome_top.failed == set([('sub', 'foo', 'a.b', 'test_one')])
@@ -77,6 +180,12 @@
         res = goutcome_top.get_outcome(('sub', 'foo', 'a.b', 'test_one'))
         assert res == 'F'
 
+        res = goutcome_top.get_key_namekey(('sub', 'foo', 'a.b', 'test_one'))
+        assert res == (key_foo, ('a.b', 'test_one'))
+
+        res = goutcome_top.get_longrepr(('sub', 'foo', 'a.b', 'test_one'))
+        assert res == "some\ntraceback\n"
+
     def test_colsizes(self):
         failed = [('a', 'abc', 'd'), ('ab', 'c', 'xy'),
                   ('ab', '', 'cd')]



More information about the Pypy-commit mailing list