338149b6d31601098455b339bafcdeb4f6779c02
[WebKit-https.git] / Tools / Scripts / webkitpy / tool / commands / earlywarningsystem.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 from webkitpy.common.config.committers import CommitterList
30 from webkitpy.common.config.ports import WebKitPort
31 from webkitpy.common.system.deprecated_logging import error, log
32 from webkitpy.common.system.executive import ScriptError
33 from webkitpy.tool.bot.expectedfailures import ExpectedFailures
34 from webkitpy.tool.bot.layouttestresultsreader import LayoutTestResultsReader
35 from webkitpy.tool.bot.queueengine import QueueEngine
36 from webkitpy.tool.bot.earlywarningsystemtask import EarlyWarningSystemTask, EarlyWarningSystemTaskDelegate, UnableToApplyPatch
37 from webkitpy.tool.commands.queues import AbstractReviewQueue
38
39
40 class AbstractEarlyWarningSystem(AbstractReviewQueue, EarlyWarningSystemTaskDelegate):
41     _build_style = "release"
42     # FIXME: Switch _run_tests from opt-in to opt-out once more bots are ready to run tests.
43     _run_tests = False
44
45     def __init__(self):
46         AbstractReviewQueue.__init__(self)
47         self.port = WebKitPort.port(self.port_name)
48
49     def should_proceed_with_work_item(self, patch):
50         return True
51
52     def begin_work_queue(self):
53         # FIXME: This violates abstraction
54         self._tool._deprecated_port = self.port
55         AbstractReviewQueue.begin_work_queue(self)
56         self._expected_failures = ExpectedFailures()
57         self._layout_test_results_reader = LayoutTestResultsReader(self._tool, self._log_directory())
58
59     def _failing_tests_message(self, task, patch):
60         results = task.results_from_patch_test_run(patch)
61         unexpected_failures = self._expected_failures.unexpected_failures_observed(results)
62         if not unexpected_failures:
63             return None
64         return "New failing tests:\n%s" % "\n".join(unexpected_failures)
65
66     def _post_reject_message_on_bug(self, tool, patch, status_id, extra_message_text=None):
67         results_link = tool.status_server.results_url_for_status(status_id)
68         message = "Attachment %s did not pass %s (%s):\nOutput: %s" % (patch.id(), self.name, self.port_name, results_link)
69         # FIXME: We might want to add some text about rejecting from the commit-queue in
70         # the case where patch.commit_queue() isn't already set to '-'.
71         if self.watchers:
72             tool.bugs.add_cc_to_bug(patch.bug_id(), self.watchers)
73         tool.bugs.set_flag_on_attachment(patch.id(), "commit-queue", "-", message, extra_message_text)
74
75     def review_patch(self, patch):
76         task = EarlyWarningSystemTask(self, patch, self._run_tests)
77         if not task.validate():
78             self._did_error(patch, "%s did not process patch." % self.name)
79             return False
80         try:
81             return task.run()
82         except UnableToApplyPatch, e:
83             self._did_error(patch, "%s unable to apply patch." % self.name)
84             return False
85         except ScriptError, e:
86             self._post_reject_message_on_bug(self._tool, patch, task.failure_status_id, self._failing_tests_message(task, patch))
87             results_archive = task.results_archive_from_patch_test_run(patch)
88             if results_archive:
89                 self._upload_results_archive_for_patch(patch, results_archive)
90             self._did_fail(patch)
91             # FIXME: We're supposed to be able to raise e again here and have
92             # one of our base classes mark the patch as fail, but there seems
93             # to be an issue with the exit_code.
94             return False
95
96     # EarlyWarningSystemDelegate methods
97
98     def parent_command(self):
99         return self.name
100
101     def run_command(self, command):
102         self.run_webkit_patch(command + [self.port.flag()])
103
104     def command_passed(self, message, patch):
105         pass
106
107     def command_failed(self, message, script_error, patch):
108         failure_log = self._log_from_script_error_for_upload(script_error)
109         return self._update_status(message, patch=patch, results_file=failure_log)
110
111     def expected_failures(self):
112         return self._expected_failures
113
114     def layout_test_results(self):
115         return self._layout_test_results_reader.results()
116
117     def archive_last_layout_test_results(self, patch):
118         return self._layout_test_results_reader.archive(patch)
119
120     def build_style(self):
121         return self._build_style
122
123     def refetch_patch(self, patch):
124         return self._tool.bugs.fetch_attachment(patch.id())
125
126     def report_flaky_tests(self, patch, flaky_test_results, results_archive):
127         pass
128
129     # StepSequenceErrorHandler methods
130
131     @classmethod
132     def handle_script_error(cls, tool, state, script_error):
133         # FIXME: Why does this not exit(1) like the superclass does?
134         log(script_error.message_with_output())
135
136
137 class GtkEWS(AbstractEarlyWarningSystem):
138     name = "gtk-ews"
139     port_name = "gtk"
140     watchers = AbstractEarlyWarningSystem.watchers + [
141         "gns@gnome.org",
142         "xan.lopez@gmail.com",
143     ]
144
145
146 class EflEWS(AbstractEarlyWarningSystem):
147     name = "efl-ews"
148     port_name = "efl"
149     watchers = AbstractEarlyWarningSystem.watchers + [
150         "leandro@profusion.mobi",
151         "antognolli@profusion.mobi",
152         "lucas.demarchi@profusion.mobi",
153         "gyuyoung.kim@samsung.com",
154     ]
155
156
157 class QtEWS(AbstractEarlyWarningSystem):
158     name = "qt-ews"
159     port_name = "qt"
160
161
162 class WinEWS(AbstractEarlyWarningSystem):
163     name = "win-ews"
164     port_name = "win"
165     # Use debug, the Apple Win port fails to link Release on 32-bit Windows.
166     # https://bugs.webkit.org/show_bug.cgi?id=39197
167     _build_style = "debug"
168
169
170 class AbstractChromiumEWS(AbstractEarlyWarningSystem):
171     port_name = "chromium"
172     watchers = AbstractEarlyWarningSystem.watchers + [
173         "dglazkov@chromium.org",
174     ]
175
176
177 class ChromiumLinuxEWS(AbstractChromiumEWS):
178     # FIXME: We should rename this command to cr-linux-ews, but that requires
179     #        a database migration. :(
180     name = "chromium-ews"
181     port_name = "chromium-xvfb"
182     _run_tests = True
183
184
185 class ChromiumWindowsEWS(AbstractChromiumEWS):
186     name = "cr-win-ews"
187
188
189 # For platforms that we can't run inside a VM (like Mac OS X), we require
190 # patches to be uploaded by committers, who are generally trustworthy folk. :)
191 class AbstractCommitterOnlyEWS(AbstractEarlyWarningSystem):
192     def process_work_item(self, patch):
193         if not patch.attacher() or not patch.attacher().can_commit:
194             self._did_error(patch, "%s cannot process patches from non-committers :(" % self.name)
195             return False
196         return AbstractEarlyWarningSystem.process_work_item(self, patch)
197
198
199 # FIXME: Inheriting from AbstractCommitterOnlyEWS is kinda a hack, but it
200 # happens to work because AbstractChromiumEWS and AbstractCommitterOnlyEWS
201 # provide disjoint sets of functionality, and Python is otherwise smart
202 # enough to handle the diamond inheritance.
203 class ChromiumMacEWS(AbstractChromiumEWS, AbstractCommitterOnlyEWS):
204     name = "cr-mac-ews"
205
206
207 class MacEWS(AbstractCommitterOnlyEWS):
208     name = "mac-ews"
209     port_name = "mac"