initial import
[vuplus_webkit] / Tools / Scripts / webkitpy / tool / bot / queueengine_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 datetime
30 import os
31 import shutil
32 import tempfile
33 import threading
34 import unittest
35
36 from webkitpy.common.system.executive import ScriptError
37 from webkitpy.common.system.outputcapture import OutputCapture
38 from webkitpy.tool.bot.queueengine import QueueEngine, QueueEngineDelegate, TerminateQueue
39
40
41 class LoggingDelegate(QueueEngineDelegate):
42     def __init__(self, test):
43         self._test = test
44         self._callbacks = []
45         self._run_before = False
46         self.stop_message = None
47
48     expected_callbacks = [
49         'queue_log_path',
50         'begin_work_queue',
51         'should_continue_work_queue',
52         'next_work_item',
53         'should_proceed_with_work_item',
54         'work_item_log_path',
55         'process_work_item',
56         'should_continue_work_queue',
57         'stop_work_queue',
58     ]
59
60     def record(self, method_name):
61         self._callbacks.append(method_name)
62
63     def queue_log_path(self):
64         self.record("queue_log_path")
65         return os.path.join(self._test.temp_dir, "queue_log_path")
66
67     def work_item_log_path(self, work_item):
68         self.record("work_item_log_path")
69         return os.path.join(self._test.temp_dir, "work_log_path", "%s.log" % work_item)
70
71     def begin_work_queue(self):
72         self.record("begin_work_queue")
73
74     def should_continue_work_queue(self):
75         self.record("should_continue_work_queue")
76         if not self._run_before:
77             self._run_before = True
78             return True
79         return False
80
81     def next_work_item(self):
82         self.record("next_work_item")
83         return "work_item"
84
85     def should_proceed_with_work_item(self, work_item):
86         self.record("should_proceed_with_work_item")
87         self._test.assertEquals(work_item, "work_item")
88         fake_patch = { 'bug_id' : 42 }
89         return (True, "waiting_message", fake_patch)
90
91     def process_work_item(self, work_item):
92         self.record("process_work_item")
93         self._test.assertEquals(work_item, "work_item")
94         return True
95
96     def handle_unexpected_error(self, work_item, message):
97         self.record("handle_unexpected_error")
98         self._test.assertEquals(work_item, "work_item")
99
100     def stop_work_queue(self, message):
101         self.record("stop_work_queue")
102         self.stop_message = message
103
104
105 class RaisingDelegate(LoggingDelegate):
106     def __init__(self, test, exception):
107         LoggingDelegate.__init__(self, test)
108         self._exception = exception
109
110     def process_work_item(self, work_item):
111         self.record("process_work_item")
112         raise self._exception
113
114
115 class NotSafeToProceedDelegate(LoggingDelegate):
116     def should_proceed_with_work_item(self, work_item):
117         self.record("should_proceed_with_work_item")
118         self._test.assertEquals(work_item, "work_item")
119         return False
120
121
122 class FastQueueEngine(QueueEngine):
123     def __init__(self, delegate):
124         QueueEngine.__init__(self, "fast-queue", delegate, threading.Event())
125
126     # No sleep for the wicked.
127     seconds_to_sleep = 0
128
129     def _sleep(self, message):
130         pass
131
132
133 class QueueEngineTest(unittest.TestCase):
134     def test_trivial(self):
135         delegate = LoggingDelegate(self)
136         self._run_engine(delegate)
137         self.assertEquals(delegate.stop_message, "Delegate terminated queue.")
138         self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
139         self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "queue_log_path")))
140         self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "work_log_path", "work_item.log")))
141
142     def test_unexpected_error(self):
143         delegate = RaisingDelegate(self, ScriptError(exit_code=3))
144         self._run_engine(delegate)
145         expected_callbacks = LoggingDelegate.expected_callbacks[:]
146         work_item_index = expected_callbacks.index('process_work_item')
147         # The unexpected error should be handled right after process_work_item starts
148         # but before any other callback.  Otherwise callbacks should be normal.
149         expected_callbacks.insert(work_item_index + 1, 'handle_unexpected_error')
150         self.assertEquals(delegate._callbacks, expected_callbacks)
151
152     def test_handled_error(self):
153         delegate = RaisingDelegate(self, ScriptError(exit_code=QueueEngine.handled_error_code))
154         self._run_engine(delegate)
155         self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
156
157     def _run_engine(self, delegate, engine=None, termination_message=None):
158         if not engine:
159             engine = QueueEngine("test-queue", delegate, threading.Event())
160         if not termination_message:
161             termination_message = "Delegate terminated queue."
162         expected_stderr = "\n%s\n" % termination_message
163         OutputCapture().assert_outputs(self, engine.run, expected_stderr=expected_stderr)
164
165     def _test_terminating_queue(self, exception, termination_message):
166         work_item_index = LoggingDelegate.expected_callbacks.index('process_work_item')
167         # The terminating error should be handled right after process_work_item.
168         # There should be no other callbacks after stop_work_queue.
169         expected_callbacks = LoggingDelegate.expected_callbacks[:work_item_index + 1]
170         expected_callbacks.append("stop_work_queue")
171
172         delegate = RaisingDelegate(self, exception)
173         self._run_engine(delegate, termination_message=termination_message)
174
175         self.assertEquals(delegate._callbacks, expected_callbacks)
176         self.assertEquals(delegate.stop_message, termination_message)
177
178     def test_terminating_error(self):
179         self._test_terminating_queue(KeyboardInterrupt(), "User terminated queue.")
180         self._test_terminating_queue(TerminateQueue(), "TerminateQueue exception received.")
181
182     def test_not_safe_to_proceed(self):
183         delegate = NotSafeToProceedDelegate(self)
184         self._run_engine(delegate, engine=FastQueueEngine(delegate))
185         expected_callbacks = LoggingDelegate.expected_callbacks[:]
186         expected_callbacks.remove('work_item_log_path')
187         expected_callbacks.remove('process_work_item')
188         self.assertEquals(delegate._callbacks, expected_callbacks)
189
190     def test_now(self):
191         """Make sure there are no typos in the QueueEngine.now() method."""
192         engine = QueueEngine("test", None, None)
193         self.assertTrue(isinstance(engine._now(), datetime.datetime))
194
195     def test_sleep_message(self):
196         engine = QueueEngine("test", None, None)
197         engine._now = lambda: datetime.datetime(2010, 1, 1)
198         expected_sleep_message = "MESSAGE Sleeping until 2010-01-01 00:02:00 (2 mins)."
199         self.assertEqual(engine._sleep_message("MESSAGE"), expected_sleep_message)
200
201     def setUp(self):
202         self.temp_dir = tempfile.mkdtemp(suffix="work_queue_test_logs")
203
204     def tearDown(self):
205         shutil.rmtree(self.temp_dir)
206
207
208 if __name__ == '__main__':
209     unittest.main()