test-webkitpy: more class renaming cleanup
[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
31 _log = logging.getLogger(__name__)
32
33
34 class Runner(object):
35     def __init__(self, stream, options, loader):
36         self.options = options
37         self.stream = stream
38         self.loader = loader
39         self.test_description = re.compile("(\w+) \(([\w.]+)\)")
40
41     def test_name(self, test):
42         m = self.test_description.match(str(test))
43         return "%s.%s" % (m.group(2), m.group(1))
44
45     def all_test_names(self, suite):
46         names = []
47         if hasattr(suite, '_tests'):
48             for t in suite._tests:
49                 names.extend(self.all_test_names(t))
50         else:
51             names.append(self.test_name(suite))
52         return names
53
54     def run(self, suite):
55         run_start_time = time.time()
56         all_test_names = self.all_test_names(suite)
57         result = unittest.TestResult()
58         stop = run_start_time
59         for test_name in all_test_names:
60             if self.options.verbose:
61                 self.stream.write(test_name)
62             num_failures = len(result.failures)
63             num_errors = len(result.errors)
64
65             start = time.time()
66             # FIXME: it's kinda lame that we re-load the test suites for each
67             # test, and this may slow things down, but this makes implementing
68             # the logging easy and will also allow us to parallelize nicely.
69             self.loader.loadTestsFromName(test_name, None).run(result)
70             stop = time.time()
71
72             err = None
73             failure = None
74             if len(result.failures) > num_failures:
75                 failure = result.failures[num_failures][1]
76             elif len(result.errors) > num_errors:
77                 err = result.errors[num_errors][1]
78             self.write_result(result, test_name, stop - start, failure, err)
79
80         self.write_summary(result, stop - run_start_time)
81
82         return result
83
84     def write_result(self, result, test_name, test_time, failure=None, err=None):
85         timing = ''
86         if self.options.timing:
87             timing = ' %.4fs' % test_time
88         if self.options.verbose:
89             if failure:
90                 msg = ' failed'
91             elif err:
92                 msg = ' erred'
93             else:
94                 msg = ' passed'
95             self.stream.write(msg + timing + '\n')
96         else:
97             if failure:
98                 msg = 'F'
99             elif err:
100                 msg = 'E'
101             else:
102                 msg = '.'
103             self.stream.write(msg)
104
105     def write_summary(self, result, run_time):
106         self.stream.write('\n')
107
108         for (test, err) in result.errors:
109             self.stream.write("=" * 80 + '\n')
110             self.stream.write("ERROR: " + self.test_name(test) + '\n')
111             self.stream.write("-" * 80 + '\n')
112             for line in err.splitlines():
113                 self.stream.write(line + '\n')
114             self.stream.write('\n')
115
116         for (test, failure) in result.failures:
117             self.stream.write("=" * 80 + '\n')
118             self.stream.write("FAILURE: " + self.test_name(test) + '\n')
119             self.stream.write("-" * 80 + '\n')
120             for line in failure.splitlines():
121                 self.stream.write(line + '\n')
122             self.stream.write('\n')
123
124         self.stream.write('-' * 80 + '\n')
125         self.stream.write('Ran %d test%s in %.3fs\n' %
126             (result.testsRun, result.testsRun != 1 and "s" or "", run_time))
127
128         if result.wasSuccessful():
129             self.stream.write('\nOK\n')
130         else:
131             self.stream.write('FAILED (failures=%d, errors=%d)\n' %
132                 (len(result.failures), len(result.errors)))