Diff
Modified: trunk/Tools/ChangeLog (124266 => 124267)
--- trunk/Tools/ChangeLog 2012-07-31 23:07:46 UTC (rev 124266)
+++ trunk/Tools/ChangeLog 2012-07-31 23:20:07 UTC (rev 124267)
@@ -1,5 +1,40 @@
2012-07-31 Dirk Pranke <[email protected]>
+ nrwt: rename worker.py to layout_test_runner.py
+ https://bugs.webkit.org/show_bug.cgi?id=92804
+
+ Reviewed by Ojan Vafai.
+
+ Home stretch of this round of manager refactoring ... I will be
+ moving all of the actual test-running code (which includes the
+ code that shards the tests for the workers) into a different
+ module, and it makes sense for that module to contain the actual
+ worker code, so I'm renaming worker.py to layout_test_runner.py.
+
+ * Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py: Renamed from Tools/Scripts/webkitpy/layout_tests/controllers/worker.py.
+ (Worker):
+ (Worker.__init__):
+ (Worker.__del__):
+ (Worker.start):
+ (Worker.handle):
+ (Worker._update_test_input):
+ (Worker._run_test):
+ (Worker.stop):
+ (Worker._timeout):
+ (Worker._kill_driver):
+ (Worker._run_test_with_timeout):
+ (Worker._clean_up_after_test):
+ (Worker._run_test_in_another_thread):
+ (Worker._run_test_in_another_thread.SingleTestThread):
+ (Worker._run_test_in_another_thread.SingleTestThread.__init__):
+ (Worker._run_test_in_another_thread.SingleTestThread.run):
+ (Worker._run_test_in_this_thread):
+ (Worker._run_single_test):
+ * Scripts/webkitpy/layout_tests/controllers/manager.py:
+ (Manager._run_tests.worker_factory):
+
+2012-07-31 Dirk Pranke <[email protected]>
+
nrwt: clean up names in sharding code
https://bugs.webkit.org/show_bug.cgi?id=92785
Copied: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py (from rev 124266, trunk/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py) (0 => 124267)
--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py (rev 0)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/layout_test_runner.py 2012-07-31 23:20:07 UTC (rev 124267)
@@ -0,0 +1,231 @@
+# Copyright (C) 2011 Google Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""Handle messages from the Manager and executes actual tests."""
+
+import logging
+import threading
+import time
+
+from webkitpy.layout_tests.controllers import single_test_runner
+from webkitpy.layout_tests.models import test_expectations
+from webkitpy.layout_tests.models import test_results
+
+
+_log = logging.getLogger(__name__)
+
+
+class Worker(object):
+ def __init__(self, caller, results_directory, options):
+ self._caller = caller
+ self._worker_number = caller.worker_number
+ self._name = caller.name
+ self._results_directory = results_directory
+ self._options = options
+
+ # The remaining fields are initialized in start()
+ self._host = None
+ self._port = None
+ self._batch_size = None
+ self._batch_count = None
+ self._filesystem = None
+ self._driver = None
+ self._tests_run_file = None
+ self._tests_run_filename = None
+
+ def __del__(self):
+ self.stop()
+
+ def start(self):
+ """This method is called when the object is starting to be used and it is safe
+ for the object to create state that does not need to be pickled (usually this means
+ it is called in a child process)."""
+ self._host = self._caller.host
+ self._filesystem = self._host.filesystem
+ self._port = self._host.port_factory.get(self._options.platform, self._options)
+
+ self._batch_count = 0
+ self._batch_size = self._options.batch_size or 0
+ tests_run_filename = self._filesystem.join(self._results_directory, "tests_run%d.txt" % self._worker_number)
+ self._tests_run_file = self._filesystem.open_text_file_for_writing(tests_run_filename)
+
+ def handle(self, name, source, test_list_name, test_inputs):
+ assert name == 'test_list'
+ start_time = time.time()
+ for test_input in test_inputs:
+ self._run_test(test_input)
+ elapsed_time = time.time() - start_time
+ self._caller.post('finished_test_list', test_list_name, len(test_inputs), elapsed_time)
+
+ def _update_test_input(self, test_input):
+ if test_input.reference_files is None:
+ # Lazy initialization.
+ test_input.reference_files = self._port.reference_files(test_input.test_name)
+ if test_input.reference_files:
+ test_input.should_run_pixel_test = True
+ else:
+ test_input.should_run_pixel_test = self._port.should_run_as_pixel_test(test_input)
+
+ def _run_test(self, test_input):
+ self._update_test_input(test_input)
+ test_timeout_sec = self._timeout(test_input)
+ start = time.time()
+ self._caller.post('started_test', test_input, test_timeout_sec)
+
+ result = self._run_test_with_timeout(test_input, test_timeout_sec)
+
+ elapsed_time = time.time() - start
+ self._caller.post('finished_test', result, elapsed_time)
+
+ self._clean_up_after_test(test_input, result)
+
+ def stop(self):
+ _log.debug("%s cleaning up" % self._name)
+ self._kill_driver()
+ if self._tests_run_file:
+ self._tests_run_file.close()
+ self._tests_run_file = None
+
+ def _timeout(self, test_input):
+ """Compute the appropriate timeout value for a test."""
+ # The DumpRenderTree watchdog uses 2.5x the timeout; we want to be
+ # larger than that. We also add a little more padding if we're
+ # running tests in a separate thread.
+ #
+ # Note that we need to convert the test timeout from a
+ # string value in milliseconds to a float for Python.
+ driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0
+ if not self._options.run_singly:
+ return driver_timeout_sec
+
+ thread_padding_sec = 1.0
+ thread_timeout_sec = driver_timeout_sec + thread_padding_sec
+ return thread_timeout_sec
+
+ def _kill_driver(self):
+ # Be careful about how and when we kill the driver; if driver.stop()
+ # raises an exception, this routine may get re-entered via __del__.
+ driver = self._driver
+ self._driver = None
+ if driver:
+ _log.debug("%s killing driver" % self._name)
+ driver.stop()
+
+ def _run_test_with_timeout(self, test_input, timeout):
+ if self._options.run_singly:
+ return self._run_test_in_another_thread(test_input, timeout)
+ return self._run_test_in_this_thread(test_input)
+
+ def _clean_up_after_test(self, test_input, result):
+ self._batch_count += 1
+ test_name = test_input.test_name
+ self._tests_run_file.write(test_name + "\n")
+
+ if result.failures:
+ # Check and kill DumpRenderTree if we need to.
+ if any([f.driver_needs_restart() for f in result.failures]):
+ self._kill_driver()
+ # Reset the batch count since the shell just bounced.
+ self._batch_count = 0
+
+ # Print the error message(s).
+ _log.debug("%s %s failed:" % (self._name, test_name))
+ for f in result.failures:
+ _log.debug("%s %s" % (self._name, f.message()))
+ elif result.type == test_expectations.SKIP:
+ _log.debug("%s %s skipped" % (self._name, test_name))
+ else:
+ _log.debug("%s %s passed" % (self._name, test_name))
+
+ if self._batch_size > 0 and self._batch_count >= self._batch_size:
+ self._kill_driver()
+ self._batch_count = 0
+
+ def _run_test_in_another_thread(self, test_input, thread_timeout_sec):
+ """Run a test in a separate thread, enforcing a hard time limit.
+
+ Since we can only detect the termination of a thread, not any internal
+ state or progress, we can only run per-test timeouts when running test
+ files singly.
+
+ Args:
+ test_input: Object containing the test filename and timeout
+ thread_timeout_sec: time to wait before killing the driver process.
+ Returns:
+ A TestResult
+ """
+ worker = self
+
+ driver = self._port.create_driver(self._worker_number)
+
+ class SingleTestThread(threading.Thread):
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self.result = None
+
+ def run(self):
+ self.result = worker._run_single_test(driver, test_input)
+
+ thread = SingleTestThread()
+ thread.start()
+ thread.join(thread_timeout_sec)
+ result = thread.result
+ if thread.isAlive():
+ # If join() returned with the thread still running, the
+ # DumpRenderTree is completely hung and there's nothing
+ # more we can do with it. We have to kill all the
+ # DumpRenderTrees to free it up. If we're running more than
+ # one DumpRenderTree thread, we'll end up killing the other
+ # DumpRenderTrees too, introducing spurious crashes. We accept
+ # that tradeoff in order to avoid losing the rest of this
+ # thread's results.
+ _log.error('Test thread hung: killing all DumpRenderTrees')
+
+ driver.stop()
+
+ if not result:
+ result = test_results.TestResult(test_input.test_name, failures=[], test_run_time=0)
+ return result
+
+ def _run_test_in_this_thread(self, test_input):
+ """Run a single test file using a shared DumpRenderTree process.
+
+ Args:
+ test_input: Object containing the test filename, uri and timeout
+
+ Returns: a TestResult object.
+ """
+ if self._driver and self._driver.has_crashed():
+ self._kill_driver()
+ if not self._driver:
+ self._driver = self._port.create_driver(self._worker_number)
+ return self._run_single_test(self._driver, test_input)
+
+ def _run_single_test(self, driver, test_input):
+ return single_test_runner.run_single_test(self._port, self._options,
+ test_input, driver, self._name)
Modified: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py (124266 => 124267)
--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py 2012-07-31 23:07:46 UTC (rev 124266)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/manager.py 2012-07-31 23:20:07 UTC (rev 124267)
@@ -45,7 +45,7 @@
import time
from webkitpy.common import message_pool
-from webkitpy.layout_tests.controllers import worker
+from webkitpy.layout_tests.controllers.layout_test_runner import Worker
from webkitpy.layout_tests.controllers.finder import LayoutTestFinder
from webkitpy.layout_tests.controllers.test_result_writer import TestResultWriter
from webkitpy.layout_tests.layout_package import json_layout_results_generator
@@ -608,7 +608,7 @@
self._printer.print_workers_and_shards(num_workers, len(all_shards), len(locked_shards))
def worker_factory(worker_connection):
- return worker.Worker(worker_connection, self.results_directory(), self._options)
+ return Worker(worker_connection, self.results_directory(), self._options)
if self._options.dry_run:
return (keyboard_interrupted, interrupted, self._worker_stats.values(), self._group_stats, self._all_results)
Deleted: trunk/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py (124266 => 124267)
--- trunk/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py 2012-07-31 23:07:46 UTC (rev 124266)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/controllers/worker.py 2012-07-31 23:20:07 UTC (rev 124267)
@@ -1,231 +0,0 @@
-# Copyright (C) 2011 Google Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""Handle messages from the Manager and executes actual tests."""
-
-import logging
-import threading
-import time
-
-from webkitpy.layout_tests.controllers import single_test_runner
-from webkitpy.layout_tests.models import test_expectations
-from webkitpy.layout_tests.models import test_results
-
-
-_log = logging.getLogger(__name__)
-
-
-class Worker(object):
- def __init__(self, caller, results_directory, options):
- self._caller = caller
- self._worker_number = caller.worker_number
- self._name = caller.name
- self._results_directory = results_directory
- self._options = options
-
- # The remaining fields are initialized in start()
- self._host = None
- self._port = None
- self._batch_size = None
- self._batch_count = None
- self._filesystem = None
- self._driver = None
- self._tests_run_file = None
- self._tests_run_filename = None
-
- def __del__(self):
- self.stop()
-
- def start(self):
- """This method is called when the object is starting to be used and it is safe
- for the object to create state that does not need to be pickled (usually this means
- it is called in a child process)."""
- self._host = self._caller.host
- self._filesystem = self._host.filesystem
- self._port = self._host.port_factory.get(self._options.platform, self._options)
-
- self._batch_count = 0
- self._batch_size = self._options.batch_size or 0
- tests_run_filename = self._filesystem.join(self._results_directory, "tests_run%d.txt" % self._worker_number)
- self._tests_run_file = self._filesystem.open_text_file_for_writing(tests_run_filename)
-
- def handle(self, name, source, test_list_name, test_inputs):
- assert name == 'test_list'
- start_time = time.time()
- for test_input in test_inputs:
- self._run_test(test_input)
- elapsed_time = time.time() - start_time
- self._caller.post('finished_test_list', test_list_name, len(test_inputs), elapsed_time)
-
- def _update_test_input(self, test_input):
- if test_input.reference_files is None:
- # Lazy initialization.
- test_input.reference_files = self._port.reference_files(test_input.test_name)
- if test_input.reference_files:
- test_input.should_run_pixel_test = True
- else:
- test_input.should_run_pixel_test = self._port.should_run_as_pixel_test(test_input)
-
- def _run_test(self, test_input):
- self._update_test_input(test_input)
- test_timeout_sec = self._timeout(test_input)
- start = time.time()
- self._caller.post('started_test', test_input, test_timeout_sec)
-
- result = self._run_test_with_timeout(test_input, test_timeout_sec)
-
- elapsed_time = time.time() - start
- self._caller.post('finished_test', result, elapsed_time)
-
- self._clean_up_after_test(test_input, result)
-
- def stop(self):
- _log.debug("%s cleaning up" % self._name)
- self._kill_driver()
- if self._tests_run_file:
- self._tests_run_file.close()
- self._tests_run_file = None
-
- def _timeout(self, test_input):
- """Compute the appropriate timeout value for a test."""
- # The DumpRenderTree watchdog uses 2.5x the timeout; we want to be
- # larger than that. We also add a little more padding if we're
- # running tests in a separate thread.
- #
- # Note that we need to convert the test timeout from a
- # string value in milliseconds to a float for Python.
- driver_timeout_sec = 3.0 * float(test_input.timeout) / 1000.0
- if not self._options.run_singly:
- return driver_timeout_sec
-
- thread_padding_sec = 1.0
- thread_timeout_sec = driver_timeout_sec + thread_padding_sec
- return thread_timeout_sec
-
- def _kill_driver(self):
- # Be careful about how and when we kill the driver; if driver.stop()
- # raises an exception, this routine may get re-entered via __del__.
- driver = self._driver
- self._driver = None
- if driver:
- _log.debug("%s killing driver" % self._name)
- driver.stop()
-
- def _run_test_with_timeout(self, test_input, timeout):
- if self._options.run_singly:
- return self._run_test_in_another_thread(test_input, timeout)
- return self._run_test_in_this_thread(test_input)
-
- def _clean_up_after_test(self, test_input, result):
- self._batch_count += 1
- test_name = test_input.test_name
- self._tests_run_file.write(test_name + "\n")
-
- if result.failures:
- # Check and kill DumpRenderTree if we need to.
- if any([f.driver_needs_restart() for f in result.failures]):
- self._kill_driver()
- # Reset the batch count since the shell just bounced.
- self._batch_count = 0
-
- # Print the error message(s).
- _log.debug("%s %s failed:" % (self._name, test_name))
- for f in result.failures:
- _log.debug("%s %s" % (self._name, f.message()))
- elif result.type == test_expectations.SKIP:
- _log.debug("%s %s skipped" % (self._name, test_name))
- else:
- _log.debug("%s %s passed" % (self._name, test_name))
-
- if self._batch_size > 0 and self._batch_count >= self._batch_size:
- self._kill_driver()
- self._batch_count = 0
-
- def _run_test_in_another_thread(self, test_input, thread_timeout_sec):
- """Run a test in a separate thread, enforcing a hard time limit.
-
- Since we can only detect the termination of a thread, not any internal
- state or progress, we can only run per-test timeouts when running test
- files singly.
-
- Args:
- test_input: Object containing the test filename and timeout
- thread_timeout_sec: time to wait before killing the driver process.
- Returns:
- A TestResult
- """
- worker = self
-
- driver = self._port.create_driver(self._worker_number)
-
- class SingleTestThread(threading.Thread):
- def __init__(self):
- threading.Thread.__init__(self)
- self.result = None
-
- def run(self):
- self.result = worker._run_single_test(driver, test_input)
-
- thread = SingleTestThread()
- thread.start()
- thread.join(thread_timeout_sec)
- result = thread.result
- if thread.isAlive():
- # If join() returned with the thread still running, the
- # DumpRenderTree is completely hung and there's nothing
- # more we can do with it. We have to kill all the
- # DumpRenderTrees to free it up. If we're running more than
- # one DumpRenderTree thread, we'll end up killing the other
- # DumpRenderTrees too, introducing spurious crashes. We accept
- # that tradeoff in order to avoid losing the rest of this
- # thread's results.
- _log.error('Test thread hung: killing all DumpRenderTrees')
-
- driver.stop()
-
- if not result:
- result = test_results.TestResult(test_input.test_name, failures=[], test_run_time=0)
- return result
-
- def _run_test_in_this_thread(self, test_input):
- """Run a single test file using a shared DumpRenderTree process.
-
- Args:
- test_input: Object containing the test filename, uri and timeout
-
- Returns: a TestResult object.
- """
- if self._driver and self._driver.has_crashed():
- self._kill_driver()
- if not self._driver:
- self._driver = self._port.create_driver(self._worker_number)
- return self._run_single_test(self._driver, test_input)
-
- def _run_single_test(self, driver, test_input):
- return single_test_runner.run_single_test(self._port, self._options,
- test_input, driver, self._name)