[pypy-svn] r60739 - in pypy/build/bot2/pypybuildbot: . test
pedronis at codespeak.net
pedronis at codespeak.net
Tue Dec 30 18:13:28 CET 2008
Author: pedronis
Date: Tue Dec 30 18:13:26 2008
New Revision: 60739
Modified:
pypy/build/bot2/pypybuildbot/summary.py
pypy/build/bot2/pypybuildbot/test/test_summary.py
Log:
- support multiple pytest log step for a builder
- have the run links go to the build pages as some people requested, from there is possible to go to the various logs
- links to builder drill down to drill page
- on builder drill-down pages show also the elapsed time of pytest runs
Modified: pypy/build/bot2/pypybuildbot/summary.py
==============================================================================
--- pypy/build/bot2/pypybuildbot/summary.py (original)
+++ pypy/build/bot2/pypybuildbot/summary.py Tue Dec 30 18:13:26 2008
@@ -1,4 +1,4 @@
-import urllib, time
+import time, urlparse, urllib
import py
html = py.xml.html
@@ -6,16 +6,33 @@
from buildbot.status.web.base import HtmlResource
from buildbot.status.builder import FAILURE, EXCEPTION
+def host_agnostic(url):
+ parts = urlparse.urlsplit(url)
+ return urlparse.urlunsplit(('','')+parts[2:])
+
+def show_elapsed(secs):
+ if secs < 5:
+ return "%.02fs" % secs
+ secs = int(round(secs))
+ if secs < 60:
+ return "%ds" % secs
+ if secs < 5*60:
+ return "%dm%d" % (secs/60, secs%60)
+ mins = int(round(secs/60.))
+ if mins < 60:
+ return "%dm" % mins
+ return "%dh%d" % (mins/60, mins%60)
+
class RevisionOutcomeSet(object):
- def __init__(self, rev, key=None, run_stdio=None):
+ def __init__(self, rev, key=None, run_info=None):
self.revision = rev
self.key = key
self._outcomes = {}
self.failed = set()
self.skipped = set()
self.longreprs = {}
- self._run_stdio = run_stdio
+ self._run_info = run_info
def populate_one(self, name, shortrepr, longrepr=None):
if shortrepr == '!':
@@ -68,8 +85,8 @@
def get_key_namekey(self, namekey):
return (self.key, namekey)
- def get_run_stdios(self):
- return {self.key: (self, self._run_stdio)}
+ def get_run_infos(self):
+ return {self.key: (self, self._run_info)}
class RevisionOutcomeSetCache(object):
@@ -96,40 +113,42 @@
builderName, buildNumber = key
builderStatus = status.getBuilder(builderName)
build = builderStatus.getBuild(buildNumber)
+ run_url = status.getURLForThing(build)
rev = int(build.getProperty("got_revision"))
- pytest_log = None
- stdio_log = None
+ pytest_logs = []
+ pytest_elapsed = 0
failure = None
- aborted = False
for step in build.getSteps():
logs = dict((log.getName(), log) for log in step.getLogs())
if 'pytestLog' in logs:
- stdio_log = logs['stdio']
- if 'aborted' in step.getText():
- aborted = True
- pytest_log = logs['pytestLog']
- break
- elif (stdio_log is None and
+ aborted = 'aborted' in step.getText()
+ pytest_logs.append((step.getName(), logs['pytestLog'], aborted))
+ ts = step.getTimes()
+ if ts[0] is not None and ts[1] is not None:
+ pytest_elapsed += ts[1]-ts[0]
+ elif (failure is None and
step.getResults()[0] in (FAILURE, EXCEPTION)):
failure = ' '.join(step.getText())
- stdio_log = logs.get('stdio')
- if stdio_log is None:
- stdio_url = "no_log"
- else:
- stdio_url = status.getURLForThing(stdio_log)
- # builbot is broken in this :(
- stdio_url = stdio_url[:-1]+"stdio"
-
- outcome_set = RevisionOutcomeSet(rev, key, stdio_url)
- if pytest_log is None or not pytest_log.hasContents():
- name = failure or '<run>'
- outcome_set.populate_one(name, '!', "no log from the test run")
- else:
- if aborted:
- outcome_set.populate_one('<run> aborted', '!', "")
- outcome_set.populate(pytest_log)
+ run_info = {'URL': run_url, 'elapsed': pytest_elapsed or None}
+ outcome_set = RevisionOutcomeSet(rev, key, run_info)
+ someresult = False
+ if pytest_logs:
+ for stepName, resultLog, aborted in pytest_logs:
+ if resultLog.hasContents():
+ someresult = True
+ if aborted:
+ outcome_set.populate_one(stepName+' aborted', '!', "")
+ outcome_set.populate(resultLog)
+
+ if not someresult:
+ if failure:
+ name = '"%s"' % failure # quote
+ else:
+ name = '<run>'
+ outcome_set.populate_one(name, '!', "no logs from the test run")
+
return outcome_set
def get(self, status, key):
@@ -203,10 +222,10 @@
def get_key_namekey(self, namekey):
return self.map[namekey[0]].get_key_namekey(namekey[1:])
- def get_run_stdios(self):
+ def get_run_infos(self):
all = {}
for outcome_set in self.map.itervalues():
- all.update(outcome_set.get_run_stdios())
+ all.update(outcome_set.get_run_infos())
return all
# ________________________________________________________________
@@ -228,10 +247,11 @@
class SummaryPage(object):
- def __init__(self):
+ def __init__(self, status):
self.sections = []
self.cur_branch=None
self.fixed_builder = False
+ self.status = status
def make_longrepr_url_for(self, outcome_set, namekey):
cachekey, namekey = outcome_set.get_key_namekey(namekey)
@@ -244,24 +264,39 @@
qs = urllib.urlencode(parms)
return "/summary/longrepr?" + qs
- def make_stdio_anchors_for(self, outcome_set):
+ def make_run_anchors_for(self, outcome_set):
anchors = []
- stdios = sorted(outcome_set.get_run_stdios().items())
- for cachekey, (run, url) in stdios:
+ infos = sorted(outcome_set.get_run_infos().items())
+ for cachekey, (run, info) in infos:
builder = cachekey[0]
anchors.append(' ')
- text = "%s [%d, %d F, %d s]" % (builder,
+ timing = ""
+ if self.fixed_builder and info['elapsed'] is not None:
+ timing = " in %s" % show_elapsed(info['elapsed'])
+ text = "%s [%d, %d F, %d s%s]" % (builder,
run.numpassed,
len(run.failed),
- len(run.skipped))
- anchors.append(html.a(text, href=url))
+ len(run.skipped),
+ timing)
+ anchors.append(html.a(text, href=host_agnostic(info['URL'])))
return anchors
def start_branch(self, branch):
self.cur_branch = branch
- branch_anchor = html.a(branch, href="/summary?branch=%s" % branch)
+ branch_anchor = html.a(branch, href="/summary?branch=%s" % branch,
+ class_="failSummary branch")
self.sections.append(html.h2(branch_anchor))
+ def _builder_anchor(self, builder):
+ if self.fixed_builder:
+ url = self.status.getURLForThing(self.status.getBuilder(builder))
+ cls = "builder"
+ else:
+ url = "/summary?builder=%s" % builder
+ cls = "builderquery"
+ return html.a(builder, href=host_agnostic(url),
+ class_=' '.join(["failSummary", cls]))
+
def _builder_num(self, outcome_set):
return outcome_set.map.values()[0].key
@@ -287,9 +322,10 @@
def add_section(self, outcome_sets):
if not outcome_sets:
return
- labels = sorted(self._label(outcome_set) for outcome_set in outcome_sets)
- by_label = sorted((self._label(outcome_set), outcome_set) for outcome_set
- in outcome_sets)
+ labels = sorted(self._label(outcome_set)
+ for outcome_set in outcome_sets)
+ by_label = sorted((self._label(outcome_set), outcome_set)
+ for outcome_set in outcome_sets)
lines = []
align = 2*len(labels)-1+len(str(labels[-1]))
@@ -300,7 +336,7 @@
count_skipped = len(outcome_set.skipped)
line = [bars(), ' ', self._label_anchor(outcome_set)]
line.append((align-len(line[0]))*" ")
- line.append(self.make_stdio_anchors_for(outcome_set))
+ line.append(self.make_run_anchors_for(outcome_set))
line.append('\n')
lines.append(line)
lines.append([bars(), "\n"])
@@ -339,7 +375,14 @@
class_="failSummary failed")])
else:
line.append(" %s" % letter)
- for width, key in zip(colwidths, failure):
+ # builder
+ builder_width = colwidths[0]
+ builder = failure[0]
+ spacing = (" %-*s" % (builder_width, 'x'*len(builder))).rstrip('x')
+ builder_anchor = self._builder_anchor(builder)
+ line.append([spacing, builder_anchor])
+
+ for width, key in zip(colwidths[1:], failure[1:]):
line.append(" %-*s" % (width, key))
lines.append(line)
lines.append("\n")
@@ -356,7 +399,7 @@
num = build.getNumber()
descr = "%s #%d" % (builderName, num)
url = status.getURLForThing(build)
- section.append(html.a(descr, href=url))
+ section.append(html.a(descr, href=host_agnostic(url)))
section.append(html.br())
self.sections.append(section)
@@ -540,7 +583,7 @@
status = self.getStatus(request)
- page = SummaryPage()
+ page = SummaryPage(status)
#page.sections.append(repr(request.args))
only_branches = request.args.get('branch', None)
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 Dec 30 18:13:26 2008
@@ -225,6 +225,30 @@
assert runs == {99: 199, 98: 198, 97: 197, 96: 196}
+def test_show_elapsed():
+ res = summary.show_elapsed(0.25)
+ assert res == "0.25s"
+ res = summary.show_elapsed(1.0)
+ assert res == "1.00s"
+ res = summary.show_elapsed(1.25)
+ assert res == "1.25s"
+ res = summary.show_elapsed(4.5)
+ assert res == "4.50s"
+ res = summary.show_elapsed(5.25)
+ assert res == "5s"
+ res = summary.show_elapsed(5.5)
+ assert res == "6s"
+ res = summary.show_elapsed(2*60+30)
+ assert res == "2m30"
+ res = summary.show_elapsed(4*60+30)
+ assert res == "4m30"
+ res = summary.show_elapsed(5*60+30)
+ assert res == "6m"
+ res = summary.show_elapsed(61*60)
+ assert res == "1h1"
+ res = summary.show_elapsed(90*60)
+ assert res == "1h30"
+
class _BuilderToStatus(object):
def __init__(self, status):
@@ -286,12 +310,16 @@
def add_builds(builder, builds):
n = getattr(builder, 'nextBuildNumber', 0)
+ t = 1000
for rev, reslog in builds:
build = status_builder.BuildStatus(builder, n)
build.setProperty('got_revision', str(rev), None)
step = build.addStepWithName('pytest')
step.logs.extend([FakeLog(step, 'pytestLog', reslog),
FakeLog(step, 'stdio')])
+ step.started = t
+ step.finished = t + (n+1)*60
+ t = step.finished + 30
build.buildFinished()
builder.addBuildToCache(build)
n += 1
@@ -322,19 +350,64 @@
s = summary.Summary()
res = witness_branches(s)
req = FakeRequest([builder])
- s.body(req)
+ out = s.body(req)
branches = res()
assert branches == {None: ({}, [build])}
+ def test_one_build_no_logs(self):
+ builder = status_builder.BuilderStatus('builder0')
+ build = status_builder.BuildStatus(builder, 0)
+ build.setProperty('got_revision', '50000', None)
+ build.buildFinished()
+ builder.addBuildToCache(build)
+ builder.nextBuildNumber = len(builder.buildCache)
+
+ s = summary.Summary()
+ res = witness_branches(s)
+ req = FakeRequest([builder])
+ out = s.body(req)
+ branches = res()
+
+ revs = branches[None][0]
+ assert revs.keys() == [50000]
+
+ assert '<run>' in out
+
+ def test_one_build_no_logs_failure(self):
+ builder = status_builder.BuilderStatus('builder0')
+ build = status_builder.BuildStatus(builder, 0)
+ build.setProperty('got_revision', '50000', None)
+ step = build.addStepWithName('step')
+ step.setText(['step', 'borken'])
+ step.stepFinished(summary.FAILURE)
+ step1 = build.addStepWithName('other')
+ step1.setText(['other', 'borken'])
+ step1.stepFinished(summary.FAILURE)
+ build.buildFinished()
+ builder.addBuildToCache(build)
+ builder.nextBuildNumber = len(builder.buildCache)
+
+ s = summary.Summary()
+ res = witness_branches(s)
+ req = FakeRequest([builder])
+ out = s.body(req)
+ branches = res()
+
+ revs = branches[None][0]
+ assert revs.keys() == [50000]
+
+ assert 'step borken' in out
+ assert 'other borken' not in out
+
def test_one_build(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b")])
+ add_builds(builder, [(60000, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
req = FakeRequest([builder])
- s.body(req)
+ out = s.body(req)
branches = res()
revs = branches[None][0]
@@ -343,10 +416,12 @@
assert outcome.revision == 60000
assert outcome.key == ('builder0', 0)
+ assert 'TEST1' in out
+
def test_two_builds(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b"),
- (60001, "F a\n. b")])
+ add_builds(builder, [(60000, "F TEST1\n. b"),
+ (60001, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
@@ -369,10 +444,12 @@
assert revs == [60000, 60001]
+ assert 'TEST1' in out
+
def test_two_builds_samerev(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b"),
- (60000, "F a\n. b")])
+ add_builds(builder, [(60000, "F TEST1\n. b"),
+ (60000, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
@@ -386,16 +463,18 @@
assert outcome.revision == 60000
assert outcome.key == ('builder0', 1)
+ assert 'TEST1' in out
+
def test_two_builds_recentrev(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b"),
- (60001, "F a\n. b")])
+ add_builds(builder, [(60000, "F TEST1\n. b"),
+ (60001, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
req = FakeRequest([builder])
req.args = {'recentrev': ['60000']}
- s.body(req)
+ out = s.body(req)
branches = res()
revs = branches[None][0]
@@ -404,11 +483,13 @@
assert outcome.revision == 60000
assert outcome.key == ('builder0', 0)
+ assert 'TEST1' in out
+
def test_many_builds_query_builder(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b"),
+ add_builds(builder, [(60000, "F TEST1\n. b"),
(60000, ". a\n. b"),
- (60001, "F a\n. b")])
+ (60001, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
@@ -437,12 +518,14 @@
('builder0', 1),
('builder0', 2)]
+ assert 'TEST1' in out
+
def test_many_builds_query_builder_builds(self):
builder = status_builder.BuilderStatus('builder0')
- add_builds(builder, [(60000, "F a\n. b"),
+ add_builds(builder, [(60000, "F TEST1\n. b"),
(60000, ". a\n. b"),
- (60001, "F a\n. b")])
+ (60001, "F TEST1\n. b")])
s = summary.Summary()
res = witness_branches(s)
@@ -467,3 +550,27 @@
assert runs == [('builder0', 0),
('builder0', 2)]
+
+ assert 'TEST1' in out
+
+ def test_many_pytestLogs(self):
+ builder = status_builder.BuilderStatus('builder1')
+ build = status_builder.BuildStatus(builder, 0)
+ build.setProperty('got_revision', '70000', None)
+ step = build.addStepWithName('pytest')
+ step.logs.extend([FakeLog(step, 'pytestLog', "F TEST1")])
+ step2 = build.addStepWithName('pytest2')
+ step2.logs.extend([FakeLog(step, 'pytestLog', ". x\nF TEST2")])
+ step2.setText(["pytest2", "aborted"])
+ build.buildFinished()
+ builder.addBuildToCache(build)
+ builder.nextBuildNumber = 1
+
+ s = summary.Summary()
+ req = FakeRequest([builder])
+ out = s.body(req)
+
+ assert 'TEST1' in out
+ assert 'TEST2' in out
+ assert 'pytest aborted' not in out
+ assert 'pytest2 aborted' in out
More information about the Pypy-commit
mailing list