WebKitTools/Scripts/webkitpy/tool/bot/queueengine_unittest.py
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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.tool.bot.queueengine import QueueEngine, QueueEngineDelegate
       
    38 
       
    39 class LoggingDelegate(QueueEngineDelegate):
       
    40     def __init__(self, test):
       
    41         self._test = test
       
    42         self._callbacks = []
       
    43         self._run_before = False
       
    44 
       
    45     expected_callbacks = [
       
    46         'queue_log_path',
       
    47         'begin_work_queue',
       
    48         'should_continue_work_queue',
       
    49         'next_work_item',
       
    50         'should_proceed_with_work_item',
       
    51         'work_item_log_path',
       
    52         'process_work_item',
       
    53         'should_continue_work_queue'
       
    54     ]
       
    55 
       
    56     def record(self, method_name):
       
    57         self._callbacks.append(method_name)
       
    58 
       
    59     def queue_log_path(self):
       
    60         self.record("queue_log_path")
       
    61         return os.path.join(self._test.temp_dir, "queue_log_path")
       
    62 
       
    63     def work_item_log_path(self, work_item):
       
    64         self.record("work_item_log_path")
       
    65         return os.path.join(self._test.temp_dir, "work_log_path", "%s.log" % work_item)
       
    66 
       
    67     def begin_work_queue(self):
       
    68         self.record("begin_work_queue")
       
    69 
       
    70     def should_continue_work_queue(self):
       
    71         self.record("should_continue_work_queue")
       
    72         if not self._run_before:
       
    73             self._run_before = True
       
    74             return True
       
    75         return False
       
    76 
       
    77     def next_work_item(self):
       
    78         self.record("next_work_item")
       
    79         return "work_item"
       
    80 
       
    81     def should_proceed_with_work_item(self, work_item):
       
    82         self.record("should_proceed_with_work_item")
       
    83         self._test.assertEquals(work_item, "work_item")
       
    84         fake_patch = { 'bug_id' : 42 }
       
    85         return (True, "waiting_message", fake_patch)
       
    86 
       
    87     def process_work_item(self, work_item):
       
    88         self.record("process_work_item")
       
    89         self._test.assertEquals(work_item, "work_item")
       
    90         return True
       
    91 
       
    92     def handle_unexpected_error(self, work_item, message):
       
    93         self.record("handle_unexpected_error")
       
    94         self._test.assertEquals(work_item, "work_item")
       
    95 
       
    96 
       
    97 class ThrowErrorDelegate(LoggingDelegate):
       
    98     def __init__(self, test, error_code):
       
    99         LoggingDelegate.__init__(self, test)
       
   100         self.error_code = error_code
       
   101 
       
   102     def process_work_item(self, work_item):
       
   103         self.record("process_work_item")
       
   104         raise ScriptError(exit_code=self.error_code)
       
   105 
       
   106 
       
   107 class NotSafeToProceedDelegate(LoggingDelegate):
       
   108     def should_proceed_with_work_item(self, work_item):
       
   109         self.record("should_proceed_with_work_item")
       
   110         self._test.assertEquals(work_item, "work_item")
       
   111         return False
       
   112 
       
   113 
       
   114 class FastQueueEngine(QueueEngine):
       
   115     def __init__(self, delegate):
       
   116         QueueEngine.__init__(self, "fast-queue", delegate, threading.Event())
       
   117 
       
   118     # No sleep for the wicked.
       
   119     seconds_to_sleep = 0
       
   120 
       
   121     def _sleep(self, message):
       
   122         pass
       
   123 
       
   124 
       
   125 class QueueEngineTest(unittest.TestCase):
       
   126     def test_trivial(self):
       
   127         delegate = LoggingDelegate(self)
       
   128         work_queue = QueueEngine("trivial-queue", delegate, threading.Event())
       
   129         work_queue.run()
       
   130         self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
       
   131         self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "queue_log_path")))
       
   132         self.assertTrue(os.path.exists(os.path.join(self.temp_dir, "work_log_path", "work_item.log")))
       
   133 
       
   134     def test_unexpected_error(self):
       
   135         delegate = ThrowErrorDelegate(self, 3)
       
   136         work_queue = QueueEngine("error-queue", delegate, threading.Event())
       
   137         work_queue.run()
       
   138         expected_callbacks = LoggingDelegate.expected_callbacks[:]
       
   139         work_item_index = expected_callbacks.index('process_work_item')
       
   140         # The unexpected error should be handled right after process_work_item starts
       
   141         # but before any other callback.  Otherwise callbacks should be normal.
       
   142         expected_callbacks.insert(work_item_index + 1, 'handle_unexpected_error')
       
   143         self.assertEquals(delegate._callbacks, expected_callbacks)
       
   144 
       
   145     def test_handled_error(self):
       
   146         delegate = ThrowErrorDelegate(self, QueueEngine.handled_error_code)
       
   147         work_queue = QueueEngine("handled-error-queue", delegate, threading.Event())
       
   148         work_queue.run()
       
   149         self.assertEquals(delegate._callbacks, LoggingDelegate.expected_callbacks)
       
   150 
       
   151     def test_not_safe_to_proceed(self):
       
   152         delegate = NotSafeToProceedDelegate(self)
       
   153         work_queue = FastQueueEngine(delegate)
       
   154         work_queue.run()
       
   155         expected_callbacks = LoggingDelegate.expected_callbacks[:]
       
   156         next_work_item_index = expected_callbacks.index('next_work_item')
       
   157         # We slice out the common part of the expected callbacks.
       
   158         # We add 2 here to include should_proceed_with_work_item, which is
       
   159         # a pain to search for directly because it occurs twice.
       
   160         expected_callbacks = expected_callbacks[:next_work_item_index + 2]
       
   161         expected_callbacks.append('should_continue_work_queue')
       
   162         self.assertEquals(delegate._callbacks, expected_callbacks)
       
   163 
       
   164     def test_now(self):
       
   165         """Make sure there are no typos in the QueueEngine.now() method."""
       
   166         engine = QueueEngine("test", None, None)
       
   167         self.assertTrue(isinstance(engine._now(), datetime.datetime))
       
   168 
       
   169     def test_sleep_message(self):
       
   170         engine = QueueEngine("test", None, None)
       
   171         engine._now = lambda: datetime.datetime(2010, 1, 1)
       
   172         expected_sleep_message = "MESSAGE Sleeping until 2010-01-01 00:02:00 (2 mins)."
       
   173         self.assertEqual(engine._sleep_message("MESSAGE"), expected_sleep_message)
       
   174 
       
   175     def setUp(self):
       
   176         self.temp_dir = tempfile.mkdtemp(suffix="work_queue_test_logs")
       
   177 
       
   178     def tearDown(self):
       
   179         shutil.rmtree(self.temp_dir)
       
   180 
       
   181 
       
   182 if __name__ == '__main__':
       
   183     unittest.main()