initial import
[vuplus_webkit] / Tools / Scripts / webkitpy / tool / mocktool.py
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 import StringIO
31 import threading
32
33 from webkitpy.common.config.committers import CommitterList, Reviewer
34 from webkitpy.common.checkout.commitinfo import CommitInfo
35 from webkitpy.common.checkout.scm import CommitMessage
36 from webkitpy.common.net.bugzilla import Bug, Attachment
37 from webkitpy.common.system.deprecated_logging import log
38 from webkitpy.common.system.executive import ScriptError
39 from webkitpy.common.system.filesystem_mock import MockFileSystem
40
41
42 def _id_to_object_dictionary(*objects):
43     dictionary = {}
44     for thing in objects:
45         dictionary[thing["id"]] = thing
46     return dictionary
47
48 # Testing
49
50 # FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
51
52
53 _patch1 = {
54     "id": 197,
55     "bug_id": 42,
56     "url": "http://example.com/197",
57     "name": "Patch1",
58     "is_obsolete": False,
59     "is_patch": True,
60     "review": "+",
61     "reviewer_email": "foo@bar.com",
62     "commit-queue": "+",
63     "committer_email": "foo@bar.com",
64     "attacher_email": "Contributer1",
65 }
66
67
68 _patch2 = {
69     "id": 128,
70     "bug_id": 42,
71     "url": "http://example.com/128",
72     "name": "Patch2",
73     "is_obsolete": False,
74     "is_patch": True,
75     "review": "+",
76     "reviewer_email": "foo@bar.com",
77     "commit-queue": "+",
78     "committer_email": "non-committer@example.com",
79     "attacher_email": "eric@webkit.org",
80 }
81
82
83 _patch3 = {
84     "id": 103,
85     "bug_id": 75,
86     "url": "http://example.com/103",
87     "name": "Patch3",
88     "is_obsolete": False,
89     "is_patch": True,
90     "review": "?",
91     "attacher_email": "eric@webkit.org",
92 }
93
94
95 _patch4 = {
96     "id": 104,
97     "bug_id": 77,
98     "url": "http://example.com/103",
99     "name": "Patch3",
100     "is_obsolete": False,
101     "is_patch": True,
102     "review": "+",
103     "commit-queue": "?",
104     "reviewer_email": "foo@bar.com",
105     "attacher_email": "Contributer2",
106 }
107
108
109 _patch5 = {
110     "id": 105,
111     "bug_id": 77,
112     "url": "http://example.com/103",
113     "name": "Patch5",
114     "is_obsolete": False,
115     "is_patch": True,
116     "review": "+",
117     "reviewer_email": "foo@bar.com",
118     "attacher_email": "eric@webkit.org",
119 }
120
121
122 _patch6 = { # Valid committer, but no reviewer.
123     "id": 106,
124     "bug_id": 77,
125     "url": "http://example.com/103",
126     "name": "ROLLOUT of r3489",
127     "is_obsolete": False,
128     "is_patch": True,
129     "commit-queue": "+",
130     "committer_email": "foo@bar.com",
131     "attacher_email": "eric@webkit.org",
132 }
133
134
135 _patch7 = { # Valid review, patch is marked obsolete.
136     "id": 107,
137     "bug_id": 76,
138     "url": "http://example.com/103",
139     "name": "Patch7",
140     "is_obsolete": True,
141     "is_patch": True,
142     "review": "+",
143     "reviewer_email": "foo@bar.com",
144     "attacher_email": "eric@webkit.org",
145 }
146
147
148 # This matches one of Bug.unassigned_emails
149 _unassigned_email = "webkit-unassigned@lists.webkit.org"
150 # This is needed for the FlakyTestReporter to believe the bug
151 # was filed by one of the webkitpy bots.
152 _commit_queue_email = "commit-queue@webkit.org"
153
154
155 # FIXME: The ids should be 1, 2, 3 instead of crazy numbers.
156
157
158 _bug1 = {
159     "id": 42,
160     "title": "Bug with two r+'d and cq+'d patches, one of which has an "
161              "invalid commit-queue setter.",
162     "reporter_email": "foo@foo.com",
163     "assigned_to_email": _unassigned_email,
164     "attachments": [_patch1, _patch2],
165     "bug_status": "UNCONFIRMED",
166 }
167
168
169 _bug2 = {
170     "id": 75,
171     "title": "Bug with a patch needing review.",
172     "reporter_email": "foo@foo.com",
173     "assigned_to_email": "foo@foo.com",
174     "attachments": [_patch3],
175     "bug_status": "ASSIGNED",
176 }
177
178
179 _bug3 = {
180     "id": 76,
181     "title": "The third bug",
182     "reporter_email": "foo@foo.com",
183     "assigned_to_email": _unassigned_email,
184     "attachments": [_patch7],
185     "bug_status": "NEW",
186 }
187
188
189 _bug4 = {
190     "id": 77,
191     "title": "The fourth bug",
192     "reporter_email": "foo@foo.com",
193     "assigned_to_email": "foo@foo.com",
194     "attachments": [_patch4, _patch5, _patch6],
195     "bug_status": "REOPENED",
196 }
197
198
199 _bug5 = {
200     "id": 78,
201     "title": "The fifth bug",
202     "reporter_email": _commit_queue_email,
203     "assigned_to_email": "foo@foo.com",
204     "attachments": [],
205     "bug_status": "RESOLVED",
206     "dup_id": 76,
207 }
208
209
210 class MockBugzillaQueries(object):
211
212     def __init__(self, bugzilla):
213         self._bugzilla = bugzilla
214
215     def _all_bugs(self):
216         return map(lambda bug_dictionary: Bug(bug_dictionary, self._bugzilla),
217                    self._bugzilla.bug_cache.values())
218
219     def fetch_bug_ids_from_commit_queue(self):
220         bugs_with_commit_queued_patches = filter(
221                 lambda bug: bug.commit_queued_patches(),
222                 self._all_bugs())
223         return map(lambda bug: bug.id(), bugs_with_commit_queued_patches)
224
225     def fetch_attachment_ids_from_review_queue(self):
226         unreviewed_patches = sum([bug.unreviewed_patches()
227                                   for bug in self._all_bugs()], [])
228         return map(lambda patch: patch.id(), unreviewed_patches)
229
230     def fetch_patches_from_commit_queue(self):
231         return sum([bug.commit_queued_patches()
232                     for bug in self._all_bugs()], [])
233
234     def fetch_bug_ids_from_pending_commit_list(self):
235         bugs_with_reviewed_patches = filter(lambda bug: bug.reviewed_patches(),
236                                             self._all_bugs())
237         bug_ids = map(lambda bug: bug.id(), bugs_with_reviewed_patches)
238         # NOTE: This manual hack here is to allow testing logging in
239         # test_assign_to_committer the real pending-commit query on bugzilla
240         # will return bugs with patches which have r+, but are also obsolete.
241         return bug_ids + [76]
242
243     def fetch_patches_from_pending_commit_list(self):
244         return sum([bug.reviewed_patches() for bug in self._all_bugs()], [])
245
246     def fetch_bugs_matching_search(self, search_string, author_email=None):
247         return [self._bugzilla.fetch_bug(78), self._bugzilla.fetch_bug(77)]
248
249
250 _mock_reviewer = Reviewer("Foo Bar", "foo@bar.com")
251
252
253 # FIXME: Bugzilla is the wrong Mock-point.  Once we have a BugzillaNetwork
254 #        class we should mock that instead.
255 # Most of this class is just copy/paste from Bugzilla.
256 class MockBugzilla(object):
257
258     bug_server_url = "http://example.com"
259
260     bug_cache = _id_to_object_dictionary(_bug1, _bug2, _bug3, _bug4, _bug5)
261
262     attachment_cache = _id_to_object_dictionary(_patch1,
263                                                 _patch2,
264                                                 _patch3,
265                                                 _patch4,
266                                                 _patch5,
267                                                 _patch6,
268                                                 _patch7)
269
270     def __init__(self):
271         self.queries = MockBugzillaQueries(self)
272         self.committers = CommitterList(reviewers=[_mock_reviewer])
273         self._override_patch = None
274
275     def create_bug(self,
276                    bug_title,
277                    bug_description,
278                    component=None,
279                    diff=None,
280                    patch_description=None,
281                    cc=None,
282                    blocked=None,
283                    mark_for_review=False,
284                    mark_for_commit_queue=False):
285         log("MOCK create_bug")
286         log("bug_title: %s" % bug_title)
287         log("bug_description: %s" % bug_description)
288         if component:
289             log("component: %s" % component)
290         if cc:
291             log("cc: %s" % cc)
292         if blocked:
293             log("blocked: %s" % blocked)
294         return 78
295
296     def quips(self):
297         return ["Good artists copy. Great artists steal. - Pablo Picasso"]
298
299     def fetch_bug(self, bug_id):
300         return Bug(self.bug_cache.get(bug_id), self)
301
302     def set_override_patch(self, patch):
303         self._override_patch = patch
304
305     def fetch_attachment(self, attachment_id):
306         if self._override_patch:
307             return self._override_patch
308
309         attachment_dictionary = self.attachment_cache.get(attachment_id)
310         if not attachment_dictionary:
311             print "MOCK: fetch_attachment: %s is not a known attachment id" % attachment_id
312             return None
313         bug = self.fetch_bug(attachment_dictionary["bug_id"])
314         for attachment in bug.attachments(include_obsolete=True):
315             if attachment.id() == int(attachment_id):
316                 return attachment
317
318     def bug_url_for_bug_id(self, bug_id):
319         return "%s/%s" % (self.bug_server_url, bug_id)
320
321     def fetch_bug_dictionary(self, bug_id):
322         return self.bug_cache.get(bug_id)
323
324     def attachment_url_for_id(self, attachment_id, action="view"):
325         action_param = ""
326         if action and action != "view":
327             action_param = "&action=%s" % action
328         return "%s/%s%s" % (self.bug_server_url, attachment_id, action_param)
329
330     def reassign_bug(self, bug_id, assignee=None, comment_text=None):
331         log("MOCK reassign_bug: bug_id=%s, assignee=%s" % (bug_id, assignee))
332         if comment_text:
333             log("-- Begin comment --")
334             log(comment_text)
335             log("-- End comment --")
336
337     def set_flag_on_attachment(self,
338                                attachment_id,
339                                flag_name,
340                                flag_value,
341                                comment_text=None,
342                                additional_comment_text=None):
343         log("MOCK setting flag '%s' to '%s' on attachment '%s' with comment '%s' and additional comment '%s'" % (
344             flag_name, flag_value, attachment_id, comment_text, additional_comment_text))
345
346     def post_comment_to_bug(self, bug_id, comment_text, cc=None):
347         log("MOCK bug comment: bug_id=%s, cc=%s\n--- Begin comment ---\n%s\n--- End comment ---\n" % (
348             bug_id, cc, comment_text))
349
350     def add_attachment_to_bug(self,
351                               bug_id,
352                               file_or_string,
353                               description,
354                               filename=None,
355                               comment_text=None):
356         log("MOCK add_attachment_to_bug: bug_id=%s, description=%s filename=%s" % (bug_id, description, filename))
357         if comment_text:
358             log("-- Begin comment --")
359             log(comment_text)
360             log("-- End comment --")
361
362     def add_patch_to_bug(self,
363                          bug_id,
364                          diff,
365                          description,
366                          comment_text=None,
367                          mark_for_review=False,
368                          mark_for_commit_queue=False,
369                          mark_for_landing=False):
370         log("MOCK add_patch_to_bug: bug_id=%s, description=%s, mark_for_review=%s, mark_for_commit_queue=%s, mark_for_landing=%s" %
371             (bug_id, description, mark_for_review, mark_for_commit_queue, mark_for_landing))
372         if comment_text:
373             log("-- Begin comment --")
374             log(comment_text)
375             log("-- End comment --")
376
377     def add_cc_to_bug(self, bug_id, ccs):
378         pass
379
380     def obsolete_attachment(self, attachment_id, message=None):
381         pass
382
383     def reopen_bug(self, bug_id, message):
384         pass
385
386     def close_bug_as_fixed(self, bug_id, message):
387         pass
388
389     def clear_attachment_flags(self, attachment_id, message):
390         pass
391
392
393 class MockBuilder(object):
394     def __init__(self, name):
395         self._name = name
396
397     def name(self):
398         return self._name
399
400     def results_url(self):
401         return "http://example.com/builders/%s/results" % self.name()
402
403     def accumulated_results_url(self):
404         return "http://example.com/f/builders/%s/results/layout-test-results" % self.name()
405
406     def force_build(self, username, comments):
407         log("MOCK: force_build: name=%s, username=%s, comments=%s" % (
408             self._name, username, comments))
409
410
411 class MockFailureMap(object):
412     def __init__(self, buildbot):
413         self._buildbot = buildbot
414
415     def is_empty(self):
416         return False
417
418     def filter_out_old_failures(self, is_old_revision):
419         pass
420
421     def failing_revisions(self):
422         return [29837]
423
424     def builders_failing_for(self, revision):
425         return [self._buildbot.builder_with_name("Builder1")]
426
427     def tests_failing_for(self, revision):
428         return ["mock-test-1"]
429
430     def failing_tests(self):
431         return set(["mock-test-1"])
432
433
434 class MockBuildBot(object):
435     def __init__(self):
436         self._mock_builder1_status = {
437             "name": "Builder1",
438             "is_green": True,
439             "activity": "building",
440         }
441         self._mock_builder2_status = {
442             "name": "Builder2",
443             "is_green": True,
444             "activity": "idle",
445         }
446
447     def builder_with_name(self, name):
448         return MockBuilder(name)
449
450     def builder_statuses(self):
451         return [
452             self._mock_builder1_status,
453             self._mock_builder2_status,
454         ]
455
456     def red_core_builders_names(self):
457         if not self._mock_builder2_status["is_green"]:
458             return [self._mock_builder2_status["name"]]
459         return []
460
461     def red_core_builders(self):
462         if not self._mock_builder2_status["is_green"]:
463             return [self._mock_builder2_status]
464         return []
465
466     def idle_red_core_builders(self):
467         if not self._mock_builder2_status["is_green"]:
468             return [self._mock_builder2_status]
469         return []
470
471     def last_green_revision(self):
472         return 9479
473
474     def light_tree_on_fire(self):
475         self._mock_builder2_status["is_green"] = False
476
477     def failure_map(self):
478         return MockFailureMap(self)
479
480
481 class MockSCM(object):
482     def __init__(self, filesystem=None, executive=None):
483         self.checkout_root = "/mock-checkout"
484         self.added_paths = set()
485         self._filesystem = filesystem or MockFileSystem()
486         self._executive = executive or MockExecutive()
487
488     def add(self, destination_path, return_exit_code=False):
489         self.added_paths.add(destination_path)
490         if return_exit_code:
491             return 0
492
493     def ensure_clean_working_directory(self, force_clean):
494         pass
495
496     def supports_local_commits(self):
497         return True
498
499     def ensure_no_local_commits(self, force_clean):
500         pass
501
502     def exists(self, path):
503         # TestRealMain.test_real_main (and several other rebaseline tests) are sensitive to this return value.
504         # We should make those tests more robust, but for now we just return True always (since no test needs otherwise).
505         return True
506
507     def absolute_path(self, *comps):
508         return self._filesystem.join(self.checkout_root, *comps)
509
510     def changed_files(self, git_commit=None):
511         return ["MockFile1"]
512
513     def changed_files_for_revision(self, revision):
514         return ["MockFile1"]
515
516     def head_svn_revision(self):
517         return 1234
518
519     def create_patch(self, git_commit, changed_files=None):
520         return "Patch1"
521
522     def commit_ids_from_commitish_arguments(self, args):
523         return ["Commitish1", "Commitish2"]
524
525     def committer_email_for_revision(self, revision):
526         return "mock@webkit.org"
527
528     def commit_locally_with_message(self, message):
529         pass
530
531     def commit_with_message(self, message, username=None, password=None, git_commit=None, force_squash=False, changed_files=None):
532         pass
533
534     def merge_base(self, git_commit):
535         return None
536
537     def commit_message_for_local_commit(self, commit_id):
538         if commit_id == "Commitish1":
539             return CommitMessage("CommitMessage1\n" \
540                 "https://bugs.example.org/show_bug.cgi?id=42\n")
541         if commit_id == "Commitish2":
542             return CommitMessage("CommitMessage2\n" \
543                 "https://bugs.example.org/show_bug.cgi?id=75\n")
544         raise Exception("Bogus commit_id in commit_message_for_local_commit.")
545
546     def diff_for_file(self, path, log=None):
547         return path + '-diff'
548
549     def diff_for_revision(self, revision):
550         return "DiffForRevision%s\nhttp://bugs.webkit.org/show_bug.cgi?id=12345" % revision
551
552     def show_head(self, path):
553         return path
554
555     def svn_revision_from_commit_text(self, commit_text):
556         return "49824"
557
558     def delete(self, path):
559         if not self._filesystem:
560             return
561         if self._filesystem.exists(path):
562             self._filesystem.remove(path)
563
564
565 class MockDEPS(object):
566     def read_variable(self, name):
567         return 6564
568
569     def write_variable(self, name, value):
570         log("MOCK: MockDEPS.write_variable(%s, %s)" % (name, value))
571
572
573 class MockCommitMessage(object):
574     def message(self):
575         return "This is a fake commit message that is at least 50 characters."
576
577 class MockCheckout(object):
578
579     _committer_list = CommitterList()
580
581     def commit_info_for_revision(self, svn_revision):
582         # The real Checkout would probably throw an exception, but this is the only way tests have to get None back at the moment.
583         if not svn_revision:
584             return None
585         return CommitInfo(svn_revision, "eric@webkit.org", {
586             "bug_id": 42,
587             "author_name": "Adam Barth",
588             "author_email": "abarth@webkit.org",
589             "author": self._committer_list.contributor_by_email("abarth@webkit.org"),
590             "reviewer_text": "Darin Adler",
591             "reviewer": self._committer_list.committer_by_name("Darin Adler"),
592             "changed_files": [
593                 "path/to/file",
594                 "another/file",
595             ],
596         })
597
598     def is_path_to_changelog(self, path):
599         return os.path.basename(path) == "ChangeLog"
600
601     def bug_id_for_revision(self, svn_revision):
602         return 12345
603
604     def recent_commit_infos_for_files(self, paths):
605         return [self.commit_info_for_revision(32)]
606
607     def modified_changelogs(self, git_commit, changed_files=None):
608         # Ideally we'd return something more interesting here.  The problem is
609         # that LandDiff will try to actually read the patch from disk!
610         return []
611
612     def commit_message_for_this_commit(self, git_commit, changed_files=None):
613         return MockCommitMessage()
614
615     def chromium_deps(self):
616         return MockDEPS()
617
618     def apply_patch(self, patch, force=False):
619         pass
620
621     def apply_reverse_diffs(self, revision):
622         pass
623
624     def suggested_reviewers(self, git_commit, changed_files=None):
625         return [_mock_reviewer]
626
627
628 class MockUser(object):
629
630     @classmethod
631     def prompt(cls, message, repeat=1, raw_input=raw_input):
632         return "Mock user response"
633
634     @classmethod
635     def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input):
636         pass
637
638     def __init__(self):
639         self.opened_urls = []
640
641     def edit(self, files):
642         pass
643
644     def edit_changelog(self, files):
645         pass
646
647     def page(self, message):
648         pass
649
650     def confirm(self, message=None, default='y'):
651         log(message)
652         return default == 'y'
653
654     def can_open_url(self):
655         return True
656
657     def open_url(self, url):
658         self.opened_urls.append(url)
659         if url.startswith("file://"):
660             log("MOCK: user.open_url: file://...")
661             return
662         log("MOCK: user.open_url: %s" % url)
663
664
665 class MockIRC(object):
666
667     def post(self, message):
668         log("MOCK: irc.post: %s" % message)
669
670     def disconnect(self):
671         log("MOCK: irc.disconnect")
672
673
674 class MockStatusServer(object):
675
676     def __init__(self, bot_id=None, work_items=None):
677         self.host = "example.com"
678         self.bot_id = bot_id
679         self._work_items = work_items or []
680
681     def patch_status(self, queue_name, patch_id):
682         return None
683
684     def svn_revision(self, svn_revision):
685         return None
686
687     def next_work_item(self, queue_name):
688         if not self._work_items:
689             return None
690         return self._work_items.pop(0)
691
692     def release_work_item(self, queue_name, patch):
693         log("MOCK: release_work_item: %s %s" % (queue_name, patch.id()))
694
695     def update_work_items(self, queue_name, work_items):
696         self._work_items = work_items
697         log("MOCK: update_work_items: %s %s" % (queue_name, work_items))
698
699     def submit_to_ews(self, patch_id):
700         log("MOCK: submit_to_ews: %s" % (patch_id))
701
702     def update_status(self, queue_name, status, patch=None, results_file=None):
703         log("MOCK: update_status: %s %s" % (queue_name, status))
704         return 187
705
706     def update_svn_revision(self, svn_revision, broken_bot):
707         return 191
708
709     def results_url_for_status(self, status_id):
710         return "http://dummy_url"
711
712
713 # FIXME: Unify with common.system.executive_mock.MockExecutive.
714 class MockExecutive(object):
715     def __init__(self, should_log=False, should_throw=False):
716         self._should_log = should_log
717         self._should_throw = should_throw
718         # FIXME: Once executive wraps os.getpid() we can just use a static pid for "this" process.
719         self._running_pids = [os.getpid()]
720
721     def check_running_pid(self, pid):
722         return pid in self._running_pids
723
724     def run_and_throw_if_fail(self, args, quiet=False, cwd=None):
725         if self._should_log:
726             log("MOCK run_and_throw_if_fail: %s, cwd=%s" % (args, cwd))
727         return "MOCK output of child process"
728
729     def run_command(self,
730                     args,
731                     cwd=None,
732                     input=None,
733                     error_handler=None,
734                     return_exit_code=False,
735                     return_stderr=True,
736                     decode_output=False):
737         if self._should_log:
738             log("MOCK run_command: %s, cwd=%s" % (args, cwd))
739         if self._should_throw:
740             raise ScriptError("MOCK ScriptError")
741         return "MOCK output of child process"
742
743
744 class MockOptions(object):
745     """Mock implementation of optparse.Values."""
746
747     def __init__(self, **kwargs):
748         # The caller can set option values using keyword arguments. We don't
749         # set any values by default because we don't know how this
750         # object will be used. Generally speaking unit tests should
751         # subclass this or provider wrapper functions that set a common
752         # set of options.
753         for key, value in kwargs.items():
754             self.__dict__[key] = value
755
756
757 class MockPort(object):
758     def name(self):
759         return "MockPort"
760
761     def layout_tests_results_path(self):
762         return "/mock-results/results.html"
763
764     def check_webkit_style_command(self):
765         return ["mock-check-webkit-style"]
766
767     def update_webkit_command(self):
768         return ["mock-update-webkit"]
769
770     def build_webkit_command(self, build_style=None):
771         return ["mock-build-webkit"]
772
773     def run_bindings_tests_command(self):
774         return ["mock-run-bindings-tests"]
775
776     def prepare_changelog_command(self):
777         return ['mock-prepare-ChangeLog']
778
779     def run_python_unittests_command(self):
780         return ['mock-test-webkitpy']
781
782     def run_perl_unittests_command(self):
783         return ['mock-test-webkitperl']
784
785     def run_javascriptcore_tests_command(self):
786         return ['mock-run-javacriptcore-tests']
787
788     def run_webkit_tests_command(self):
789         return ['mock-run-webkit-tests']
790
791
792 class MockTestPort1(object):
793     def skips_layout_test(self, test_name):
794         return test_name in ["media/foo/bar.html", "foo"]
795
796
797 class MockTestPort2(object):
798     def skips_layout_test(self, test_name):
799         return test_name == "media/foo/bar.html"
800
801
802 class MockPortFactory(object):
803     def get_all(self, options=None):
804         return {"test_port1": MockTestPort1(), "test_port2": MockTestPort2()}
805
806
807 class MockPlatformInfo(object):
808     def display_name(self):
809         return "MockPlatform 1.0"
810
811
812 class MockWorkspace(object):
813     def find_unused_filename(self, directory, name, extension, search_limit=10):
814         return "%s/%s.%s" % (directory, name, extension)
815
816     def create_zip(self, zip_path, source_path):
817         return object()  # Something that is not None
818
819
820 class MockWeb(object):
821     def get_binary(self, url, convert_404_to_None=False):
822         return "MOCK Web result, convert 404 to None=%s" % convert_404_to_None
823
824
825 class MockTool(object):
826     def __init__(self, log_executive=False):
827         self.wakeup_event = threading.Event()
828         self.bugs = MockBugzilla()
829         self.buildbot = MockBuildBot()
830         self.executive = MockExecutive(should_log=log_executive)
831         self.web = MockWeb()
832         self.workspace = MockWorkspace()
833         self._irc = None
834         self.user = MockUser()
835         self._scm = MockSCM()
836         self._chromium_buildbot = MockBuildBot()
837         # Various pieces of code (wrongly) call filesystem.chdir(checkout_root).
838         # Making the checkout_root exist in the mock filesystem makes that chdir not raise.
839         self.filesystem = MockFileSystem(dirs=set([self._scm.checkout_root]))
840         self._port = MockPort()
841         self._checkout = MockCheckout()
842         self.status_server = MockStatusServer()
843         self.irc_password = "MOCK irc password"
844         self.port_factory = MockPortFactory()
845         self.platform = MockPlatformInfo()
846
847     def scm(self):
848         return self._scm
849
850     def checkout(self):
851         return self._checkout
852
853     def chromium_buildbot(self):
854         return self._chromium_buildbot
855
856     def ensure_irc_connected(self, delegate):
857         if not self._irc:
858             self._irc = MockIRC()
859
860     def irc(self):
861         return self._irc
862
863     def path(self):
864         return "echo"
865
866     def port(self):
867         return self._port
868
869
870 class MockBrowser(object):
871     params = {}
872
873     def open(self, url):
874         pass
875
876     def select_form(self, name):
877         pass
878
879     def __setitem__(self, key, value):
880         self.params[key] = value
881
882     def submit(self):
883         return StringIO.StringIO()