testing: improved automatic integration + reporting

Merge branches are removed locally before fetching from the remote repo.
Ensures that obsolete branched which where removed from the remote repo
also no longer exist locally. By itself, "git fetch" doesn't do that.

Automatic integration ignores branches which were already merged into
the upstream remote branch. Avoids redundant listing of branches
in the integration report which didn't really need to be merged for the
test run.

Unstaged and staged changes ("stash") are included in the patch report.
The patches are sorted from oldest to most recent. Fixed the fallback
code when a patch has no Subject.

The scripts for reporting tests results are taken from the merged
source code. That ensures that the reporting matches the tests
that were run. The versions from the boot-strapping SyncEvolution
code are the fallback, just in case that automatic integration
fails.

The URLs in the on-disk HTML are all relative. That allows rsyncing
the results directories without having to translate hrefs. The version
sent via mail still has absolute external URLs (the original motivation
for embedding the full URL).
This commit is contained in:
Patrick Ohly 2011-11-09 10:21:19 +00:00
parent 37f360d33d
commit 33bbf5df57
3 changed files with 44 additions and 16 deletions

View File

@ -23,8 +23,9 @@
<!-- TODO: Add more checkings to avoid generate un-welformed html files -->
<xsl:strip-space elements="*"/>
<xsl:param name="cmp_result_file" select="''"/> <!-- comparsion result file, necessary -->
<xsl:param name="url" select="''"/> <!-- root URL, at least pass . for relative URLs -->
<xsl:output method="html" indent="yes" encoding="UTF-8"/>
<!-- comparison result strings -->
<xsl:variable name="equal" select="'equal'"/>
<xsl:variable name="equal-in-semantics" select="'equal-in-semantics'"/>
@ -34,7 +35,7 @@
<xsl:variable name="invalid-value" select="'invalid-value'"/>
<!-- log file suffix name -->
<xsl:variable name="log-file-suffix" select="'.html'"/>
<xsl:variable name="log-file-suffix" select="'.log.html'"/>
<xsl:template match="/">
<xsl:choose>
@ -57,7 +58,8 @@
<xsl:template name="generate-html-body">
<xsl:param name="cmp_result_tree"/>
<body>
<xsl:variable name="log-dir-uri" select="nightly-test/log-info/uri"/>
<!-- xsl:variable name="log-dir-uri" select="nightly-test/log-info/uri"/ -->
<xsl:variable name="log-dir-uri" select="$url"/>
<xsl:call-template name="generate-source-info">
<xsl:with-param name="sourceinfo" select="nightly-test/source-info"/>
</xsl:call-template>

View File

