def testRevertCulprit(self, mock_revert, *_): repo_name = 'chromium' revision = 'rev1' build_key = 'm/b/123' revert_cl = RevertCL() revert_cl.revert_cl_url = 'url' revert_cl.created_time = datetime.datetime(2018, 6, 20, 0, 0, 0) mock_revert.return_value = (constants.CREATED_BY_FINDIT, revert_cl, None) culprit = WfSuspectedCL.Create(repo_name, revision, 1) culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) self.assertEqual( constants.CREATED_BY_FINDIT, culprit_action.RevertCulprit(pipeline_input, 'pipeline_id')) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEqual(revert_cl, culprit.revert_cl) self.assertEqual(analysis_status.COMPLETED, culprit.revert_status)
def RevertCulprit(urlsafe_key, build_id, build_failure_type, sample_step_name, codereview_info): """Creates a revert of a culprit and adds reviewers. Args: urlsafe_key (str): Key to the ndb model. build_id (str): Id of the sample failed build. build_failure_type (int): Failure type: compile, test or flake. sample_step_name (str): Sample failed step in the failed build. codereview_info (dict): Code review information about the culprit. Returns: (int, string, string): - Status of the reverting; - change_id of the revert; - reason why revert is skipped if it is skipped. """ culprit = entity_util.GetEntityFromUrlsafeKey(urlsafe_key) repo_name = culprit.repo_name revision = culprit.revision # 0. Gets information about this culprit. culprit_commit_position = codereview_info['commit_position'] culprit_change_id = codereview_info['review_change_id'] codereview = _GetCodeReview(codereview_info) print codereview if not codereview or not culprit_change_id: # pragma: no cover logging.error('Failed to get change id for %s/%s', repo_name, revision) return services_constants.ERROR, None, None culprit_cl_info = codereview.GetClDetails(culprit_change_id) if not culprit_cl_info: # pragma: no cover logging.error('Failed to get cl_info for %s/%s', repo_name, revision) return services_constants.ERROR, None, None # Checks if the culprit is a revert. If yes, bail out. if _IsCulpritARevert(culprit_cl_info): return (services_constants.SKIPPED, None, services_constants.CULPRIT_IS_A_REVERT) if culprit_cl_info.auto_revert_off: return services_constants.SKIPPED, None, services_constants.AUTO_REVERT_OFF # 1. Checks if a revert CL by sheriff has been created. reverts = culprit_cl_info.GetRevertCLsByRevision(revision) if reverts is None: # pragma: no cover # if no reverts, reverts should be [], only when some error happens it will # be None. logging.error('Failed to find patchset_id for %s/%s' % (repo_name, revision)) return services_constants.ERROR, None, None findit_revert = None for revert in reverts: if _IsOwnerFindit(revert.reverting_user_email): findit_revert = revert break if reverts and not findit_revert: # Sheriff(s) created the revert CL(s). return (services_constants.CREATED_BY_SHERIFF, None, services_constants.REVERTED_BY_SHERIFF) revert_change_id = None if findit_revert: revert_change_id = findit_revert.reverting_cl.change_id # 2. Crreate revert CL. # TODO (chanli): Better handle cases where 2 analyses are trying to revert # at the same time. if not revert_change_id: revert_reason = culprit.GenerateRevertReason(build_id, culprit_commit_position, revision, sample_step_name) if not revert_reason: # pragma: no cover. logging.error('Failed to get the reason for revert culprit %s', culprit.key.urlsafe() if culprit else '') return services_constants.ERROR, None, None bug_id = _GetBugIdForCulprit(culprit) revert_change_id = codereview.CreateRevert( revert_reason, culprit_change_id, culprit_cl_info.GetPatchsetIdByRevision(revision), bug_id=bug_id) if not revert_change_id: # pragma: no cover return services_constants.ERROR, None, None # Save revert CL info and notification info to culprit. revert_cl = None if not culprit.revert_cl: revert_cl = RevertCL() revert_cl.revert_cl_url = codereview.GetCodeReviewUrl(revert_change_id) revert_cl.created_time = time_util.GetUTCNow() # 3. Add reviewers. # If Findit cannot commit the revert, add sheriffs as reviewers and ask them # to 'LGTM' and commit the revert. action_settings = waterfall_config.GetActionSettings() can_commit_revert = (action_settings.get('auto_commit_revert_compile') if build_failure_type == failure_type.COMPILE else action_settings.get('auto_commit_revert_test')) if not can_commit_revert: success = _AddReviewers(revision, culprit, codereview, revert_change_id, False) if not success: # pragma: no cover logging.error('Failed to add reviewers for revert of' ' culprit %s/%s' % (repo_name, revision)) return services_constants.ERROR, revert_cl, None return services_constants.CREATED_BY_FINDIT, revert_cl, None
def testGenerateFinditMetrics(self): reverted_suspected_cl = WfSuspectedCL.Create('chromium', 'r1', 1) reverted_revert_cl = RevertCL() reverted_revert_cl.status = revert_cl_status.COMMITTED reverted_suspected_cl.revert_cl = reverted_revert_cl duplicate_fast_suspected_cl = WfSuspectedCL.Create('chromium', 'r2', 2) duplicate_revert_cl = RevertCL() duplicate_revert_cl.created_time = datetime(2017, 3, 15, 1) duplicate_revert_cl.status = revert_cl_status.DUPLICATE duplicate_fast_suspected_cl.revert_cl = duplicate_revert_cl duplicate_fast_suspected_cl.sheriff_action_time = datetime( 2017, 3, 15, 2) duplicate_slow_suspected_cl = WfSuspectedCL.Create('chromium', 'r3', 3) duplicate_revert_cl = RevertCL() duplicate_revert_cl.created_time = datetime(2017, 3, 15, 2) duplicate_revert_cl.status = revert_cl_status.DUPLICATE duplicate_slow_suspected_cl.revert_cl = duplicate_revert_cl duplicate_slow_suspected_cl.sheriff_action_time = datetime( 2017, 3, 15, 1) false_positive_suspected_cl = WfSuspectedCL.Create('chromium', 'r4', 4) false_positive_revert_cl = RevertCL() false_positive_revert_cl.status = revert_cl_status.FALSE_POSITIVE false_positive_suspected_cl.revert_cl = false_positive_revert_cl slow_suspected_cl = WfSuspectedCL.Create('chromium', 'r5', 5) slow_suspected_cl.cr_notification_time = datetime(2017, 3, 15, 2) slow_suspected_cl.should_be_reverted = True slow_suspected_cl.sheriff_action_time = datetime(2017, 3, 15, 1) false_positive_suspected_cl_no_revert = WfSuspectedCL.Create( 'chromium', 'r6', 6) expected_metrics = { 'revert_cls_detected': 5, 'revert_cls_created': 4, 'revert_cls_committed': 1, 'duplicate_revert_cls': 2, 'sheriffs_faster': 2, 'false_positives': 1, 'findit_faster': 1, 'faster_than_sheriff_metrics': { 'ninetieth_percentile': '01:00:00', 'average': '01:00:00', 'total': 1, 'fiftieth_percentile': '01:00:00', 'seventieth_percentile': '01:00:00' }, 'slower_than_sheriff_metrics': { 'ninetieth_percentile': '01:00:00', 'average': '01:00:00', 'total': 2, 'fiftieth_percentile': '01:00:00', 'seventieth_percentile': '01:00:00' } } metrics = auto_revert_metrics._GenerateFinditMetrics([ reverted_suspected_cl, duplicate_fast_suspected_cl, duplicate_slow_suspected_cl, false_positive_suspected_cl, slow_suspected_cl, false_positive_suspected_cl_no_revert ]) self.assertEqual(expected_metrics, metrics)
def _RevertCulprit(master_name, builder_name, build_number, repo_name, revision): culprit = _UpdateCulprit(repo_name, revision) if culprit.revert_cl and culprit.revert_status == status.COMPLETED: return CREATED_BY_FINDIT # 0. Gets information about this culprit. culprit_info = (suspected_cl_util.GetCulpritInfo(repo_name, revision)) culprit_commit_position = culprit_info['commit_position'] culprit_change_id = culprit_info['review_change_id'] culprit_host = culprit_info['review_server_host'] codereview = codereview_util.GetCodeReviewForReview(culprit_host) if not codereview or not culprit_change_id: # pragma: no cover logging.error('Failed to get change id for %s/%s' % (repo_name, revision)) return ERROR culprit_cl_info = codereview.GetClDetails(culprit_change_id) if not culprit_cl_info: # pragma: no cover logging.error('Failed to get cl_info for %s/%s' % (repo_name, revision)) return ERROR # Checks if the culprit is a revert created by Findit. If yes, bail out. if _IsOwnerFindit(culprit_cl_info.owner_email): _UpdateCulprit(repo_name, revision, status.SKIPPED, skip_revert_reason=CULPRIT_OWNED_BY_FINDIT) return SKIPPED if culprit_cl_info.auto_revert_off: _UpdateCulprit(repo_name, revision, status.SKIPPED, skip_revert_reason=AUTO_REVERT_OFF) return SKIPPED # 1. Checks if a revert CL by sheriff has been created. reverts = culprit_cl_info.GetRevertCLsByRevision(revision) if reverts is None: # pragma: no cover # if no reverts, reverts should be [], only when some error happens it will # be None. logging.error('Failed to find patchset_id for %s/%s' % (repo_name, revision)) return ERROR findit_revert = None for revert in reverts: if _IsOwnerFindit(revert.reverting_user_email): findit_revert = revert break if reverts and not findit_revert: # Sheriff(s) created the revert CL(s). return CREATED_BY_SHERIFF # 2. Reverts the culprit. if not _LatestBuildFailed(master_name, builder_name, build_number): # The latest build didn't fail, skip. _UpdateCulprit(repo_name, revision, status.SKIPPED, skip_revert_reason=NEWEST_BUILD_GREEN) return SKIPPED revert_change_id = None if findit_revert: revert_change_id = findit_revert.reverting_cl.change_id # TODO (chanli): Better handle cases where 2 analyses are trying to revert # at the same time. if not revert_change_id: _UpdateCulprit(repo_name, revision, status.RUNNING) revert_reason = textwrap.dedent(""" Findit (https://goo.gl/kROfz5) identified CL at revision %s as the culprit for failures in the build cycles as shown on: https://findit-for-me.appspot.com/waterfall/culprit?key=%s""") % ( culprit_commit_position or revision, culprit.key.urlsafe()) revert_change_id = codereview.CreateRevert( revert_reason, culprit_change_id, culprit_cl_info.GetPatchsetIdByRevision(revision)) if not revert_change_id: # pragma: no cover _UpdateCulprit(repo_name, revision, status.ERROR) logging.error('Revert for culprit %s/%s failed.' % (repo_name, revision)) culprit.put() return ERROR # Save revert CL info and notification info to culprit. if not culprit.revert_cl: revert_cl = RevertCL() revert_cl.revert_cl_url = codereview.GetCodeReviewUrl(revert_change_id) revert_cl.created_time = time_util.GetUTCNow() _UpdateCulprit(repo_name, revision, None, revert_cl=revert_cl) # 3. Add reviewers. sheriffs = rotations.current_sheriffs() message = textwrap.dedent(""" Sheriffs: Please confirm and "Quick L-G-T-M & CQ" this revert if it is correct. If it is a false positive, please close it. Findit (https://goo.gl/kROfz5) identified the original CL as the culprit for failures in the build cycles as shown on: https://findit-for-me.appspot.com/waterfall/culprit?key=%s""") % ( culprit.key.urlsafe()) success = codereview.AddReviewers(revert_change_id, sheriffs, message) if not success: # pragma: no cover _UpdateCulprit(repo_name, revision, status.ERROR) logging.error('Failed to add reviewers for revert of culprit %s/%s' % (repo_name, revision)) return ERROR _UpdateCulprit(repo_name, revision, revert_status=status.COMPLETED) return CREATED_BY_FINDIT