def test_pushes_within_changes(mocker): push_first = {'1': {'changesets': ['a']}} other_pushes = {'2': {'changesets': ['b']}, '3': {'changesets': ['c']}} retry_get = mocker.patch('mozregression.json_pushes.retry_get') response = Mock(json=Mock(side_effect=[push_first, other_pushes])) retry_get.return_value = response jpushes = JsonPushes() pushes = jpushes.pushes_within_changes('fromchset', "tochset") assert pushes[0].push_id == '1' assert pushes[0].changeset == 'a' assert pushes[1].push_id == '2' assert pushes[1].changeset == 'b' assert pushes[2].push_id == '3' assert pushes[2].changeset == 'c' retry_get.assert_has_calls([ call('https://hg.mozilla.org/integration/mozilla-inbound/json-pushes' '?changeset=fromchset'), call().raise_for_status(), call().json(), call('https://hg.mozilla.org/integration/mozilla-inbound/json-pushes' '?fromchange=fromchset&tochange=tochset'), call().raise_for_status(), call().json() ])
def _bisect_integration(self, good_rev, bad_rev, ensure_good_and_bad=False, expand=0): LOG.info("Getting %s builds between %s and %s" % (self.fetch_config.integration_branch, good_rev, bad_rev)) handler = IntegrationHandler(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 integration 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_integration(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 integration branches, this can not happen if changesets # are incorrect - so builds are probably too old LOG.info( 'There are no build artifacts for these changesets (they are probably too old).' ) return 1 return 0
def test_pushes_within_changes(mocker): push_first = {'1': {'changesets': ['a']}} other_pushes = { '2': {'changesets': ['b']}, '3': {'changesets': ['c']} } retry_get = mocker.patch('mozregression.json_pushes.retry_get') response = Mock(json=Mock(side_effect=[push_first, other_pushes])) retry_get.return_value = response jpushes = JsonPushes() pushes = jpushes.pushes_within_changes('fromchset', "tochset") assert pushes[0].push_id == '1' assert pushes[0].changeset == 'a' assert pushes[1].push_id == '2' assert pushes[1].changeset == 'b' assert pushes[2].push_id == '3' assert pushes[2].changeset == 'c' retry_get.assert_has_calls([ call('https://hg.mozilla.org/integration/mozilla-inbound/json-pushes' '?changeset=fromchset'), call().raise_for_status(), call().json(), call('https://hg.mozilla.org/integration/mozilla-inbound/json-pushes' '?fromchange=fromchset&tochange=tochset'), call().raise_for_status(), call().json() ])
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 handle_merge(self): # let's check if we are facing a merge, and in that case, # continue the bisection from the merged branch. result = None LOG.debug("Starting merge handling...") # we have to check the commit of the most recent push most_recent_push = self.build_range[1] jp = JsonPushes(most_recent_push.repo_name) push = jp.push(most_recent_push.changeset, full='1') msg = push.changeset['desc'] LOG.debug("Found commit message:\n%s\n" % msg) branch = find_branch_in_merge_commit(msg) if not (branch and len(push.changesets) >= 2): return try: # so, this is a merge. We can find the oldest and youngest # changesets, and the branch where the merge comes from. oldest = push.changesets[0]['node'] # exclude the merge commit youngest = push.changesets[-2]['node'] LOG.debug("This is a merge from %s" % branch) # we can't use directly the youngest changeset because we # don't know yet if it is good. # # PUSH1 PUSH2 # [1 2] [3 4 5 6 7] # G MERGE B # # so first, grab it. This needs to be done on the right branch. jp2 = JsonPushes(branch) raw = [int(p.push_id) for p in jp2.pushes_within_changes(oldest, youngest)] data = jp2.pushes( startID=str(min(raw) - 2), endID=str(max(raw)), ) oldest = data[0].changesets[0] youngest = data[-1].changesets[-1] # we are ready to bisect further LOG.info("************* Switching to %s" % branch) gr, br = self._reverse_if_find_fix(oldest, youngest) result = (branch, gr, br) except MozRegressionError: LOG.debug("Got exception", exc_info=True) raise MozRegressionError( "Unable to exploit the merge commit. Origin branch is {}, and" " the commit message for {} was:\n{}".format( most_recent_push.repo_name, most_recent_push.short_changeset, msg ) ) LOG.debug('End merge handling') return result
def handle_merge(self): # let's check if we are facing a merge, and in that case, # continue the bisection from the merged branch. result = None LOG.debug("Starting merge handling...") # we have to check the commit of the most recent push most_recent_push = self.build_range[1] jp = JsonPushes(most_recent_push.repo_name) push = jp.push(most_recent_push.changeset, full='1') msg = push.changeset['desc'] LOG.debug("Found commit message:\n%s\n" % msg) branch = find_branch_in_merge_commit(msg) if not (branch and len(push.changesets) >= 2): return try: # so, this is a merge. We can find the oldest and youngest # changesets, and the branch where the merge comes from. oldest = push.changesets[0]['node'] # exclude the merge commit youngest = push.changesets[-2]['node'] LOG.debug("This is a merge from %s" % branch) # we can't use directly the youngest changeset because we # don't know yet if it is good. # # PUSH1 PUSH2 # [1 2] [3 4 5 6 7] # G MERGE B # # so first, grab it. This needs to be done on the right branch. jp2 = JsonPushes(branch) raw = [int(p.push_id) for p in jp2.pushes_within_changes(oldest, youngest)] data = jp2.pushes( startID=str(min(raw) - 2), endID=str(max(raw)), ) oldest = data[0].changesets[0] youngest = data[-1].changesets[-1] # we are ready to bisect further LOG.info("************* Switching to %s" % branch) gr, br = self._reverse_if_find_fix(oldest, youngest) result = (branch, gr, br) except MozRegressionError: LOG.debug("Got exception", exc_info=True) raise MozRegressionError( "Unable to exploit the merge commit. Origin branch is {}, and" " the commit message for {} was:\n{}".format( most_recent_push.repo_name, most_recent_push.short_changeset, msg ) ) LOG.debug('End merge handling') return result
def test_push_with_date_raise_appropriate_error(): jpushes = JsonPushes(branch='inbound') jpushes.pushes_within_changes = Mock(side_effect=EmptyPushlogError) with pytest.raises(EmptyPushlogError) as ctx: jpushes.push(date(2015, 1, 1)) assert str(ctx.value) == \ 'No pushes available for the date 2015-01-01 on inbound.'
def test_push_with_date_raise_appropriate_error(): jpushes = JsonPushes(branch='inbound') jpushes.pushes_within_changes = Mock(side_effect=EmptyPushlogError) with pytest.raises(EmptyPushlogError) as ctx: jpushes.push(date(2015, 1, 1)) assert str(ctx.value) == \ 'No pushes available for the date 2015-01-01 on inbound.'
def test_pushes_within_changes_using_dates(mocker): p1 = {'changesets': ['abc'], 'date': 12345} p2 = {'changesets': ['def'], 'date': 67891} pushes = {'1': p1, '2': p2} retry_get = mocker.patch('mozregression.json_pushes.retry_get') retry_get.return_value = Mock(json=Mock(return_value=pushes)) jpushes = JsonPushes(branch='m-i') pushes = jpushes.pushes_within_changes(date(2015, 1, 1), date(2015, 2, 2)) assert pushes[0].push_id == '1' assert pushes[1].push_id == '2' retry_get.assert_called_once_with( 'https://hg.mozilla.org/integration/mozilla-inbound/json-pushes?' 'enddate=2015-02-03&startdate=2015-01-01')
def test_pushes_within_changes_using_dates(mocker): p1 = {"changesets": ["abc"], "date": 12345} p2 = {"changesets": ["def"], "date": 67891} pushes = {"1": p1, "2": p2} retry_get = mocker.patch("mozregression.json_pushes.retry_get") retry_get.return_value = Mock(json=Mock(return_value=pushes)) jpushes = JsonPushes(branch="m-i") pushes = jpushes.pushes_within_changes(date(2015, 1, 1), date(2015, 2, 2)) assert pushes[0].push_id == "1" assert pushes[1].push_id == "2" retry_get.assert_called_once_with( "https://hg.mozilla.org/integration/mozilla-inbound/json-pushes?" "enddate=2015-02-03&startdate=2015-01-01")
def test_pushes_within_changes_using_dates(mocker): p1 = {'changesets': ['abc'], 'date': 12345} p2 = {'changesets': ['def'], 'date': 67891} pushes = {'1': p1, '2': p2} retry_get = mocker.patch('mozregression.json_pushes.retry_get') retry_get.return_value = Mock(json=Mock(return_value=pushes)) jpushes = JsonPushes(branch='m-i') pushes = jpushes.pushes_within_changes(date(2015, 1, 1), date(2015, 2, 2)) assert pushes[0].push_id == '1' assert pushes[1].push_id == '2' retry_get.assert_called_once_with( 'https://hg.mozilla.org/integration/mozilla-inbound/json-pushes?' 'startdate=2015-01-01&enddate=2015-02-03' )
def handle_merge(self): # let's check if we are facing a merge, and in that case, # continue the bisection from the merged branch. result = None LOG.debug("Starting merge handling...") # we have to check the commit of the most recent push most_recent_push = self.build_range[1] jp = JsonPushes(most_recent_push.repo_name) push = jp.push(most_recent_push.changeset, full='1') msg = push.changeset['desc'] LOG.debug("Found commit message:\n%s\n" % msg) branch = find_branch_in_merge_commit(msg, most_recent_push.repo_name) if not (branch and len(push.changesets) >= 2): # We did not find a branch, lets check the integration branches if we are bisecting m-c LOG.debug( "Did not find a branch, checking all integration branches") if get_name(most_recent_push.repo_name) == 'mozilla-central' and \ len(push.changesets) >= 2: branch = self._choose_integration_branch( most_recent_push.changeset) jp2 = JsonPushes(branch) try: data = jp2.pushes_within_changes( push.changesets[0]['node'], push.changesets[-1]['node']) except MozRegressionError, exc: LOG.error( "Failed to find changes in branch '%s' (error: %s)" % (branch, exc)) raise LOG.info("************* Switching to %s by" " process of elimination (no branch detected in" " commit message)" % branch) gr, br = self._reverse_if_find_fix(data[0].changeset, data[-1].changeset) return (branch, gr, br) else: return
def test_pushes_within_changes(mocker): push_first = {"1": {"changesets": ["a"]}} other_pushes = {"2": {"changesets": ["b"]}, "3": {"changesets": ["c"]}} retry_get = mocker.patch("mozregression.json_pushes.retry_get") response = Mock(json=Mock(side_effect=[push_first, other_pushes])) retry_get.return_value = response jpushes = JsonPushes() pushes = jpushes.pushes_within_changes("fromchset", "tochset") assert pushes[0].push_id == "1" assert pushes[0].changeset == "a" assert pushes[1].push_id == "2" assert pushes[1].changeset == "b" assert pushes[2].push_id == "3" assert pushes[2].changeset == "c" retry_get.assert_has_calls([ call("https://hg.mozilla.org/mozilla-central/json-pushes" "?changeset=fromchset"), call("https://hg.mozilla.org/mozilla-central/json-pushes" "?fromchange=fromchset&tochange=tochset"), ])
def handle_merge(self): # let's check if we are facing a merge, and in that case, # continue the bisection from the merged branch. result = None LOG.debug("Starting merge handling...") # we have to check the commit of the most recent push most_recent_push = self.build_range[1] jp = JsonPushes(most_recent_push.repo_name) push = jp.push(most_recent_push.changeset, full='1') msg = push.changeset['desc'] LOG.debug("Found commit message:\n%s\n" % msg) branch = find_branch_in_merge_commit(msg, most_recent_push.repo_name) if not (branch and len(push.changesets) >= 2): # We did not find a branch, lets check the integration branches if we are bisecting m-c LOG.debug( "Did not find a branch, checking all integration branches") if get_name(most_recent_push.repo_name) == 'mozilla-central' and \ len(push.changesets) >= 2: branch = self._choose_integration_branch( most_recent_push.changeset) oldest = push.changesets[0]['node'] youngest = push.changesets[-1]['node'] LOG.info("************* Switching to %s by" " process of elimination (no branch detected in" " commit message)" % branch) else: return else: # so, this is a merge. see how many changesets are in it, if it # is just one, we have our answer if len(push.changesets) == 2: LOG.info("Merge commit has only two revisions (one of which " "is the merge): we are done") return # Otherwise, we can find the oldest and youngest # changesets, and the branch where the merge comes from. oldest = push.changesets[0]['node'] # exclude the merge commit youngest = push.changesets[-2]['node'] LOG.info("************* Switching to %s" % branch) # we can't use directly the oldest changeset because we # don't know yet if it is good. # # PUSH1 PUSH2 # [1 2] [3 4 5 6 7] # G MERGE B # # so first grab the previous push to get the last known good # changeset. This needs to be done on the right branch. try: jp2 = JsonPushes(branch) raw = [ int(p.push_id) for p in jp2.pushes_within_changes(oldest, youngest) ] data = jp2.pushes( startID=str(min(raw) - 2), endID=str(max(raw)), ) older = data[0].changeset youngest = data[-1].changeset # we are ready to bisect further gr, br = self._reverse_if_find_fix(older, youngest) result = (branch, gr, br) except MozRegressionError: LOG.debug("Got exception", exc_info=True) raise MozRegressionError( "Unable to exploit the merge commit. Origin branch is {}, and" " the commit message for {} was:\n{}".format( most_recent_push.repo_name, most_recent_push.short_changeset, msg)) LOG.debug('End merge handling') return result
def handle_merge(self): # let's check if we are facing a merge, and in that case, # continue the bisection from the merged branch. result = None LOG.debug("Starting merge handling...") # we have to check the commit of the most recent push most_recent_push = self.build_range[1] jp = JsonPushes(most_recent_push.repo_name) push = jp.push(most_recent_push.changeset, full='1') msg = push.changeset['desc'] LOG.debug("Found commit message:\n%s\n" % msg) branch = find_branch_in_merge_commit(msg, most_recent_push.repo_name) if not (branch and len(push.changesets) >= 2): # We did not find a branch, lets check the integration branches if we are bisecting m-c LOG.debug("Did not find a branch, checking all integration branches") if get_name(most_recent_push.repo_name) == 'mozilla-central' and \ len(push.changesets) >= 2: branch = self._choose_integration_branch(most_recent_push.changeset) oldest = push.changesets[0]['node'] youngest = push.changesets[-1]['node'] LOG.info("************* Switching to %s by" " process of elimination (no branch detected in" " commit message)" % branch) else: return else: # so, this is a merge. see how many changesets are in it, if it # is just one, we have our answer if len(push.changesets) == 2: LOG.info("Merge commit has only two revisions (one of which " "is the merge): we are done") return # Otherwise, we can find the oldest and youngest # changesets, and the branch where the merge comes from. oldest = push.changesets[0]['node'] # exclude the merge commit youngest = push.changesets[-2]['node'] LOG.info("************* Switching to %s" % branch) # we can't use directly the oldest changeset because we # don't know yet if it is good. # # PUSH1 PUSH2 # [1 2] [3 4 5 6 7] # G MERGE B # # so first grab the previous push to get the last known good # changeset. This needs to be done on the right branch. try: jp2 = JsonPushes(branch) raw = [int(p.push_id) for p in jp2.pushes_within_changes(oldest, youngest)] data = jp2.pushes( startID=str(min(raw) - 2), endID=str(max(raw)), ) older = data[0].changeset youngest = data[-1].changeset # we are ready to bisect further gr, br = self._reverse_if_find_fix(older, youngest) result = (branch, gr, br) except MozRegressionError: LOG.debug("Got exception", exc_info=True) raise MozRegressionError( "Unable to exploit the merge commit. Origin branch is {}, and" " the commit message for {} was:\n{}".format( most_recent_push.repo_name, most_recent_push.short_changeset, msg ) ) LOG.debug('End merge handling') return result