remove support for junit-style xml output from test-webkitpy
[WebKit-https.git] / Tools / Scripts / webkitpy / test / main.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 """unit testing code for webkitpy."""
25
26 import logging
27 import optparse
28 import StringIO
29 import sys
30 import traceback
31 import unittest
32
33 from webkitpy.common.system.filesystem import FileSystem
34 from webkitpy.test.test_finder import TestFinder
35 from webkitpy.test.runner import TestRunner
36
37 _log = logging.getLogger(__name__)
38
39
40 class Tester(object):
41     def __init__(self, filesystem=None):
42         self.finder = TestFinder(filesystem or FileSystem())
43         self.stream = sys.stderr
44
45     def add_tree(self, top_directory, starting_subdirectory=None):
46         self.finder.add_tree(top_directory, starting_subdirectory)
47
48     def _parse_args(self):
49         parser = optparse.OptionParser(usage='usage: %prog [options] [args...]')
50         parser.add_option('-a', '--all', action='store_true', default=False,
51                           help='run all the tests'),
52         parser.add_option('-c', '--coverage', action='store_true', default=False,
53                           help='generate code coverage info (requires http://pypi.python.org/pypi/coverage)'),
54         parser.add_option('-q', '--quiet', action='store_true', default=False,
55                           help='run quietly (errors, warnings, and progress only)'),
56         parser.add_option('-t', '--timing', action='store_true', default=False,
57                           help='display per-test execution time (implies --verbose)'),
58         parser.add_option('-v', '--verbose', action='count', default=0,
59                           help='verbose output (specify once for individual test results, twice for debug messages)')
60         parser.add_option('--skip-integrationtests', action='store_true', default=False,
61                           help='do not run the integration tests')
62
63         parser.epilog = ('[args...] is an optional list of modules, test_classes, or individual tests. '
64                          'If no args are given, all the tests will be run.')
65
66         return parser.parse_args()
67
68     def _configure(self, options):
69         self._options = options
70
71         if options.timing:
72             # --timing implies --verbose
73             options.verbose = max(options.verbose, 1)
74
75         log_level = logging.INFO
76         if options.quiet:
77             log_level = logging.WARNING
78         elif options.verbose == 2:
79             log_level = logging.DEBUG
80         self._configure_logging(log_level)
81
82     def _configure_logging(self, log_level):
83         """Configure the root logger.
84
85         Configure the root logger not to log any messages from webkitpy --
86         except for messages from the autoinstall module.  Also set the
87         logging level as described below.
88         """
89         handler = logging.StreamHandler(self.stream)
90         # We constrain the level on the handler rather than on the root
91         # logger itself.  This is probably better because the handler is
92         # configured and known only to this module, whereas the root logger
93         # is an object shared (and potentially modified) by many modules.
94         # Modifying the handler, then, is less intrusive and less likely to
95         # interfere with modifications made by other modules (e.g. in unit
96         # tests).
97         handler.name = __name__
98         handler.setLevel(log_level)
99         formatter = logging.Formatter("%(message)s")
100         handler.setFormatter(formatter)
101
102         logger = logging.getLogger()
103         logger.addHandler(handler)
104         logger.setLevel(logging.NOTSET)
105
106         # Filter out most webkitpy messages.
107         #
108         # Messages can be selectively re-enabled for this script by updating
109         # this method accordingly.
110         def filter(record):
111             """Filter out autoinstall and non-third-party webkitpy messages."""
112             # FIXME: Figure out a way not to use strings here, for example by
113             #        using syntax like webkitpy.test.__name__.  We want to be
114             #        sure not to import any non-Python 2.4 code, though, until
115             #        after the version-checking code has executed.
116             if (record.name.startswith("webkitpy.common.system.autoinstall") or
117                 record.name.startswith("webkitpy.test")):
118                 return True
119             if record.name.startswith("webkitpy"):
120                 return False
121             return True
122
123         testing_filter = logging.Filter()
124         testing_filter.filter = filter
125
126         # Display a message so developers are not mystified as to why
127         # logging does not work in the unit tests.
128         _log.info("Suppressing most webkitpy logging while running unit tests.")
129         handler.addFilter(testing_filter)
130
131     def run(self):
132         options, args = self._parse_args()
133         self._configure(options)
134
135         self.finder.clean_trees()
136
137         names = self.finder.find_names(args, self._options.skip_integrationtests, self._options.all)
138         return self._run_tests(names)
139
140     def _run_tests(self, names):
141         if self._options.coverage:
142             try:
143                 import webkitpy.thirdparty.autoinstalled.coverage as coverage
144             except ImportError, e:
145                 _log.error("Failed to import 'coverage'; can't generate coverage numbers.")
146                 return False
147             cov = coverage.coverage()
148             cov.start()
149
150         # Make sure PYTHONPATH is set up properly.
151         sys.path = self.finder.additional_paths(sys.path) + sys.path
152
153         _log.debug("Loading the tests...")
154
155         loader = unittest.defaultTestLoader
156         suites = []
157         for name in names:
158             if self.finder.is_module(name):
159                 # if we failed to load a name and it looks like a module,
160                 # try importing it directly, because loadTestsFromName()
161                 # produces lousy error messages for bad modules.
162                 try:
163                     __import__(name)
164                 except ImportError, e:
165                     _log.fatal('Failed to import %s:' % name)
166                     self._log_exception()
167                     return False
168
169             suites.append(loader.loadTestsFromName(name, None))
170
171         test_suite = unittest.TestSuite(suites)
172         test_runner = TestRunner(self.stream, self._options, loader)
173
174         _log.debug("Running the tests.")
175         result = test_runner.run(test_suite)
176         if self._options.coverage:
177             cov.stop()
178             cov.save()
179             cov.report(show_missing=False)
180         return result.wasSuccessful()
181
182     def _log_exception(self):
183         s = StringIO.StringIO()
184         traceback.print_exc(file=s)
185         for l in s.buflist:
186             _log.error('  ' + l.rstrip())