summaryrefslogtreecommitdiff
path: root/Tools/Scripts/webkitpy/test
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-07-18 13:59:13 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-07-18 13:59:28 +0200
commit4d6084feccab99c0a7b3ecef26bb49c41dd50201 (patch)
treefd1195897f551eee6d5a15d07ff5733b15aa2a5c /Tools/Scripts/webkitpy/test
parentae901828d4689ab9e89113f6b6ea8042b37a9fda (diff)
downloadqtwebkit-4d6084feccab99c0a7b3ecef26bb49c41dd50201.tar.gz
Imported WebKit commit ff52235a78888e5cb8e286a828a8698042200e67 (http://svn.webkit.org/repository/webkit/trunk@122948)
New snapshot that should fix the rendering issues recently introduced
Diffstat (limited to 'Tools/Scripts/webkitpy/test')
-rw-r--r--Tools/Scripts/webkitpy/test/finder.py6
-rw-r--r--Tools/Scripts/webkitpy/test/finder_unittest.py17
-rw-r--r--Tools/Scripts/webkitpy/test/main.py4
-rw-r--r--Tools/Scripts/webkitpy/test/main_unittest.py2
-rw-r--r--Tools/Scripts/webkitpy/test/printer.py108
-rw-r--r--Tools/Scripts/webkitpy/test/runner.py76
-rw-r--r--Tools/Scripts/webkitpy/test/runner_unittest.py51
7 files changed, 149 insertions, 115 deletions
diff --git a/Tools/Scripts/webkitpy/test/finder.py b/Tools/Scripts/webkitpy/test/finder.py
index 132072d82..fcbb0e9cf 100644
--- a/Tools/Scripts/webkitpy/test/finder.py
+++ b/Tools/Scripts/webkitpy/test/finder.py
@@ -101,7 +101,7 @@ class Finder(object):
return tree.to_module(path)
return None
- def find_names(self, args, skip_integrationtests, find_all):
+ def find_names(self, args, skip_integrationtests, find_all, skip_if_parallel=True):
suffixes = ['_unittest.py']
if not skip_integrationtests:
suffixes.append('_integrationtest.py')
@@ -112,7 +112,7 @@ class Finder(object):
names.extend(self._find_names_for_arg(arg, suffixes))
return names
- return self._default_names(suffixes, find_all)
+ return self._default_names(suffixes, find_all, skip_if_parallel)
def _find_names_for_arg(self, arg, suffixes):
realpath = self.filesystem.realpath(arg)
@@ -145,7 +145,7 @@ class Finder(object):
return tree.find_modules(suffixes, path)
return []
- def _default_names(self, suffixes, find_all):
+ def _default_names(self, suffixes, find_all, skip_if_parallel):
modules = []
for tree in self.trees:
modules.extend(tree.find_modules(suffixes))
diff --git a/Tools/Scripts/webkitpy/test/finder_unittest.py b/Tools/Scripts/webkitpy/test/finder_unittest.py
index 09048b159..386c579c7 100644
--- a/Tools/Scripts/webkitpy/test/finder_unittest.py
+++ b/Tools/Scripts/webkitpy/test/finder_unittest.py
@@ -47,18 +47,15 @@ class FinderTest(unittest.TestCase):
# Here we have to jump through a hoop to make sure test-webkitpy doesn't log
# any messages from these tests :(.
self.root_logger = logging.getLogger()
- self.log_handler = None
- for h in self.root_logger.handlers:
- if getattr(h, 'name', None) == 'webkitpy.test.printer':
- self.log_handler = h
- break
- if self.log_handler:
- self.log_level = self.log_handler.level
- self.log_handler.level = logging.CRITICAL
+ self.log_levels = []
+ self.log_handlers = self.root_logger.handlers[:]
+ for handler in self.log_handlers:
+ self.log_levels.append(handler.level)
+ handler.level = logging.CRITICAL
def tearDown(self):
- if self.log_handler:
- self.log_handler.setLevel(self.log_level)
+ for handler in self.log_handlers:
+ handler.level = self.log_levels.pop(0)
def test_additional_system_paths(self):
self.assertEquals(self.finder.additional_paths(['/usr']),
diff --git a/Tools/Scripts/webkitpy/test/main.py b/Tools/Scripts/webkitpy/test/main.py
index 2048d9e59..abb297b2b 100644
--- a/Tools/Scripts/webkitpy/test/main.py
+++ b/Tools/Scripts/webkitpy/test/main.py
@@ -63,6 +63,8 @@ class Tester(object):
help='do not run the integration tests')
parser.add_option('-p', '--pass-through', action='store_true', default=False,
help='be debugger friendly by passing captured output through to the system')
+ parser.add_option('-j', '--child-processes', action='store', type='int', default=1,
+ help='number of tests to run in parallel')
parser.epilog = ('[args...] is an optional list of modules, test_classes, or individual tests. '
'If no args are given, all the tests will be run.')
@@ -75,7 +77,7 @@ class Tester(object):
self.finder.clean_trees()
- names = self.finder.find_names(args, self._options.skip_integrationtests, self._options.all)
+ names = self.finder.find_names(args, self._options.skip_integrationtests, self._options.all, self._options.child_processes != 1)
if not names:
_log.error('No tests to run')
return False
diff --git a/Tools/Scripts/webkitpy/test/main_unittest.py b/Tools/Scripts/webkitpy/test/main_unittest.py
index 2cf6df4a2..61e49a7b9 100644
--- a/Tools/Scripts/webkitpy/test/main_unittest.py
+++ b/Tools/Scripts/webkitpy/test/main_unittest.py
@@ -41,7 +41,7 @@ class TesterTest(unittest.TestCase):
root_logger.handlers = []
tester.printer.stream = errors
- tester.finder.find_names = lambda args, skip_integration, run_all: []
+ tester.finder.find_names = lambda args, skip_integration, run_all, skip_if_parallel: []
oc = OutputCapture()
try:
oc.capture_output()
diff --git a/Tools/Scripts/webkitpy/test/printer.py b/Tools/Scripts/webkitpy/test/printer.py
index 77e28b8d1..042fba13c 100644
--- a/Tools/Scripts/webkitpy/test/printer.py
+++ b/Tools/Scripts/webkitpy/test/printer.py
@@ -22,10 +22,10 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import logging
-import re
import StringIO
from webkitpy.common.system import outputcapture
+from webkitpy.layout_tests.views.metered_stream import MeteredStream
_log = logging.getLogger(__name__)
@@ -33,12 +33,14 @@ _log = logging.getLogger(__name__)
class Printer(object):
def __init__(self, stream, options=None):
self.stream = stream
+ self.meter = None
self.options = options
- self.test_description = re.compile("(\w+) \(([\w.]+)\)")
-
- def test_name(self, test):
- m = self.test_description.match(str(test))
- return "%s.%s" % (m.group(2), m.group(1))
+ self.num_tests = 0
+ self.num_completed = 0
+ self.running_tests = []
+ self.completed_tests = []
+ if options:
+ self.configure(options)
def configure(self, options):
self.options = options
@@ -53,6 +55,8 @@ class Printer(object):
elif options.verbose == 2:
log_level = logging.DEBUG
+ self.meter = MeteredStream(self.stream, (options.verbose == 2))
+
handler = logging.StreamHandler(self.stream)
# We constrain the level on the handler rather than on the root
# logger itself. This is probably better because the handler is
@@ -98,59 +102,65 @@ class Printer(object):
if self.options.pass_through:
outputcapture.OutputCapture.stream_wrapper = _CaptureAndPassThroughStream
- def print_started_test(self, test_name):
+ def print_started_test(self, source, test_name):
+ self.running_tests.append(test_name)
+ if len(self.running_tests) > 1:
+ suffix = ' (+%d)' % (len(self.running_tests) - 1)
+ else:
+ suffix = ''
+
if self.options.verbose:
- self.stream.write(test_name)
+ write = self.meter.write_update
+ else:
+ write = self.meter.write_throttled_update
+
+ write(self._test_line(self.running_tests[0], suffix))
def print_finished_test(self, result, test_name, test_time, failure, err):
- timing = ''
- if self.options.timing:
- timing = ' %.4fs' % test_time
- if self.options.verbose:
- if failure:
- msg = ' failed'
- elif err:
- msg = ' erred'
+ write = self.meter.writeln
+ if failure:
+ lines = failure[0][1].splitlines() + ['']
+ suffix = ' failed:'
+ elif err:
+ lines = err[0][1].splitlines() + ['']
+ suffix = ' erred:'
+ else:
+ suffix = ' passed'
+ lines = []
+ if self.options.verbose:
+ write = self.meter.writeln
else:
- msg = ' passed'
- self.stream.write(msg + timing + '\n')
+ write = self.meter.write_throttled_update
+ if self.options.timing:
+ suffix += ' %.4fs' % test_time
+
+ self.num_completed += 1
+
+ if test_name == self.running_tests[0]:
+ self.completed_tests.insert(0, [test_name, suffix, lines])
else:
- if failure:
- msg = 'F'
- elif err:
- msg = 'E'
+ self.completed_tests.append([test_name, suffix, lines])
+ self.running_tests.remove(test_name)
+
+ for test_name, msg, lines in self.completed_tests:
+ if lines:
+ self.meter.writeln(self._test_line(test_name, msg))
+ for line in lines:
+ self.meter.writeln(' ' + line)
else:
- msg = '.'
- self.stream.write(msg)
+ write(self._test_line(test_name, msg))
+ self.completed_tests = []
- def print_result(self, result, run_time):
- self.stream.write('\n')
-
- for (test, err) in result.errors:
- self.stream.write("=" * 80 + '\n')
- self.stream.write("ERROR: " + self.test_name(test) + '\n')
- self.stream.write("-" * 80 + '\n')
- for line in err.splitlines():
- self.stream.write(line + '\n')
- self.stream.write('\n')
-
- for (test, failure) in result.failures:
- self.stream.write("=" * 80 + '\n')
- self.stream.write("FAILURE: " + self.test_name(test) + '\n')
- self.stream.write("-" * 80 + '\n')
- for line in failure.splitlines():
- self.stream.write(line + '\n')
- self.stream.write('\n')
-
- self.stream.write('-' * 80 + '\n')
- self.stream.write('Ran %d test%s in %.3fs\n' %
- (result.testsRun, result.testsRun != 1 and "s" or "", run_time))
+ def _test_line(self, test_name, suffix):
+ return '[%d/%d] %s%s' % (self.num_completed, self.num_tests, test_name, suffix)
+ def print_result(self, result, run_time):
+ write = self.meter.writeln
+ write('Ran %d test%s in %.3fs' % (result.testsRun, result.testsRun != 1 and "s" or "", run_time))
if result.wasSuccessful():
- self.stream.write('\nOK\n')
+ write('\nOK\n')
else:
- self.stream.write('FAILED (failures=%d, errors=%d)\n' %
- (len(result.failures), len(result.errors)))
+ write('FAILED (failures=%d, errors=%d)\n' % (len(result.failures), len(result.errors)))
class _CaptureAndPassThroughStream(object):
diff --git a/Tools/Scripts/webkitpy/test/runner.py b/Tools/Scripts/webkitpy/test/runner.py
index 9c952075e..fd8af6fe0 100644
--- a/Tools/Scripts/webkitpy/test/runner.py
+++ b/Tools/Scripts/webkitpy/test/runner.py
@@ -23,18 +23,30 @@
"""code to actually run a list of python tests."""
import logging
+import re
import time
import unittest
+from webkitpy.common import message_pool
_log = logging.getLogger(__name__)
+_test_description = re.compile("(\w+) \(([\w.]+)\)")
+
+
+def _test_name(test):
+ m = _test_description.match(str(test))
+ return "%s.%s" % (m.group(2), m.group(1))
+
+
class Runner(object):
def __init__(self, printer, options, loader):
self.options = options
self.printer = printer
self.loader = loader
+ self.result = unittest.TestResult()
+ self.worker_factory = lambda caller: _Worker(caller, self.loader)
def all_test_names(self, suite):
names = []
@@ -42,34 +54,48 @@ class Runner(object):
for t in suite._tests:
names.extend(self.all_test_names(t))
else:
- names.append(self.printer.test_name(suite))
+ names.append(_test_name(suite))
return names
def run(self, suite):
run_start_time = time.time()
all_test_names = self.all_test_names(suite)
+ self.printer.num_tests = len(all_test_names)
+
+ with message_pool.get(self, self.worker_factory, int(self.options.child_processes)) as pool:
+ pool.run(('test', test_name) for test_name in all_test_names)
+
+ self.printer.print_result(self.result, time.time() - run_start_time)
+ return self.result
+
+ def handle(self, message_name, source, test_name, delay=None, result=None):
+ if message_name == 'started_test':
+ self.printer.print_started_test(source, test_name)
+ return
+
+ self.result.testsRun += 1
+ self.result.errors.extend(result.errors)
+ self.result.failures.extend(result.failures)
+ self.printer.print_finished_test(source, test_name, delay, result.failures, result.errors)
+
+
+class _Worker(object):
+ def __init__(self, caller, loader):
+ self._caller = caller
+ self._loader = loader
+
+ def handle(self, message_name, source, test_name):
+ assert message_name == 'test'
result = unittest.TestResult()
- stop = run_start_time
- for test_name in all_test_names:
- self.printer.print_started_test(test_name)
- num_failures = len(result.failures)
- num_errors = len(result.errors)
-
- start = time.time()
- # FIXME: it's kinda lame that we re-load the test suites for each
- # test, and this may slow things down, but this makes implementing
- # the logging easy and will also allow us to parallelize nicely.
- self.loader.loadTestsFromName(test_name, None).run(result)
- stop = time.time()
-
- err = None
- failure = None
- if len(result.failures) > num_failures:
- failure = result.failures[num_failures][1]
- elif len(result.errors) > num_errors:
- err = result.errors[num_errors][1]
- self.printer.print_finished_test(result, test_name, stop - start, failure, err)
-
- self.printer.print_result(result, stop - run_start_time)
-
- return result
+ start = time.time()
+ self._caller.post('started_test', test_name)
+ self._loader.loadTestsFromName(test_name, None).run(result)
+
+ # The tests in the TestResult contain file objects and other unpicklable things; we only
+ # care about the test name, so we rewrite the result to replace the test with the test name.
+ # FIXME: We need an automated test for this, but I don't know how to write an automated
+ # test that will fail in this case that doesn't get picked up by test-webkitpy normally :(.
+ result.failures = [(_test_name(failure[0]), failure[1]) for failure in result.failures]
+ result.errors = [(_test_name(error[0]), error[1]) for error in result.errors]
+
+ self._caller.post('finished_test', test_name, time.time() - start, result)
diff --git a/Tools/Scripts/webkitpy/test/runner_unittest.py b/Tools/Scripts/webkitpy/test/runner_unittest.py
index 1cf0146fb..07c5c31ea 100644
--- a/Tools/Scripts/webkitpy/test/runner_unittest.py
+++ b/Tools/Scripts/webkitpy/test/runner_unittest.py
@@ -20,13 +20,14 @@
# 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.
+import logging
import re
import StringIO
import unittest
from webkitpy.tool.mocktool import MockOptions
from webkitpy.test.printer import Printer
-from webkitpy.test.runner import Runner
+from webkitpy.test.runner import Runner, _Worker
class FakeModuleSuite(object):
@@ -69,44 +70,42 @@ class FakeLoader(object):
class RunnerTest(unittest.TestCase):
- def test_regular(self):
- options = MockOptions(verbose=0, timing=False)
+ def setUp(self):
+ # Here we have to jump through a hoop to make sure test-webkitpy doesn't log
+ # any messages from these tests :(.
+ self.root_logger = logging.getLogger()
+ self.log_levels = []
+ self.log_handlers = self.root_logger.handlers[:]
+ for handler in self.log_handlers:
+ self.log_levels.append(handler.level)
+ handler.level = logging.CRITICAL
+
+ def tearDown(self):
+ for handler in self.log_handlers:
+ handler.level = self.log_levels.pop(0)
+
+ def assert_run(self, verbose=0, timing=False, child_processes=1, quiet=False):
+ options = MockOptions(verbose=verbose, timing=timing, child_processes=child_processes, quiet=quiet, pass_through=False)
stream = StringIO.StringIO()
loader = FakeLoader(('test1 (Foo)', '.', ''),
('test2 (Foo)', 'F', 'test2\nfailed'),
('test3 (Foo)', 'E', 'test3\nerred'))
- result = Runner(Printer(stream, options), options, loader).run(loader.top_suite())
+ runner = Runner(Printer(stream, options), options, loader)
+ result = runner.run(loader.top_suite())
self.assertFalse(result.wasSuccessful())
self.assertEquals(result.testsRun, 3)
self.assertEquals(len(result.failures), 1)
self.assertEquals(len(result.errors), 1)
# FIXME: check the output from the test
+ def test_regular(self):
+ self.assert_run()
+
def test_verbose(self):
- options = MockOptions(verbose=1, timing=False)
- stream = StringIO.StringIO()
- loader = FakeLoader(('test1 (Foo)', '.', ''),
- ('test2 (Foo)', 'F', 'test2\nfailed'),
- ('test3 (Foo)', 'E', 'test3\nerred'))
- result = Runner(Printer(stream, options), options, loader).run(loader.top_suite())
- self.assertFalse(result.wasSuccessful())
- self.assertEquals(result.testsRun, 3)
- self.assertEquals(len(result.failures), 1)
- self.assertEquals(len(result.errors), 1)
- # FIXME: check the output from the test
+ self.assert_run(verbose=1)
def test_timing(self):
- options = MockOptions(verbose=0, timing=True)
- stream = StringIO.StringIO()
- loader = FakeLoader(('test1 (Foo)', '.', ''),
- ('test2 (Foo)', 'F', 'test2\nfailed'),
- ('test3 (Foo)', 'E', 'test3\nerred'))
- result = Runner(Printer(stream, options), options, loader).run(loader.top_suite())
- self.assertFalse(result.wasSuccessful())
- self.assertEquals(result.testsRun, 3)
- self.assertEquals(len(result.failures), 1)
- self.assertEquals(len(result.errors), 1)
- # FIXME: check the output from the test
+ self.assert_run(timing=True)
if __name__ == '__main__':