Implement multi-branch testing

This commit is contained in:
Sergio Durigan Junior 2015-01-23 18:56:28 -05:00
parent 23f1f9c7e9
commit a2079f0b3b
5 changed files with 104 additions and 56 deletions

View file

@ -109,7 +109,7 @@
], ],
"schedulers" : [ { "type" : "AnyBranchScheduler", "name" : "master", "schedulers" : [ { "type" : "AnyBranchScheduler", "name" : "master",
"change_filter" : "master_filter", "change_filter" : "all_gdb_filter",
"builderNames" : [ "Fedora-x86_64-m64", "builderNames" : [ "Fedora-x86_64-m64",
"Fedora-x86_64-m32", "Fedora-x86_64-m32",
"Fedora-x86_64-native-gdbserver-m64", "Fedora-x86_64-native-gdbserver-m64",

View file

@ -3,6 +3,7 @@
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
from buildbot.steps.shell import ShellCommand from buildbot.steps.shell import ShellCommand
from sumfiles import DejaResults from sumfiles import DejaResults
from gdbgitdb import switch_to_branch
class GdbCatSumfileCommand(ShellCommand): class GdbCatSumfileCommand(ShellCommand):
name = 'regressions' name = 'regressions'
@ -18,6 +19,10 @@ class GdbCatSumfileCommand(ShellCommand):
branch = self.getProperty('branch') branch = self.getProperty('branch')
if branch is None: if branch is None:
branch = 'master' branch = 'master'
# Switch to the right branch inside the BUILDER repo
switch_to_branch (builder, branch)
parser = DejaResults() parser = DejaResults()
cur_results = parser.read_sum_text(self.getLog('stdio').getText()) cur_results = parser.read_sum_text(self.getLog('stdio').getText())
if not istry or istry == 'no': if not istry or istry == 'no':
@ -26,15 +31,16 @@ class GdbCatSumfileCommand(ShellCommand):
baseline = parser.read_sum_file(builder, rev) baseline = parser.read_sum_file(builder, rev)
result = SUCCESS result = SUCCESS
if baseline is not None: if baseline is not None:
report = parser.compute_regressions(builder, cur_results, baseline) report = parser.compute_regressions (builder, branch,
cur_results, baseline)
if report is not '': if report is not '':
self.addCompleteLog('regressions', report) self.addCompleteLog ('regressions', report)
result = FAILURE result = FAILURE
if not istry or istry == 'no': if not istry or istry == 'no':
parser.write_sum_file(cur_results, builder, rev) parser.write_sum_file (cur_results, builder, branch)
# If there was no previous baseline, then this run # If there was no previous baseline, then this run
# gets the honor. # gets the honor.
if baseline is None: if baseline is None:
baseline = cur_results baseline = cur_results
parser.write_baseline(baseline, builder, branch) parser.write_baseline (baseline, builder, branch)
return result return result

View file

@ -23,6 +23,27 @@ log files of the COMMIT that was tested."""
return None return None
def switch_to_branch (builder, branch):
"""Switch (or create) to BRANCH on BUILDER repo."""
repodir = os.path.join (get_web_base (), builder)
repo = git.Repo.init (path = repodir)
if 'master' not in repo.heads:
with open (os.path.join (repodir, 'README'), 'w') as f:
f.write ("git repo for GDB test results")
with open (os.path.join (repodir, '.gitignore'), 'w') as f:
f.write ("*xfails*\n")
repo.index.add (['README', '.gitignore'])
repo.index.commit ('Initial commit')
repo.index.write ()
if branch not in repo.heads:
myhead = repo.create_head (branch)
else:
myhead = repo.heads[branch]
myhead.checkout ()
class SaveGDBResults (ShellCommand): class SaveGDBResults (ShellCommand):
name = 'save build results' name = 'save build results'
description = 'saving build results' description = 'saving build results'
@ -40,7 +61,7 @@ class SaveGDBResults (ShellCommand):
repodir = get_web_base () repodir = get_web_base ()
builder_dir = os.path.join (repodir, builder) builder_dir = os.path.join (repodir, builder)
# TODO: Include timestamp in the tag name? # TODO: Include timestamp in the tag name?
full_tag = "%s-%s" % (builder, rev) full_tag = "%s-%s-%s" % (builder, rev, branch)
if branch is None: if branch is None:
branch = 'master' branch = 'master'
@ -55,7 +76,9 @@ class SaveGDBResults (ShellCommand):
if 'master' not in repo.heads: if 'master' not in repo.heads:
with open (os.path.join (repodir, 'README'), 'w') as f: with open (os.path.join (repodir, 'README'), 'w') as f:
f.write ("git repo for GDB test results") f.write ("git repo for GDB test results")
repo.index.add (['README']) with open (os.path.join (repodir, '.gitignore'), 'w') as f:
f.write ("*xfail*\n")
repo.index.add (['README', '.gitignore'])
repo.index.commit ('Initial commit') repo.index.commit ('Initial commit')
repo.index.write () repo.index.write ()
@ -68,9 +91,9 @@ class SaveGDBResults (ShellCommand):
myhead.checkout () myhead.checkout ()
repo.index.add (['%s/gdb.sum' % builder, repo.index.add (['%s/gdb.sum' % builder,
'%s/gdb.log' % builder, '%s/gdb.log' % builder,
'%s/%s/baseline' % (builder, branch)]) '%s/baseline' % builder])
if repo.is_dirty (): if repo.is_dirty ():
repo.index.commit ('Log files for %s' % full_tag) repo.index.commit ('Log files for %s -- branch %s' % (full_tag, branch))
repo.index.write () repo.index.write ()
repo.create_tag (full_tag) repo.create_tag (full_tag)
return SUCCESS return SUCCESS
@ -81,7 +104,7 @@ class SaveGDBResults (ShellCommand):
istry = self.getProperty ('isTryBuilder') istry = self.getProperty ('isTryBuilder')
branch = self.getProperty ('branch') branch = self.getProperty ('branch')
repodir = os.path.join (get_web_base (), builder) repodir = os.path.join (get_web_base (), builder)
full_tag = "%s-%s" % (datetime.now ().strftime ("%Y%m%d-%H%M%S"), rev) full_tag = "%s-%s-%s" % (datetime.now ().strftime ("%Y%m%d-%H%M%S"), rev, branch)
if branch is None: if branch is None:
branch = 'master' branch = 'master'
@ -94,20 +117,33 @@ class SaveGDBResults (ShellCommand):
if 'master' not in repo.heads: if 'master' not in repo.heads:
with open (os.path.join (repodir, 'README'), 'w') as f: with open (os.path.join (repodir, 'README'), 'w') as f:
f.write ("git repo for GDB test results -- %s" % builder) f.write ("git repo for GDB test results -- %s" % builder)
repo.index.add (['README']) with open (os.path.join (repodir, '.gitignore'), 'w') as f:
f.write ("*xfail*\n")
repo.index.add (['README', '.gitignore'])
repo.index.commit ('Initial commit') repo.index.commit ('Initial commit')
repo.index.write () repo.index.write ()
if branch not in repo.heads:
myhead = repo.create_head (branch)
else:
myhead = repo.heads[branch]
myhead.checkout ()
if full_tag not in repo.tags: if full_tag not in repo.tags:
repo.index.add (['gdb.sum', repo.index.add (['gdb.sum',
'gdb.log', 'gdb.log',
'%s/baseline' % branch]) 'baseline'])
if repo.is_dirty (): if repo.is_dirty ():
repo.index.commit ('Log files for %s' % full_tag) repo.index.commit ('Log files for %s -- branch %s' % (full_tag, branch))
repo.index.write () repo.index.write ()
repo.create_tag (full_tag) repo.create_tag (full_tag)
# Returning the HEAD to master
repo.heads['master'].checkout ()
return SUCCESS return SUCCESS
def evaluateCommand (self, cmd): def evaluateCommand (self, cmd):
# We can change this scheme for the other one if needed # We can change this scheme for the other one if needed
# FIXME: the _evaluateCommand_builder_branch function needs
# adjustment because of the multi-branch testing...
return self._evaluateCommand_single_repo (cmd) return self._evaluateCommand_single_repo (cmd)

View file

@ -46,29 +46,26 @@ class DejaResults(object):
test_name = nname test_name = nname
out_dict[test_name] = result out_dict[test_name] = result
def _write_sum_file(self, sum_dict, subdir, revision, filename): def _write_sum_file(self, sum_dict, subdir, rev_or_branch, filename):
global gdb_web_base global gdb_web_base
if revision: if not rev_or_branch:
bdir = os.path.join(gdb_web_base, subdir, revision)
else:
bdir = os.path.join (gdb_web_base, subdir) bdir = os.path.join (gdb_web_base, subdir)
if not os.path.isdir(bdir): else:
os.makedirs(bdir, 0755) bdir = os.path.join (gdb_web_base, subdir, rev_or_branch)
fname = os.path.join(bdir, filename) if not os.path.isdir (bdir):
keys = sum_dict.keys() os.makedirs (bdir, 0755)
keys.sort() fname = os.path.join (bdir, filename)
f = open(fname, 'w') keys = sum_dict.keys ()
for k in keys: keys.sort ()
f.write(sum_dict[k] + ': ' + k + '\n') with open (fname, 'w') as f:
f.close() for k in keys:
f.write (sum_dict[k] + ': ' + k + '\n')
def write_sum_file(self, sum_dict, builder, revision): def write_sum_file(self, sum_dict, builder, branch):
self._write_sum_file (sum_dict, builder, None, 'gdb.sum') self._write_sum_file (sum_dict, builder, None, 'gdb.sum')
# self._write_sum_file(sum_dict, builder, revision, 'gdb.sum')
def write_baseline(self, sum_dict, builder, branch): def write_baseline(self, sum_dict, builder, branch):
self._write_sum_file(sum_dict, os.path.join(builder, branch), self._write_sum_file(sum_dict, builder, None, 'baseline')
None, 'baseline')
# Read a .sum file. # Read a .sum file.
# The builder name is BUILDER. # The builder name is BUILDER.
@ -76,50 +73,48 @@ class DejaResults(object):
# revision; to read the baseline file for a branch, use `read_baseline'. # revision; to read the baseline file for a branch, use `read_baseline'.
# Returns a dictionary holding the .sum contents, or None if the # Returns a dictionary holding the .sum contents, or None if the
# file did not exist. # file did not exist.
def _read_sum_file(self, subdir, revision, filename): def _read_sum_file(self, subdir, rev_or_branch, filename):
global gdb_web_base global gdb_web_base
if not revision: if not rev_or_branch:
fname = os.path.join(gdb_web_base, subdir, filename) fname = os.path.join (gdb_web_base, subdir, filename)
else: else:
fname = os.path.join (gdb_web_base, subdir, revision, filename) fname = os.path.join (gdb_web_base, subdir, rev_or_branch, filename)
if os.path.exists(fname): if os.path.exists (fname):
result = {} result = {}
f = open(fname, 'r') with open (fname, 'r') as f:
for line in f: for line in f:
self.parse_sum_line (result, line) self.parse_sum_line (result, line)
f.close()
else: else:
result = None result = None
return result return result
def read_sum_file (self, builder, revision): def read_sum_file (self, builder, branch):
return self._read_sum_file (builder, None, 'gdb.sum') return self._read_sum_file (builder, None, 'gdb.sum')
# return self._read_sum_file (builder, revision, 'gdb.sum')
def read_baseline(self, builder, branch): def read_baseline(self, builder, branch):
return self._read_sum_file(os.path.join(builder, branch), return self._read_sum_file (builder, None, 'baseline')
None, 'baseline')
def read_xfail (self, builder): def read_xfail (self, builder, branch):
return self._read_sum_file (builder, None, 'xfail') return self._read_sum_file (builder, os.path.join ('xfails', branch),
'xfail')
# Parse some text as a .sum file and return the resulting # Parse some text as a .sum file and return the resulting
# dictionary. # dictionary.
def read_sum_text(self, text): def read_sum_text (self, text):
cur_file = StringIO(text) cur_file = StringIO (text)
cur_results = {} cur_results = {}
for line in cur_file.readlines(): for line in cur_file.readlines ():
self.parse_sum_line(cur_results, line) self.parse_sum_line (cur_results, line)
return cur_results return cur_results
# Compute regressions between RESULTS and BASELINE on BUILDER. # Compute regressions between RESULTS and BASELINE on BUILDER.
# BASELINE will be modified if any new PASSes are seen. # BASELINE will be modified if any new PASSes are seen.
# Returns a regression report, as a string. # Returns a regression report, as a string.
def compute_regressions(self, builder, results, baseline): def compute_regressions (self, builder, branch, results, baseline):
our_keys = results.keys() our_keys = results.keys ()
our_keys.sort() our_keys.sort ()
result = '' result = ''
xfails = self.read_xfail (builder) xfails = self.read_xfail (builder, branch)
if xfails is None: if xfails is None:
xfails = {} xfails = {}
for key in our_keys: for key in our_keys:

View file

@ -33,6 +33,7 @@ import os.path
import urllib import urllib
from json import load from json import load
import random import random
import re
#################################### ####################################
#################################### ####################################
@ -62,12 +63,22 @@ c['protocols'] = {'pb': {'port': 16123}}
# the 'change_source' setting tells the buildmaster how it should find out # the 'change_source' setting tells the buildmaster how it should find out
# about source code changes. # about source code changes.
# RE representing which branches to track on the GDB repository
branches_to_watch = re.compile ("(refs/heads/)?(master|gdb-\d+\.\d+-branch)")
# Function which decides whether BRANCH should be used or not
def should_watch_branch (branch):
if re.match (branches_to_watch, branch):
return True
else:
return False
from buildbot.changes.gitpoller import GitPoller from buildbot.changes.gitpoller import GitPoller
c['change_source'] = [] c['change_source'] = []
c['change_source'].append(GitPoller( c['change_source'].append(GitPoller(
repourl = r'git://sourceware.org/git/binutils-gdb.git', repourl = r'git://sourceware.org/git/binutils-gdb.git',
workdir = os.path.expanduser (os.path.join ('~/', 'buildbot-master-binutils-gdb')), workdir = os.path.expanduser (os.path.join ('~/', 'buildbot-master-binutils-gdb')),
branches = [ r'master' ], branches = should_watch_branch,
pollinterval = 60 * 3)) pollinterval = 60 * 3))
# 'status' is a list of Status Targets. The results of each build will be # 'status' is a list of Status Targets. The results of each build will be
@ -127,7 +138,7 @@ send to the gdb-testers mailing list."""
git_url = "http://gdb-build.sergiodj.net/cgit" git_url = "http://gdb-build.sergiodj.net/cgit"
# Subject # Subject
subj = "Failures on %s" % name subj = "Failures on %s, branch %s" % (name, build.getSourceStamps ()[0].branch)
# Body # Body
text = "" text = ""
@ -581,7 +592,7 @@ class RunTestGDBIndexBuildBSD (RunTestGDBIndexBuild, RunTestGDBBSD_Common):
pass pass
# For now, we only support testing the "master" branch. # For now, we only support testing the "master" branch.
master_filter = ChangeFilter (branch = [ r'master' ]) all_gdb_filter = ChangeFilter (branch_fn = should_watch_branch)
############################### ###############################
#### Configuration loading #### #### Configuration loading ####