Add structured test results to Excuse objects

Add Excuse.addtest() for adding a test type/package/arch/result, so that the
excuses YAML will get structured test results instead of pre-formatted HTML.
Move the HTML rendering into Excuse.html() instead.

This supports a "test type" whose only value is "autopkgtest" right now, but
we will have "bootest", perhaps "piuparts" and other tests in the future.

Drop the "(<ver> is unbuilt/uninstallable)" note from excuses.html as this is
really a per-architecture property, not a per-tested-source one. This needs to
be re-thought and generalized.
This commit is contained in:
Martin Pitt 2015-08-25 12:21:51 +02:00
parent 66f6a066d2
commit 71b07bc66a
4 changed files with 34 additions and 22 deletions

View File

@ -32,14 +32,6 @@ import kombu
from consts import (AUTOPKGTEST, BINARIES, DEPENDS, RDEPENDS, SOURCE, VERSION) from consts import (AUTOPKGTEST, BINARIES, DEPENDS, RDEPENDS, SOURCE, VERSION)
ADT_EXCUSES_LABELS = {
"PASS": '<span style="background:#87d96c">Pass</span>',
"ALWAYSFAIL": '<span style="background:#e5c545">Always failed</span>',
"REGRESSION": '<span style="background:#ff6666">Regression</span>',
"RUNNING": '<span style="background:#99ddff">Test in progress</span>',
}
def srchash(src): def srchash(src):
'''archive hash prefix for source package''' '''archive hash prefix for source package'''

View File

