diff options
| author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-07-18 13:59:13 +0200 |
|---|---|---|
| committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-07-18 13:59:28 +0200 |
| commit | 4d6084feccab99c0a7b3ecef26bb49c41dd50201 (patch) | |
| tree | fd1195897f551eee6d5a15d07ff5733b15aa2a5c /Tools/Scripts/webkitpy/test | |
| parent | ae901828d4689ab9e89113f6b6ea8042b37a9fda (diff) | |
| download | qtwebkit-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.py | 6 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/finder_unittest.py | 17 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/main.py | 4 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/main_unittest.py | 2 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/printer.py | 108 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/runner.py | 76 | ||||
| -rw-r--r-- | Tools/Scripts/webkitpy/test/runner_unittest.py | 51 |
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__': |
