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 testSubmitRevertSucceed(self, mock_fn, mock_commit, *_): repo_name = 'chromium' revision = 'rev1' commit_position = 123 cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', [], datetime(2017, 2, 1, 0, 0, 0))) mock_fn.return_value = cl_info mock_commit.return_value = True culprit = WfSuspectedCL.Create(repo_name, revision, commit_position) revert = RevertCL() revert_change_id = '54321' revert.revert_cl_url = 'https://%s/q/%s' % (self.review_server_host, revert_change_id) culprit.revert_cl = revert culprit.revert_status = status.COMPLETED culprit.put() revert_status = constants.CREATED_BY_FINDIT pipeline_input = SubmitRevertCLParameters( cl_key=culprit.key.urlsafe(), revert_status=revert_status, failure_type=failure_type.COMPILE) pipeline = SubmitRevertCLPipeline(pipeline_input) self.assertEqual(constants.COMMITTED, pipeline.run(pipeline_input)) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEqual(culprit.revert_submission_status, status.COMPLETED) mock_commit.assert_called_with(revert_change_id)
def testAddedReviewerFailedBefore(self, mock_fn, mock_send, *_): repo_name = 'chromium' revision = 'rev1' cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', [], datetime(2017, 2, 1, 0, 0, 0))) cl_info.owner_email = '*****@*****.**' revert_cl = ClInfo('revert_review_host', '123V3127') revert_cl.url = 'https://chromium-review.googlesource.com/54321' cl_info.reverts.append( Revert('20001', revert_cl, constants.DEFAULT_SERVICE_ACCOUNT, datetime(2017, 2, 1, 1, 0, 0))) mock_fn.return_value = cl_info culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = status.RUNNING culprit.cr_notification_status = status.COMPLETED culprit.put() revert_status, _, _ = gerrit.RevertCulprit(culprit.key.urlsafe(), 'm/b/1', failure_type.COMPILE, 'compile', self.codereview_info) self.assertEquals(revert_status, services_constants.CREATED_BY_FINDIT)
def testLatestBuildSucceeded(self, mock_fn, *_): repo_name = 'chromium' revision = 'rev1' cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.owner_email = '*****@*****.**' cl_info.commits.append( Commit('20001', 'rev1', datetime(2017, 2, 1, 0, 0, 0))) revert_cl = ClInfo('revert_review_host', '123V3127') revert_cl.url = 'https://codereview.chromium.org/54321' cl_info.reverts.append( Revert('20001', revert_cl, constants.DEFAULT_SERVICE_ACCOUNT, datetime(2017, 2, 1, 1, 0, 0))) mock_fn.return_value = cl_info culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = status.RUNNING culprit.put() pipeline = CreateRevertCLPipeline('m', 'b', 123, repo_name, revision) revert_status = pipeline.run('m', 'b', 123, repo_name, revision) self.assertEquals(revert_status, create_revert_cl_pipeline.SKIPPED) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEqual(culprit.revert_status, status.SKIPPED) self.assertIsNotNone(culprit.revert_cl) self.assertEqual(culprit.skip_revert_reason, create_revert_cl_pipeline.NEWEST_BUILD_GREEN)
def testCheckRevertStatusOfSuspectedCLFalsePositive(self, mock_fn, *_): mock_fn.return_value = { 'commit_position': 1, 'code_review_url': 'codereview.chromium.org/123', 'review_server_host': 'codereview.chromium.org', 'review_change_id': '123' } suspected_cl = WfSuspectedCL.Create('chromium', 'a1b2c3d4', 1) suspected_cl.revert_cl = RevertCL() check_reverted_cls._CheckRevertStatusOfSuspectedCL(suspected_cl) self.assertEqual(revert_cl_status.FALSE_POSITIVE, suspected_cl.revert_cl.status)
def testCheckRevertStatusOfSuspectedCLRevertedNoCodeReview( self, mock_fn, _): mock_fn.return_value = { 'commit_position': 1, 'code_review_url': 'badhost.org/123', 'review_server_host': 'badhost.org/123', 'review_change_id': '123' } suspected_cl = WfSuspectedCL.Create('chromium', 'a1b2c3d4', 1) suspected_cl.revert_cl = RevertCL() self.assertEqual( (None, None, None), check_reverted_cls._CheckRevertStatusOfSuspectedCL(suspected_cl))
def testRevertHasCompleted(self): repo_name = 'chromium' revision = 'rev1' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = status.COMPLETED culprit.put() pipeline = CreateRevertCLPipeline('m', 'b', 123, repo_name, revision) revert_status = pipeline.run('m', 'b', 123, repo_name, revision) self.assertEquals(revert_status, create_revert_cl_pipeline.CREATED_BY_FINDIT)
def testSendNotification(self): repo_name = 'chromium' revision = 'rev' commit_position = 123 revert_cl_url = 'revert_url' commit_status = constants.SKIPPED culprit = WfSuspectedCL.Create(repo_name, revision, 1) culprit.revert_cl = RevertCL() culprit.put() self.mock(irc, 'IRCClient', MockedIRCClient) self.assertTrue( irc.SendMessageToIrc(revert_cl_url, commit_position, revision, culprit.key.urlsafe(), commit_status))
def testRevertHasCompleted(self): repo_name = 'chromium' revision = 'rev1' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = analysis_status.COMPLETED culprit.put() parameters = CreateRevertCLParameters(cl_key=culprit.key.urlsafe(), build_key='build_key') pipeline_id = 'pipeline_id' self.assertFalse( culprit_action._CanCreateRevertForCulprit(parameters, pipeline_id))
def testSubmitRevertSucceed(self, mock_fn, mock_commit, mock_add, *_): repo_name = 'chromium' revision = 'rev1' commit_position = 123 cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', [], datetime(2017, 2, 1, 0, 0, 0))) mock_fn.return_value = cl_info mock_commit.return_value = True culprit = WfSuspectedCL.Create(repo_name, revision, commit_position) revert_for_culprit = RevertCL() revert_change_id = '54321' revert_for_culprit.revert_cl_url = 'https://%s/q/%s' % ( self.review_server_host, revert_change_id) culprit.revert_cl = revert_for_culprit culprit.revert_status = status.COMPLETED culprit.put() revert_status = services_constants.CREATED_BY_FINDIT commit_status = gerrit.CommitRevert( SubmitRevertCLParameters(cl_key=culprit.key.urlsafe(), revert_status=revert_status), self.codereview_info) self.assertEqual(services_constants.COMMITTED, commit_status) mock_commit.assert_called_once_with(revert_change_id) culprit_link = ( 'https://analysis.chromium.org/waterfall/culprit?key=%s' % (culprit.key.urlsafe())) false_positive_bug_link = gerrit.CreateFinditWrongBugLink( gerrit.FINDIT_BUILD_FAILURE_COMPONENT, culprit_link, revision) message = textwrap.dedent(""" Sheriffs, CL owner or CL reviewers: Please confirm this revert if it is correct. If it is a false positive, please reland the original CL and report this false positive at %s. For more information about Findit auto-revert: %s. Sheriffs, it'll be much appreciated if you could take a couple minutes to fill out this survey: %s.""") % ( false_positive_bug_link, gerrit._MANUAL_LINK, gerrit._SURVEY_LINK) mock_add.assert_called_once_with(revert_change_id, ['*****@*****.**'], message)
def testCanCommitRevert(self, *_): repo_name = 'chromium' revision = 'rev1' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = analysis_status.COMPLETED culprit.put() parameters = SubmitRevertCLParameters( cl_key=culprit.key.urlsafe(), revert_status=constants.CREATED_BY_FINDIT) pipeline_id = 'pipeline_id' self.assertTrue( culprit_action._CanCommitRevert(parameters, pipeline_id, 'codereview_info'))
def testCheckRevertStatusOfSuspectedCLReverted(self, mock_fn, *_): mock_fn.return_value = { 'commit_position': 1, 'code_review_url': 'codereview.chromium.org/q/123', 'review_server_host': 'codereview.chromium.org', 'review_change_id': '123' } suspected_cl = WfSuspectedCL.Create('chromium', 'a1b2c3d4', 1) suspected_cl.revert_cl = RevertCL() result = check_reverted_cls._CheckRevertStatusOfSuspectedCL( suspected_cl) self.assertTrue(result[0]) self.assertEqual(result[1], 'https://codereview.chromium.org/q/123') self.assertEqual(result[2], revert_cl_status.COMMITTED) self.assertEqual(revert_cl_status.COMMITTED, suspected_cl.revert_cl.status)
def testSendNotificationException(self, mocked_logging, _): repo_name = 'chromium' revision = 'rev' commit_position = 123 revert_cl_url = 'revert_url' commit_status = constants.SKIPPED culprit = WfSuspectedCL.Create(repo_name, revision, 1) culprit.revert_cl = RevertCL() culprit.put() self.mock(irc, 'IRCClient', MockedIRCClient) self.assertFalse( irc.SendMessageToIrc(revert_cl_url, commit_position, revision, culprit.key.urlsafe(), commit_status)) expected_message = 'Sending message to IRC failed with An exception.' mocked_logging.assert_called_with(expected_message)
def testCannotCommitRevertByAnotherAnalysis(self): repo_name = 'chromium' revision = 'rev1' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_cl = RevertCL() culprit.revert_status = analysis_status.COMPLETED culprit.revert_submission_status = analysis_status.RUNNING culprit.submit_revert_pipeline_id = 'pipeline_id' culprit.put() parameters = SubmitRevertCLParameters( cl_key=culprit.key.urlsafe(), revert_status=constants.CREATED_BY_FINDIT) pipeline_id = 'another_pipeline' self.assertFalse( culprit_action._CanCommitRevert(parameters, pipeline_id, 'codereview_info'))
def testSendNotificationToIRC(self, mock_mo, _): repo_name = 'chromium' revision = 'rev' revert_status = constants.CREATED_BY_FINDIT commit_status = constants.SKIPPED culprit = WfSuspectedCL.Create(repo_name, revision, 1) culprit.revert_cl = RevertCL() culprit.revert_cl.revert_cl_url = 'revert_url' culprit.put() pipeline_input = SendNotificationToIrcParameters( cl_key=culprit.key.urlsafe(), revert_status=revert_status, commit_status=commit_status, failure_type=failure_type.COMPILE) self.assertTrue(culprit_action.SendMessageToIRC(pipeline_input)) parameters = {'type': 'compile', 'action_taken': 'irc_notified'} mock_mo.assert_called_once_with(parameters)
def testCommitRevertNoCodeReview(self, _): repo_name = 'chromium' revision = 'rev1' commit_position = 123 cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', [], datetime(2017, 2, 1, 0, 0, 0))) culprit = WfSuspectedCL.Create(repo_name, revision, commit_position) revert_for_culprit = RevertCL() revert_change_id = '54321' revert_for_culprit.revert_cl_url = 'https://%s/q/%s' % ( self.review_server_host, revert_change_id) culprit.revert_cl = revert_for_culprit culprit.revert_status = status.COMPLETED culprit.put() revert_status = services_constants.CREATED_BY_FINDIT self.assertEquals( services_constants.ERROR, gerrit.CommitRevert( SubmitRevertCLParameters(cl_key=culprit.key.urlsafe(), revert_status=revert_status), 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 testSubmitRevertFailed(self, mock_fn, mock_commit, mock_add, *_): repo_name = 'chromium' revision = 'rev1' commit_position = 123 cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', [], datetime(2017, 2, 1, 0, 0, 0))) mock_fn.return_value = cl_info mock_commit.return_value = False culprit = WfSuspectedCL.Create(repo_name, revision, commit_position) revert_for_culprit = RevertCL() revert_change_id = '54321' revert_for_culprit.revert_cl_url = 'https://%s/q/%s' % ( self.review_server_host, revert_change_id) culprit.revert_cl = revert_for_culprit culprit.revert_status = status.COMPLETED culprit.put() revert_status = services_constants.CREATED_BY_FINDIT commit_status = gerrit.CommitRevert( SubmitRevertCLParameters(cl_key=culprit.key.urlsafe(), revert_status=revert_status), self.codereview_info) self.assertEqual(services_constants.ERROR, commit_status) mock_commit.assert_called_once_with(revert_change_id) culprit_link = ( 'https://analysis.chromium.org/waterfall/culprit?key=%s' % culprit.key.urlsafe()) false_positive_bug_link = gerrit.CreateFinditWrongBugLink( gerrit.FINDIT_BUILD_FAILURE_COMPONENT, culprit_link, revision) auto_revert_bug_query = urllib.urlencode({ 'status': 'Available', 'components': 'Tools>Test>FindIt>Autorevert', 'summary': 'Auto Revert failed on %s' % revision, 'comment': 'Detail is %s' % culprit_link }) auto_revert_bug_link = ( 'https://bugs.chromium.org/p/chromium/issues/entry?%s') % ( auto_revert_bug_query) message = textwrap.dedent(""" Sheriffs, CL owner or CL reviewers: Please submit this revert if it is correct. If it is a false positive, please abandon and report it at %s. If failed to submit the revert, please abandon it and report the failure at %s. For more information about Findit auto-revert: %s. Sheriffs, it'll be much appreciated if you could take a couple minutes to fill out this survey: %s.""") % ( false_positive_bug_link, auto_revert_bug_link, gerrit._MANUAL_LINK, gerrit._SURVEY_LINK) mock_add.assert_called_once_with('54321', ['*****@*****.**'], message)
def testTestLevelResultIsReturned(self, mock_fn): master_name = 'm' builder_name = 'b' build_number = 5 master_url = 'https://build.chromium.org/p/%s' % master_name builds = { 'builds': [{ 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'failed_steps': ['a', 'b on platform'] }] } task = WfSwarmingTask.Create(master_name, builder_name, 4, 'b on platform') task.parameters['ref_name'] = 'b' task.status = analysis_status.COMPLETED task.put() try_job = WfTryJob.Create(master_name, builder_name, 4) try_job.status = analysis_status.COMPLETED try_job.test_results = [{ 'culprit': { 'a': { 'repo_name': 'chromium', 'revision': 'r4_2', 'commit_position': 42, 'url': None, }, 'b': { 'tests': { 'Unittest3.Subtest1': { 'repo_name': 'chromium', 'revision': 'r4_10', 'commit_position': 410, 'url': None, }, } } }, }] try_job.put() analysis = WfAnalysis.Create(master_name, builder_name, build_number) analysis.status = analysis_status.COMPLETED analysis.failure_result_map = { 'a': '/'.join([master_name, builder_name, '4']), 'b on platform': { 'Unittest1.Subtest1': '/'.join([master_name, builder_name, '3']), 'Unittest2.Subtest1': '/'.join([master_name, builder_name, '4']), 'Unittest3.Subtest1': '/'.join([master_name, builder_name, '4']), }, } analysis.result = { 'failures': [{ 'step_name': 'a', 'first_failure': 4, 'last_pass': 3, 'supported': True, 'suspected_cls': [{ 'build_number': 4, 'repo_name': 'chromium', 'revision': 'r4_2_failed', 'commit_position': None, 'url': None, 'score': 2, 'hints': { 'modified f4_2.cc (and it was in log)': 2, }, }], }, { 'step_name': 'b on platform', 'first_failure': 3, 'last_pass': 2, 'supported': True, 'suspected_cls': [{ 'build_number': 3, 'repo_name': 'chromium', 'revision': 'r3_1', 'commit_position': None, 'url': None, 'score': 5, 'hints': { 'added x/y/f3_1.cc (and it was in log)': 5, }, }, { 'build_number': 4, 'repo_name': 'chromium', 'revision': 'r4_1', 'commit_position': None, 'url': None, 'score': 2, 'hints': { 'modified f4.cc (and it was in log)': 2, }, }], 'tests': [{ 'test_name': 'Unittest1.Subtest1', 'first_failure': 3, 'last_pass': 2, 'suspected_cls': [{ 'build_number': 2, 'repo_name': 'chromium', 'revision': 'r2_1', 'commit_position': None, 'url': None, 'score': 5, 'hints': { 'added x/y/f99_1.cc (and it was in log)': 5, }, }] }, { 'test_name': 'Unittest2.Subtest1', 'first_failure': 4, 'last_pass': 2, 'suspected_cls': [{ 'build_number': 2, 'repo_name': 'chromium', 'revision': 'r2_1', 'commit_position': None, 'url': None, 'score': 5, 'hints': { 'added x/y/f99_1.cc (and it was in log)': 5, }, }] }, { 'test_name': 'Unittest3.Subtest1', 'first_failure': 4, 'last_pass': 2, 'suspected_cls': [] }] }, { 'step_name': 'c', 'first_failure': 4, 'last_pass': 3, 'supported': False, 'suspected_cls': [], }] } analysis.put() suspected_cl_42 = WfSuspectedCL.Create('chromium', 'r4_2', 42) suspected_cl_42.builds = { build_util.CreateBuildId(master_name, builder_name, 5): { 'approaches': [analysis_approach_type.TRY_JOB] } } suspected_cl_42.put() suspected_cl_21 = WfSuspectedCL.Create('chromium', 'r2_1', None) suspected_cl_21.builds = { build_util.CreateBuildId(master_name, builder_name, 3): { 'approaches': [analysis_approach_type.HEURISTIC], 'top_score': 5 }, build_util.CreateBuildId(master_name, builder_name, 4): { 'approaches': [analysis_approach_type.HEURISTIC], 'top_score': 5 }, build_util.CreateBuildId(master_name, builder_name, build_number): { 'approaches': [analysis_approach_type.HEURISTIC], 'top_score': 5 } } suspected_cl_21.put() suspected_cl_410 = WfSuspectedCL.Create('chromium', 'r4_10', None) suspected_cl_410.builds = { build_util.CreateBuildId(master_name, builder_name, 4): { 'approaches': [ analysis_approach_type.HEURISTIC, analysis_approach_type.TRY_JOB ], 'top_score': 5 }, build_util.CreateBuildId(master_name, builder_name, build_number): { 'approaches': [analysis_approach_type.HEURISTIC], 'top_score': 5 } } revert_cl = RevertCL() revert_cl.revert_cl_url = 'revert_cl_url' suspected_cl_410.revert_cl = revert_cl suspected_cl_410.put() def confidence_side_effect(_, build_info, first_build_info): if (first_build_info and first_build_info.get('approaches') == [ analysis_approach_type.HEURISTIC, analysis_approach_type.TRY_JOB ]): return 100, analysis_approach_type.TRY_JOB if build_info and build_info.get('top_score'): return 90, analysis_approach_type.HEURISTIC return 98, analysis_approach_type.TRY_JOB mock_fn.side_effect = confidence_side_effect expected_results = [{ 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'a', 'is_sub_test': False, 'first_known_failed_build_number': 4, 'suspected_cls': [{ 'repo_name': 'chromium', 'revision': 'r4_2', 'commit_position': 42, 'confidence': 98, 'analysis_approach': 'TRY_JOB' }], 'analysis_approach': 'TRY_JOB', 'is_flaky_test': False, 'try_job_status': 'FINISHED', 'has_findings': True, 'is_finished': True, 'is_supported': True, }, { 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'b on platform', 'is_sub_test': True, 'test_name': 'Unittest1.Subtest1', 'first_known_failed_build_number': 3, 'suspected_cls': [{ 'repo_name': 'chromium', 'revision': 'r2_1', 'confidence': 90, 'analysis_approach': 'HEURISTIC' }], 'analysis_approach': 'HEURISTIC', 'is_flaky_test': False, 'try_job_status': 'FINISHED', 'has_findings': True, 'is_finished': True, 'is_supported': True, }, { 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'b on platform', 'is_sub_test': True, 'test_name': 'Unittest2.Subtest1', 'first_known_failed_build_number': 4, 'suspected_cls': [{ 'repo_name': 'chromium', 'revision': 'r2_1', 'confidence': 90, 'analysis_approach': 'HEURISTIC' }], 'analysis_approach': 'HEURISTIC', 'is_flaky_test': False, 'try_job_status': 'FINISHED', 'has_findings': True, 'is_finished': True, 'is_supported': True, }, { 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'b on platform', 'is_sub_test': True, 'test_name': 'Unittest3.Subtest1', 'first_known_failed_build_number': 4, 'suspected_cls': [{ 'repo_name': 'chromium', 'revision': 'r4_10', 'commit_position': 410, 'analysis_approach': 'TRY_JOB', 'confidence': 100, 'revert_cl_url': 'revert_cl_url' }], 'analysis_approach': 'TRY_JOB', 'is_flaky_test': False, 'try_job_status': 'FINISHED', 'has_findings': True, 'is_finished': True, 'is_supported': True, }, { 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'c', 'is_sub_test': False, 'analysis_approach': 'HEURISTIC', 'is_flaky_test': False, 'has_findings': False, 'is_finished': True, 'is_supported': False, }] self._MockMasterIsSupported(supported=True) response = self.call_api('AnalyzeBuildFailures', body=builds) self.assertEqual(200, response.status_int) self.assertItemsEqual(expected_results, response.json_body.get('results'))
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
def testRevertCLUrl(self): culprit = BaseSuspectedCL.Create('chromium', 'r1', 123) revert_cl = RevertCL() revert_cl.revert_cl_url = 'review_url' culprit.revert_cl = revert_cl self.assertEqual('review_url', culprit.revert_cl_url)
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 testTryJobResultReturnedForCompileFailure(self, _): master_name = 'm' builder_name = 'b' build_number = 8 master_url = 'https://build.chromium.org/p/%s' % master_name builds = { 'builds': [{ 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'failed_steps': ['compile'] }] } try_job = WfTryJob.Create(master_name, builder_name, 3) try_job.status = analysis_status.COMPLETED try_job.compile_results = [{ 'culprit': { 'compile': { 'repo_name': 'chromium', 'revision': 'r3', 'commit_position': 3, 'url': None, }, }, }] try_job.put() analysis = WfAnalysis.Create(master_name, builder_name, build_number) analysis.status = analysis_status.COMPLETED analysis.build_failure_type = failure_type.COMPILE analysis.failure_result_map = { 'compile': '/'.join([master_name, builder_name, '3']), } analysis.result = { 'failures': [{ 'step_name': 'compile', 'first_failure': 3, 'last_pass': 1, 'supported': True, 'suspected_cls': [{ 'build_number': 3, 'repo_name': 'chromium', 'revision': 'git_hash2', 'commit_position': 288, 'score': 1, 'hints': { 'modify d/e/f.cc': 1, } }] }] } analysis.put() culprit = WfSuspectedCL.Create('chromium', 'r3', 3) culprit.revert_submission_status = analysis_status.COMPLETED revert = RevertCL() revert.revert_cl_url = 'revert_cl_url' culprit.revert_cl = revert culprit.put() expected_results = [{ 'master_url': master_url, 'builder_name': builder_name, 'build_number': build_number, 'step_name': 'compile', 'is_sub_test': False, 'first_known_failed_build_number': 3, 'suspected_cls': [ { 'repo_name': 'chromium', 'revision': 'r3', 'commit_position': 3, 'analysis_approach': 'TRY_JOB', 'revert_cl_url': 'revert_cl_url', 'revert_committed': True }, ], 'analysis_approach': 'TRY_JOB', 'is_flaky_test': False, 'try_job_status': 'FINISHED', 'has_findings': True, 'is_finished': True, 'is_supported': True, }] self._MockMasterIsSupported(supported=True) response = self.call_api('AnalyzeBuildFailures', body=builds) self.assertEqual(200, response.status_int) self.assertEqual(expected_results, response.json_body.get('results'))