Clean up ChunkedUpdateDrawingAreaProxy
[WebKit-https.git] / Tools / Scripts / webkitpy / tool / commands / queues_unittest.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
31 from webkitpy.common.checkout.scm import CheckoutNeedsUpdate
32 from webkitpy.common.net.bugzilla import Attachment
33 from webkitpy.common.system.outputcapture import OutputCapture
34 from webkitpy.thirdparty.mock import Mock
35 from webkitpy.tool.commands.commandtest import CommandsTest
36 from webkitpy.tool.commands.queues import *
37 from webkitpy.tool.commands.queuestest import QueuesTest
38 from webkitpy.tool.commands.stepsequence import StepSequence
39 from webkitpy.tool.mocktool import MockTool, MockSCM, MockStatusServer
40
41
42 class TestQueue(AbstractPatchQueue):
43     name = "test-queue"
44
45
46 class TestReviewQueue(AbstractReviewQueue):
47     name = "test-review-queue"
48
49
50 class TestFeederQueue(FeederQueue):
51     _sleep_duration = 0
52
53
54 class AbstractQueueTest(CommandsTest):
55     def test_log_directory(self):
56         self.assertEquals(TestQueue()._log_directory(), "test-queue-logs")
57
58     def _assert_run_webkit_patch(self, run_args, port=None):
59         queue = TestQueue()
60         tool = MockTool()
61         tool.status_server.bot_id = "gort"
62         tool.executive = Mock()
63         queue.bind_to_tool(tool)
64         queue._options = Mock()
65         queue._options.port = port
66
67         queue.run_webkit_patch(run_args)
68         expected_run_args = ["echo", "--status-host=example.com", "--bot-id=gort"]
69         if port:
70             expected_run_args.append("--port=%s" % port)
71         expected_run_args.extend(run_args)
72         tool.executive.run_and_throw_if_fail.assert_called_with(expected_run_args)
73
74     def test_run_webkit_patch(self):
75         self._assert_run_webkit_patch([1])
76         self._assert_run_webkit_patch(["one", 2])
77         self._assert_run_webkit_patch([1], port="mockport")
78
79     def test_iteration_count(self):
80         queue = TestQueue()
81         queue._options = Mock()
82         queue._options.iterations = 3
83         self.assertTrue(queue.should_continue_work_queue())
84         self.assertTrue(queue.should_continue_work_queue())
85         self.assertTrue(queue.should_continue_work_queue())
86         self.assertFalse(queue.should_continue_work_queue())
87
88     def test_no_iteration_count(self):
89         queue = TestQueue()
90         queue._options = Mock()
91         self.assertTrue(queue.should_continue_work_queue())
92         self.assertTrue(queue.should_continue_work_queue())
93         self.assertTrue(queue.should_continue_work_queue())
94         self.assertTrue(queue.should_continue_work_queue())
95
96     def _assert_log_message(self, script_error, log_message):
97         failure_log = AbstractQueue._log_from_script_error_for_upload(script_error, output_limit=10)
98         self.assertTrue(failure_log.read(), log_message)
99
100     def test_log_from_script_error_for_upload(self):
101         self._assert_log_message(ScriptError("test"), "test")
102         # In python 2.5 unicode(Exception) is busted. See:
103         # http://bugs.python.org/issue2517
104         # With no good workaround, we just ignore these tests.
105         if not hasattr(Exception, "__unicode__"):
106             return
107
108         unicode_tor = u"WebKit \u2661 Tor Arne Vestb\u00F8!"
109         utf8_tor = unicode_tor.encode("utf-8")
110         self._assert_log_message(ScriptError(unicode_tor), utf8_tor)
111         script_error = ScriptError(unicode_tor, output=unicode_tor)
112         expected_output = "%s\nLast %s characters of output:\n%s" % (utf8_tor, 10, utf8_tor[-10:])
113         self._assert_log_message(script_error, expected_output)
114
115
116 class FeederQueueTest(QueuesTest):
117     def test_feeder_queue(self):
118         queue = TestFeederQueue()
119         tool = MockTool(log_executive=True)
120         expected_stderr = {
121             "begin_work_queue": self._default_begin_work_queue_stderr("feeder-queue", MockSCM.fake_checkout_root),
122             "should_proceed_with_work_item": "",
123             "next_work_item": "",
124             "process_work_item": """Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
125 Warning, attachment 128 on bug 42 has invalid committer (non-committer@example.com)
126 MOCK setting flag 'commit-queue' to '-' on attachment '128' with comment 'Rejecting attachment 128 from commit-queue.' and additional comment 'non-committer@example.com does not have committer permissions according to http://trac.webkit.org/browser/trunk/Tools/Scripts/webkitpy/common/config/committers.py.
127
128 - If you do not have committer rights please read http://webkit.org/coding/contributing.html for instructions on how to use bugzilla flags.
129
130 - If you have committer rights please correct the error in Tools/Scripts/webkitpy/common/config/committers.py by adding yourself to the file (no review needed).  The commit-queue restarts itself every 2 hours.  After restart the commit-queue will correctly respect your committer rights.'
131 MOCK: update_work_items: commit-queue [106, 197]
132 Feeding commit-queue items [106, 197]
133 Feeding EWS (1 r? patch, 1 new)
134 MOCK: submit_to_ews: 103
135 """,
136             "handle_unexpected_error": "Mock error message\n",
137         }
138         self.assert_queue_outputs(queue, tool=tool, expected_stderr=expected_stderr)
139
140
141 class AbstractPatchQueueTest(CommandsTest):
142     def test_next_patch(self):
143         queue = AbstractPatchQueue()
144         tool = MockTool()
145         queue.bind_to_tool(tool)
146         queue._options = Mock()
147         queue._options.port = None
148         self.assertEquals(queue._next_patch(), None)
149         tool.status_server = MockStatusServer(work_items=[2, 197])
150         expected_stdout = "MOCK: fetch_attachment: 2 is not a known attachment id\n"  # A mock-only message to prevent us from making mistakes.
151         expected_stderr = "MOCK: release_work_item: None 2\n"
152         patch_id = OutputCapture().assert_outputs(self, queue._next_patch, [], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
153         self.assertEquals(patch_id, None)  # 2 is an invalid patch id
154         self.assertEquals(queue._next_patch().id(), 197)
155
156
157 class NeedsUpdateSequence(StepSequence):
158     def _run(self, tool, options, state):
159         raise CheckoutNeedsUpdate([], 1, "", None)
160
161
162 class AlwaysCommitQueueTool(object):
163     def __init__(self):
164         self.status_server = MockStatusServer()
165
166     def command_by_name(self, name):
167         return CommitQueue
168
169
170 class SecondThoughtsCommitQueue(CommitQueue):
171     def __init__(self):
172         self._reject_patch = False
173         CommitQueue.__init__(self)
174
175     def run_command(self, command):
176         # We want to reject the patch after the first validation,
177         # so wait to reject it until after some other command has run.
178         self._reject_patch = True
179         return CommitQueue.run_command(self, command)
180
181     def refetch_patch(self, patch):
182         if not self._reject_patch:
183             return self._tool.bugs.fetch_attachment(patch.id())
184
185         attachment_dictionary = {
186             "id": patch.id(),
187             "bug_id": patch.bug_id(),
188             "name": "Rejected",
189             "is_obsolete": True,
190             "is_patch": False,
191             "review": "-",
192             "reviewer_email": "foo@bar.com",
193             "commit-queue": "-",
194             "committer_email": "foo@bar.com",
195             "attacher_email": "Contributer1",
196         }
197         return Attachment(attachment_dictionary, None)
198
199
200 class CommitQueueTest(QueuesTest):
201     def test_commit_queue(self):
202         expected_stderr = {
203             "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
204             "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
205             "next_work_item": "",
206             "process_work_item": """MOCK: update_status: commit-queue Cleaned working directory
207 MOCK: update_status: commit-queue Applied patch
208 MOCK: update_status: commit-queue Built patch
209 MOCK: update_status: commit-queue Passed tests
210 MOCK: update_status: commit-queue Landed patch
211 MOCK: update_status: commit-queue Pass
212 MOCK: release_work_item: commit-queue 197
213 """,
214             "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting attachment 197 from commit-queue.' and additional comment 'Mock error message'\n",
215             "handle_script_error": "ScriptError error message\n",
216         }
217         self.assert_queue_outputs(CommitQueue(), expected_stderr=expected_stderr)
218
219     def test_commit_queue_failure(self):
220         expected_stderr = {
221             "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
222             "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
223             "next_work_item": "",
224             "process_work_item": """MOCK: update_status: commit-queue Cleaned working directory
225 MOCK: update_status: commit-queue Patch does not apply
226 MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting attachment 197 from commit-queue.' and additional comment 'MOCK script error'
227 MOCK: update_status: commit-queue Fail
228 MOCK: release_work_item: commit-queue 197
229 """,
230             "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting attachment 197 from commit-queue.' and additional comment 'Mock error message'\n",
231             "handle_script_error": "ScriptError error message\n",
232         }
233         queue = CommitQueue()
234
235         def mock_run_webkit_patch(command):
236             if command == ['clean']:
237                 # We want cleaning to succeed so we can error out on a step
238                 # that causes the commit-queue to reject the patch.
239                 return
240             raise ScriptError('MOCK script error')
241
242         queue.run_webkit_patch = mock_run_webkit_patch
243         self.assert_queue_outputs(queue, expected_stderr=expected_stderr)
244
245     def test_rollout(self):
246         tool = MockTool(log_executive=True)
247         tool.buildbot.light_tree_on_fire()
248         expected_stderr = {
249             "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
250             "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing patch\n",
251             "next_work_item": "",
252             "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean']
253 MOCK: update_status: commit-queue Cleaned working directory
254 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--non-interactive', 197]
255 MOCK: update_status: commit-queue Applied patch
256 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
257 MOCK: update_status: commit-queue Built patch
258 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build-and-test', '--no-clean', '--no-update', '--test', '--non-interactive']
259 MOCK: update_status: commit-queue Passed tests
260 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 197]
261 MOCK: update_status: commit-queue Landed patch
262 MOCK: update_status: commit-queue Pass
263 MOCK: release_work_item: commit-queue 197
264 """,
265             "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '197' with comment 'Rejecting attachment 197 from commit-queue.' and additional comment 'Mock error message'\n",
266             "handle_script_error": "ScriptError error message\n",
267         }
268         self.assert_queue_outputs(CommitQueue(), tool=tool, expected_stderr=expected_stderr)
269
270     def test_rollout_lands(self):
271         tool = MockTool(log_executive=True)
272         tool.buildbot.light_tree_on_fire()
273         rollout_patch = tool.bugs.fetch_attachment(106)  # _patch6, a rollout patch.
274         assert(rollout_patch.is_rollout())
275         expected_stderr = {
276             "begin_work_queue": self._default_begin_work_queue_stderr("commit-queue", MockSCM.fake_checkout_root),
277             "should_proceed_with_work_item": "MOCK: update_status: commit-queue Processing rollout patch\n",
278             "next_work_item": "",
279             "process_work_item": """MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'clean']
280 MOCK: update_status: commit-queue Cleaned working directory
281 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'apply-attachment', '--non-interactive', 106]
282 MOCK: update_status: commit-queue Applied patch
283 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'build', '--no-clean', '--no-update', '--build-style=both']
284 MOCK: update_status: commit-queue Built patch
285 MOCK run_and_throw_if_fail: ['echo', '--status-host=example.com', 'land-attachment', '--force-clean', '--ignore-builders', '--non-interactive', '--parent-command=commit-queue', 106]
286 MOCK: update_status: commit-queue Landed patch
287 MOCK: update_status: commit-queue Pass
288 MOCK: release_work_item: commit-queue 106
289 """,
290             "handle_unexpected_error": "MOCK setting flag 'commit-queue' to '-' on attachment '106' with comment 'Rejecting attachment 106 from commit-queue.' and additional comment 'Mock error message'\n",
291             "handle_script_error": "ScriptError error message\n",
292         }
293         self.assert_queue_outputs(CommitQueue(), tool=tool, work_item=rollout_patch, expected_stderr=expected_stderr)
294
295     def test_auto_retry(self):
296         queue = CommitQueue()
297         options = Mock()
298         options.parent_command = "commit-queue"
299         tool = AlwaysCommitQueueTool()
300         sequence = NeedsUpdateSequence(None)
301
302         expected_stderr = "Commit failed because the checkout is out of date.  Please update and try again.\nMOCK: update_status: commit-queue Tests passed, but commit failed (checkout out of date).  Updating, then landing without building or re-running tests.\n"
303         state = {'patch': None}
304         OutputCapture().assert_outputs(self, sequence.run_and_handle_errors, [tool, options, state], expected_exception=TryAgain, expected_stderr=expected_stderr)
305
306         self.assertEquals(options.update, True)
307         self.assertEquals(options.build, False)
308         self.assertEquals(options.test, False)
309
310     def test_manual_reject_during_processing(self):
311         queue = SecondThoughtsCommitQueue()
312         queue.bind_to_tool(MockTool())
313         queue._options = Mock()
314         queue._options.port = None
315         expected_stderr = """MOCK: update_status: commit-queue Cleaned working directory
316 MOCK: update_status: commit-queue Applied patch
317 MOCK: update_status: commit-queue Built patch
318 MOCK: update_status: commit-queue Passed tests
319 MOCK: update_status: commit-queue Retry
320 MOCK: release_work_item: commit-queue 197
321 """
322         OutputCapture().assert_outputs(self, queue.process_work_item, [QueuesTest.mock_work_item], expected_stderr=expected_stderr)
323
324     def test_report_flaky_tests(self):
325         queue = CommitQueue()
326         queue.bind_to_tool(MockTool())
327         expected_stderr = """MOCK bug comment: bug_id=76, cc=None
328 --- Begin comment ---
329 The commit-queue just saw foo/bar.html flake while processing attachment 197 on bug 42.
330 Port: MockPort  Platform: MockPlatform 1.0
331 --- End comment ---
332
333 MOCK bug comment: bug_id=76, cc=None
334 --- Begin comment ---
335 The commit-queue just saw bar/baz.html flake while processing attachment 197 on bug 42.
336 Port: MockPort  Platform: MockPlatform 1.0
337 --- End comment ---
338
339 MOCK bug comment: bug_id=42, cc=None
340 --- Begin comment ---
341 The commit-queue encountered the following flaky tests while processing attachment 197:
342
343 foo/bar.html bug 76 (author: abarth@webkit.org)
344 bar/baz.html bug 76 (author: abarth@webkit.org)
345 The commit-queue is continuing to process your patch.
346 --- End comment ---
347
348 """
349         OutputCapture().assert_outputs(self, queue.report_flaky_tests, [QueuesTest.mock_work_item, ["foo/bar.html", "bar/baz.html"]], expected_stderr=expected_stderr)
350
351     def test_layout_test_results(self):
352         queue = CommitQueue()
353         queue.bind_to_tool(MockTool())
354         queue._read_file_contents = lambda path: None
355         self.assertEquals(queue.layout_test_results(), None)
356         queue._read_file_contents = lambda path: ""
357         self.assertEquals(queue.layout_test_results(), None)
358
359
360 class StyleQueueTest(QueuesTest):
361     def test_style_queue(self):
362         expected_stderr = {
363             "begin_work_queue": self._default_begin_work_queue_stderr("style-queue", MockSCM.fake_checkout_root),
364             "next_work_item": "",
365             "should_proceed_with_work_item": "MOCK: update_status: style-queue Checking style\n",
366             "process_work_item": "MOCK: update_status: style-queue Pass\nMOCK: release_work_item: style-queue 197\n",
367             "handle_unexpected_error": "Mock error message\n",
368             "handle_script_error": "MOCK: update_status: style-queue ScriptError error message\nMOCK bug comment: bug_id=42, cc=[]\n--- Begin comment ---\nAttachment 197 did not pass style-queue:\n\nScriptError error message\n\nIf any of these errors are false positives, please file a bug against check-webkit-style.\n--- End comment ---\n\n",
369         }
370         expected_exceptions = {
371             "handle_script_error": SystemExit,
372         }
373         self.assert_queue_outputs(StyleQueue(), expected_stderr=expected_stderr, expected_exceptions=expected_exceptions)