Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Tools / Scripts / webkitpy / test / printer.py
1 # Copyright (C) 2012 Google, Inc.
2 # Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions
6 # are met:
7 # 1.  Redistributions of source code must retain the above copyright
8 #     notice, this list of conditions and the following disclaimer.
9 # 2.  Redistributions in binary form must reproduce the above copyright
10 #     notice, this list of conditions and the following disclaimer in the
11 #     documentation and/or other materials provided with the distribution.
12 #
13 # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
14 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
17 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
24 import StringIO
25 import logging
26
27 from webkitpy.common.system import outputcapture
28 from webkitpy.common.system.systemhost import SystemHost
29 from webkitpy.layout_tests.views.metered_stream import MeteredStream
30
31 _log = logging.getLogger(__name__)
32
33
34 class Printer(object):
35     def __init__(self, stream, options=None):
36         self.stream = stream
37         self.meter = None
38         self.options = options
39         self.num_tests = 0
40         self.num_completed = 0
41         self.num_errors = 0
42         self.num_failures = 0
43         self.running_tests = []
44         self.completed_tests = []
45         if options:
46             self.configure(options)
47
48     def configure(self, options):
49         self.options = options
50
51         if options.timing:
52             # --timing implies --verbose
53             options.verbose = max(options.verbose, 1)
54
55         log_level = logging.INFO
56         if options.quiet:
57             log_level = logging.WARNING
58         elif options.verbose >= 2:
59             log_level = logging.DEBUG
60
61         self.meter = MeteredStream(self.stream, (options.verbose >= 2),
62             number_of_columns=SystemHost().platform.terminal_width())
63
64         handler = logging.StreamHandler(self.stream)
65         # We constrain the level on the handler rather than on the root
66         # logger itself.  This is probably better because the handler is
67         # configured and known only to this module, whereas the root logger
68         # is an object shared (and potentially modified) by many modules.
69         # Modifying the handler, then, is less intrusive and less likely to
70         # interfere with modifications made by other modules (e.g. in unit
71         # tests).
72         handler.name = __name__
73         handler.setLevel(log_level)
74         formatter = logging.Formatter("%(message)s")
75         handler.setFormatter(formatter)
76
77         logger = logging.getLogger()
78         logger.addHandler(handler)
79         logger.setLevel(logging.NOTSET)
80
81         # Filter out most webkitpy messages.
82         #
83         # Messages can be selectively re-enabled for this script by updating
84         # this method accordingly.
85         def filter_records(record):
86             """Filter out non-third-party webkitpy messages."""
87             # FIXME: Figure out a way not to use strings here, for example by
88             #        using syntax like webkitpy.test.__name__.  We want to be
89             #        sure not to import any non-Python 2.4 code, though, until
90             #        after the version-checking code has executed.
91             if (record.name.startswith("webkitpy.test")):
92                 return True
93             if record.name.startswith("webkitpy"):
94                 return False
95             return True
96
97         testing_filter = logging.Filter()
98         testing_filter.filter = filter_records
99
100         # Display a message so developers are not mystified as to why
101         # logging does not work in the unit tests.
102         _log.info("Suppressing most webkitpy logging while running unit tests.")
103         handler.addFilter(testing_filter)
104
105         if self.options.pass_through:
106             outputcapture.OutputCapture.stream_wrapper = _CaptureAndPassThroughStream
107
108     def write_update(self, msg):
109         self.meter.write_update(msg)
110
111     def print_started_test(self, source, test_name):
112         self.running_tests.append(test_name)
113         if len(self.running_tests) > 1:
114             suffix = ' (+%d)' % (len(self.running_tests) - 1)
115         else:
116             suffix = ''
117
118         if self.options.verbose:
119             write = self.meter.write_update
120         else:
121             write = self.meter.write_throttled_update
122
123         write(self._test_line(self.running_tests[0], suffix))
124
125     def print_finished_test(self, source, test_name, test_time, failures, errors):
126         write = self.meter.writeln
127         if failures:
128             lines = failures[0].splitlines() + ['']
129             suffix = ' failed:'
130             self.num_failures += 1
131         elif errors:
132             lines = errors[0].splitlines() + ['']
133             suffix = ' erred:'
134             self.num_errors += 1
135         else:
136             suffix = ' passed'
137             lines = []
138             if self.options.verbose:
139                 write = self.meter.writeln
140             else:
141                 write = self.meter.write_throttled_update
142         if self.options.timing:
143             suffix += ' %.4fs' % test_time
144
145         self.num_completed += 1
146
147         if test_name == self.running_tests[0]:
148             self.completed_tests.insert(0, [test_name, suffix, lines])
149         else:
150             self.completed_tests.append([test_name, suffix, lines])
151         self.running_tests.remove(test_name)
152
153         for test_name, msg, lines in self.completed_tests:
154             if lines:
155                 self.meter.writeln(self._test_line(test_name, msg))
156                 for line in lines:
157                     self.meter.writeln('  ' + line)
158             else:
159                 write(self._test_line(test_name, msg))
160         self.completed_tests = []
161
162     def _test_line(self, test_name, suffix):
163         format_string = '[%d/%d] %s%s'
164         status_line = format_string % (self.num_completed, self.num_tests, test_name, suffix)
165         if len(status_line) > self.meter.number_of_columns():
166             overflow_columns = len(status_line) - self.meter.number_of_columns()
167             ellipsis = '...'
168             if len(test_name) < overflow_columns + len(ellipsis) + 3:
169                 # We don't have enough space even if we elide, just show the test method name.
170                 test_name = test_name.split('.')[-1]
171             else:
172                 new_length = len(test_name) - overflow_columns - len(ellipsis)
173                 prefix = int(new_length / 2)
174                 test_name = test_name[:prefix] + ellipsis + test_name[-(new_length - prefix):]
175         return format_string % (self.num_completed, self.num_tests, test_name, suffix)
176
177     def print_result(self, run_time):
178         write = self.meter.writeln
179         write('Ran %d test%s in %.3fs' % (self.num_completed, self.num_completed != 1 and "s" or "", run_time))
180         if self.num_failures or self.num_errors:
181             write('FAILED (failures=%d, errors=%d)\n' % (self.num_failures, self.num_errors))
182         else:
183             write('\nOK\n')
184
185
186 class _CaptureAndPassThroughStream(object):
187     def __init__(self, stream):
188         self._buffer = StringIO.StringIO()
189         self._stream = stream
190
191     def write(self, msg):
192         self._stream.write(msg)
193
194         # Note that we don't want to capture any output generated by the debugger
195         # because that could cause the results of capture_output() to be invalid.
196         if not self._message_is_from_pdb():
197             self._buffer.write(msg)
198
199     def _message_is_from_pdb(self):
200         # We will assume that if the pdb module is in the stack then the output
201         # is being generated by the python debugger (or the user calling something
202         # from inside the debugger).
203         import inspect
204         import pdb
205         stack = inspect.stack()
206         return any(frame[1] == pdb.__file__.replace('.pyc', '.py') for frame in stack)
207
208     def flush(self):
209         self._stream.flush()
210
211     def getvalue(self):
212         return self._buffer.getvalue()