@ -212,7 +212,7 @@ from britney_util import (old_libraries_format, same_source, undo_changes,
from consts import (VERSION, SECTION, BINARIES, MAINTAINER, FAKESRC, from consts import (VERSION, SECTION, BINARIES, MAINTAINER, FAKESRC,
SOURCE, SOURCEVER, ARCHITECTURE, DEPENDS, CONFLICTS, SOURCE, SOURCEVER, ARCHITECTURE, DEPENDS, CONFLICTS,
PROVIDES, RDEPENDS, RCONFLICTS, MULTIARCH, ESSENTIAL) PROVIDES, RDEPENDS, RCONFLICTS, MULTIARCH, ESSENTIAL)
from autopkgtest import AutoPackageTest, ADT_EXCUSES_LABELS, srchash from autopkgtest import AutoPackageTest, srchash
from boottest import BootTest from boottest import BootTest
@ -1909,17 +1909,11 @@ class Britney(object):
adtpass = True adtpass = True
for passed, adtsrc, adtver, arch_status in autopkgtest.results( for passed, adtsrc, adtver, arch_status in autopkgtest.results(
e.name, e.ver[1]): e.name, e.ver[1]):
archmsg = [] for arch in arch_status:
for arch in sorted(arch_status):
url = cloud_url % {'h': srchash(adtsrc), 's': adtsrc, url = cloud_url % {'h': srchash(adtsrc), 's': adtsrc,
'r': self.options.series, 'a': arch} 'r': self.options.series, 'a': arch}
archmsg.append('<a href="%s">%s: %s</a>' % e.addtest('autopkgtest', '%s %s' % (adtsrc, adtver),
(url, arch, ADT_EXCUSES_LABELS[arch_status[arch]])) arch, arch_status[arch], url)
if adtsrc in autopkgtest_excludes:
note = ' (%s is unbuilt/uninstallable)' % self.sources['unstable'][adtsrc][VERSION]
else:
note = ''
e.addhtml('autopkgtest for %s %s%s: %s' % (adtsrc, adtver, note, ', '.join(archmsg)))
# hints can override failures # hints can override failures
if not passed: if not passed:

View File

@ -16,6 +16,15 @@
import re import re
EXCUSES_LABELS = {
"PASS": '<span style="background:#87d96c">Pass</span>',
"FAIL": '<span style="background:#ff6666">Failed</span>',
"ALWAYSFAIL": '<span style="background:#e5c545">Always failed</span>',
"REGRESSION": '<span style="background:#ff6666">Regression</span>',
"RUNNING": '<span style="background:#99ddff">Test in progress</span>',
}
class Excuse(object): class Excuse(object):
"""Excuse class """Excuse class
@ -62,6 +71,9 @@ class Excuse(object):
self.oldbugs = set() self.oldbugs = set()
self.reason = {} self.reason = {}
self.htmlline = [] self.htmlline = []
# type (e. g. "autopkgtest") -> package (e. g. "foo 2-1") -> arch ->
# ['PASS'|'ALWAYSFAIL'|'REGRESSION'|'RUNNING', url]
self.tests = {}
def sortkey(self): def sortkey(self):
if self.daysold == None: if self.daysold == None:
@ -165,6 +177,15 @@ class Excuse(object):
else: else:
res = res + ("<li>%d days old (needed %d days)\n" % res = res + ("<li>%d days old (needed %d days)\n" %
(self.daysold, self.mindays)) (self.daysold, self.mindays))
for testtype in sorted(self.tests):
for pkg in sorted(self.tests[testtype]):
archmsg = []
for arch in sorted(self.tests[testtype][pkg]):
status, url = self.tests[testtype][pkg][arch]
archmsg.append('<a href="%s">%s: %s</a>' %
(url, arch, EXCUSES_LABELS[status]))
res = res + ("<li>%s for %s: %s</li>\n" % (testtype, pkg, ', '.join(archmsg)))
for x in self.htmlline: for x in self.htmlline:
res = res + "<li>" + x + "\n" res = res + "<li>" + x + "\n"
lastdep = "" lastdep = ""
@ -193,6 +214,10 @@ class Excuse(object):
""""adding reason""" """"adding reason"""
self.reason[reason] = 1 self.reason[reason] = 1
def addtest(self, type_, package, arch, state, url):
"""Add test result"""
self.tests.setdefault(type_, {}).setdefault(package, {})[arch] = [state, url]
# TODO merge with html() # TODO merge with html()
def text(self): def text(self):
"""Render the excuse in text""" """Render the excuse in text"""
@ -248,5 +273,6 @@ class Excuse(object):
else: else:
excusedata["reason"] = list(self.reason.keys()) excusedata["reason"] = list(self.reason.keys())
excusedata["is-candidate"] = self.is_valid excusedata["is-candidate"] = self.is_valid
excusedata["tests"] = self.tests
return excusedata return excusedata

View File

@ -385,7 +385,7 @@ lightgreen 1 i386 green 2
r'\blightgreen\b.*>1</a> to .*>2<', r'\blightgreen\b.*>1</a> to .*>2<',
r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for lightgreen 1 \(2 is unbuilt/uninstallable\): .*amd64.*Regression.*i386.*Regression', r'autopkgtest for lightgreen 1: .*amd64.*Regression.*i386.*Regression',
r'lightgreen has no up-to-date binaries on any arch'], r'lightgreen has no up-to-date binaries on any arch'],
['Valid candidate']) ['Valid candidate'])
@ -483,7 +483,7 @@ lightgreen 1 i386 green 2
r'\blightgreen\b.*>1</a> to .*>2<', r'\blightgreen\b.*>1</a> to .*>2<',
r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for lightgreen 1 \(2 is unbuilt/uninstallable\): .*amd64.*Regression.*i386.*Regression', r'autopkgtest for lightgreen 1: .*amd64.*Regression.*i386.*Regression',
r'lightgreen has no up-to-date binaries on any arch'], r'lightgreen has no up-to-date binaries on any arch'],
['Valid candidate']) ['Valid candidate'])
self.assertEqual( self.assertEqual(
@ -528,7 +528,7 @@ lightgreen 1 i386 green 2
r'\blightgreen\b.*>1</a> to .*>2<', r'\blightgreen\b.*>1</a> to .*>2<',
r'autopkgtest for green 2: .*amd64.*in progress.*i386.*in progress', r'autopkgtest for green 2: .*amd64.*in progress.*i386.*in progress',
r'autopkgtest for darkgreen 1: .*amd64.*in progress.*i386.*in progress', r'autopkgtest for darkgreen 1: .*amd64.*in progress.*i386.*in progress',
r'autopkgtest for lightgreen 1 \(2 is unbuilt/uninstallable\): .*amd64.*in progress.*i386.*in progress', r'autopkgtest for lightgreen 1: .*amd64.*in progress.*i386.*in progress',
r'lightgreen has no up-to-date binaries on any arch'], r'lightgreen has no up-to-date binaries on any arch'],
['Valid candidate']) ['Valid candidate'])
self.assertEqual( self.assertEqual(
@ -555,7 +555,7 @@ lightgreen 1 i386 green 2
r'\blightgreen\b.*>1</a> to .*>2<', r'\blightgreen\b.*>1</a> to .*>2<',
r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for green 2: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass', r'autopkgtest for darkgreen 1: .*amd64.*Pass.*i386.*Pass',
r'autopkgtest for lightgreen 2 \(2 is unbuilt/uninstallable\): .*amd64.*Regression.*i386.*Regression', r'autopkgtest for lightgreen 2: .*amd64.*Regression.*i386.*Regression',
r'lightgreen has no up-to-date binaries on any arch'], r'lightgreen has no up-to-date binaries on any arch'],
['Valid candidate']) ['Valid candidate'])