Home | History | Annotate | Download | only in webkitpy
      1 # Copyright (C) 2009 Google Inc. All rights reserved.
      2 #
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #    * Redistributions of source code must retain the above copyright
      8 # notice, this list of conditions and the following disclaimer.
      9 #    * Redistributions in binary form must reproduce the above
     10 # copyright notice, this list of conditions and the following disclaimer
     11 # in the documentation and/or other materials provided with the
     12 # distribution.
     13 #    * Neither the name of Google Inc. nor the names of its
     14 # contributors may be used to endorse or promote products derived from
     15 # this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import os
     30 
     31 from webkitpy.bugzilla import Bug, Attachment
     32 from webkitpy.committers import CommitterList, Reviewer
     33 from webkitpy.mock import Mock
     34 from webkitpy.scm import CommitMessage
     35 from webkitpy.webkit_logging import log
     36 
     37 
     38 def _id_to_object_dictionary(*objects):
     39     dictionary = {}
     40     for thing in objects:
     41         dictionary[thing["id"]] = thing
     42     return dictionary
     43 
     44 
     45 # FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
     46 
     47 
     48 _patch1 = {
     49     "id": 197,
     50     "bug_id": 42,
     51     "url": "http://example.com/197",
     52     "is_obsolete": False,
     53     "is_patch": True,
     54     "review": "+",
     55     "reviewer_email": "foo (at] bar.com",
     56     "commit-queue": "+",
     57     "committer_email": "foo (at] bar.com",
     58     "attacher_email": "Contributer1",
     59 }
     60 
     61 
     62 _patch2 = {
     63     "id": 128,
     64     "bug_id": 42,
     65     "url": "http://example.com/128",
     66     "is_obsolete": False,
     67     "is_patch": True,
     68     "review": "+",
     69     "reviewer_email": "foo (at] bar.com",
     70     "commit-queue": "+",
     71     "committer_email": "non-committer (at] example.com",
     72     "attacher_email": "eric (at] webkit.org",
     73 }
     74 
     75 
     76 _patch3 = {
     77     "id": 103,
     78     "bug_id": 75,
     79     "url": "http://example.com/103",
     80     "is_obsolete": False,
     81     "is_patch": True,
     82     "review": "?",
     83     "attacher_email": "eric (at] webkit.org",
     84 }
     85 
     86 
     87 _patch4 = {
     88     "id": 104,
     89     "bug_id": 77,
     90     "url": "http://example.com/103",
     91     "is_obsolete": False,
     92     "is_patch": True,
     93     "review": "+",
     94     "commit-queue": "?",
     95     "reviewer_email": "foo (at] bar.com",
     96     "attacher_email": "Contributer2",
     97 }
     98 
     99 
    100 _patch5 = {
    101     "id": 105,
    102     "bug_id": 77,
    103     "url": "http://example.com/103",
    104     "is_obsolete": False,
    105     "is_patch": True,
    106     "review": "+",
    107     "reviewer_email": "foo (at] bar.com",
    108     "attacher_email": "eric (at] webkit.org",
    109 }
    110 
    111 
    112 _patch6 = { # Valid committer, but no reviewer.
    113     "id": 106,
    114     "bug_id": 77,
    115     "url": "http://example.com/103",
    116     "is_obsolete": False,
    117     "is_patch": True,
    118     "commit-queue": "+",
    119     "committer_email": "foo (at] bar.com",
    120     "attacher_email": "eric (at] webkit.org",
    121 }
    122 
    123 
    124 _patch7 = { # Valid review, patch is marked obsolete.
    125     "id": 107,
    126     "bug_id": 76,
    127     "url": "http://example.com/103",
    128     "is_obsolete": True,
    129     "is_patch": True,
    130     "review": "+",
    131     "reviewer_email": "foo (at] bar.com",
    132     "attacher_email": "eric (at] webkit.org",
    133 }
    134 
    135 
    136 # This must be defined before we define the bugs, thus we don't use
    137 # MockBugzilla.unassigned_email directly.
    138 _unassigned_email = "unassigned (at] example.com"
    139 
    140 
    141 # FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
    142 
    143 
    144 _bug1 = {
    145     "id": 42,
    146     "title": "Bug with two r+'d and cq+'d patches, one of which has an "
    147              "invalid commit-queue setter.",
    148     "assigned_to_email": _unassigned_email,
    149     "attachments": [_patch1, _patch2],
    150 }
    151 
    152 
    153 _bug2 = {
    154     "id": 75,
    155     "title": "Bug with a patch needing review.",
    156     "assigned_to_email": "foo (at] foo.com",
    157     "attachments": [_patch3],
    158 }
    159 
    160 
    161 _bug3 = {
    162     "id": 76,
    163     "title": "The third bug",
    164     "assigned_to_email": _unassigned_email,
    165     "attachments": [_patch7],
    166 }
    167 
    168 
    169 _bug4 = {
    170     "id": 77,
    171     "title": "The fourth bug",
    172     "assigned_to_email": "foo (at] foo.com",
    173     "attachments": [_patch4, _patch5, _patch6],
    174 }
    175 
    176 
    177 class MockBugzillaQueries(Mock):
    178 
    179     def __init__(self, bugzilla):
    180         Mock.__init__(self)
    181         self._bugzilla = bugzilla
    182 
    183     def _all_bugs(self):
    184         return map(lambda bug_dictionary: Bug(bug_dictionary, self._bugzilla),
    185                    self._bugzilla.bug_cache.values())
    186 
    187     def fetch_bug_ids_from_commit_queue(self):
    188         bugs_with_commit_queued_patches = filter(
    189                 lambda bug: bug.commit_queued_patches(),
    190                 self._all_bugs())
    191         return map(lambda bug: bug.id(), bugs_with_commit_queued_patches)
    192 
    193     def fetch_attachment_ids_from_review_queue(self):
    194         unreviewed_patches = sum([bug.unreviewed_patches()
    195                                   for bug in self._all_bugs()], [])
    196         return map(lambda patch: patch.id(), unreviewed_patches)
    197 
    198     def fetch_patches_from_commit_queue(self):
    199         return sum([bug.commit_queued_patches()
    200                     for bug in self._all_bugs()], [])
    201 
    202     def fetch_bug_ids_from_pending_commit_list(self):
    203         bugs_with_reviewed_patches = filter(lambda bug: bug.reviewed_patches(),
    204                                             self._all_bugs())
    205         bug_ids = map(lambda bug: bug.id(), bugs_with_reviewed_patches)
    206         # NOTE: This manual hack here is to allow testing logging in
    207         # test_assign_to_committer the real pending-commit query on bugzilla
    208         # will return bugs with patches which have r+, but are also obsolete.
    209         return bug_ids + [76]
    210 
    211     def fetch_patches_from_pending_commit_list(self):
    212         return sum([bug.reviewed_patches() for bug in self._all_bugs()], [])
    213 
    214 
    215 # FIXME: Bugzilla is the wrong Mock-point.  Once we have a BugzillaNetwork
    216 #        class we should mock that instead.
    217 # Most of this class is just copy/paste from Bugzilla.
    218 
    219 
    220 class MockBugzilla(Mock):
    221 
    222     bug_server_url = "http://example.com"
    223 
    224     unassigned_email = _unassigned_email
    225 
    226     bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4)
    227 
    228     attachment_cache = _id_to_object_dictionary(_patch1,
    229                                                 _patch2,
    230                                                 _patch3,
    231                                                 _patch4,
    232                                                 _patch5,
    233                                                 _patch6,
    234                                                 _patch7)
    235 
    236     def __init__(self):
    237         Mock.__init__(self)
    238         self.queries = MockBugzillaQueries(self)
    239         self.committers = CommitterList(reviewers=[Reviewer("Foo Bar",
    240                                                             "foo (at] bar.com")])
    241 
    242     def fetch_bug(self, bug_id):
    243         return Bug(self.bug_cache.get(bug_id), self)
    244 
    245     def fetch_attachment(self, attachment_id):
    246         # This could be changed to .get() if we wish to allow failed lookups.
    247         attachment_dictionary = self.attachment_cache[attachment_id]
    248         bug = self.fetch_bug(attachment_dictionary["bug_id"])
    249         for attachment in bug.attachments(include_obsolete=True):
    250             if attachment.id() == int(attachment_id):
    251                 return attachment
    252 
    253     def bug_url_for_bug_id(self, bug_id):
    254         return "%s/%s" % (self.bug_server_url, bug_id)
    255 
    256     def fetch_bug_dictionary(self, bug_id):
    257         return self.bug_cache.get(bug_id)
    258 
    259     def attachment_url_for_id(self, attachment_id, action="view"):
    260         action_param = ""
    261         if action and action != "view":
    262             action_param = "&action=%s" % action
    263         return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param)
    264 
    265 
    266 class MockBuildBot(Mock):
    267 
    268     def builder_statuses(self):
    269         return [{
    270             "name": "Builder1",
    271             "is_green": True,
    272         }, {
    273             "name": "Builder2",
    274             "is_green": True,
    275         }]
    276 
    277     def red_core_builders_names(self):
    278         return []
    279 
    280 
    281 class MockSCM(Mock):
    282 
    283     def __init__(self):
    284         Mock.__init__(self)
    285         self.checkout_root = os.getcwd()
    286 
    287     def create_patch(self):
    288         return "Patch1"
    289 
    290     def commit_ids_from_commitish_arguments(self, args):
    291         return ["Commitish1", "Commitish2"]
    292 
    293     def commit_message_for_local_commit(self, commit_id):
    294         if commit_id == "Commitish1":
    295             return CommitMessage("CommitMessage1\n" \
    296                 "https://bugs.example.org/show_bug.cgi?id=42\n")
    297         if commit_id == "Commitish2":
    298             return CommitMessage("CommitMessage2\n" \
    299                 "https://bugs.example.org/show_bug.cgi?id=75\n")
    300         raise Exception("Bogus commit_id in commit_message_for_local_commit.")
    301 
    302     def create_patch_from_local_commit(self, commit_id):
    303         if commit_id == "Commitish1":
    304             return "Patch1"
    305         if commit_id == "Commitish2":
    306             return "Patch2"
    307         raise Exception("Bogus commit_id in commit_message_for_local_commit.")
    308 
    309     def diff_for_revision(self, revision):
    310         return "DiffForRevision%s\n" \
    311                "http://bugs.webkit.org/show_bug.cgi?id=12345" % revision
    312 
    313     def svn_revision_from_commit_text(self, commit_text):
    314         return "49824"
    315 
    316     def modified_changelogs(self):
    317         # Ideally we'd return something more interesting here.  The problem is
    318         # that LandDiff will try to actually read the path from disk!
    319         return []
    320 
    321 
    322 class MockUser(object):
    323 
    324     @staticmethod
    325     def prompt(message, repeat=1, raw_input=raw_input):
    326         return "Mock user response"
    327 
    328     def edit(self, files):
    329         pass
    330 
    331     def page(self, message):
    332         pass
    333 
    334     def confirm(self, message=None):
    335         return True
    336 
    337     def open_url(self, url):
    338         log("MOCK: user.open_url: %s" % url)
    339         pass
    340 
    341 
    342 class MockStatusServer(object):
    343 
    344     def __init__(self):
    345         self.host = "example.com"
    346 
    347     def patch_status(self, queue_name, patch_id):
    348         return None
    349 
    350     def update_status(self, queue_name, status, patch=None, results_file=None):
    351         return 187
    352 
    353 
    354 class MockBugzillaTool():
    355 
    356     def __init__(self):
    357         self.bugs = MockBugzilla()
    358         self.buildbot = MockBuildBot()
    359         self.executive = Mock()
    360         self.user = MockUser()
    361         self._scm = MockSCM()
    362         self.status_server = MockStatusServer()
    363 
    364     def scm(self):
    365         return self._scm
    366 
    367     def path(self):
    368         return "echo"
    369