test-webkitpy: clean up logging to make it ninja-esque
[WebKit-https.git] / Tools / Scripts / webkitpy / test / runner.py
1 # Copyright (C) 2012 Google, Inc.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions
5 # are met:
6 # 1.  Redistributions of source code must retain the above copyright
7 #     notice, this list of conditions and the following disclaimer.
8 # 2.  Redistributions in binary form must reproduce the above copyright
9 #     notice, this list of conditions and the following disclaimer in the
10 #     documentation and/or other materials provided with the distribution.
11 #
12 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23 """code to actually run a list of python tests."""
24
25 import logging
26 import re
27 import time
28 import unittest
29
30 from webkitpy.common import message_pool
31
32 _log = logging.getLogger(__name__)
33
34
35 _test_description = re.compile("(\w+) \(([\w.]+)\)")
36
37
38 def _test_name(test):
39     m = _test_description.match(str(test))
40     return "%s.%s" % (m.group(2), m.group(1))
41
42
43 class Runner(object):
44     def __init__(self, printer, options, loader):
45         self.options = options
46         self.printer = printer
47         self.loader = loader
48         self.result = unittest.TestResult()
49         self.worker_factory = lambda caller: _Worker(caller, self.loader)
50
51     def all_test_names(self, suite):
52         names = []
53         if hasattr(suite, '_tests'):
54             for t in suite._tests:
55                 names.extend(self.all_test_names(t))
56         else:
57             names.append(_test_name(suite))
58         return names
59
60     def run(self, suite):
61         run_start_time = time.time()
62         all_test_names = self.all_test_names(suite)
63         self.printer.num_tests = len(all_test_names)
64
65         with message_pool.get(self, self.worker_factory, int(self.options.child_processes)) as pool:
66             pool.run(('test', test_name) for test_name in all_test_names)
67
68         self.printer.print_result(self.result, time.time() - run_start_time)
69         return self.result
70
71     def handle(self, message_name, source, test_name, delay=None, result=None):
72         if message_name == 'started_test':
73             self.printer.print_started_test(source, test_name)
74             return
75
76         self.result.testsRun += 1
77         self.result.errors.extend(result.errors)
78         self.result.failures.extend(result.failures)
79         self.printer.print_finished_test(source, test_name, delay, result.failures, result.errors)
80
81
82 class _Worker(object):
83     def __init__(self, caller, loader):
84         self._caller = caller
85         self._loader = loader
86
87     def handle(self, message_name, source, test_name):
88         assert message_name == 'test'
89         result = unittest.TestResult()
90         start = time.time()
91         self._caller.post('started_test', test_name)
92         self._loader.loadTestsFromName(test_name, None).run(result)
93
94         # The tests in the TestResult contain file objects and other unpicklable things; we only
95         # care about the test name, so we rewrite the result to replace the test with the test name.
96         # FIXME: We need an automated test for this, but I don't know how to write an automated
97         # test that will fail in this case that doesn't get picked up by test-webkitpy normally :(.
98         result.failures = [(_test_name(failure[0]), failure[1]) for failure in result.failures]
99         result.errors = [(_test_name(error[0]), error[1]) for error in result.errors]
100
101         self._caller.post('finished_test', test_name, time.time() - start, result)