Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / 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 StringIO
27 import logging
28 import multiprocessing
29 import optparse
30 import os
31 import sys
32 import time
33 import traceback
34 import unittest
35
36 from webkitpy.common.webkit_finder import WebKitFinder
37 from webkitpy.common.system.filesystem import FileSystem
38 from webkitpy.common.system.executive import Executive
39 from webkitpy.test.finder import Finder
40 from webkitpy.test.printer import Printer
41 from webkitpy.test.runner import Runner, unit_test_name
42
43 _log = logging.getLogger(__name__)
44
45
46 up = os.path.dirname
47 webkit_root = up(up(up(up(up(os.path.abspath(__file__))))))
48
49
50 def main():
51     filesystem = FileSystem()
52     wkf = WebKitFinder(filesystem)
53     tester = Tester(filesystem, wkf)
54     tester.add_tree(wkf.path_from_webkit_base('Tools', 'Scripts'), 'webkitpy')
55
56     tester.skip(('webkitpy.common.checkout.scm.scm_unittest',), 'are really, really, slow', 31818)
57     if sys.platform == 'win32':
58         tester.skip(('webkitpy.common.checkout', 'webkitpy.common.config', 'webkitpy.tool', 'webkitpy.w3c', 'webkitpy.layout_tests.layout_package.bot_test_expectations'), 'fail horribly on win32', 54526)
59
60     # This only needs to run on Unix, so don't worry about win32 for now.
61     appengine_sdk_path = '/usr/local/google_appengine'
62     if os.path.exists(appengine_sdk_path):
63         if not appengine_sdk_path in sys.path:
64             sys.path.append(appengine_sdk_path)
65         import dev_appserver
66         from google.appengine.dist import use_library
67         use_library('django', '1.2')
68         dev_appserver.fix_sys_path()
69         tester.add_tree(wkf.path_from_webkit_base('Tools', 'TestResultServer'))
70     else:
71         _log.info('Skipping TestResultServer tests; the Google AppEngine Python SDK is not installed.')
72
73     return not tester.run()
74
75
76 class Tester(object):
77     def __init__(self, filesystem=None, webkit_finder=None):
78         self.filesystem = filesystem or FileSystem()
79         self.executive = Executive()
80         self.finder = Finder(self.filesystem)
81         self.printer = Printer(sys.stderr)
82         self.webkit_finder = webkit_finder or WebKitFinder(self.filesystem)
83         self._options = None
84
85     def add_tree(self, top_directory, starting_subdirectory=None):
86         self.finder.add_tree(top_directory, starting_subdirectory)
87
88     def skip(self, names, reason, bugid):
89         self.finder.skip(names, reason, bugid)
90
91     def _parse_args(self, argv):
92         parser = optparse.OptionParser(usage='usage: %prog [options] [args...]')
93         parser.add_option('-a', '--all', action='store_true', default=False,
94                           help='run all the tests')
95         parser.add_option('-c', '--coverage', action='store_true', default=False,
96                           help='generate code coverage info')
97         parser.add_option('-j', '--child-processes', action='store', type='int', default=(1 if sys.platform == 'win32' else multiprocessing.cpu_count()),
98                           help='number of tests to run in parallel (default=%default)')
99         parser.add_option('-p', '--pass-through', action='store_true', default=False,
100                           help='be debugger friendly by passing captured output through to the system')
101         parser.add_option('-q', '--quiet', action='store_true', default=False,
102                           help='run quietly (errors, warnings, and progress only)')
103         parser.add_option('-t', '--timing', action='store_true', default=False,
104                           help='display per-test execution time (implies --verbose)')
105         parser.add_option('-v', '--verbose', action='count', default=0,
106                           help='verbose output (specify once for individual test results, twice for debug messages)')
107
108         parser.epilog = ('[args...] is an optional list of modules, test_classes, or individual tests. '
109                          'If no args are given, all the tests will be run.')
110
111         return parser.parse_args(argv)
112
113     def run(self):
114         argv = sys.argv[1:]
115         self._options, args = self._parse_args(argv)
116
117         # Make sure PYTHONPATH is set up properly.
118         sys.path = self.finder.additional_paths(sys.path) + sys.path
119
120         # FIXME: coverage needs to be in sys.path for its internal imports to work.
121         thirdparty_path = self.webkit_finder.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'thirdparty')
122         if not thirdparty_path in sys.path:
123             sys.path.append(thirdparty_path)
124
125         self.printer.configure(self._options)
126
127         # Do this after configuring the printer, so that logging works properly.
128         if self._options.coverage:
129             argv = ['-j', '1'] + [arg for arg in argv if arg not in ('-c', '--coverage', '-j', '--child-processes')]
130             _log.warning('Checking code coverage, so running things serially')
131             return self._run_under_coverage(argv)
132
133         self.finder.clean_trees()
134
135         names = self.finder.find_names(args, self._options.all)
136         if not names:
137             _log.error('No tests to run')
138             return False
139
140         return self._run_tests(names)
141
142     def _run_under_coverage(self, argv):
143         # coverage doesn't run properly unless its parent dir is in PYTHONPATH.
144         # This means we need to add that dir to the environment. Also, the
145         # report output is best when the paths are relative to the Scripts dir.
146         dirname = self.filesystem.dirname
147         script_dir = dirname(dirname(dirname(__file__)))
148         thirdparty_dir = self.filesystem.join(script_dir, 'webkitpy', 'thirdparty')
149
150         env = os.environ.copy()
151         python_path = env.get('PYTHONPATH', '')
152         python_path = python_path + os.pathsep + thirdparty_dir
153         env['PYTHONPATH'] = python_path
154
155         prefix_cmd = [sys.executable, 'webkitpy/thirdparty/coverage']
156         exit_code = self.executive.call(prefix_cmd + ['run', __file__] + argv, cwd=script_dir, env=env)
157         if not exit_code:
158             exit_code = self.executive.call(prefix_cmd + ['report', '--omit', 'webkitpy/thirdparty/*,/usr/*,/Library/*'], cwd=script_dir, env=env)
159         return (exit_code == 0)
160
161     def _run_tests(self, names):
162         self.printer.write_update("Checking imports ...")
163         if not self._check_imports(names):
164             return False
165
166         self.printer.write_update("Finding the individual test methods ...")
167         loader = unittest.TestLoader()
168         tests = self._test_names(loader, names)
169
170         self.printer.write_update("Running the tests ...")
171         self.printer.num_tests = len(tests)
172         start = time.time()
173         test_runner = Runner(self.printer, loader, self.webkit_finder)
174         test_runner.run(tests, self._options.child_processes)
175
176         self.printer.print_result(time.time() - start)
177
178         return not self.printer.num_errors and not self.printer.num_failures
179
180     def _check_imports(self, names):
181         for name in names:
182             if self.finder.is_module(name):
183                 # if we failed to load a name and it looks like a module,
184                 # try importing it directly, because loadTestsFromName()
185                 # produces lousy error messages for bad modules.
186                 try:
187                     __import__(name)
188                 except ImportError:
189                     _log.fatal('Failed to import %s:' % name)
190                     self._log_exception()
191                     return False
192         return True
193
194     def _test_names(self, loader, names):
195         tests = []
196         for name in names:
197             tests.extend(self._all_test_names(loader.loadTestsFromName(name, None)))
198         return tests
199
200     def _all_test_names(self, suite):
201         names = []
202         if hasattr(suite, '_tests'):
203             for t in suite._tests:
204                 names.extend(self._all_test_names(t))
205         else:
206             names.append(unit_test_name(suite))
207         return names
208
209     def _log_exception(self):
210         s = StringIO.StringIO()
211         traceback.print_exc(file=s)
212         for l in s.buflist:
213             _log.error('  ' + l.rstrip())
214
215
216
217 if __name__ == '__main__':
218     sys.exit(main())