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:
parent
37f360d33d
commit
33bbf5df57
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue