def _bisect_inbounds(self, good_rev, bad_rev): self._logger.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix) result = self._do_bisect(handler, good_rev, bad_rev) if result == Bisection.FINISHED: self._logger.info("Oh noes, no (more) inbound revisions :(") handler.print_range() if handler.good_revision == handler.bad_revision: self._logger.warning( "It seems that you used two changesets that are in" " in the same push. Check the pushlog url." ) elif len(handler.build_range) == 2: # range reduced to 2 pushes: one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old self._logger.info( 'There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
def _bisect_inbounds(self, good_rev, bad_rev, ensure_good_and_bad=False): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad) result = self._do_bisect(handler, good_rev, bad_rev) if result == Bisection.FINISHED: LOG.info("Oh noes, no (more) inbound revisions :(") handler.print_range() if handler.good_revision == handler.bad_revision: LOG.warning("It seems that you used two changesets that are in" " in the same push. Check the pushlog url.") elif len(handler.build_range) == 2: # range reduced to 2 pushes: one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info('There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
def _bisect_inbounds(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad) result = self._do_bisect(handler, good_rev, bad_rev, expand=expand) if result == Bisection.FINISHED: LOG.info("No more inbound revisions, bisection finished.") handler.print_range() if handler.good_revision == handler.bad_revision: LOG.warning("It seems that you used two changesets that are in" " the same push. Check the pushlog url.") elif len(handler.build_range) == 2: # range reduced to 2 pushes (at least ones with builds): # one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev, expand=DEFAULT_EXPAND) else: # This code is broken, it prints out the message even when # there are multiple bug numbers or commits in the range. # Somebody should fix it before re-enabling it. return 0 # print a bug if: # (1) there really is only one bad push (and we're not # just missing the builds for some intermediate builds) # (2) there is only one bug number in that push jp = JsonPushes(handler.build_range[1].repo_name) num_pushes = len( jp.pushes_within_changes( handler.build_range[0].changeset, handler.build_range[1].changeset)) if num_pushes == 2: bugids = find_bugids_in_push( handler.build_range[1].repo_name, handler.build_range[1].changeset) if len(bugids) == 1: word = 'fix' if handler.find_fix else 'regression' LOG.info("Looks like the following bug has the " " changes which introduced the" " {}:\n{}".format(word, bug_url(bugids[0]))) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info('There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
def _bisect_inbounds(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad) result = self._do_bisect(handler, good_rev, bad_rev, expand=expand) if result == Bisection.FINISHED: LOG.info("No more inbound revisions, bisection finished.") handler.print_range() if handler.good_revision == handler.bad_revision: LOG.warning( "It seems that you used two changesets that are in" " the same push. Check the pushlog url." ) elif len(handler.build_range) == 2: # range reduced to 2 pushes (at least ones with builds): # one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev, expand=DEFAULT_EXPAND) else: # This code is broken, it prints out the message even when # there are multiple bug numbers or commits in the range. # Somebody should fix it before re-enabling it. return 0 # print a bug if: # (1) there really is only one bad push (and we're not # just missing the builds for some intermediate builds) # (2) there is only one bug number in that push jp = JsonPushes(handler.build_range[1].repo_name) num_pushes = len(jp.pushes_within_changes( handler.build_range[0].changeset, handler.build_range[1].changeset)) if num_pushes == 2: bugids = find_bugids_in_push( handler.build_range[1].repo_name, handler.build_range[1].changeset ) if len(bugids) == 1: word = 'fix' if handler.find_fix else 'regression' LOG.info("Looks like the following bug has the " " changes which introduced the" " {}:\n{}".format(word, bug_url(bugids[0]))) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info( 'There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
def bisect_inbound(self, good_rev, bad_rev): # Remember, InboundHandler is just a changeset based bisector. It will # still potentially bisect m-c first. handler = InboundHandler() result = self.bisector.bisect(handler, good_rev, bad_rev, expand=0) if result == Bisection.FINISHED: print "No more m-c revisions :(" handler.print_range() # Try switching over to the integration branch. if len(handler.build_range) == 2: result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self.bisect_inbound(good_rev, bad_rev) return (handler.good_revision, handler.bad_revision)
def _bisect_inbounds(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad) result = self._do_bisect(handler, good_rev, bad_rev, expand=expand) if result == Bisection.FINISHED: LOG.info("Oh noes, no (more) inbound revisions :(") handler.print_range() if handler.good_revision == handler.bad_revision: LOG.warning( "It seems that you used two changesets that are in" " in the same push. Check the pushlog url." ) elif len(handler.build_range) == 2: # range reduced to 2 pushes: one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev, expand=DEFAULT_EXPAND) else: bugids = find_bugids_in_push( handler.build_range[1].repo_name, handler.build_range[1].changeset ) if len(bugids) == 1: word = 'fix' if handler.find_fix else 'regression' LOG.info("Looks like the following bug has the changes" " which introduced the {}:\n{}".format( word, bug_url(bugids[0]))) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info( 'There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
def bisect_further(self): assert self.bisection self.started.emit() handler = self.bisection.handler try: nhandler = InboundHandler(find_fix=self.bisection.handler.find_fix) Bisector.bisect(self, nhandler, handler.good_revision, handler.bad_revision) except MozRegressionError: self._finish_on_exception(None)
def _bisect_inbounds(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.inbound_branch, good_rev, bad_rev)) handler = InboundHandler(find_fix=self.options.find_fix, ensure_good_and_bad=ensure_good_and_bad) result = self._do_bisect(handler, good_rev, bad_rev, expand=expand) if result == Bisection.FINISHED: LOG.info("Oh noes, no (more) inbound revisions :(") handler.print_range() if handler.good_revision == handler.bad_revision: LOG.warning("It seems that you used two changesets that are in" " in the same push. Check the pushlog url.") elif len(handler.build_range) == 2: # range reduced to 2 pushes: one good, one bad. result = handler.handle_merge() if result: branch, good_rev, bad_rev = result self.fetch_config.set_repo(branch) return self._bisect_inbounds(good_rev, bad_rev, expand=DEFAULT_EXPAND) else: bugids = find_bugids_in_push( handler.build_range[1].repo_name, handler.build_range[1].changeset) if len(bugids) == 1: word = 'fix' if handler.find_fix else 'regression' LOG.info("Looks like the following bug has the changes" " which introduced the {}:\n{}".format( word, bug_url(bugids[0]))) elif result == Bisection.USER_EXIT: self._print_resume_info(handler) else: # NO_DATA. With inbounds, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info('There are no build artifacts on inbound for these' ' changesets (they are probably too old).') return 1 return 0
class TestInboundHandler(unittest.TestCase): def setUp(self): self.handler = InboundHandler() @patch('mozregression.bisector.LOG') def test_print_progress(self, logger): log = [] logger.info = log.append self.handler.set_build_range([ Mock(short_changeset='12'), Mock(short_changeset='123'), Mock(short_changeset='1234'), Mock(short_changeset='12345'), ]) new_data = [ Mock(short_changeset='1234'), Mock(short_changeset='12345') ] self.handler._print_progress(new_data) self.assertIn('from [12, 12345] (4 revisions)', log[0]) self.assertIn('to [1234, 12345] (2 revisions)', log[0]) self.assertIn('1 steps left', log[0]) @patch('mozregression.bisector.LOG') def test_user_exit(self, logger): log = [] logger.info = log.append self.handler.good_revision = '3' self.handler.bad_revision = '1' self.handler.user_exit(0) self.assertEqual('Newest known good inbound revision: 3', log[0]) self.assertEqual('Oldest known bad inbound revision: 1', log[1])
class TestInboundHandler(unittest.TestCase): def setUp(self): self.handler = InboundHandler() @patch('mozregression.bisector.LOG') def test_print_progress(self, logger): log = [] logger.info = log.append self.handler.set_build_range([ Mock(short_changeset='12'), Mock(short_changeset='123'), Mock(short_changeset='1234'), Mock(short_changeset='12345'), ]) new_data = [ Mock(short_changeset='1234'), Mock(short_changeset='12345') ] self.handler._print_progress(new_data) self.assertIn('from [12, 12345] (4 builds)', log[0]) self.assertIn('to [1234, 12345] (2 builds)', log[0]) self.assertIn('1 steps left', log[0]) @patch('mozregression.bisector.LOG') def test_user_exit(self, logger): log = [] logger.info = log.append self.handler.good_revision = '3' self.handler.bad_revision = '1' self.handler.user_exit(0) self.assertEqual('Newest known good inbound revision: 3', log[0]) self.assertEqual('Oldest known bad inbound revision: 1', log[1])
class TestInboundHandler(unittest.TestCase): def setUp(self): self.handler = InboundHandler() @patch("mozregression.bisector.LOG") def test_print_progress(self, logger): log = [] logger.info = log.append self.handler.set_build_range( [ Mock(short_changeset="12"), Mock(short_changeset="123"), Mock(short_changeset="1234"), Mock(short_changeset="12345"), ] ) new_data = [Mock(short_changeset="1234"), Mock(short_changeset="12345")] self.handler._print_progress(new_data) self.assertIn("from [12, 12345] (4 revisions)", log[0]) self.assertIn("to [1234, 12345] (2 revisions)", log[0]) self.assertIn("1 steps left", log[0]) @patch("mozregression.bisector.LOG") def test_user_exit(self, logger): log = [] logger.info = log.append self.handler.good_revision = "3" self.handler.bad_revision = "1" self.handler.user_exit(0) self.assertEqual("Newest known good inbound revision: 3", log[0]) self.assertEqual("Oldest known bad inbound revision: 1", log[1])
def nightlies_to_inbound(self): """ Call this when going from nightlies to inbound. """ assert self.bisection self.started.emit() try: # first we need to find the changesets first, last = self.bisection.handler.find_inbound_changesets() # create the inbound handler, and go with that handler = InboundHandler(find_fix=self.bisection.handler.find_fix) Bisector.bisect(self, handler, first, last) except MozRegressionError: self._finish_on_exception(None)
class TestInboundHandler(unittest.TestCase): def setUp(self): self.handler = InboundHandler() def test_build_infos(self): fetch_config = create_config('firefox', 'linux', 64) fetch_config.set_inbound_branch('my-branch') self.handler.set_build_data([{'changeset': '1', 'repository': 'my'}]) result = self.handler.build_infos(0, fetch_config) self.assertEqual(result, { 'changeset': '1', 'repository': 'my', 'build_type': 'inbound', 'app_name': 'firefox', 'repo': 'my-branch', }) def test_print_progress(self): log = [] self.handler._logger = Mock(info=log.append) self.handler.set_build_data([ {'revision': '12'}, {'revision': '123'}, {'revision': '1234'}, {'revision': '12345'}, ]) new_data = [{'revision': '1234'}, {'revision': '12345'}] self.handler._print_progress(new_data) self.assertIn('from [12, 12345] (4 revisions)', log[0]) self.assertIn('to [1234, 12345] (2 revisions)', log[0]) self.assertIn('1 steps left', log[0]) def test_user_exit(self): log = [] self.handler._logger = Mock(info=log.append) self.handler.good_revision = '3' self.handler.bad_revision = '1' self.handler.user_exit(0) self.assertEqual('Newest known good inbound revision: 3', log[0]) self.assertEqual('Oldest known bad inbound revision: 1', log[1])
def bisect_further(self): assert self.bisection self.started.emit() handler = self.bisection.handler try: nhandler = InboundHandler(find_fix=self.bisection.handler.find_fix) Bisector.bisect(self, nhandler, handler.good_revision, handler.bad_revision, expand=DEFAULT_EXPAND, interrupt=self.should_stop.is_set) except MozRegressionError: self._finish_on_exception(None) except StopIteration: self.finished.emit(None, Bisection.USER_EXIT)
def bisect(self, fetch_config, options): self.stop() # global preferences global_prefs = get_prefs() # apply the global prefs now apply_prefs(global_prefs) self.bisector = GuiBisector(fetch_config, persist=global_prefs['persist']) # 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) self.bisector.download_manager.download_progress.connect( self.show_dl_progress) self.bisector.test_runner.evaluate_started.connect(self.evaluate) self.bisector.finished.connect(self.bisection_finished) self.bisector_created.emit(self.bisector) if options['bisect_type'] == 'nightlies': handler = NightlyHandler(find_fix=options['find_fix']) good = options['good_date'] bad = options['bad_date'] else: handler = InboundHandler(find_fix=options['find_fix']) good = options['good_changeset'] bad = options['bad_changeset'] # options for the app launcher launcher_kwargs = {} for name in ('profile', 'preferences'): if name in options: value = options[name] if value: launcher_kwargs[name] = value self.bisector.test_runner.launcher_kwargs = launcher_kwargs self.thread.start() self.bisector._bisect_args = (handler, good, bad) # this will be called in the worker thread. QTimer.singleShot(0, self.bisector.bisect) self.running_state_changed.emit(True)
def init_worker(self, fetch_config, options): AbstractBuildRunner.init_worker(self, fetch_config, options) self.worker.test_runner.evaluate_started.connect(self.evaluate) self.worker.finished.connect(self.bisection_finished) self.worker.handle_merge.connect(self.handle_merge) self.worker.choose_next_build.connect(self.choose_next_build) good, bad = options.pop('good'), options.pop('bad') if is_date_or_datetime(good) and is_date_or_datetime(bad) \ and not fetch_config.should_use_taskcluster(): handler = NightlyHandler(find_fix=options['find_fix']) else: handler = InboundHandler(find_fix=options['find_fix']) self.worker._bisect_args = (handler, good, bad) self.worker.download_in_background = \ self.global_prefs['background_downloads'] return self.worker.bisect
def setUp(self): self.handler = InboundHandler()