@ -68,7 +68,7 @@ def extractPatchSummary(patchfile):
m = patchsummary.match(line)
if m:
return author + m.group(1)
return os.path.filename(patchfile)
return os.path.basename(patchfile)
def step1(resultdir, result, indents, dir, resulturi, shellprefix, srcdir):
'''Step1 of the result checking, collect system information and
@ -81,6 +81,7 @@ def step1(resultdir, result, indents, dir, resulturi, shellprefix, srcdir):
# include information prepared by GitCopy in runtests.py
result.write(indent+'<source-info>\n')
files = os.listdir(resultdir)
files.sort()
for source in files:
m = re.match('(.*)-source.log', source)
if m:

View File

@ -35,6 +35,17 @@ def abspath(path):
"""Absolute path after expanding vars and user."""
return os.path.abspath(os.path.expanduser(os.path.expandvars(path)))
def findInPaths(name, dirs):
"""find existing item in one of the directories, return None if
no directories give, absolute path to existing item or (as fallbac)
last dir + name"""
fullname = None
for dir in dirs:
fullname = os.path.join(abspath(dir), name)
if os.access(fullname, os.F_OK):
break
return fullname
def del_dir(path):
if not os.access(path, os.F_OK):
return
@ -180,6 +191,11 @@ class Context:
self.lastresultdir = lastresultdir
self.datadir = datadir
def findTestFile(self, name):
"""find item in SyncEvolution test directory, first using the
generated source of the current test, then the bootstrapping code"""
return findInPaths(name, (os.path.join(sync.basedir, "test"), self.datadir))
def runCommand(self, cmdstr, dumpCommands=False):
"""Log and run the given command, throwing an exception if it fails."""
cmd = shlex.split(cmdstr)
@ -303,23 +319,27 @@ class Context:
shell = re.sub(r'\S*valgrind\S*', '', options.shell)
prefix = re.sub(r'\S*valgrind\S*', '', options.testprefix)
uri = self.uri or ("file:///" + self.resultdir)
self.runCommand("resultchecker.py " +self.resultdir+" "+"'"+",".join(run_servers)+"'"+" "+uri +" "+srcdir + " '" + shell + " " + testprefix +" '"+" '" +backenddir +"'");
resultchecker = self.findTestFile("resultchecker.py")
compare = self.findTestFile("compare.xsl")
generateHTML = self.findTestFile("generate-html.xsl")
self.runCommand(resultchecker + " " +self.resultdir+" "+"'"+",".join(run_servers)+"'"+" "+uri +" "+srcdir + " '" + shell + " " + testprefix +" '"+" '" +backenddir +"'");
# transform to html
self.runCommand("xsltproc -o " + self.resultdir + "/cmp_result.xml --stringparam cmp_file " + self.lastresultdir +"/nightly.xml "+self.datadir +"/compare.xsl "+ self.resultdir+"/nightly.xml")
self.runCommand("xsltproc -o " + self.resultdir + "/nightly.html --stringparam cmp_result_file " + self.resultdir + "/cmp_result.xml " + self.datadir +"/generate-html.xsl "+ self.resultdir+"/nightly.xml")
self.runCommand("xsltproc -o " + self.resultdir + "/cmp_result.xml --stringparam cmp_file " + self.lastresultdir +"/nightly.xml "+compare+" "+ self.resultdir+"/nightly.xml")
# produce HTML with URLs relative to current directory of the nightly.html
self.runCommand("xsltproc -o " + self.resultdir + "/nightly.html --stringparam url . --stringparam cmp_result_file " + self.resultdir + "/cmp_result.xml " + generateHTML + " "+ self.resultdir+"/nightly.xml")
# report result by email
if self.recipients:
server = smtplib.SMTP(self.mailhost)
msg=''
try:
resulthtml = open (self.resultdir + "/nightly.html")
line=resulthtml.readline()
while(line!=''):
msg=msg+line
line=resulthtml.readline()
resulthtml.close()
msg = open(self.resultdir + "/nightly.html").read()
except IOError:
msg = '''<html><body><h1>Error: No HTML report generated!</h1></body></html>\n'''
# insert absolute URL into hrefs so that links can be opened directly in
# the mail reader
msg = re.sub(r'href="([a-zA-Z0-9./])',
'href="' + uri + r'/\1',
msg)
body = StringIO.StringIO()
writer = MimeWriter.MimeWriter (body)
writer.addheader("From", self.sender)
@ -458,7 +478,6 @@ class GitCopy(GitCheckoutBase, Action):
context.runCommand("(mkdir -p %s && cp -a -l %s/%s %s) || ( rm -rf %s && false )" %
(self.workdir, self.sourcedir, self.name, self.workdir, self.basedir))
os.chdir(self.basedir)
context.runCommand("git fetch && git fetch --tags")
cmd = " && ".join([
'rm -f %(patchlog)s',
'echo "save local changes with stash under a fixed name <rev>-nightly"',
@ -469,12 +488,18 @@ class GitCopy(GitCheckoutBase, Action):
'git checkout -q $( git show-ref --head --hash | head -1 )',
'if git branch | grep -q -w "^..%(revision)s$"; then git branch -D %(revision)s; fi',
'if git branch | grep -q -w "^..nightly$"; then git branch -D nightly; fi',
# fetch
'echo "remove stale merge branches and fetch anew"',
'git branch -r -D $( git branch -r | grep -e "/for-%(revision)s/" ) ',
'git branch -D $( git branch | grep -e "^ for-%(revision)s/" ) ',
'git fetch',
'git fetch --tags',
# pick tag or remote branch
'if git tag | grep -q -w %(revision)s; then base=%(revision)s; git checkout -f -b nightly %(revision)s; ' \
'else base=origin/%(revision)s; git checkout -f -b nightly origin/%(revision)s; fi',
# integrate remote branches first, followed by local ones;
# the hope is that local branches apply cleanly on top of the remote ones
'for patch in $( (git branch -r; git branch) | sed -e "s/^..//" | grep -e "^for-%(revision)s/" -e "/for-%(revision)s/" ); do ' \
'for patch in $( (git branch -r --no-merged origin/%(revision)s; git branch --no-merged origin/%(revision)s) | sed -e "s/^..//" | grep -e "^for-%(revision)s/" -e "/for-%(revision)s/" ); do ' \
'if git merge $patch; then echo >>%(patchlog)s $patch: okay; ' \
'else echo >>%(patchlog)s $patch: failed to apply; git reset --hard; fi; done',
'echo "restore <rev>-nightly and create permanent branch <rev>-nightly-before-<date>-<time> if that fails or new tree is different"',
@ -487,7 +512,7 @@ class GitCopy(GitCheckoutBase, Action):
'git format-patch -o .. $base..nightly',
'(cd ..; for i in [0-9]*.patch; do [ ! -f "$i" ] || mv $i %(name)s-$i; done)',
'git describe --tags --always nightly | sed -e "s/\(.*\)-\([0-9][0-9]*\)-g\(.*\)/\\1 + \\2 commit(s) = \\3/" >>%(patchlog)s',
'( git status | grep -q "working directory clean" && echo "working directory clean" || echo "working directory dirty" ) >>%(patchlog)s'
'( git status | grep -q "working directory clean" && echo "working directory clean" || ( echo "working directory dirty" && ( echo From: nightly testing ; echo Subject: [PATCH 1/1] uncommitted changes ; echo ; git status; echo; git diff HEAD ) >../%(name)s-1000-unstaged.patch ) ) >>%(patchlog)s'
]) % self
context.runCommand(cmd, dumpCommands=True)