https://bugs.webkit.org/show_bug.cgi?id=117514
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / run_webkit_tests.py
1 # Copyright (C) 2010 Google Inc. All rights reserved.
2 # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
3 # Copyright (C) 2011 Apple Inc. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are
7 # met:
8 #
9 #     * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 #     * Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following disclaimer
13 # in the documentation and/or other materials provided with the
14 # distribution.
15 #     * Neither the name of Google Inc. nor the names of its
16 # contributors may be used to endorse or promote products derived from
17 # this software without specific prior written permission.
18 #
19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 import logging
32 import optparse
33 import os
34 import signal
35 import sys
36 import traceback
37
38 from webkitpy.common.host import Host
39 from webkitpy.layout_tests.controllers.manager import Manager
40 from webkitpy.port import configuration_options, platform_options
41 from webkitpy.layout_tests.views import buildbot_results
42 from webkitpy.layout_tests.views import printing
43
44
45 _log = logging.getLogger(__name__)
46
47
48 # This mirrors what the shell normally does.
49 INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128
50
51 # This is a randomly chosen exit code that can be tested against to
52 # indicate that an unexpected exception occurred.
53 EXCEPTIONAL_EXIT_STATUS = 254
54
55
56 def main(argv, stdout, stderr):
57     options, args = parse_args(argv)
58
59     if options.platform and 'test' in options.platform:
60         # It's a bit lame to import mocks into real code, but this allows the user
61         # to run tests against the test platform interactively, which is useful for
62         # debugging test failures.
63         from webkitpy.common.host_mock import MockHost
64         host = MockHost()
65     else:
66         host = Host()
67
68     if options.lint_test_files:
69         from webkitpy.layout_tests.lint_test_expectations import lint
70         return lint(host, options, stderr)
71
72     try:
73         port = host.port_factory.get(options.platform, options)
74     except NotImplementedError, e:
75         # FIXME: is this the best way to handle unsupported port names?
76         print >> stderr, str(e)
77         return EXCEPTIONAL_EXIT_STATUS
78
79     try:
80         run_details = run(port, options, args, stderr)
81         if run_details.exit_code != -1:
82             bot_printer = buildbot_results.BuildBotPrinter(stdout, options.debug_rwt_logging)
83             bot_printer.print_results(run_details)
84
85         return run_details.exit_code
86     except KeyboardInterrupt:
87         return INTERRUPTED_EXIT_STATUS
88     except BaseException as e:
89         if isinstance(e, Exception):
90             print >> stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e))
91             traceback.print_exc(file=stderr)
92         return EXCEPTIONAL_EXIT_STATUS
93
94
95 def parse_args(args):
96     option_group_definitions = []
97
98     option_group_definitions.append(("Platform options", platform_options()))
99     option_group_definitions.append(("Configuration options", configuration_options()))
100     option_group_definitions.append(("Printing Options", printing.print_options()))
101
102     option_group_definitions.append(("EFL-specific Options", [
103         optparse.make_option("--webprocess-cmd-prefix", type="string",
104             default=False, help="Prefix used when spawning the Web process (Debug mode only)"),
105     ]))
106
107     option_group_definitions.append(("WebKit Options", [
108         optparse.make_option("--gc-between-tests", action="store_true", default=False,
109             help="Force garbage collection between each test"),
110         optparse.make_option("--complex-text", action="store_true", default=False,
111             help="Use the complex text code path for all text (Mac OS X and Windows only)"),
112         optparse.make_option("-l", "--leaks", action="store_true", default=False,
113             help="Enable leaks checking (Mac OS X only)"),
114         optparse.make_option("-g", "--guard-malloc", action="store_true", default=False,
115             help="Enable Guard Malloc (Mac OS X only)"),
116         optparse.make_option("--threaded", action="store_true", default=False,
117             help="Run a concurrent JavaScript thread with each test"),
118         optparse.make_option("--webkit-test-runner", "-2", action="store_true",
119             help="Use WebKitTestRunner rather than DumpRenderTree."),
120         # FIXME: We should merge this w/ --build-directory and only have one flag.
121         optparse.make_option("--root", action="store",
122             help="Path to a directory containing the executables needed to run tests."),
123     ]))
124
125     option_group_definitions.append(("Results Options", [
126         optparse.make_option("-p", "--pixel", "--pixel-tests", action="store_true",
127             dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
128         optparse.make_option("--no-pixel", "--no-pixel-tests", action="store_false",
129             dest="pixel_tests", help="Disable pixel-to-pixel PNG comparisons"),
130         optparse.make_option("--no-sample-on-timeout", action="store_false",
131             dest="sample_on_timeout", help="Don't run sample on timeout (Mac OS X only)"),
132         optparse.make_option("--no-ref-tests", action="store_true",
133             dest="no_ref_tests", help="Skip all ref tests"),
134         optparse.make_option("--tolerance",
135             help="Ignore image differences less than this percentage (some "
136                 "ports may ignore this option)", type="float"),
137         optparse.make_option("--results-directory", help="Location of test results"),
138         optparse.make_option("--build-directory",
139             help="Path to the directory under which build files are kept (should not include configuration)"),
140         optparse.make_option("--add-platform-exceptions", action="store_true", default=False,
141             help="Save generated results into the *most-specific-platform* directory rather than the *generic-platform* directory"),
142         optparse.make_option("--new-baseline", action="store_true",
143             default=False, help="Save generated results as new baselines "
144                  "into the *most-specific-platform* directory, overwriting whatever's "
145                  "already there. Equivalent to --reset-results --add-platform-exceptions"),
146         optparse.make_option("--reset-results", action="store_true",
147             default=False, help="Reset expectations to the "
148                  "generated results in their existing location."),
149         optparse.make_option("--no-new-test-results", action="store_false",
150             dest="new_test_results", default=True,
151             help="Don't create new baselines when no expected results exist"),
152
153         #FIXME: we should support a comma separated list with --pixel-test-directory as well.
154         optparse.make_option("--pixel-test-directory", action="append", default=[], dest="pixel_test_directories",
155             help="A directory where it is allowed to execute tests as pixel tests. "
156                  "Specify multiple times to add multiple directories. "
157                  "This option implies --pixel-tests. If specified, only those tests "
158                  "will be executed as pixel tests that are located in one of the "
159                  "directories enumerated with the option. Some ports may ignore this "
160                  "option while others can have a default value that can be overridden here."),
161
162         optparse.make_option("--skip-failing-tests", action="store_true",
163             default=False, help="Skip tests that are expected to fail. "
164                  "Note: When using this option, you might miss new crashes "
165                  "in these tests."),
166         optparse.make_option("--additional-drt-flag", action="append",
167             default=[], help="Additional command line flag to pass to DumpRenderTree "
168                  "Specify multiple times to add multiple flags."),
169         optparse.make_option("--driver-name", type="string",
170             help="Alternative DumpRenderTree binary to use"),
171         optparse.make_option("--additional-platform-directory", action="append",
172             default=[], help="Additional directory where to look for test "
173                  "baselines (will take precendence over platform baselines). "
174                  "Specify multiple times to add multiple search path entries."),
175         optparse.make_option("--additional-expectations", action="append", default=[],
176             help="Path to a test_expectations file that will override previous expectations. "
177                  "Specify multiple times for multiple sets of overrides."),
178         optparse.make_option("--compare-port", action="store", default=None,
179             help="Use the specified port's baselines first"),
180         optparse.make_option("--no-show-results", action="store_false",
181             default=True, dest="show_results",
182             help="Don't launch a browser with results after the tests "
183                  "are done"),
184         optparse.make_option("--full-results-html", action="store_true",
185             default=False,
186             help="Show all failures in results.html, rather than only regressions"),
187         optparse.make_option("--clobber-old-results", action="store_true",
188             default=False, help="Clobbers test results from previous runs."),
189         optparse.make_option("--http", action="store_true", dest="http",
190             default=True, help="Run HTTP and WebSocket tests (default)"),
191         optparse.make_option("--no-http", action="store_false", dest="http",
192             help="Don't run HTTP and WebSocket tests"),
193         optparse.make_option("--ignore-metrics", action="store_true", dest="ignore_metrics",
194             default=False, help="Ignore rendering metrics related information from test "
195             "output, only compare the structure of the rendertree."),
196         optparse.make_option("--nocheck-sys-deps", action="store_true",
197             default=False,
198             help="Don't check the system dependencies (themes)"),
199
200     ]))
201
202     option_group_definitions.append(("Testing Options", [
203         optparse.make_option("--build", dest="build",
204             action="store_true", default=True,
205             help="Check to ensure the DumpRenderTree build is up-to-date "
206                  "(default)."),
207         optparse.make_option("--no-build", dest="build",
208             action="store_false", help="Don't check to see if the "
209                                        "DumpRenderTree build is up-to-date."),
210         optparse.make_option("-n", "--dry-run", action="store_true",
211             default=False,
212             help="Do everything but actually run the tests or upload results."),
213         optparse.make_option("--wrapper",
214             help="wrapper command to insert before invocations of "
215                  "DumpRenderTree; option is split on whitespace before "
216                  "running. (Example: --wrapper='valgrind --smc-check=all')"),
217         optparse.make_option("-i", "--ignore-tests", action="append", default=[],
218             help="directories or test to ignore (may specify multiple times)"),
219         optparse.make_option("--test-list", action="append",
220             help="read list of tests to run from file", metavar="FILE"),
221         optparse.make_option("--skipped", action="store", default="default",
222             help=("control how tests marked SKIP are run. "
223                  "'default' == Skip tests unless explicitly listed on the command line, "
224                  "'ignore' == Run them anyway, "
225                  "'only' == only run the SKIP tests, "
226                  "'always' == always skip, even if listed on the command line.")),
227         optparse.make_option("--force", dest="skipped", action="store_const", const='ignore',
228             help="Run all tests, even those marked SKIP in the test list (same as --skipped=ignore)"),
229         optparse.make_option("--time-out-ms",
230             help="Set the timeout for each test"),
231         optparse.make_option("--order", action="store", default="natural",
232             help=("determine the order in which the test cases will be run. "
233                   "'none' == use the order in which the tests were listed either in arguments or test list, "
234                   "'natural' == use the natural order (default), "
235                   "'random' == randomize the test order.")),
236         optparse.make_option("--run-chunk",
237             help=("Run a specified chunk (n:l), the nth of len l, "
238                  "of the layout tests")),
239         optparse.make_option("--run-part", help=("Run a specified part (n:m), "
240                   "the nth of m parts, of the layout tests")),
241         optparse.make_option("--batch-size",
242             help=("Run a the tests in batches (n), after every n tests, "
243                   "DumpRenderTree is relaunched."), type="int", default=None),
244         optparse.make_option("--run-singly", action="store_true",
245             default=False, help="run a separate DumpRenderTree for each test (implies --verbose)"),
246         optparse.make_option("--child-processes",
247             help="Number of DumpRenderTrees to run in parallel."),
248         # FIXME: Display default number of child processes that will run.
249         optparse.make_option("-f", "--fully-parallel", action="store_true",
250             help="run all tests in parallel"),
251         optparse.make_option("--exit-after-n-failures", type="int", default=None,
252             help="Exit after the first N failures instead of running all "
253             "tests"),
254         optparse.make_option("--exit-after-n-crashes-or-timeouts", type="int",
255             default=None, help="Exit after the first N crashes instead of "
256             "running all tests"),
257         optparse.make_option("--iterations", type="int", default=1, help="Number of times to run the set of tests (e.g. ABCABCABC)"),
258         optparse.make_option("--repeat-each", type="int", default=1, help="Number of times to run each test (e.g. AAABBBCCC)"),
259         optparse.make_option("--retry-failures", action="store_true",
260             default=True,
261             help="Re-try any tests that produce unexpected results (default)"),
262         optparse.make_option("--no-retry-failures", action="store_false",
263             dest="retry_failures",
264             help="Don't re-try any tests that produce unexpected results."),
265         optparse.make_option("--max-locked-shards", type="int", default=0,
266             help="Set the maximum number of locked shards"),
267         optparse.make_option("--additional-env-var", type="string", action="append", default=[],
268             help="Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)"),
269         optparse.make_option("--profile", action="store_true",
270             help="Output per-test profile information."),
271         optparse.make_option("--profiler", action="store",
272             help="Output per-test profile information, using the specified profiler."),
273     ]))
274
275     option_group_definitions.append(("Miscellaneous Options", [
276         optparse.make_option("--lint-test-files", action="store_true",
277         default=False, help=("Makes sure the test files parse for all "
278                             "configurations. Does not run any tests.")),
279     ]))
280
281     # FIXME: Move these into json_results_generator.py
282     option_group_definitions.append(("Result JSON Options", [
283         optparse.make_option("--master-name", help="The name of the buildbot master."),
284         optparse.make_option("--builder-name", default="",
285             help=("The name of the builder shown on the waterfall running "
286                   "this script e.g. WebKit.")),
287         optparse.make_option("--build-name", default="DUMMY_BUILD_NAME",
288             help=("The name of the builder used in its path, e.g. "
289                   "webkit-rel.")),
290         optparse.make_option("--build-number", default="DUMMY_BUILD_NUMBER",
291             help=("The build number of the builder running this script.")),
292         optparse.make_option("--test-results-server", default="",
293             help=("If specified, upload results json files to this appengine "
294                   "server.")),
295     ]))
296
297     option_parser = optparse.OptionParser()
298
299     for group_name, group_options in option_group_definitions:
300         option_group = optparse.OptionGroup(option_parser, group_name)
301         option_group.add_options(group_options)
302         option_parser.add_option_group(option_group)
303
304     return option_parser.parse_args(args)
305
306
307 def _set_up_derived_options(port, options):
308     """Sets the options values that depend on other options values."""
309     if not options.child_processes:
310         options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES",
311                                                  str(port.default_child_processes()))
312     if not options.max_locked_shards:
313         options.max_locked_shards = int(os.environ.get("WEBKIT_TEST_MAX_LOCKED_SHARDS",
314                                                        str(port.default_max_locked_shards())))
315
316     if not options.configuration:
317         options.configuration = port.default_configuration()
318
319     if options.pixel_tests is None:
320         options.pixel_tests = port.default_pixel_tests()
321
322     if not options.time_out_ms:
323         options.time_out_ms = str(port.default_timeout_ms())
324
325     options.slow_time_out_ms = str(5 * int(options.time_out_ms))
326
327     if options.additional_platform_directory:
328         additional_platform_directories = []
329         for path in options.additional_platform_directory:
330             additional_platform_directories.append(port.host.filesystem.abspath(path))
331         options.additional_platform_directory = additional_platform_directories
332
333     if not options.http and options.skipped in ('ignore', 'only'):
334         _log.warning("--force/--skipped=%s overrides --no-http." % (options.skipped))
335         options.http = True
336
337     if options.ignore_metrics and (options.new_baseline or options.reset_results):
338         _log.warning("--ignore-metrics has no effect with --new-baselines or with --reset-results")
339
340     if options.new_baseline:
341         options.reset_results = True
342         options.add_platform_exceptions = True
343
344     if options.pixel_test_directories:
345         options.pixel_tests = True
346         varified_dirs = set()
347         pixel_test_directories = options.pixel_test_directories
348         for directory in pixel_test_directories:
349             # FIXME: we should support specifying the directories all the ways we support it for additional
350             # arguments specifying which tests and directories to run. We should also move the logic for that
351             # to Port.
352             filesystem = port.host.filesystem
353             if not filesystem.isdir(filesystem.join(port.layout_tests_dir(), directory)):
354                 _log.warning("'%s' was passed to --pixel-test-directories, which doesn't seem to be a directory" % str(directory))
355             else:
356                 varified_dirs.add(directory)
357
358         options.pixel_test_directories = list(varified_dirs)
359
360     if options.run_singly:
361         options.verbose = True
362
363
364 def run(port, options, args, logging_stream):
365     logger = logging.getLogger()
366     logger.setLevel(logging.DEBUG if options.debug_rwt_logging else logging.INFO)
367
368     try:
369         printer = printing.Printer(port, options, logging_stream, logger=logger)
370
371         _set_up_derived_options(port, options)
372         manager = Manager(port, options, printer)
373         printer.print_config(port.results_directory())
374
375         run_details = manager.run(args)
376         _log.debug("Testing completed, Exit status: %d" % run_details.exit_code)
377         return run_details
378     finally:
379         printer.cleanup()
380
381 if __name__ == '__main__':
382     sys.exit(main(sys.argv[1:], sys.stdout, sys.stderr))