| # 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 __future__ import absolute_import, division, print_function |
| |
| 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 |
| 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() |
| |