diff options
Diffstat (limited to 'regtest')
-rw-r--r-- | regtest/Bisect.py | 113 | ||||
-rw-r--r-- | regtest/builder/__init__.py | 86 | ||||
-rw-r--r-- | regtest/builder/autotools.py | 63 | ||||
-rw-r--r-- | regtest/commands/find-regression.py | 77 |
4 files changed, 339 insertions, 0 deletions
diff --git a/regtest/Bisect.py b/regtest/Bisect.py new file mode 100644 index 00000000..715424fb --- /dev/null +++ b/regtest/Bisect.py @@ -0,0 +1,113 @@ +# Bisect.py +# +# Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from builder import get_builder +from TestRun import TestRun +from Config import Config +import os +import subprocess +import sys + +class GitBisect: + def __init__(self, srcdir): + self.srcdir = srcdir + + def __run_cmd(self, cmd): + p = subprocess.Popen(cmd, cwd=self.srcdir, stdout=subprocess.PIPE) + stdout, stderr = p.communicate() + if stdout: + sys.stdout.write(stdout) + + status = p.returncode + print status + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + + return stdout + + def __finished(self, output): + if not output: + return True + + return "is the first bad commit" in output + + def start(self, bad=None, good=None): + cmd = ['git', 'bisect', 'start'] + if bad is not None: + cmd.append(bad) + if good is not None: + cmd.append(good) + self.__run_cmd(cmd) + + def good(self): + cmd = ['git', 'bisect', 'good'] + output = self.__run_cmd(cmd) + return self.__finished(output) + + def bad(self): + cmd = ['git', 'bisect', 'bad'] + output = self.__run_cmd(cmd) + return self.__finished(output) + + def reset(self): + cmd = ['git', 'bisect', 'reset'] + self.__run_cmd(cmd) + +class Bisect: + + def __init__(self, test, refsdir, outdir): + self._test = test + self._refsdir = refsdir + self._outdir = outdir + self.config = Config() + self._builder = get_builder(self.config.builder) + + # Add run-tests options to config + self.config.keep_results = False + self.config.create_diffs = False + self.config.update_refs = False + + def __get_current_revision(self): + p = subprocess.Popen(['git', 'rev-parse', 'HEAD'], cwd=self.config.src_dir, stdout=subprocess.PIPE) + return p.communicate()[0] + + def run(self): + bisect = GitBisect(self.config.src_dir) + # TODO: save revision in .md5 files and get the good + # revision from refs when not provided by command line + try: + bisect.start(self.config.bad, self.config.good) + except: + print("Couldn't start git bisect") + return + finished = False + while not finished: + test_runner = TestRun(os.path.dirname(self._test), self._refsdir, self._outdir) + try: + self._builder.build() + except: + print("Impossible to find regression, build is broken in revision: %s" % (self.__get_current_revision())) + break + test_runner.run_test(os.path.basename(self._test)) + if test_runner._n_passed == 0: + finished = bisect.bad() + else: + finished = bisect.good() + + bisect.reset() + diff --git a/regtest/builder/__init__.py b/regtest/builder/__init__.py new file mode 100644 index 00000000..649c68d9 --- /dev/null +++ b/regtest/builder/__init__.py @@ -0,0 +1,86 @@ +# builder +# +# Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from Config import Config +import os +import sys +import subprocess + +__all__ = [ 'register_builder', + 'get_builder', + 'UnknownBuilderError', + 'Builder' ] + +class UnknownBuilderError(Exception): + '''Unknown builder''' + +class Builder: + + def __init__(self): + self.config = Config() + self._srcdir = self.config.src_dir + self._prefix = self.config.prefix + + def number_of_cpus(self): + if not sys.platform.startswith("linux"): + # TODO + return 0 + + n_cpus = subprocess.Popen(['nproc'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] + if n_cpus: + return int(n_cpus) + + n_cpus = 0 + f = open('/proc/cpuinfo', 'r') + for line in f.readlines(): + if 'processor' in line: + n_cpus += 1 + f.close() + + return n_cpus + + def _configure(self): + raise NotImplementedError + + def _build(self): + raise NotImplementedError + + def build(self): + self._configure() + self._build() + + +_builders = {} +def register_builder(builder_name, builder_class): + _builders[builder_name] = builder_class + +def _get_builder(builder_name): + if builder_name not in _builders: + try: + __import__('builder.%s' % builder_name) + except ImportError: + pass + + if builder_name not in _builders: + raise UnknownBuilderError('Invalid %s builder' % builder_name) + + return _builders[builder_name] + +def get_builder(builder_name): + builder_class = _get_builder(builder_name) + return builder_class() diff --git a/regtest/builder/autotools.py b/regtest/builder/autotools.py new file mode 100644 index 00000000..4dd05659 --- /dev/null +++ b/regtest/builder/autotools.py @@ -0,0 +1,63 @@ +# autotools.py +# +# Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from builder import Builder, register_builder +import os +import subprocess + +class Autotools(Builder): + + def _configure(self): + autogen = os.path.join(self._srcdir, 'autogen.sh') + cmd = [autogen, '--prefix=%s' % (self._prefix), '--enable-utils'] + + # Disable frontends and tests + cmd.extend(['--disable-poppler-glib', + '--disable-poppler-qt4', + '--disable-poppler-cpp', + '--disable-gtk-test']) + + backends = self.config.backends + if backends: + # Disable backends. Text and ps can't be disabled. + if 'cairo' not in backends: + cmd.append('--disable-cairo-output') + if 'splash' not in backends: + cmd.append('--disable-splash-output') + else: + cmd.extend(['--enable-cairo-output', + '--enable-splash-output']) + + p = subprocess.Popen(cmd, cwd=self._srcdir) + status = p.wait() + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + + def _build(self): + make = os.environ.get('MAKE', 'make') + cmd = [make] + n_cpus = self.number_of_cpus() + if n_cpus: + cmd.append('-j%d' % (n_cpus)) + + p = subprocess.Popen(cmd, cwd=self._srcdir) + status = p.wait() + if not os.WIFEXITED(status) or os.WEXITSTATUS(status) != 0: + raise Exception('Command %s returned non-zero exit status %d' % (str(cmd), status)) + +register_builder('autotools', Autotools) diff --git a/regtest/commands/find-regression.py b/regtest/commands/find-regression.py new file mode 100644 index 00000000..1a46eee8 --- /dev/null +++ b/regtest/commands/find-regression.py @@ -0,0 +1,77 @@ +# find-regression.py +# +# Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +from commands import Command, register_command +from Bisect import Bisect +from Timer import Timer +from Config import Config +import os +import tempfile + +class FindRegression(Command): + + name = 'find-regression' + usage_args = '[ options ... ] test ' + description = 'Find revision that introduced a regression for the given test' + + def __init__(self): + Command.__init__(self) + parser = self._get_args_parser() + parser.add_argument('--refs-dir', + action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), + help = 'Directory containing the references') + parser.add_argument('-o', '--out-dir', + action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), + help = 'Directory containing the results') + parser.add_argument('--src-dir', + action = 'store', dest = 'src_dir', default = os.path.abspath("../"), + help = 'Directory of poppler sources') + parser.add_argument('--builder', + action = 'store', dest = 'builder', + choices=['autotools'], default = 'autotools', + help = 'Build system to use') + parser.add_argument('--prefix', + action = 'store', dest = 'prefix', default = '/usr/local', + help = 'Build prefix') + parser.add_argument('--good', + action = 'store', dest = 'good', metavar = 'REVISION', + help = 'Good revision') + parser.add_argument('--bad', + action = 'store', dest = 'bad', default = 'HEAD', metavar = 'REVISION', + help = 'Bad revision') + parser.add_argument('test') + + def run(self, options): + config = Config() + config.src_dir = options['src_dir'] + config.builder = options['builder'] + config.prefix = options['prefix'] + config.good = options['good'] + config.bad = options['bad'] + + doc = options['test'] + if not os.path.isfile(doc): + print("Invalid test %s: not a regulat file" % (doc)) + return + + t = Timer() + bisect = Bisect(options['test'], options['refs_dir'], options['out_dir']) + bisect.run() + print("Tests run in %s" % (t.elapsed_str())) + +register_command('find-regression', FindRegression) |