2015-03-20 15:38:09 +03:00
|
|
|
import sys
|
2015-03-23 16:15:40 +03:00
|
|
|
from PyQt4.QtCore import QObject, pyqtSignal as Signal, pyqtSlot as Slot, \
|
|
|
|
QThread, QTimer
|
|
|
|
from PyQt4.QtGui import QMessageBox, QDialog, QRadioButton
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
from mozregression.bisector import Bisector, Bisection, NightlyHandler, \
|
|
|
|
InboundHandler
|
|
|
|
from mozregression.download_manager import BuildDownloadManager
|
|
|
|
from mozregression.test_runner import TestRunner
|
2015-09-07 20:58:20 +03:00
|
|
|
from mozregression.errors import MozRegressionError, LauncherError
|
2015-08-17 03:24:03 +03:00
|
|
|
from mozregression.network import get_http_session
|
2015-09-11 12:24:59 +03:00
|
|
|
from mozregression.persist_limit import PersistLimit
|
2015-03-20 15:38:09 +03:00
|
|
|
|
2015-03-22 14:31:56 +03:00
|
|
|
from mozregui.ui.verdict import Ui_Verdict
|
2015-03-30 13:15:55 +03:00
|
|
|
from mozregui.global_prefs import get_prefs, apply_prefs
|
2015-09-12 09:04:44 +03:00
|
|
|
from mozregui.skip_chooser import SkipDialog
|
2015-03-22 14:31:56 +03:00
|
|
|
|
2015-03-20 15:38:09 +03:00
|
|
|
Bisection.EXCEPTION = -1 # new possible value of bisection end
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
class GuiBuildDownloadManager(QObject, BuildDownloadManager):
|
|
|
|
download_progress = Signal(object, int, int)
|
|
|
|
download_started = Signal(object)
|
2015-03-22 14:47:21 +03:00
|
|
|
download_finished = Signal(object, str)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-09-11 12:24:59 +03:00
|
|
|
def __init__(self, destdir, persist_limit, **kwargs):
|
2015-03-06 17:29:01 +03:00
|
|
|
QObject.__init__(self)
|
2015-09-11 12:24:59 +03:00
|
|
|
persist_limit = PersistLimit(persist_limit)
|
2015-08-24 19:14:33 +03:00
|
|
|
BuildDownloadManager.__init__(self, destdir,
|
2015-07-16 11:31:08 +03:00
|
|
|
session=get_http_session(),
|
2015-09-11 12:24:59 +03:00
|
|
|
persist_limit=persist_limit,
|
2015-07-16 11:31:08 +03:00
|
|
|
**kwargs)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
def _download_started(self, task):
|
|
|
|
self.download_started.emit(task)
|
|
|
|
BuildDownloadManager._download_started(self, task)
|
|
|
|
|
|
|
|
def _download_finished(self, task):
|
2015-03-21 13:09:04 +03:00
|
|
|
try:
|
2015-03-22 14:47:21 +03:00
|
|
|
self.download_finished.emit(task, task.get_dest())
|
2015-03-21 13:09:04 +03:00
|
|
|
except RuntimeError:
|
|
|
|
# in some cases, closing the application may destroy the
|
|
|
|
# underlying c++ QObject, causing this signal to fail.
|
|
|
|
# Skip this silently.
|
|
|
|
pass
|
2015-03-06 17:29:01 +03:00
|
|
|
BuildDownloadManager._download_finished(self, task)
|
|
|
|
|
|
|
|
def focus_download(self, build_info):
|
|
|
|
build_url, fname = self._extract_download_info(build_info)
|
|
|
|
dest = self.get_dest(fname)
|
2015-08-24 12:11:15 +03:00
|
|
|
build_info.build_file = dest
|
2015-03-06 17:29:01 +03:00
|
|
|
# first, stop all downloads in background (except the one for this
|
|
|
|
# build if any)
|
|
|
|
self.cancel(cancel_if=lambda dl: dest != dl.get_dest())
|
|
|
|
|
|
|
|
dl = self.download(build_url, fname)
|
|
|
|
if dl:
|
|
|
|
dl.set_progress(self.download_progress.emit)
|
2015-03-22 14:47:21 +03:00
|
|
|
else:
|
|
|
|
# file already downloaded.
|
|
|
|
# emit the finished signal so bisection goes on
|
|
|
|
self.download_finished.emit(None, dest)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
class GuiTestRunner(QObject, TestRunner):
|
2015-09-07 20:58:20 +03:00
|
|
|
evaluate_started = Signal(str)
|
2015-03-06 17:29:01 +03:00
|
|
|
evaluate_finished = Signal()
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
QObject.__init__(self)
|
|
|
|
TestRunner.__init__(self)
|
|
|
|
self.verdict = None
|
|
|
|
self.launcher = None
|
|
|
|
self.launcher_kwargs = {}
|
|
|
|
|
|
|
|
def evaluate(self, build_info, allow_back=False):
|
2015-09-07 20:58:20 +03:00
|
|
|
try:
|
|
|
|
self.launcher = self.create_launcher(build_info)
|
|
|
|
self.launcher.start(**self.launcher_kwargs)
|
|
|
|
build_info.update_from_app_info(self.launcher.get_app_info())
|
|
|
|
except LauncherError, exc:
|
|
|
|
self.evaluate_started.emit(str(exc))
|
|
|
|
else:
|
|
|
|
self.evaluate_started.emit('')
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
def finish(self, verdict):
|
2015-09-07 20:58:20 +03:00
|
|
|
if self.launcher:
|
|
|
|
try:
|
|
|
|
self.launcher.stop()
|
|
|
|
except LauncherError:
|
|
|
|
pass # silently pass stop process error
|
|
|
|
self.launcher.cleanup()
|
2015-03-06 17:29:01 +03:00
|
|
|
self.verdict = verdict
|
|
|
|
self.evaluate_finished.emit()
|
|
|
|
|
|
|
|
|
|
|
|
class GuiBisector(QObject, Bisector):
|
2015-03-20 20:04:16 +03:00
|
|
|
started = Signal()
|
2015-03-07 15:16:01 +03:00
|
|
|
finished = Signal(object, int)
|
2015-09-12 09:04:44 +03:00
|
|
|
choose_next_build = Signal()
|
2015-03-21 01:50:40 +03:00
|
|
|
step_started = Signal(object)
|
|
|
|
step_build_found = Signal(object, object)
|
2015-03-28 19:14:19 +03:00
|
|
|
step_testing = Signal(object, object)
|
2015-03-21 01:50:40 +03:00
|
|
|
step_finished = Signal(object, str)
|
2015-11-14 16:06:28 +03:00
|
|
|
handle_merge = Signal(object, str, str, str)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-09-11 12:24:59 +03:00
|
|
|
def __init__(self, fetch_config, download_dir, persist_limit):
|
2015-03-06 17:29:01 +03:00
|
|
|
QObject.__init__(self)
|
2015-08-24 19:14:33 +03:00
|
|
|
Bisector.__init__(self, fetch_config, GuiTestRunner(),
|
2015-09-11 12:24:59 +03:00
|
|
|
GuiBuildDownloadManager(download_dir, persist_limit))
|
2015-03-06 17:29:01 +03:00
|
|
|
self.bisection = None
|
|
|
|
self.mid = None
|
|
|
|
self.build_infos = None
|
2015-03-18 15:31:03 +03:00
|
|
|
self._bisect_args = None
|
2015-03-20 15:38:09 +03:00
|
|
|
self.error = None
|
2015-09-12 09:04:44 +03:00
|
|
|
self._next_build_index = None
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-03-07 22:44:34 +03:00
|
|
|
self.download_manager.download_finished.connect(
|
|
|
|
self._build_dl_finished)
|
2015-03-06 17:29:01 +03:00
|
|
|
self.test_runner.evaluate_finished.connect(self._evaluate_finished)
|
|
|
|
|
2015-03-20 15:38:09 +03:00
|
|
|
def _finish_on_exception(self, bisection):
|
|
|
|
self.error = sys.exc_info()
|
|
|
|
self.finished.emit(bisection, Bisection.EXCEPTION)
|
|
|
|
|
2015-03-18 15:31:03 +03:00
|
|
|
@Slot()
|
|
|
|
def bisect(self):
|
|
|
|
# this is a slot so it will be called in the thread
|
2015-03-20 20:04:16 +03:00
|
|
|
self.started.emit()
|
2015-03-20 15:38:09 +03:00
|
|
|
try:
|
|
|
|
Bisector.bisect(self, *self._bisect_args)
|
|
|
|
except MozRegressionError:
|
|
|
|
self._finish_on_exception(None)
|
2015-03-18 15:31:03 +03:00
|
|
|
|
2015-03-20 21:11:15 +03:00
|
|
|
@Slot()
|
2015-11-14 16:06:28 +03:00
|
|
|
def bisect_further(self):
|
2015-03-20 21:11:15 +03:00
|
|
|
assert self.bisection
|
|
|
|
self.started.emit()
|
2015-11-14 16:06:28 +03:00
|
|
|
handler = self.bisection.handler
|
2015-03-20 21:11:15 +03:00
|
|
|
try:
|
2015-11-14 16:06:28 +03:00
|
|
|
nhandler = InboundHandler(find_fix=self.bisection.handler.find_fix)
|
|
|
|
Bisector.bisect(self, nhandler, handler.good_revision,
|
|
|
|
handler.bad_revision)
|
2015-03-20 21:11:15 +03:00
|
|
|
except MozRegressionError:
|
|
|
|
self._finish_on_exception(None)
|
|
|
|
|
2015-11-14 16:06:28 +03:00
|
|
|
@Slot()
|
|
|
|
def check_merge(self):
|
|
|
|
handler = self.bisection.handler
|
|
|
|
try:
|
|
|
|
result = handler.handle_merge()
|
|
|
|
except MozRegressionError:
|
|
|
|
self._finish_on_exception(None)
|
|
|
|
return
|
|
|
|
if result is None:
|
|
|
|
self.bisection.no_more_merge = True
|
|
|
|
self.finished.emit(self.bisection, Bisection.FINISHED)
|
|
|
|
else:
|
|
|
|
self.handle_merge.emit(self.bisection, *result)
|
|
|
|
|
2015-08-31 11:55:14 +03:00
|
|
|
def _bisect(self, handler, build_range):
|
|
|
|
self.bisection = Bisection(handler, build_range,
|
2015-03-07 22:44:34 +03:00
|
|
|
self.download_manager,
|
2015-03-06 17:29:01 +03:00
|
|
|
self.test_runner,
|
|
|
|
dl_in_background=False)
|
2015-03-20 20:04:16 +03:00
|
|
|
self._bisect_next()
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
@Slot()
|
|
|
|
def _bisect_next(self):
|
2015-03-18 15:31:03 +03:00
|
|
|
# this is executed in the working thread
|
2015-03-20 15:38:09 +03:00
|
|
|
try:
|
|
|
|
self.mid = mid = self.bisection.search_mid_point()
|
|
|
|
except MozRegressionError:
|
|
|
|
self._finish_on_exception(self.bisection)
|
|
|
|
return
|
2015-09-12 09:04:44 +03:00
|
|
|
|
|
|
|
# if our last answer was skip, and that the next build
|
|
|
|
# to use is not chosen yet, ask to choose it.
|
|
|
|
if (self._next_build_index is None and
|
|
|
|
self.test_runner.verdict == 's' and
|
|
|
|
len(self.bisection.build_range) > 3):
|
|
|
|
self.choose_next_build.emit()
|
|
|
|
return
|
|
|
|
|
|
|
|
if self._next_build_index is not None:
|
|
|
|
# here user asked for specific build (eg from choose_next_build)
|
|
|
|
self.mid = mid = self._next_build_index
|
|
|
|
# this will download build infos if required
|
|
|
|
if self.bisection.build_range[mid] is False:
|
|
|
|
# in case no build info is found, ask to choose again
|
|
|
|
self.choose_next_build.emit()
|
|
|
|
return
|
|
|
|
self._next_build_index = None
|
|
|
|
|
|
|
|
self.step_started.emit(self.bisection)
|
2015-03-07 22:44:34 +03:00
|
|
|
result = self.bisection.init_handler(mid)
|
2015-03-06 17:29:01 +03:00
|
|
|
if result != Bisection.RUNNING:
|
2015-03-07 15:16:01 +03:00
|
|
|
self.finished.emit(self.bisection, result)
|
2015-03-06 17:29:01 +03:00
|
|
|
else:
|
2015-08-31 11:55:14 +03:00
|
|
|
self.build_infos = self.bisection.handler.build_range[mid]
|
2015-03-06 17:29:01 +03:00
|
|
|
self.download_manager.focus_download(self.build_infos)
|
2015-03-21 01:50:40 +03:00
|
|
|
self.step_build_found.emit(self.bisection, self.build_infos)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-03-18 15:31:03 +03:00
|
|
|
@Slot()
|
|
|
|
def _evaluate(self):
|
|
|
|
# this is called in the working thread, so installation does not
|
|
|
|
# block the ui.
|
|
|
|
self.bisection.evaluate(self.build_infos)
|
|
|
|
|
2015-03-22 14:47:21 +03:00
|
|
|
@Slot(object, str)
|
|
|
|
def _build_dl_finished(self, dl, dest):
|
2015-03-18 15:31:03 +03:00
|
|
|
# here we are not in the working thread, since the connection was
|
|
|
|
# done in the constructor
|
2015-08-24 12:11:15 +03:00
|
|
|
if not dest == self.build_infos.build_file:
|
2015-03-06 17:29:01 +03:00
|
|
|
return
|
2015-03-22 14:47:21 +03:00
|
|
|
if dl is not None and (dl.is_canceled() or dl.error()):
|
2015-03-06 17:29:01 +03:00
|
|
|
# todo handle this
|
|
|
|
return
|
2015-03-28 19:14:19 +03:00
|
|
|
self.step_testing.emit(self.bisection, self.build_infos)
|
2015-03-18 15:31:03 +03:00
|
|
|
# call this in the thread
|
|
|
|
QTimer.singleShot(0, self._evaluate)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
@Slot()
|
|
|
|
def _evaluate_finished(self):
|
2015-03-18 15:31:03 +03:00
|
|
|
# here we are not in the working thread, since the connection was
|
|
|
|
# done in the constructor
|
2015-03-21 01:50:40 +03:00
|
|
|
self.step_finished.emit(self.bisection, self.test_runner.verdict)
|
2015-03-07 22:44:34 +03:00
|
|
|
result = self.bisection.handle_verdict(self.mid,
|
|
|
|
self.test_runner.verdict)
|
2015-03-06 17:29:01 +03:00
|
|
|
if result != Bisection.RUNNING:
|
2015-03-07 15:16:01 +03:00
|
|
|
self.finished.emit(self.bisection, result)
|
2015-03-06 17:29:01 +03:00
|
|
|
else:
|
2015-03-18 15:31:03 +03:00
|
|
|
# call this in the thread
|
|
|
|
QTimer.singleShot(0, self._bisect_next)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
|
|
|
|
2015-03-22 14:31:56 +03:00
|
|
|
def get_verdict(parent=None):
|
|
|
|
dlg = QDialog(parent)
|
|
|
|
ui = Ui_Verdict()
|
|
|
|
ui.setupUi(dlg)
|
|
|
|
if dlg.exec_() != QDialog.Accepted:
|
|
|
|
return 'e' # exit bisection
|
|
|
|
for radiobox in dlg.findChildren(QRadioButton):
|
|
|
|
if radiobox.isChecked():
|
|
|
|
return radiobox.objectName()
|
|
|
|
|
|
|
|
|
2015-03-06 17:29:01 +03:00
|
|
|
class BisectRunner(QObject):
|
2015-03-07 15:16:01 +03:00
|
|
|
bisector_created = Signal(object)
|
2015-03-29 12:54:01 +03:00
|
|
|
running_state_changed = Signal(bool)
|
2015-03-07 15:16:01 +03:00
|
|
|
|
2015-03-06 17:29:01 +03:00
|
|
|
def __init__(self, mainwindow):
|
|
|
|
QObject.__init__(self)
|
|
|
|
self.mainwindow = mainwindow
|
|
|
|
self.bisector = None
|
2015-03-18 15:31:03 +03:00
|
|
|
self.thread = None
|
2015-03-29 13:25:24 +03:00
|
|
|
self.pending_threads = []
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-03-20 15:55:00 +03:00
|
|
|
def bisect(self, fetch_config, options):
|
2015-03-06 19:35:32 +03:00
|
|
|
self.stop()
|
2015-03-30 13:15:55 +03:00
|
|
|
|
|
|
|
# global preferences
|
|
|
|
global_prefs = get_prefs()
|
|
|
|
# apply the global prefs now
|
|
|
|
apply_prefs(global_prefs)
|
|
|
|
|
2015-08-24 19:14:33 +03:00
|
|
|
download_dir = global_prefs['persist']
|
2015-09-11 12:24:59 +03:00
|
|
|
persist_limit = int(abs(global_prefs['persist_size_limit'])
|
|
|
|
* 1073741824)
|
2015-08-24 19:14:33 +03:00
|
|
|
if not download_dir:
|
|
|
|
download_dir = self.mainwindow.persist
|
2015-05-15 22:30:23 +03:00
|
|
|
|
2015-03-30 13:15:55 +03:00
|
|
|
self.bisector = GuiBisector(fetch_config,
|
2015-09-11 12:24:59 +03:00
|
|
|
download_dir, persist_limit)
|
2015-03-18 15:31:03 +03:00
|
|
|
# create a QThread, and move self.bisector in it. This will
|
|
|
|
# allow to the self.bisector slots (connected after the move)
|
|
|
|
# to be automatically called in the thread.
|
|
|
|
self.thread = QThread()
|
|
|
|
self.bisector.moveToThread(self.thread)
|
2015-03-06 17:29:01 +03:00
|
|
|
self.bisector.test_runner.evaluate_started.connect(
|
|
|
|
self.evaluate)
|
2015-03-06 22:18:26 +03:00
|
|
|
self.bisector.finished.connect(self.bisection_finished)
|
2015-11-14 16:06:28 +03:00
|
|
|
self.bisector.handle_merge.connect(self.handle_merge)
|
2015-09-12 09:04:44 +03:00
|
|
|
self.bisector.choose_next_build.connect(self.choose_next_build)
|
2015-03-20 20:04:16 +03:00
|
|
|
self.bisector_created.emit(self.bisector)
|
2015-03-06 17:29:01 +03:00
|
|
|
if options['bisect_type'] == 'nightlies':
|
2015-03-30 11:22:37 +03:00
|
|
|
handler = NightlyHandler(find_fix=options['find_fix'])
|
2015-04-29 04:03:39 +03:00
|
|
|
good = options['good_date']
|
|
|
|
bad = options['bad_date']
|
2015-03-06 17:29:01 +03:00
|
|
|
else:
|
2015-03-30 11:22:37 +03:00
|
|
|
handler = InboundHandler(find_fix=options['find_fix'])
|
2015-04-29 04:03:39 +03:00
|
|
|
good = options['good_changeset']
|
|
|
|
bad = options['bad_changeset']
|
2015-03-27 00:55:46 +03:00
|
|
|
|
|
|
|
# options for the app launcher
|
2015-03-28 15:57:59 +03:00
|
|
|
launcher_kwargs = {}
|
|
|
|
for name in ('profile', 'preferences'):
|
|
|
|
if name in options:
|
|
|
|
value = options[name]
|
|
|
|
if value:
|
|
|
|
launcher_kwargs[name] = value
|
2015-08-19 18:54:33 +03:00
|
|
|
|
|
|
|
# add add-ons paths to the app launcher
|
|
|
|
launcher_kwargs['addons'] = options['addons']
|
2015-03-28 15:57:59 +03:00
|
|
|
self.bisector.test_runner.launcher_kwargs = launcher_kwargs
|
2015-03-27 00:55:46 +03:00
|
|
|
|
2015-03-18 15:31:03 +03:00
|
|
|
self.thread.start()
|
2015-04-29 04:03:39 +03:00
|
|
|
self.bisector._bisect_args = (handler, good, bad)
|
2015-03-18 15:31:03 +03:00
|
|
|
# this will be called in the worker thread.
|
|
|
|
QTimer.singleShot(0, self.bisector.bisect)
|
2015-03-06 17:29:01 +03:00
|
|
|
|
2015-03-29 12:54:01 +03:00
|
|
|
self.running_state_changed.emit(True)
|
|
|
|
|
2015-03-06 19:35:32 +03:00
|
|
|
@Slot()
|
2015-03-29 13:25:24 +03:00
|
|
|
def stop(self, wait=True):
|
2015-03-06 19:35:32 +03:00
|
|
|
if self.bisector:
|
2015-03-29 13:25:24 +03:00
|
|
|
self.bisector.finished.disconnect(self.bisection_finished)
|
2015-03-06 19:35:32 +03:00
|
|
|
self.bisector.download_manager.cancel()
|
2015-03-18 15:31:03 +03:00
|
|
|
self.bisector = None
|
|
|
|
if self.thread:
|
|
|
|
self.thread.quit()
|
2015-03-29 13:25:24 +03:00
|
|
|
if wait:
|
|
|
|
# wait for thread(s) completion - this is the case when
|
|
|
|
# user close the application
|
|
|
|
self.thread.wait()
|
|
|
|
for thread in self.pending_threads:
|
|
|
|
thread.wait()
|
|
|
|
else:
|
|
|
|
# do not block, just keep track of the thread - we got here
|
|
|
|
# when user cancel the bisection with the button.
|
|
|
|
self.pending_threads.append(self.thread)
|
|
|
|
self.thread.finished.connect(self._remove_pending_thread)
|
2015-03-18 15:31:03 +03:00
|
|
|
self.thread = None
|
2015-03-29 12:54:01 +03:00
|
|
|
self.running_state_changed.emit(False)
|
2015-03-06 19:35:32 +03:00
|
|
|
|
2015-03-29 13:25:24 +03:00
|
|
|
@Slot()
|
|
|
|
def _remove_pending_thread(self):
|
|
|
|
for thread in self.pending_threads[:]:
|
|
|
|
if thread.isFinished():
|
|
|
|
self.pending_threads.remove(thread)
|
|
|
|
|
2015-09-07 20:58:20 +03:00
|
|
|
@Slot(str)
|
|
|
|
def evaluate(self, err_message):
|
|
|
|
if not err_message:
|
|
|
|
verdict = get_verdict(self.mainwindow)
|
|
|
|
else:
|
|
|
|
QMessageBox.warning(
|
|
|
|
self.mainwindow,
|
|
|
|
"Launcher Error",
|
|
|
|
("An error occured while starting the process, so the build"
|
|
|
|
" will be skipped. Error message:<br><strong>%s</strong>"
|
|
|
|
% err_message)
|
|
|
|
)
|
|
|
|
verdict = 's'
|
2015-03-06 17:29:01 +03:00
|
|
|
self.bisector.test_runner.finish(verdict)
|
2015-03-06 22:18:26 +03:00
|
|
|
|
2015-09-12 09:04:44 +03:00
|
|
|
@Slot()
|
|
|
|
def choose_next_build(self):
|
|
|
|
dlg = SkipDialog(self.bisector.bisection.build_range)
|
|
|
|
self.bisector._next_build_index = dlg.choose_next_build()
|
|
|
|
QTimer.singleShot(0, self.bisector._bisect_next)
|
|
|
|
|
2015-03-07 15:16:01 +03:00
|
|
|
@Slot(object, int)
|
|
|
|
def bisection_finished(self, bisection, resultcode):
|
2015-03-22 14:55:44 +03:00
|
|
|
if resultcode == Bisection.USER_EXIT:
|
|
|
|
msg = "Bisection stopped."
|
|
|
|
dialog = QMessageBox.information
|
|
|
|
elif resultcode == Bisection.NO_DATA:
|
2015-03-06 22:18:26 +03:00
|
|
|
msg = "Unable to find enough data to bisect."
|
|
|
|
dialog = QMessageBox.warning
|
2015-03-20 15:38:09 +03:00
|
|
|
elif resultcode == Bisection.EXCEPTION:
|
|
|
|
msg = "Error: %s" % self.bisector.error[1]
|
|
|
|
dialog = QMessageBox.critical
|
2015-03-06 22:18:26 +03:00
|
|
|
else:
|
2015-11-14 16:06:28 +03:00
|
|
|
fetch_config = self.bisector.fetch_config
|
|
|
|
if fetch_config.can_go_inbound() and not \
|
|
|
|
getattr(bisection, 'no_more_merge', False):
|
|
|
|
if isinstance(bisection.handler, NightlyHandler):
|
|
|
|
handler = bisection.handler
|
2015-12-02 11:06:15 +03:00
|
|
|
fetch_config.set_repo(
|
2015-11-14 16:06:28 +03:00
|
|
|
fetch_config.get_nightly_repo(handler.bad_date))
|
|
|
|
QTimer.singleShot(0, self.bisector.bisect_further)
|
2015-03-29 02:49:00 +03:00
|
|
|
else:
|
2015-11-14 16:06:28 +03:00
|
|
|
# check merge, try to bisect further
|
|
|
|
QTimer.singleShot(0, self.bisector.check_merge)
|
2015-03-20 21:11:15 +03:00
|
|
|
return
|
2015-11-14 16:06:28 +03:00
|
|
|
else:
|
|
|
|
# no inbound, bisection is done.
|
|
|
|
self.stop()
|
2015-03-06 22:18:26 +03:00
|
|
|
msg = "The bisection is done."
|
|
|
|
dialog = QMessageBox.information
|
|
|
|
dialog(self.mainwindow, "End of the bisection", msg)
|
2015-03-20 15:38:09 +03:00
|
|
|
self.stop()
|
2015-11-14 16:06:28 +03:00
|
|
|
|
|
|
|
@Slot(object, str, str, str)
|
|
|
|
def handle_merge(self, bisection, branch, good_rev, bad_rev):
|
|
|
|
if QMessageBox.question(
|
|
|
|
self.mainwindow,
|
|
|
|
"Merge found",
|
|
|
|
"Found a merge from %s. Do you want to bisect further ?"
|
|
|
|
% branch,
|
|
|
|
QMessageBox.Yes | QMessageBox.No,
|
|
|
|
QMessageBox.Yes):
|
2015-12-02 11:06:15 +03:00
|
|
|
self.bisector.fetch_config.set_repo(str(branch))
|
2015-11-14 16:06:28 +03:00
|
|
|
bisection.handler.good_revision = str(good_rev)
|
|
|
|
bisection.handler.bad_revision = str(bad_rev)
|
|
|
|
QTimer.singleShot(0, self.bisector.bisect_further)
|
|
|
|
else:
|
|
|
|
self.stop()
|