| # Copyright 2017 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import print_function |
| from __future__ import division |
| from __future__ import absolute_import |
| |
| import datetime |
| import mock |
| import sys |
| |
| from tracing.value.diagnostics import generic_set |
| from tracing.value.diagnostics import reserved_infos |
| |
| from dashboard.common import layered_cache |
| from dashboard.common import utils |
| from dashboard.models import histogram |
| from dashboard.pinpoint.models import change |
| from dashboard.pinpoint.models import errors |
| from dashboard.pinpoint.models import job |
| from dashboard.pinpoint import test |
| |
| _CHROMIUM_URL = 'https://chromium.googlesource.com/chromium/src' |
| _COMMENT_STARTED = (u"""\U0001f4cd Pinpoint job started. |
| https://testbed.example.com/job/1""") |
| |
| _COMMENT_COMPLETED_NO_COMPARISON = ( |
| u"""<b>\U0001f4cd Job complete. See results below.</b> |
| https://testbed.example.com/job/1""") |
| |
| _COMMENT_COMPLETED_NO_DIFFERENCES = ( |
| u"""<b>\U0001f4cd Couldn't reproduce a difference.</b> |
| https://testbed.example.com/job/1""") |
| |
| _COMMENT_COMPLETED_NO_DIFFERENCES_DUE_TO_FAILURE = ( |
| u"""<b>\U0001f63f Job finished with errors.</b> |
| https://testbed.example.com/job/1 |
| |
| One or both of the initial changes failed to produce any results. |
| Perhaps the job is misconfigured or the tests are broken? See the job |
| page for details.""") |
| |
| _COMMENT_FAILED = (u"""\U0001f63f Pinpoint job stopped with an error. |
| https://testbed.example.com/job/1 |
| |
| Error string""") |
| |
| _COMMENT_CODE_REVIEW = (u"""\U0001f4cd Job complete. |
| |
| See results at: https://testbed.example.com/job/1""") |
| |
| |
| def FakeCommitAsDict(commit_self): |
| """Fake for Commit.AsDict. |
| |
| Returns a canned commit dict based on the Commit's git_hash, which must start |
| with the prefix "git_hash_". |
| |
| Use like:: |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict', |
| autospec=True) |
| def testFoo(self, commit_as_dict): |
| commit_as_dict.side_effect = FakeCommitAsDict |
| ... |
| """ |
| git_hash = commit_self.git_hash |
| n = git_hash[len('git_hash_'):] |
| return { |
| 'repository': 'chromium', |
| 'git_hash': git_hash, |
| 'url': 'https://example.com/repository/+/' + git_hash, |
| 'author': 'author%s@chromium.org' % (n,), |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| |
| |
| @mock.patch.object(job.results2, 'GetCachedResults2', |
| mock.MagicMock(return_value='http://foo')) |
| class JobTest(test.TestCase): |
| |
| @mock.patch.object( |
| job.timing_record, 'GetSimilarHistoricalTimings', |
| mock.MagicMock( |
| return_value=job.timing_record.EstimateResult( |
| job.timing_record.Timings( |
| datetime.timedelta(seconds=10), datetime.timedelta( |
| seconds=5), datetime.timedelta( |
| seconds=100)), ['try', 'linux']))) |
| @mock.patch.object(job.scheduler, 'QueueStats', |
| mock.MagicMock(return_value=[])) |
| def testAsDictOptions_Estimate(self): |
| j = job.Job.New((), (), bug_id=123456) |
| |
| d = j.AsDict([job.OPTION_ESTIMATE]) |
| self.assertTrue('estimate' in d) |
| self.assertEqual(d['estimate']['timings'][0], 10) |
| self.assertEqual(d['estimate']['timings'][1], 5) |
| self.assertEqual(d['estimate']['timings'][2], 100) |
| self.assertEqual(d['estimate']['tags'], ['try', 'linux']) |
| |
| def testAsDictOptions_Inputs(self): |
| j = job.Job.New((), (), bug_id=123456) |
| d = j.AsDict([job.OPTION_INPUTS]) |
| self.assertEqual(d['state'], []) |
| |
| @mock.patch.object(job.timing_record, 'GetSimilarHistoricalTimings', |
| mock.MagicMock(return_value=None)) |
| @mock.patch.object(job.scheduler, 'QueueStats', |
| mock.MagicMock(return_value=[])) |
| def testAsDictOptions_EstimateFails(self): |
| j = job.Job.New((), (), bug_id=123456) |
| |
| d = j.AsDict([job.OPTION_ESTIMATE]) |
| self.assertFalse('estimate' in d) |
| |
| |
| class RetryTest(test.TestCase): |
| |
| def setUp(self): |
| super(RetryTest, self).setUp() |
| |
| def testStarted_RecoverableError_BacksOff(self): |
| j = job.Job.New((), (), comparison_mode='performance') |
| j.Start() |
| j.state.Explore = mock.MagicMock(side_effect=errors.RecoverableError(None)) |
| j._Schedule = mock.MagicMock() |
| j.put = mock.MagicMock() |
| j.Fail = mock.MagicMock() |
| j.Run() |
| j.Run() |
| j.Run() |
| self.assertEqual(j._Schedule.call_args_list[0], |
| mock.call(countdown=job._TASK_INTERVAL * 2)) |
| self.assertEqual(j._Schedule.call_args_list[1], |
| mock.call(countdown=job._TASK_INTERVAL * 4)) |
| self.assertEqual(j._Schedule.call_args_list[2], |
| mock.call(countdown=job._TASK_INTERVAL * 8)) |
| self.assertFalse(j.Fail.called) |
| j.Run() |
| self.assertTrue(j.Fail.called) |
| |
| def testStarted_RecoverableError_Resets(self): |
| j = job.Job.New((), (), comparison_mode='performance') |
| j.Start() |
| j.state.Explore = mock.MagicMock(side_effect=errors.RecoverableError(None)) |
| j._Schedule = mock.MagicMock() |
| j.put = mock.MagicMock() |
| j.Fail = mock.MagicMock() |
| j.Run() |
| j.Run() |
| j.Run() |
| self.assertEqual(j._Schedule.call_args_list[0], |
| mock.call(countdown=job._TASK_INTERVAL * 2)) |
| self.assertEqual(j._Schedule.call_args_list[1], |
| mock.call(countdown=job._TASK_INTERVAL * 4)) |
| self.assertEqual(j._Schedule.call_args_list[2], |
| mock.call(countdown=job._TASK_INTERVAL * 8)) |
| self.assertFalse(j.Fail.called) |
| j.state.Explore = mock.MagicMock() |
| j.Run() |
| self.assertEqual(0, j.retry_count) |
| |
| |
| @mock.patch('dashboard.pinpoint.models.job_state.JobState.ChangesExamined', |
| lambda _: 10) |
| @mock.patch('dashboard.common.utils.ServiceAccountHttp', mock.MagicMock()) |
| class BugCommentTest(test.TestCase): |
| |
| def setUp(self): |
| super(BugCommentTest, self).setUp() |
| self.add_bug_comment = mock.MagicMock() |
| self.get_issue = mock.MagicMock() |
| patcher = mock.patch('dashboard.services.issue_tracker_service.' |
| 'IssueTrackerService') |
| issue_tracker_service = patcher.start() |
| issue_tracker_service.return_value = mock.MagicMock( |
| AddBugComment=self.add_bug_comment, GetIssue=self.get_issue) |
| self.addCleanup(patcher.stop) |
| |
| def testNoBug(self): |
| j = job.Job.New((), ()) |
| j.Start() |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(self.add_bug_comment.called) |
| |
| def testStarted(self): |
| j = job.Job.New((), (), bug_id=123456) |
| j.Start() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| _COMMENT_STARTED, |
| labels=mock.ANY, |
| send_email=True, |
| project='chromium') |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Started', labels) |
| self.assertNotIn('-Pinpoint-Job-Started', labels) |
| |
| def testCompletedNoComparison(self): |
| j = job.Job.New((), (), bug_id=123456) |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| _COMMENT_COMPLETED_NO_COMPARISON, |
| labels=['Pinpoint-Tryjob-Completed'], |
| project='chromium', |
| ) |
| |
| def testCompletedNoDifference(self): |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| _COMMENT_COMPLETED_NO_DIFFERENCES, |
| labels=mock.ANY, |
| status='WontFix', |
| project='chromium', |
| ) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-No-Repro', labels) |
| self.assertNotIn('-Pinpoint-No-Repro', labels) |
| |
| @mock.patch.object(job.job_state.JobState, 'FirstOrLastChangeFailed') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedNoDifferenceDueToFailureAtOneChange( |
| self, differences, first_or_last_change_failed): |
| differences.return_value = [] |
| first_or_last_change_failed.return_value = True |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| _COMMENT_COMPLETED_NO_DIFFERENCES_DUE_TO_FAILURE, |
| labels=mock.ANY, |
| project='chromium', |
| ) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Failed', labels) |
| self.assertNotIn('-Pinpoint-Job-Failed', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithCommit(self, differences, result_values, commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [0], [1.23456] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author@chromium.org', |
| labels=mock.ANY, |
| cc_list=['author@chromium.org'], |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('<b>Subject.</b>', message) |
| self.assertIn('https://example.com/repository/+/git_hash', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMergeIntoExisting(self, differences, result_values, |
| commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [0], [1.23456] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = { |
| 'status': 'Untriaged', |
| 'id': '111222', |
| 'projectId': 'chromium' |
| } |
| layered_cache.SetExternal('commit_hash_git_hash', 'chromium:111222') |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author@chromium.org', |
| cc_list=[], |
| labels=mock.ANY, |
| merge_issue='111222', |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('https://example.com/repository/+/git_hash', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedSkipsMergeWhenDuplicate(self, differences, result_values, |
| commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [0], [1.23456] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| |
| def _GetIssue(bug_id, project='chromium'): |
| if bug_id == '111222': |
| return {'status': 'Duplicate', 'projectId': project, 'id': '111222'} |
| else: |
| return {'status': 'Untriaged', 'projectId': project, 'id': str(bug_id)} |
| |
| self.get_issue.side_effect = _GetIssue |
| layered_cache.SetExternal('commit_hash_git_hash', 'chromium:111222') |
| j = job.Job.New((), (), |
| bug_id=123456, |
| comparison_mode='performance', |
| project='chromium') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author@chromium.org', |
| labels=mock.ANY, |
| cc_list=['author@chromium.org'], |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('https://example.com/repository/+/git_hash', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithInvalidIssue(self, differences, result_values, |
| commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [0], [1.23456] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = None |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.assertFalse(self.add_bug_comment.called) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithCommitAndDocs(self, differences, result_values, |
| commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [1.23456], [0] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), |
| bug_id=123456, |
| comparison_mode='performance', |
| tags={'test_path': 'master/bot/benchmark'}) |
| diag_dict = generic_set.GenericSet([[u'Benchmark doc link', |
| u'http://docs']]) |
| diag = histogram.SparseDiagnostic( |
| data=diag_dict.AsDict(), |
| start_revision=1, |
| end_revision=sys.maxsize, |
| name=reserved_infos.DOCUMENTATION_URLS.name, |
| test=utils.TestKey('master/bot/benchmark')) |
| diag.put() |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author@chromium.org', |
| labels=mock.ANY, |
| cc_list=['author@chromium.org'], |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('http://docs', message) |
| self.assertIn('Benchmark doc link', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.patch.GerritPatch.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithPatch(self, differences, result_values, patch_as_dict): |
| commits = (change.Commit('chromium', 'git_hash'),) |
| patch = change.GerritPatch('https://codereview.com', 672011, '2f0d5c7') |
| c = change.Change(commits, patch) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [40], [20] |
| patch_as_dict.return_value = { |
| 'url': 'https://codereview.com/c/672011/2f0d5c7', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author@chromium.org', |
| labels=mock.ANY, |
| cc_list=['author@chromium.org'], |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('https://codereview.com/c/672011/2f0d5c7', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.patch.GerritPatch.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedDoesReassign(self, differences, result_values, |
| patch_as_dict): |
| commits = (change.Commit('chromium', 'git_hash'),) |
| patch = change.GerritPatch('https://codereview.com', 672011, '2f0d5c7') |
| c = change.Change(commits, patch) |
| c = change.Change(commits, patch) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [40], [20] |
| patch_as_dict.return_value = { |
| 'url': 'https://codereview.com/c/672011/2f0d5c7', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = { |
| 'status': 'Assigned', |
| 'owner': { |
| 'email': 'some-author@somewhere.org' |
| } |
| } |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| owner='author@chromium.org', |
| status=None, |
| cc_list=['author@chromium.org', 'some-author@somewhere.org'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('https://codereview.com/c/672011/2f0d5c7', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.patch.GerritPatch.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedDoesNotReopen(self, differences, result_values, |
| patch_as_dict): |
| commits = (change.Commit('chromium', 'git_hash'),) |
| patch = change.GerritPatch('https://codereview.com', 672011, '2f0d5c7') |
| c = change.Change(commits, patch) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [40], [20] |
| patch_as_dict.return_value = { |
| 'url': 'https://codereview.com/c/672011/2f0d5c7', |
| 'author': 'author@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| } |
| self.get_issue.return_value = {'status': 'Fixed'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| owner=None, |
| status=None, |
| cc_list=['author@chromium.org'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('10 revisions compared', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMultipleDifferences(self, differences, result_values, |
| commit_as_dict): |
| c0 = change.Change((change.Commit('chromium', 'git_hash_0'),)) |
| c1 = change.Change((change.Commit('chromium', 'git_hash_1'),)) |
| c2 = change.Change((change.Commit('chromium', 'git_hash_2'),)) |
| c2_5 = change.Change((change.Commit('chromium', 'git_hash_2_5'))) |
| c3 = change.Change((change.Commit('chromium', 'git_hash_3'),)) |
| change_map = {c0: [50], c1: [0], c2: [40], c2_5: [0], c3: []} |
| differences.return_value = [(c0, c1), (c1, c2), (c2_5, c3)] |
| result_values.side_effect = lambda c: change_map.get(c, []) |
| commit_as_dict.side_effect = ( |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_1', |
| 'url': 'https://example.com/repository/+/git_hash_1', |
| 'author': 'author1@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_2', |
| 'url': 'https://example.com/repository/+/git_hash_2', |
| 'author': 'author2@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_3', |
| 'url': 'https://example.com/repository/+/git_hash_3', |
| 'author': 'author3@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| ) |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| |
| # We now only CC folks from the top commit. |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author1@chromium.org', |
| cc_list=['author1@chromium.org'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found significant differences at 2 commits.', message) |
| self.assertIn('1. Subject.', message) |
| self.assertIn('transitions from "no values" to "some values"', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Multiple-Culprits', labels) |
| self.assertNotIn('-Pinpoint-Multiple-Culprits', labels) |
| self.assertIn('Pinpoint-Multiple-MissingValues', labels) |
| self.assertNotIn('-Pinpoint-Multiple-MissingValues', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict', |
| autospec=True) |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMultipleDifferences_BlameAbsoluteLargest( |
| self, differences, result_values, commit_as_dict): |
| c1 = change.Change((change.Commit('chromium', 'git_hash_1'),)) |
| c2 = change.Change((change.Commit('chromium', 'git_hash_2'),)) |
| c3 = change.Change((change.Commit('chromium', 'git_hash_3'),)) |
| change_map = {c1: [10], c2: [0], c3: [-100]} |
| differences.return_value = [(None, c1), (c1, c2), (c2, c3)] |
| result_values.side_effect = lambda c: change_map.get(c, []) |
| commit_as_dict.side_effect = FakeCommitAsDict |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| |
| # We now only CC folks from the top commit. |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author3@chromium.org', |
| cc_list=['author3@chromium.org'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found significant differences at 2 commits.', message) |
| self.assertIn('https://example.com/repository/+/git_hash_3', message) |
| self.assertIn('https://example.com/repository/+/git_hash_2', message) |
| self.assertIn('https://example.com/repository/+/git_hash_1', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Multiple-Culprits', labels) |
| self.assertNotIn('-Pinpoint-Multiple-Culprits', labels) |
| self.assertIn('Pinpoint-Multiple-MissingValues', labels) |
| self.assertNotIn('-Pinpoint-Multiple-MissingValues', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict', |
| autospec=True) |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMultipleDifferences_TenCulpritsCcTopTwo( |
| self, differences, result_values, commit_as_dict): |
| self.Parameterized_TestCompletedMultipleDifferences(10, 2, differences, |
| result_values, |
| commit_as_dict) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict', |
| autospec=True) |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMultipleDifferences_HundredCulpritsCcTopThree( |
| self, differences, result_values, commit_as_dict): |
| self.Parameterized_TestCompletedMultipleDifferences(100, 3, differences, |
| result_values, |
| commit_as_dict) |
| |
| def Parameterized_TestCompletedMultipleDifferences(self, number_culprits, |
| expected_num_ccs, |
| differences, result_values, |
| commit_as_dict): |
| changes = [ |
| change.Change((change.Commit('chromium', 'git_hash_%d' % (i,)),)) |
| for i in range(1, number_culprits + 1) |
| ] |
| # Return [(None,c1), (c1,c2), (c2,c3), ...] |
| differences.return_value = zip([None] + changes, changes) |
| |
| # Ensure culprits are ordered by deriving change results values from commit |
| # names. E.g.: |
| # Change(git_hash_1) -> result_value=[1], |
| # Change(git_hash_2) -> result_value=[4], |
| # etc. |
| def ResultValuesFromFakeGitHash(change_obj): |
| if change_obj is None: |
| return [0] |
| v = int(change_obj.commits[0].git_hash[len('git_hash_'):]) |
| return [v * v] # Square the value to ensure increasing deltas. |
| |
| result_values.side_effect = ResultValuesFromFakeGitHash |
| commit_as_dict.side_effect = FakeCommitAsDict |
| |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| expected_ccs = [ |
| 'author%d@chromium.org' % (i,) |
| for i in range(number_culprits, number_culprits - expected_num_ccs, -1) |
| ] |
| |
| # We only CC folks from the top commits. |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner=expected_ccs[0], |
| cc_list=sorted(expected_ccs), |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Multiple-Culprits', labels) |
| self.assertNotIn('-Pinpoint-Multiple-Culprits', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedMultipleDifferences_NoDeltas(self, differences, |
| result_values, commit_as_dict): |
| """Regression test for http://crbug.com/1078680. |
| |
| Picks people to notify even when none of the differences have deltas (they |
| are all transitions to/from "No values"). |
| """ |
| # Two differences, neither has deltas (50 -> No Values, No Values -> 50). |
| c0 = change.Change((change.Commit('chromium', 'git_hash_0'),)) |
| c1 = change.Change((change.Commit('chromium', 'git_hash_1'),)) |
| c2 = change.Change((change.Commit('chromium', 'git_hash_2'),)) |
| change_map = {c0: [50], c1: [], c2: [50]} |
| differences.return_value = [(c0, c1), (c1, c2)] |
| result_values.side_effect = lambda c: change_map.get(c, []) |
| commit_as_dict.side_effect = ( |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_1', |
| 'url': 'https://example.com/repository/+/git_hash_1', |
| 'author': 'author1@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_2', |
| 'url': 'https://example.com/repository/+/git_hash_2', |
| 'author': 'author2@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| ) |
| |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| |
| # Notifies the owner of the first change in the list of differences, seeing |
| # as they are all equally small. |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='author1@chromium.org', |
| cc_list=['author1@chromium.org'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Missing Values', message) |
| self.assertIn('author1@chromium.org', message) |
| self.assertIn('author2@chromium.org', message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Multiple-MissingValues', labels) |
| self.assertNotIn('-Pinpoint-Multiple-MissingValues', labels) |
| self.assertNotIn('Pinpoint-Multiple-Culprits', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithAutoroll(self, differences, result_values, |
| commit_as_dict): |
| c = change.Change((change.Commit('chromium', 'git_hash'),)) |
| differences.return_value = [(None, c)] |
| result_values.side_effect = [20], [30] |
| commit_as_dict.return_value = { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash', |
| 'url': 'https://example.com/repository/+/git_hash', |
| 'author': 'chromium-autoroll@skia-public.iam.gserviceaccount.com', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.\n\nTBR=sheriff@bar.com', |
| } |
| |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.put() |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| mock.ANY, |
| status='Assigned', |
| owner='sheriff@bar.com', |
| cc_list=['chromium-autoroll@skia-public.iam.gserviceaccount.com'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| message = self.add_bug_comment.call_args[0][1] |
| self.assertIn('Found a significant difference at 1 commit.', message) |
| self.assertIn('chromium-autoroll@skia-public.iam.gserviceaccount.com', |
| message) |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Completed', labels) |
| self.assertNotIn('-Pinpoint-Job-Completed', labels) |
| self.assertIn('Pinpoint-Culprit-Found', labels) |
| self.assertNotIn('-Pinpoint-Culprit-Found', labels) |
| |
| @mock.patch('dashboard.pinpoint.models.change.commit.Commit.AsDict') |
| @mock.patch.object(job.job_state.JobState, 'ResultValues') |
| @mock.patch.object(job.job_state.JobState, 'Differences') |
| def testCompletedWithAutorollCulpritButNotMostRecent(self, differences, |
| result_values, |
| commit_as_dict): |
| """Regression test for http://crbug.com/1076756. |
| |
| When an autoroll has the biggest delta, assigns to its sheriff even when it |
| is not the latest change. |
| """ |
| c0 = change.Change((change.Commit('chromium', 'git_hash_0'),)) |
| c1 = change.Change((change.Commit('chromium', 'git_hash_1'),)) |
| c2 = change.Change((change.Commit('chromium', 'git_hash_2'),)) |
| change_map = {c0: [0], c1: [10], c2: [10]} |
| differences.return_value = [(c0, c1), (c1, c2)] |
| result_values.side_effect = lambda c: change_map.get(c, []) |
| commit_as_dict.side_effect = ( |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_1', |
| 'url': 'https://example.com/repository/+/git_hash_1', |
| 'author': 'chromium-autoroll@skia-public.iam.gserviceaccount.com', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.\n\nTBR=sheriff@bar.com', |
| }, |
| { |
| 'repository': 'chromium', |
| 'git_hash': 'git_hash_2', |
| 'url': 'https://example.com/repository/+/git_hash_2', |
| 'author': 'author2@chromium.org', |
| 'subject': 'Subject.', |
| 'message': 'Subject.\n\nCommit message.', |
| }, |
| ) |
| |
| self.get_issue.return_value = {'status': 'Untriaged'} |
| j = job.Job.New((), (), bug_id=123456, comparison_mode='performance') |
| j.put() |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| self.assertFalse(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| mock.ANY, |
| mock.ANY, |
| status='Assigned', |
| owner='sheriff@bar.com', |
| cc_list=['chromium-autoroll@skia-public.iam.gserviceaccount.com'], |
| labels=mock.ANY, |
| merge_issue=None, |
| project='chromium') |
| |
| @mock.patch.object(job.job_state.JobState, 'ScheduleWork', |
| mock.MagicMock(side_effect=AssertionError('Error string'))) |
| def testFailed(self): |
| j = job.Job.New((), (), bug_id=123456) |
| with self.assertRaises(AssertionError): |
| j.Run() |
| |
| self.ExecuteDeferredTasks('default') |
| self.assertTrue(j.failed) |
| self.add_bug_comment.assert_called_once_with( |
| 123456, |
| _COMMENT_FAILED, |
| send_email=True, |
| labels=mock.ANY, |
| project='chromium') |
| labels = self.add_bug_comment.call_args[1]['labels'] |
| self.assertIn('Pinpoint-Job-Failed', labels) |
| |
| @mock.patch.object(job.job_state.JobState, 'ScheduleWork', |
| mock.MagicMock(side_effect=AssertionError('Error string'))) |
| def testFailed_ExceptionDetailsFieldAdded(self): |
| j = job.Job.New((), (), bug_id=123456) |
| with self.assertRaises(AssertionError): |
| j.Run() |
| |
| j.exception = j.exception_details['traceback'] |
| exception_details = job.Job.exception_details |
| delattr(job.Job, 'exception_details') |
| j.put() |
| self.assertTrue(j.failed) |
| self.assertFalse(hasattr(j, 'exception_details')) |
| job.Job.exception_details = exception_details |
| j = j.key.get(use_cache=False) |
| self.assertTrue(j.failed) |
| self.assertTrue(hasattr(j, 'exception_details')) |
| self.assertEqual(j.exception, j.exception_details['traceback']) |
| self.assertTrue( |
| j.exception_details['message'] in j.exception.splitlines()[-1]) |
| |
| @mock.patch('dashboard.services.gerrit_service.PostChangeComment') |
| def testCompletedUpdatesGerrit(self, post_change_comment): |
| j = job.Job.New((), (), |
| gerrit_server='https://review.com', |
| gerrit_change_id='123456') |
| j.Run() |
| self.ExecuteDeferredTasks('default') |
| post_change_comment.assert_called_once_with('https://review.com', '123456', |
| _COMMENT_CODE_REVIEW) |