def setUp(self): super(GerritTest, self).setUp() self.http_client = DummyHttpClient() self.server_hostname = 'server.host.name' self.gerrit = Gerrit(self.server_hostname) self.gerrit.HTTP_CLIENT = self.http_client self.maxDiff = None
def _GetCodeReview(code_review_data): """Get codereview object based on review_server_host in code_review_data.""" if (not code_review_data or not code_review_data.get('review_server_host') or not codereview_util.IsCodeReviewGerrit( code_review_data['review_server_host'])): return None return Gerrit(code_review_data['review_server_host'])
def GetCodeReviewForReview(review_server_host, code_review_settings=None): """Returns an instance of CodeReview implementation or None if unknown.""" if not review_server_host: return None if IsCodeReviewRietveld(review_server_host, code_review_settings): return Rietveld(review_server_host) elif IsCodeReviewGerrit(review_server_host, code_review_settings): return Gerrit(review_server_host) return None
def NotifyCulprit(culprit, bug_id): """Sends a notification to a code review page. Args: culprit (FlakeCulprit): The culprit identified to have introduced flakiness. bud_id (int): An optional bug id to include in the notification. Pass None if not specified. Returns: Bool indicating whether a notification was sent. """ assert culprit revision = culprit.revision culprit_info = git.GetCodeReviewInfoForACommit(revision) review_server_host = culprit_info.get('review_server_host') review_change_id = culprit_info.get('review_change_id') code_review_settings = waterfall_config.GetCodeReviewSettings() codereview = Gerrit( review_server_host ) if review_server_host and codereview_util.IsCodeReviewGerrit( review_server_host, code_review_settings) else None sent = False if codereview and review_change_id: message = _GenerateMessageText(culprit, bug_id) sent = codereview.PostMessage(review_change_id, message) else: # Occasionally, a commit was not uploaded for code-review. logging.error('No code-review url for %s/%s', culprit.repo_name, culprit.revision) status = analysis_status.COMPLETED if sent else analysis_status.ERROR suspected_cl_util.UpdateCulpritNotificationStatus(culprit.key.urlsafe(), status) return sent
from infra_api_clients.codereview.gerrit import Gerrit from libs import analysis_status as status from libs import time_util from model.base_suspected_cl import RevertCL from model.flake.analysis.flake_culprit import FlakeCulprit from model.flake.analysis.master_flake_analysis import MasterFlakeAnalysis from model.wf_suspected_cl import WfSuspectedCL from services import constants as services_constants from services import gerrit from services.parameters import SendNotificationForCulpritParameters from services.parameters import SubmitRevertCLParameters from waterfall import buildbot from waterfall import waterfall_config from waterfall.test import wf_testcase _CODEREVIEW = Gerrit('chromium-review.googlesource.com') # pylint:disable=unused-argument, unused-variable # https://crbug.com/947753 class GerritTest(wf_testcase.WaterfallTestCase): def setUp(self): super(GerritTest, self).setUp() self.culprit_commit_position = 123 self.culprit_code_review_url = ( 'https://chromium-review.googlesource.com/12345') self.review_server_host = 'chromium-review.googlesource.com' self.review_change_id = '12345' self.codereview_info = {
class GerritTest(testing.AppengineTestCase): def setUp(self): super(GerritTest, self).setUp() self.http_client = DummyHttpClient() self.server_hostname = 'server.host.name' self.gerrit = Gerrit(self.server_hostname) self.gerrit.HTTP_CLIENT = self.http_client self.maxDiff = None def testGetCodeReviewUrl(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' self.assertEqual( 'https://server.host.name/q/%s' % change_id, self.gerrit.GetCodeReviewUrl(change_id)) def testPostMessage(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' response_str = '{}' self.http_client._SetPostMessageResponse( self.server_hostname, change_id, response_str) # This message should not change when being urlencoded or jsonized message = 'FinditWasHere' self.assertTrue(self.gerrit.PostMessage(change_id, message)) _url, data, _headers = self.http_client.requests[0] self.assertIn(message, data) def testAddReviewerNew(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.http_client._AddReviewerResponse(self.server_hostname, change_id, '*****@*****.**') self.assertTrue(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testAddReviewerNewAliased(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.http_client._AddReviewerResponse(self.server_hostname, change_id, '*****@*****.**') self.assertTrue(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testAddReviewerExisting(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.assertTrue(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testAddReviewerExistingAliased(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.http_client._AddReviewerResponse(self.server_hostname, change_id, None) self.assertTrue(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testAddReviewerMany(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.http_client._AddReviewerResponse(self.server_hostname, change_id, '*****@*****.**') self.http_client._AddReviewerResponse(self.server_hostname, change_id, '*****@*****.**') self.assertTrue(self.gerrit.AddReviewers(change_id, ['*****@*****.**', '*****@*****.**', '*****@*****.**'])) def testAddReviewerFailure(self): change_id = 'Iabc12345' self.http_client._SetReviewersResponse(self.server_hostname, change_id, ['*****@*****.**']) self.assertFalse(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testGetClInfoCQCommit(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-03-24 01:07:39.000000000', 'reviewers': { 'REVIEWER': [ {'email': '*****@*****.**'}, {'email': '*****@*****.**'}, {'email': '*****@*****.**'}, {'email': '*****@*****.**'} ], 'CC': [{'email': '*****@*****.**'}] }, 'messages': [{ 'author': {'email': '*****@*****.**'}, 'date': '2017-03-24 00:58:25.000000000', 'message': 'Patch Set 3: Commit-Queue+2', '_revision_number': 3 }], 'current_revision': '4bd07b5148508d3d100d9d4eafb9f4d233d7fa15', 'revisions': { '4bd07b5148508d3d100d9d4eafb9f4d233d7fa15': { '_number': 3, 'commit': { 'committer': { 'email': '*****@*****.**' }, 'message': 'some message' } } } }): cl_info = self.gerrit.GetClDetails(change_id) self.assertNotEqual( cl_info.commit_attempts.values()[0].last_cq_timestamp, cl_info.commits[0].timestamp) def testGetClInfoManualCommit(self): change_id = 'I7ecd56d7d0c3fef90cfe998a29b948c5032980e4' with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I7ecd56d7d0c3fef90cfe998a29b948c5032980e4', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-03-08 04:43:30.000000000', 'reviewers': { 'REVIEWER': [ {'email': '*****@*****.**'}, {'email': '*****@*****.**'}, {'email': '*****@*****.**' }, ], 'CC': [] }, 'messages': [], 'current_revision': '9ff179a3962bf3465815d3a85ad46e1c3b4a9e27', 'revisions': { '9ff179a3962bf3465815d3a85ad46e1c3b4a9e27': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': 'some message' } } } }): cl_info = self.gerrit.GetClDetails(change_id) self.assertEqual( cl_info.commit_attempts.values()[0].last_cq_timestamp, cl_info.commits[0].timestamp) def testGetClInfoRevertedCommit(self): change_id = 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0' with mock.patch.object( self.gerrit, '_Get', side_effect=[{ 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-02-27 18:56:54.000000000', '_number': 446905, 'reviewers': { 'REVIEWER': [ {'email': '*****@*****.**'}, {'email': '*****@*****.**'}, {'email': '*****@*****.**'} ], 'CC': [] }, 'messages': [ { 'id': 'b7d6785c324297ec4f1e6b2de34cf83f4c58e87c', 'author': {'email': '*****@*****.**'}, 'date': '2017-02-27 18:47:15.000000000', 'message': 'Patch Set 1: Commit-Queue+2', '_revision_number': 1 }, { 'id': 'b9e04aec4bffed0284c1f53cc5a9c88818807368', 'tag': 'autogenerated:gerrit:revert', 'author': {'email': '*****@*****.**'}, 'date': '2017-02-27 19:04:51.000000000', 'message': 'Created a revert of this change as ' 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', '_revision_number': 2 } ], 'current_revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'revisions': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT= True\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' }, } } }, { 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'status': 'MERGED', 'submitted': '2017-02-27 19:05:03.000000000', 'owner': { 'email': '*****@*****.**' }, '_number': 446788, 'reviewers': { 'REVIEWER': [ { 'email': '*****@*****.**', }, { 'email': '*****@*****.**', }, { 'email': '*****@*****.**', } ] }, 'messages': [ { 'id': '30496ce351a43c0b74d812e9e40b440f5acff9d5', 'author': { 'email': '*****@*****.**', }, 'date': '2017-02-27 19:04:53.000000000', 'message': 'Patch Set 1: Code-Review+1 Commit-Queue+2', '_revision_number': 1 }, ], 'current_revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'revisions': { 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', 'date': '2017-02-27 19:05:03.000000000', 'tz': 0 }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' }, } } }]): cl_info = self.gerrit.GetClDetails(change_id) self.assertEqual(cl_info.serialize(), { 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': ['*****@*****.**', '*****@*****.**', '*****@*****.**'], 'closed': True, 'commits': [{'patchset_id': 2, 'timestamp': '2017-02-27 18:56:54 UTC', 'revision': 'edda1046ce724695004242e943f59f5e1b2d00ff'}], 'cc': [], 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'reverts': [{'patchset_id': 2, 'reverting_user_email': '*****@*****.**', 'timestamp': '2017-02-27 19:04:51 UTC', 'reverting_cl': { 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': ['*****@*****.**', '*****@*****.**', '*****@*****.**'], 'closed': True, 'commits': [{ 'patchset_id': 2, 'timestamp': '2017-02-27 19:05:03 UTC', 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824'}], 'cc': [], 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'reverts': [], 'commit_attempts': [{ 'patchset_id': 1, 'timestamp': '2017-02-27 19:04:53 UTC', 'committing_user_email': '*****@*****.**'}]}}], 'commit_attempts': [{'patchset_id': 1, 'timestamp': '2017-02-27 18:47:15 UTC', 'committing_user_email': '*****@*****.**'}]}) def testCreateRevertSuccessful(self): change_id = 'I123456' reverting_change_id = 'I987654' response = self.http_client._MakeResponse( {'change_id': reverting_change_id}) url = 'https://%s/a/changes/%s/revert' % (self.server_hostname, change_id) self.http_client.SetResponse(url, (200, response)) self.assertEqual(reverting_change_id, self.gerrit.CreateRevert( 'Reason', change_id)) def testCreateRevertFailure(self): change_id = 'I123456' self.assertFalse(self.gerrit.CreateRevert('Reason', change_id)) def testRequestAddsAuthenticationPrefix(self): self.gerrit._AuthenticatedRequest(['changes', '123']) url, _payload, _headers = self.http_client.requests[0] self.assertEqual('https://server.host.name/a/changes/123', url) def testRequestKeepsAuthenticationPrefix(self): self.gerrit._AuthenticatedRequest(['a', 'changes', '123']) url, _payload, _headers = self.http_client.requests[0] self.assertEqual('https://server.host.name/a/changes/123', url)
def _CheckRevertStatusOfSuspectedCL(suspected_cl): """Updates suspected_cl with findings about what happened to its revert CL. Args: suspected_cl (wf_suspected_cl): A WfSuspectedCL entity. Returns: processed (bool): True if the suspected cl's revert outcome was determined, False if reverting was not relevant. None if the cl needed reverting but an outcome could not be determined. url (str): The code review url for manual investigation. status (str): The eventual outcome of the revert cl. """ revert_cl = suspected_cl.revert_cl if not revert_cl and not suspected_cl.should_be_reverted: # Findit did not deem this suspected cl as needing revert. No action needed. return False, None, None revision = suspected_cl.revision culprit_info = git.GetCodeReviewInfoForACommit(revision) review_server_host = culprit_info.get('review_server_host') change_id = culprit_info.get('review_change_id') if not review_server_host or not change_id: # pragma: no cover # TODO(lijeffrey): Handle cases a patch was committed without review. return None, None, None code_review_settings = FinditConfig().Get().code_review_settings codereview = Gerrit( review_server_host ) if review_server_host and codereview_util.IsCodeReviewGerrit( review_server_host, code_review_settings) else None if not codereview: logging.error('Could not get codereview for %s/q/%s', review_server_host, change_id) return None, None, None cl_info = codereview.GetClDetails(change_id) code_review_url = codereview.GetCodeReviewUrl(change_id) if not cl_info: logging.error('Could not get CL details for %s/q/%s', review_server_host, change_id) return None, code_review_url, None reverts_to_check = cl_info.GetRevertCLsByRevision(revision) if not reverts_to_check: logging.error('Could not get revert info for %s/q/%s', review_server_host, change_id) return None, code_review_url, None reverts_to_check.sort(key=lambda x: x.timestamp) if revert_cl: # Findit attempted to create a revert cl. any_revert_committed = False # Check whose revert CL was first commited. for revert in reverts_to_check: # pragma: no branch reverting_user = revert.reverting_user_email revert_commits = revert.reverting_cl.commits if revert_commits: # pragma: no branch any_revert_committed = True revert_commit = revert_commits[0] if reverting_user == constants.DEFAULT_SERVICE_ACCOUNT: # The sheriff used Findit's reverting CL. cq_attempt = revert.reverting_cl.commit_attempts[ revert_commit.patchset_id] _UpdateSuspectedCL( suspected_cl, sheriff_action_time=cq_attempt.last_cq_timestamp, revert_commit_timestamp=revert_commit.timestamp, revert_status=revert_cl_status.COMMITTED) break else: # Sheriff used own revert CL. _UpdateSuspectedCL( suspected_cl, sheriff_action_time=revert_commit.timestamp, revert_status=revert_cl_status.DUPLICATE) break # No revert was ever committed. False positive. if not any_revert_committed: # TODO(crbug.com/702056): Close the revert CLs that were not used. _UpdateSuspectedCL(suspected_cl, revert_status=revert_cl_status.FALSE_POSITIVE) elif suspected_cl.should_be_reverted: # pragma: no branch # Findit could have created a revert CL, but the sheriff was too fast. # Find the first revert that was successfully landed. for revert in reverts_to_check: # pragma: no branch revert_commits = revert.reverting_cl.commits if revert_commits: # pragma: no branch _UpdateSuspectedCL(suspected_cl, sheriff_action_time=revert.timestamp) break return True, code_review_url, (revert_cl.status if revert_cl else revert_cl_status.DUPLICATE)
class GerritTest(testing.AppengineTestCase): def setUp(self): super(GerritTest, self).setUp() self.http_client = DummyHttpClient() self.server_hostname = 'server.host.name' self.gerrit = Gerrit(self.server_hostname) self.gerrit.HTTP_CLIENT = self.http_client self.maxDiff = None def testGetCodeReviewUrl(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' self.assertEqual('https://server.host.name/q/%s' % change_id, self.gerrit.GetCodeReviewUrl(change_id)) def testPostMessage(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' response_str = '{}' self.http_client._SetPostMessageResponse(self.server_hostname, change_id, response_str) # This message should not change when being urlencoded or jsonized message = 'FinditWasHere' self.assertTrue(self.gerrit.PostMessage(change_id, message)) _url, data, _headers = self.http_client.requests[0] self.assertIn(message, data) def testPostMessageNoEmail(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' response_str = '{}' self.http_client._SetPostMessageResponse(self.server_hostname, change_id, response_str) # This message should not change when being urlencoded or jsonized message = 'FinditWasHere' self.assertTrue(self.gerrit.PostMessage(change_id, message, False)) _url, data, _headers = self.http_client.requests[0] self.assertIn(message, data) def testAddReviewerNew(self): change_id = 'Iabc12345' response = { 'reviewers': { '*****@*****.**': { 'input': '*****@*****.**', 'reviewers': [{ 'email': '*****@*****.**' }] } } } self.http_client._SetPostMessageResponse(self.server_hostname, change_id, json.dumps(response)) self.assertTrue( self.gerrit.AddReviewers(change_id, ['*****@*****.**'], 'message')) def testAddReviewerWrongformat(self): change_id = 'Iabc12345' self.assertTrue( self.gerrit.AddReviewers(change_id, ['[email protected]@d.com'], 'message')) def testAddReviewerExisting(self): change_id = 'Iabc12345' response = { 'reviewers': { '*****@*****.**': { 'input': '*****@*****.**', 'reviewers': [] } } } self.http_client._SetPostMessageResponse(self.server_hostname, change_id, json.dumps(response)) self.assertTrue( self.gerrit.AddReviewers(change_id, ['*****@*****.**'], 'message')) @mock.patch.object(logging, 'error') def testAddReviewerMany(self, mock_logging): # pylint:disable=unused-argument change_id = 'Iabc12345' response = { 'reviewers': { '*****@*****.**': { 'input': '*****@*****.**', 'reviewers': [{ 'email': '*****@*****.**' }] }, '*****@*****.**': { 'input': '*****@*****.**', 'reviewers': [{ 'email': '*****@*****.**' }] }, '*****@*****.**': { 'input': '*****@*****.**', 'reviewers': [{ 'email': '*****@*****.**' }] }, } } self.http_client._SetPostMessageResponse(self.server_hostname, change_id, json.dumps(response)) self.assertTrue( self.gerrit.AddReviewers(change_id, [ '*****@*****.**', '*****@*****.**', '*****@*****.**', '[email protected]@d.com' ], 'message')) # Assertions have never worked properly because we were using mock 1.0.1. # After rolling to mock 2.0.0, which fixes assertions, these assertions now # fail. https://crbug.com/947753. # pylint:disable=unused-argument # mock_logging.assert_has_called_with( # 'Reviewer\'s email is in wrong format: ' # '[email protected]@d.com') def testAddReviewerFailure(self): change_id = 'Iabc12345' self.assertFalse(self.gerrit.AddReviewers(change_id, ['*****@*****.**'])) def testGetClInfoCQCommit(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-03-24 01:07:39.000000000', 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }], 'CC': [{ 'email': '*****@*****.**' }] }, 'messages': [{ 'author': { 'email': '*****@*****.**' }, 'date': '2017-03-24 00:58:25.000000000', 'message': 'Patch Set 3: Commit-Queue+2', '_revision_number': 3 }], 'current_revision': '4bd07b5148508d3d100d9d4eafb9f4d233d7fa15', 'revisions': { '4bd07b5148508d3d100d9d4eafb9f4d233d7fa15': { '_number': 3, 'commit': { 'committer': { 'email': '*****@*****.**' }, 'message': 'some message', 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] } } }, 'subject': 'subject' }): cl_info = self.gerrit.GetClDetails(change_id) self.assertNotEqual( cl_info.commit_attempts.values()[0].last_cq_timestamp, cl_info.commits[0].timestamp) def testGetClInfoManualCommit(self): change_id = 'I7ecd56d7d0c3fef90cfe998a29b948c5032980e4' with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I7ecd56d7d0c3fef90cfe998a29b948c5032980e4', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-03-08 04:43:30.000000000', 'reviewers': { 'REVIEWER': [ { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, ], 'CC': [] }, 'messages': [], 'current_revision': '9ff179a3962bf3465815d3a85ad46e1c3b4a9e27', 'revisions': { '9ff179a3962bf3465815d3a85ad46e1c3b4a9e27': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': 'some message', 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] } } }, 'subject': 'subject' }): cl_info = self.gerrit.GetClDetails(change_id) self.assertEqual(cl_info.commit_attempts.values()[0].last_cq_timestamp, cl_info.commits[0].timestamp) def testGetClInfoRevertedCommit(self): change_id = 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0' with mock.patch.object( self.gerrit, '_Get', side_effect=[{ 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-02-27 18:56:54.000000000', '_number': 446905, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }], 'CC': [] }, 'messages': [{ 'id': 'b7d6785c324297ec4f1e6b2de34cf83f4c58e87c', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 18:47:15.000000000', 'message': 'Patch Set 1: Commit-Queue+2', '_revision_number': 1 }, { 'id': 'b9e04aec4bffed0284c1f53cc5a9c88818807368', 'tag': 'autogenerated:gerrit:revert', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 19:04:51.000000000', 'message': 'Created a revert of this change as ' 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', '_revision_number': 2 }], 'current_revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'revisions': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT= True\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUGS : 12345, 67890', 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] }, } }, 'subject': 'subject' }, { 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'status': 'MERGED', 'submitted': '2017-02-27 19:05:03.000000000', 'owner': { 'email': '*****@*****.**' }, '_number': 446788, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**', }, { 'email': '*****@*****.**', }, { 'email': '*****@*****.**', }] }, 'messages': [ { 'id': '30496ce351a43c0b74d812e9e40b440f5acff9d5', 'author': { 'email': '*****@*****.**', }, 'date': '2017-02-27 19:04:53.000000000', 'message': 'Patch Set 1: Code-Review+1 Commit-Queue+2', '_revision_number': 1 }, ], 'current_revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'revisions': { 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', 'date': '2017-02-27 19:05:03.000000000', 'tz': 0 }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUG: 123455', 'parents': [{ 'commit': ('b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b' ), }] }, } }, 'subject': 'subject', 'revert_of': 446905 }]): cl_info = self.gerrit.GetClDetails(change_id) expected_cl_info = { 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': [ '*****@*****.**', '*****@*****.**', '*****@*****.**' ], 'closed': True, 'commits': [{ 'patchset_id': 2, 'timestamp': '2017-02-27 18:56:54 UTC', 'revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'parent_revisions': ['ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f'] }], 'cc': [], 'subject': 'subject', 'description': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT= True\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUGS : 12345, 67890', 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'reverts': [{ 'patchset_id': 2, 'reverting_user_email': '*****@*****.**', 'timestamp': '2017-02-27 19:04:51 UTC', 'reverting_cl': { 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': [ '*****@*****.**', '*****@*****.**', '*****@*****.**' ], 'closed': True, 'commits': [{ 'patchset_id': 2, 'timestamp': '2017-02-27 19:05:03 UTC', 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'parent_revisions': ['b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b'] }], 'cc': [], 'subject': 'subject', 'description': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\n' 'Commit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUG: 123455', 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'reverts': [], 'commit_attempts': [{ 'patchset_id': 1, 'timestamp': '2017-02-27 19:04:53 UTC', 'committing_user_email': '*****@*****.**' }], 'revert_of': 446905, 'patchsets': { 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824': { 'patchset_id': 2, 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'parent_revisions': ['b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b'] } } } }], 'commit_attempts': [{ 'patchset_id': 1, 'timestamp': '2017-02-27 18:47:15 UTC', 'committing_user_email': '*****@*****.**' }], 'revert_of': None, 'patchsets': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { 'patchset_id': 2, 'revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'parent_revisions': ['ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f'] } } } self.assertEqual(cl_info.serialize(), expected_cl_info) def testCreateRevertSuccessful(self): change_id = 'I123456' reverting_change_id = 'I987654' response = self.http_client._MakeResponse( {'change_id': reverting_change_id}) url = 'https://%s/a/changes/%s/revert' % (self.server_hostname, change_id) self.http_client.SetResponse(url, (200, response)) with mock.patch.object(self.gerrit, '_GenerateRevertCLDescription', return_value='Reason'): self.assertEqual(reverting_change_id, self.gerrit.CreateRevert('Reason', change_id)) def testCreateRevertFailure(self): change_id = 'I123456' with mock.patch.object(self.gerrit, '_GenerateRevertCLDescription', return_value='Reason'): self.assertFalse(self.gerrit.CreateRevert('Reason', change_id)) def testRequestAddsAuthenticationPrefix(self): self.gerrit._AuthenticatedRequest(['changes', '123']) url, _payload, _headers = self.http_client.requests[0] self.assertEqual('https://server.host.name/a/changes/123', url) def testRequestKeepsAuthenticationPrefix(self): self.gerrit._AuthenticatedRequest(['a', 'changes', '123']) url, _payload, _headers = self.http_client.requests[0] self.assertEqual('https://server.host.name/a/changes/123', url) @mock.patch.object(time_util, 'GetUTCNow', return_value=datetime.datetime(2017, 2, 27, 20, 0, 0)) def testGenerateRevertCLDescriptionBypassCQ(self, _): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' reason = 'Reason' original_cl_description = textwrap.dedent(""" cl title some description NOAUTOREVERT= True BUGS : 12345, 67890 Cq-Include-Trybots: m1.b1:m2.b2 Change-Id: someid Reviewed-on: cl_url Commit-Queue: owner Reviewed-by: reviewers """).strip() expected_description = textwrap.dedent(""" Revert "cl title" This reverts commit edda1046ce724695004242e943f59f5e1b2d00ff. Reason for revert: Reason Original change's description: > cl title > > some description > > NOAUTOREVERT= True > BUGS : 12345, 67890 > Cq-Include-Trybots: m1.b1:m2.b2 > Change-Id: someid > Reviewed-on: cl_url > Commit-Queue: owner > Reviewed-by: reviewers No-Presubmit: true No-Tree-Checks: true No-Try: true BUGS : 12345, 67890 Cq-Include-Trybots: m1.b1:m2.b2 """).strip() with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-02-27 18:56:54.000000000', '_number': 446905, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }], 'CC': [] }, 'messages': [{ 'id': 'b7d6785c324297ec4f1e6b2de34cf83f4c58e87c', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 18:47:15.000000000', 'message': 'Patch Set 1: Commit-Queue+2', '_revision_number': 1 }], 'current_revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'revisions': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': original_cl_description, 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] }, } }, 'subject': 'cl title' }): # Assertions have never worked properly because we were using mock 1.0.1. # After rolling to mock 2.0.0, which fixes assertions, these assertions # now fail. https://crbug.com/948213 # pylint:disable=unused-argument # self.assertEqual( # expected_description, # self.gerrit._GenerateRevertCLDescription(change_id, reason)) pass @mock.patch.object(time_util, 'GetUTCNow', return_value=datetime.datetime(2017, 6, 1, 1, 0, 0)) def testGenerateRevertCLDescription(self, _): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' reason = 'Reason' original_cl_description = textwrap.dedent(""" cl title some description NOAUTOREVERT= True BUGS : 12345, 67890 Cq-Include-Trybots: m1.b1:m2.b2 Change-Id: someid Reviewed-on: cl_url Commit-Queue: owner Reviewed-by: reviewers """).strip() expected_description = textwrap.dedent(""" Revert "cl title" This reverts commit edda1046ce724695004242e943f59f5e1b2d00ff. Reason for revert: Reason Original change's description: > cl title > > some description > > NOAUTOREVERT= True > BUGS : 12345, 67890 > Cq-Include-Trybots: m1.b1:m2.b2 > Change-Id: someid > Reviewed-on: cl_url > Commit-Queue: owner > Reviewed-by: reviewers # Not skipping CQ checks because original CL landed > 1 day ago. BUGS : 12345, 67890 Cq-Include-Trybots: m1.b1:m2.b2 """).strip() with mock.patch.object( self.gerrit, '_Get', return_value={ 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-02-27 18:56:54.000000000', '_number': 446905, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }], 'CC': [] }, 'messages': [{ 'id': 'b7d6785c324297ec4f1e6b2de34cf83f4c58e87c', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 18:47:15.000000000', 'message': 'Patch Set 1: Commit-Queue+2', '_revision_number': 1 }], 'current_revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'revisions': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': original_cl_description, 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] }, } }, 'subject': 'cl title' }): # Assertions have never worked properly because we were using mock 1.0.1. # After rolling to mock 2.0.0, which fixes assertions, these assertions # now fail. https://crbug.com/948213 # pylint:disable=unused-argument # self.assertEqual( # expected_description, # self.gerrit._GenerateRevertCLDescription(change_id, reason)) pass def testGetBugLine(self): expected_results = { 'message': '', 'BUG: 24343\n': 'BUG: 24343\n', 'Bug: 23234\n': 'Bug: 23234\n', 'issue: 34254\n': 'issue: 34254\n' } for k, v in expected_results.iteritems(): self.assertEqual(v, self.gerrit._GetBugLine(k)) self.assertEqual('Bug: 12345\n', self.gerrit._GetBugLine('', '12345')) self.assertEqual('BUG: 12345, 67890\n', self.gerrit._GetBugLine('BUG: 12345\n', '67890')) def testGetCQTryBotLine(self): expected_results = { 'message': '', 'cq-include-trybots: m1.b1:m2.b2\n': 'cq-include-trybots: m1.b1:m2.b2\n', 'CQ_INCLUDE_TRYBOTS= m1.b1:m2.b2 \n': 'CQ_INCLUDE_TRYBOTS= m1.b1:m2.b2\n', 'Cq_Include_Trybots= m1.b1:m2.b2\n': 'Cq_Include_Trybots= m1.b1:m2.b2\n' } for k, v in expected_results.iteritems(): self.assertEqual(v, self.gerrit._GetCQTryBotLine(k)) @mock.patch.object(time_util, 'GetUTCNow', return_value=datetime.datetime(2017, 2, 7, 1, 0, 0)) def testGetCQFlagsOrExplanationWithinOneDay(self, _): time = datetime.datetime(2017, 2, 7, 0, 0, 0) self.assertEqual( 'No-Presubmit: true\nNo-Tree-Checks: true\nNo-Try: true\n', self.gerrit._GetCQFlagsOrExplanation(time)) def testSubmitRevert(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' response = self.http_client._MakeResponse({'change_id': change_id}) self.http_client._SetSubmitRevertResponse(self.server_hostname, change_id, response) self.assertTrue(self.gerrit.SubmitRevert(change_id)) @mock.patch.object(logging, 'error') def testSubmitRevertSubmitRuleFailed(self, mock_logging): change_id = '123456' url = 'https://%s/a/changes/%s/submit' % (self.server_hostname, change_id) response = 'Change 123456: needs Is-Pure-Revert' self.http_client.SetResponse(url, (409, response)) self.assertFalse(self.gerrit.SubmitRevert(change_id)) # Assertions have never worked properly because we were using mock 1.0.1. # After rolling to mock 2.0.0, which fixes assertions, these assertions now # fail. https://crbug.com/947753. # pylint:disable=unused-argument # mock_logging.assert_has_called_with( # 'Committing revert failed: Change 123456: needs Is-Pure-Revert') def testGetChangeIdFromReviewUrl(self): change_id = 'I40bc1e744806f2c4aadf0ce6609aaa61b4019fa7' url = 'https://server.host.name/q/%s' % change_id self.assertEqual(change_id, self.gerrit.GetChangeIdFromReviewUrl(url)) def testGet(self): path_parts = ['changes', '12345', 'detail'] with mock.patch.object(self.gerrit, '_HandleResponse', return_value='return_value'): self.assertEqual(self.gerrit._Get(path_parts), 'return_value') def testQueryClsNoQueryParames(self): self.assertEquals([], self.gerrit.QueryCls({})) def testQueryCls(self): with mock.patch.object( self.gerrit, '_Get', return_value=[{ 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'status': 'MERGED', 'owner': { 'email': '*****@*****.**' }, 'submitted': '2017-02-27 18:56:54.000000000', '_number': 446905, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }, { 'email': '*****@*****.**' }], 'CC': [] }, 'messages': [{ 'id': 'b7d6785c324297ec4f1e6b2de34cf83f4c58e87c', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 18:47:15.000000000', 'message': 'Patch Set 1: Commit-Queue+2', '_revision_number': 1 }, { 'id': 'b9e04aec4bffed0284c1f53cc5a9c88818807368', 'author': { 'email': '*****@*****.**' }, 'date': '2017-02-27 19:04:51.000000000', 'message': 'Created a revert of this change as ' 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', '_revision_number': 2 }], 'current_revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'revisions': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT= True\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUGS : 12345, 67890', 'parents': [{ 'commit': ('ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f' ), }] }, } }, 'subject': 'subject' }, { 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'status': 'MERGED', 'submitted': '2017-02-27 19:05:03.000000000', 'owner': { 'email': '*****@*****.**' }, '_number': 446788, 'reviewers': { 'REVIEWER': [{ 'email': '*****@*****.**', }, { 'email': '*****@*****.**', }, { 'email': '*****@*****.**', }] }, 'messages': [ { 'id': '30496ce351a43c0b74d812e9e40b440f5acff9d5', 'author': { 'email': '*****@*****.**', }, 'date': '2017-02-27 19:04:53.000000000', 'message': 'Patch Set 1: Code-Review+1 Commit-Queue+2', '_revision_number': 1 }, ], 'current_revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'revisions': { 'bd1db4534d7dc3f3f9c693ca0ac3e67caf56789': { '_number': 1, 'commit': { 'committer': { 'email': '*****@*****.**', 'date': '2017-02-27 19:05:03.000000000', 'tz': 0 }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUG: 123455', 'parents': [{ 'commit': ('b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b' ), }] }, }, 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824': { '_number': 2, 'commit': { 'committer': { 'email': '*****@*****.**', 'date': '2017-02-27 19:05:03.000000000', 'tz': 0 }, 'message': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUG: 123455', 'parents': [{ 'commit': ('b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b' ), }] }, } }, 'subject': 'subject', 'revert_of': 446905 }]): cls_info = self.gerrit.QueryCls({'author': '*****@*****.**'}) expected_cl_info = [{ 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': [ '*****@*****.**', '*****@*****.**', '*****@*****.**' ], 'closed': True, 'commits': [{ 'patchset_id': 2, 'timestamp': '2017-02-27 18:56:54 UTC', 'revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'parent_revisions': ['ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f'] }], 'cc': [], 'subject': 'subject', 'description': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT= True\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\nCommit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUGS : 12345, 67890', 'change_id': 'I4303e1b7166aaab873587a3fda0ec907d3d8ace0', 'reverts': [], 'commit_attempts': [{ 'patchset_id': 1, 'timestamp': '2017-02-27 18:47:15 UTC', 'committing_user_email': '*****@*****.**' }], 'revert_of': None, 'patchsets': { 'edda1046ce724695004242e943f59f5e1b2d00ff': { 'patchset_id': 2, 'revision': 'edda1046ce724695004242e943f59f5e1b2d00ff', 'parent_revisions': ['ef9ced2eee002d8c143d64d1e66e0a8eaeb8078f'] } } }, { 'server_hostname': 'server.host.name', 'auto_revert_off': True, 'owner_email': '*****@*****.**', 'reviewers': [ '*****@*****.**', '*****@*****.**', '*****@*****.**' ], 'closed': True, 'commits': [{ 'patchset_id': 2, 'timestamp': '2017-02-27 19:05:03 UTC', 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'parent_revisions': ['b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b'] }], 'cc': [], 'subject': 'subject', 'description': 'cl title\n\nsome description\n\n' 'NOAUTOREVERT=TRUE\n\nChange-Id: ' 'someid\nReviewed-on: cl_url\n' 'Commit-Queue: ' 'owner\nReviewed-by: reviewers\n\n' 'BUG: 123455', 'change_id': 'If02ca1cd494579d6bb92a157bf1819e3689cd6b1', 'reverts': [], 'commit_attempts': [{ 'patchset_id': 1, 'timestamp': '2017-02-27 19:04:53 UTC', 'committing_user_email': '*****@*****.**' }], 'revert_of': 446905, 'patchsets': { 'bd1db4534d7dc3f3f9c693ca0ac3e67caf56789': { 'patchset_id': 1, 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf56789', 'parent_revisions': ['b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b'] }, 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824': { 'patchset_id': 2, 'revision': 'bd1db4534d7dc3f3f9c693ca0ac3e67caf484824', 'parent_revisions': ['b63383e4e3f2b774ce0e6d3bfafb4adc84e48f2b'] } } }] s_results = [cl_info.serialize() for cl_info in cls_info] self.assertEqual(expected_cl_info, s_results)
class CreateRevertCLPipelineTest(wf_testcase.WaterfallTestCase): def setUp(self): super(CreateRevertCLPipelineTest, self).setUp() self.culprit_commit_position = 123 self.culprit_code_review_url = 'https://codereview.chromium.org/12345' self.review_server_host = 'codereview.chromium.org' self.review_change_id = '12345' def MockGetCodeReviewInfoForACommit(*_): culprit_info = { 'commit_position': self.culprit_commit_position, 'code_review_url': self.culprit_code_review_url, 'review_server_host': self.review_server_host, 'review_change_id': self.review_change_id } return culprit_info self.mock(git, 'GetCodeReviewInfoForACommit', MockGetCodeReviewInfoForACommit) @mock.patch.object(gerrit, '_GetCodeReview', return_value=Gerrit('host')) @mock.patch.object(time_util, 'GetUTCNow', return_value=datetime(2017, 2, 1, 16, 0, 0)) @mock.patch.object(Gerrit, 'PostMessage', return_value=True) @mock.patch.object(Gerrit, 'AddReviewers', return_value=True) @mock.patch.object(rotations, 'current_sheriffs', return_value=['*****@*****.**']) @mock.patch.object(Gerrit, 'CreateRevert') @mock.patch.object(Gerrit, 'GetClDetails') def testRevertCLSucceed(self, mock_fn, mock_revert, *_): repo_name = 'chromium' revision = 'rev1' commit_position = 123 build_key = 'm/b/123' cl_info = ClInfo(self.review_server_host, self.review_change_id) cl_info.commits.append( Commit('20001', 'rev1', ['rev0'], datetime(2017, 2, 1, 0, 0, 0))) cl_info.owner_email = '*****@*****.**' mock_fn.return_value = cl_info mock_revert.return_value = '54321' culprit = WfSuspectedCL.Create(repo_name, revision, commit_position) culprit.builds = { build_key: { 'status': None, 'failures': { 'step': ['test1'] } } } culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) pipeline = CreateRevertCLPipeline(pipeline_input) revert_status = pipeline.run(pipeline_input) self.assertEquals(revert_status, constants.CREATED_BY_FINDIT) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEqual(culprit.revert_status, status.COMPLETED) self.assertIsNotNone(culprit.revert_cl) 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://analysis.chromium.org/waterfall/culprit?key=%s\n Sample Failed Build: %s\n Sample Failed Step: %s""") % (commit_position, culprit.key.urlsafe(), buildbot.CreateBuildUrl('m', 'b', '123'), 'step') mock_revert.assert_called_with(reason, self.review_change_id, '20001', bug_id=None) @mock.patch.object(culprit_action, '_CanCreateRevertForCulprit', return_value=False) def testRevertSkipped(self, _): repo_name = 'chromium' revision = 'rev1' build_key = 'm/b/123' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_status = status.SKIPPED culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) pipeline = CreateRevertCLPipeline(pipeline_input) revert_status = pipeline.run(pipeline_input) self.assertEqual(constants.SKIPPED, revert_status) def testLogUnexpectedAborting(self): repo_name = 'chromium' revision = 'rev1' build_key = 'm/b/123' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_status = status.RUNNING culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) CreateRevertCLPipeline(pipeline_input).OnAbort(pipeline_input) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEquals(culprit.revert_status, status.ERROR) def testLogUnexpectedAbortingNoChange(self): repo_name = 'chromium' revision = 'rev1' build_key = 'm/b/123' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) CreateRevertCLPipeline(pipeline_input).OnAbort(pipeline_input) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertIsNone(culprit.revert_status) def testLogUnexpectedAbortingPipelineIdNotMatch(self): repo_name = 'chromium' revision = 'rev1' build_key = 'm/b/123' culprit = WfSuspectedCL.Create(repo_name, revision, 123) culprit.revert_pipeline_id = 'pipeline_id' culprit.put() pipeline_input = CreateRevertCLParameters( cl_key=culprit.key.urlsafe(), build_key=build_key, failure_type=failure_type.COMPILE) pipeline = CreateRevertCLPipeline(pipeline_input) pipeline.start() pipeline.OnAbort(pipeline_input) culprit = WfSuspectedCL.Get(repo_name, revision) self.assertEqual(culprit.revert_pipeline_id, 'pipeline_id')