From 61190329f7589362c43544d1e78182d61c4322b4 Mon Sep 17 00:00:00 2001 From: Joe Gordon Date: Tue, 4 Feb 2014 15:26:24 -0800 Subject: [PATCH] Map failed jobs to bugs in gerrit comment Instead of just listing which bugs were seen in an entire gerrit event (multiple jenkins/zuul jobs), list which bugs were seen in which job. If one of the jobs has an unrecognized error don't display the comment about running recheck, just list which bugs were seen on which jobs (and which has an unrecognized error) Change-Id: I55b2eb8f0efe43ab22540294150d4bc9f5885510 --- elastic_recheck/elasticRecheck.py | 48 +++++++++++++++++++---- elastic_recheck/tests/unit/test_stream.py | 34 ++++++++++++++++ 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/elastic_recheck/elasticRecheck.py b/elastic_recheck/elasticRecheck.py index 8f91bca6..28b0d47f 100644 --- a/elastic_recheck/elasticRecheck.py +++ b/elastic_recheck/elasticRecheck.py @@ -112,13 +112,41 @@ class FailEvent(object): def name(self): return "%s,%s" % (self.change, self.rev) - def bug_urls(self): - if not self.get_all_bugs(): + def bug_urls(self, bugs=None): + if bugs is None: + bugs = self.get_all_bugs() + if not bugs: return None urls = ['https://bugs.launchpad.net/bugs/%s' % x for - x in self.get_all_bugs()] + x in bugs] return urls + def bug_urls_map(self): + """Produce map of which jobs failed due to which bugs.""" + if not self.get_all_bugs(): + return None + bug_map = {} + for job in self.failed_jobs: + if len(job.bugs) is 0: + bug_map[job.name] = None + else: + bug_map[job.name] = ', '.join(self.bug_urls(job.bugs)) + bug_list = [] + for job in bug_map: + if bug_map[job] is None: + bug_list.append("%s: unrecognized error" % job) + else: + bug_list.append("%s: %s" % (job, bug_map[job])) + return bug_list + + def is_fully_classified(self): + if self.get_all_bugs() is None: + return True + for job in self.failed_jobs: + if len(job.bugs) is 0: + return False + return True + def queue(self): # Assume one queue per gerrit event if len(self.failed_jobs) == 0: @@ -288,19 +316,23 @@ class Stream(object): def leave_comment(self, event, debug=False): if event.get_all_bugs(): - message = """I noticed tempest failed, I think you hit bug(s): + message = """I noticed jenkins failed, I think you hit bug(s): - %(bugs)s - +""" % {'bugs': "\n- ".join(event.bug_urls_map())} + if event.is_fully_classified(): + message += """ We don't automatically recheck or reverify, so please consider doing that manually if someone hasn't already. For a code review which is not yet approved, you can recheck by leaving a code review comment with just the text: - recheck bug %(bug)s""" % {'bugs': "\n- ".join(event.bug_urls()), - 'bug': list(event.get_all_bugs())[0]} + recheck bug %(bug)s""" % {'bug': list(event.get_all_bugs())[0]} + else: + message += """ +You have some unrecognized errors.""" else: - message = ("I noticed tempest failed, refer to: " + message = ("I noticed jenkins failed, refer to: " "https://wiki.openstack.org/wiki/" "GerritJenkinsGithub#Test_Failures") LOG.debug("Compiled comment for commit %s:\n%s" % diff --git a/elastic_recheck/tests/unit/test_stream.py b/elastic_recheck/tests/unit/test_stream.py index 37889c4c..db00ff3a 100644 --- a/elastic_recheck/tests/unit/test_stream.py +++ b/elastic_recheck/tests/unit/test_stream.py @@ -50,10 +50,12 @@ class TestStream(tests.TestCase): self.assertTrue(event.is_openstack_project()) self.assertEqual(event.queue(), "check") self.assertEqual(event.bug_urls(), None) + self.assertEqual(event.bug_urls_map(), None) self.assertEqual(sorted(event.failed_job_names()), ['gate-keystone-python26', 'gate-keystone-python27']) self.assertEqual(event.get_all_bugs(), None) + self.assertTrue(event.is_fully_classified()) event = stream.get_failed_tempest() self.assertEqual(event.change, "63078") @@ -108,3 +110,35 @@ class TestStream(tests.TestCase): self.assertNotIn('gate-tempest-dsvm-neutron-large-ops', job_names) self.assertNotIn('check-grenade-dsvm', job_names) self.assertNotIn('check-swift-dsvm-functional', job_names) + + def test_event(self): + with mock.patch.object( + elasticRecheck.Stream, '_does_es_have_data') as mock_data: + mock_data.return_value = True + stream = elasticRecheck.Stream("", "", "") + + event = stream.get_failed_tempest() + # Add bugs + for job in event.failed_jobs: + if job.name == 'gate-keystone-python26': + job.bugs = ['123456'] + self.assertEqual(event.change, "64749") + self.assertEqual(event.rev, "6") + self.assertEqual(event.project, "openstack/keystone") + self.assertEqual(event.name(), "64749,6") + self.assertEqual(event.url, "https://review.openstack.org/64749") + self.assertEqual(sorted(event.short_build_uuids()), + ["5dd41fe", "d3fd328"]) + self.assertTrue(event.is_openstack_project()) + self.assertEqual(event.queue(), "check") + self.assertEqual(event.bug_urls(), + ['https://bugs.launchpad.net/bugs/123456']) + self.assertEqual(event.bug_urls_map(), + ['gate-keystone-python27: unrecognized error', + 'gate-keystone-python26: ' + 'https://bugs.launchpad.net/bugs/123456']) + self.assertEqual(sorted(event.failed_job_names()), + ['gate-keystone-python26', + 'gate-keystone-python27']) + self.assertEqual(event.get_all_bugs(), ['123456']) + self.assertFalse(event.is_fully_classified())