Make it possible to use an existing simulator instance for one-off testing
[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, 2016 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 sys
35 import traceback
36
37 from webkitpy.common.host import Host
38 from webkitpy.layout_tests.controllers.manager import Manager
39 from webkitpy.layout_tests.models.test_run_results import INTERRUPTED_EXIT_STATUS
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 is a randomly chosen exit code that can be tested against to
49 # indicate that an unexpected exception occurred.
50 EXCEPTIONAL_EXIT_STATUS = 254
51
52
53 def main(argv, stdout, stderr):
54     options, args = parse_args(argv)
55
56     if options.platform and 'test' in options.platform:
57         # It's a bit lame to import mocks into real code, but this allows the user
58         # to run tests against the test platform interactively, which is useful for
59         # debugging test failures.
60         from webkitpy.common.host_mock import MockHost
61         host = MockHost()
62     else:
63         host = Host()
64
65     if options.lint_test_files:
66         from webkitpy.layout_tests.lint_test_expectations import lint
67         return lint(host, options, stderr)
68
69     try:
70         port = host.port_factory.get(options.platform, options)
71     except NotImplementedError, e:
72         # FIXME: is this the best way to handle unsupported port names?
73         print >> stderr, str(e)
74         return EXCEPTIONAL_EXIT_STATUS
75
76     if options.print_expectations:
77         return _print_expectations(port, options, args, stderr)
78
79     try:
80         # Force all tests to use a smaller stack so that stack overflow tests can run faster.
81         stackSizeInBytes = int(1.5 * 1024 * 1024)
82         options.additional_env_var.append('JSC_maxPerThreadStackUsage=' + str(stackSizeInBytes))
83         options.additional_env_var.append('__XPC_JSC_maxPerThreadStackUsage=' + str(stackSizeInBytes))
84         run_details = run(port, options, args, stderr)
85         if run_details.exit_code != -1 and not run_details.initial_results.keyboard_interrupted:
86             bot_printer = buildbot_results.BuildBotPrinter(stdout, options.debug_rwt_logging)
87             bot_printer.print_results(run_details)
88
89         return run_details.exit_code
90     # We still need to handle KeyboardInterrupt, at least for webkitpy unittest cases.
91     except KeyboardInterrupt:
92         return INTERRUPTED_EXIT_STATUS
93     except BaseException as e:
94         if isinstance(e, Exception):
95             print >> stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e))
96             traceback.print_exc(file=stderr)
97         return EXCEPTIONAL_EXIT_STATUS
98
99
100 def parse_args(args):
101     option_group_definitions = []
102
103     option_group_definitions.append(("Platform options", platform_options()))
104     option_group_definitions.append(("Configuration options", configuration_options()))
105     option_group_definitions.append(("Printing Options", printing.print_options()))
106
107     option_group_definitions.append(("EFL-specific Options", [
108         optparse.make_option("--webprocess-cmd-prefix", type="string",
109             default=False, help="Prefix used when spawning the Web process (Debug mode only)"),
110     ]))
111
112     option_group_definitions.append(("Feature Switches", [
113         optparse.make_option("--complex-text", action="store_true", default=False,
114             help="Use the complex text code path for all text (OS X and Windows only)"),
115         optparse.make_option("--accelerated-drawing", action="store_true", default=False,
116             help="Use accelerated drawing (OS X only)"),
117         optparse.make_option("--remote-layer-tree", action="store_true", default=False,
118             help="Use the remote layer tree drawing model (OS X WebKit2 only)"),
119     ]))
120
121     option_group_definitions.append(("WebKit Options", [
122         optparse.make_option("--gc-between-tests", action="store_true", default=False,
123             help="Force garbage collection between each test"),
124         optparse.make_option("-l", "--leaks", action="store_true", default=False,
125             help="Enable leaks checking (OS X and Gtk+ only)"),
126         optparse.make_option("-g", "--guard-malloc", action="store_true", default=False,
127             help="Enable Guard Malloc (OS X only)"),
128         optparse.make_option("--threaded", action="store_true", default=False,
129             help="Run a concurrent JavaScript thread with each test"),
130         optparse.make_option("--dump-render-tree", "-1", action="store_false", default=True, dest="webkit_test_runner",
131             help="Use DumpRenderTree rather than WebKitTestRunner."),
132         # FIXME: We should merge this w/ --build-directory and only have one flag.
133         optparse.make_option("--root", action="store",
134             help="Path to a directory containing the executables needed to run tests."),
135     ]))
136
137     option_group_definitions.append(("Results Options", [
138         optparse.make_option("-p", "--pixel", "--pixel-tests", action="store_true",
139             dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
140         optparse.make_option("--no-pixel", "--no-pixel-tests", action="store_false",
141             dest="pixel_tests", help="Disable pixel-to-pixel PNG comparisons"),
142         optparse.make_option("--no-sample-on-timeout", action="store_false", default=True,
143             dest="sample_on_timeout", help="Don't run sample on timeout (OS X only)"),
144         optparse.make_option("--no-ref-tests", action="store_true",
145             dest="no_ref_tests", help="Skip all ref tests"),
146         optparse.make_option("--tolerance",
147             help="Ignore image differences less than this percentage (some "
148                 "ports may ignore this option)", type="float"),
149         optparse.make_option("--results-directory", help="Location of test results"),
150         optparse.make_option("--build-directory",
151             help="Path to the directory under which build files are kept (should not include configuration)"),
152         optparse.make_option("--add-platform-exceptions", action="store_true", default=False,
153             help="Save generated results into the *most-specific-platform* directory rather than the *generic-platform* directory"),
154         optparse.make_option("--new-baseline", action="store_true",
155             default=False, help="Save generated results as new baselines "
156                  "into the *most-specific-platform* directory, overwriting whatever's "
157                  "already there. Equivalent to --reset-results --add-platform-exceptions"),
158         optparse.make_option("--reset-results", action="store_true",
159             default=False, help="Reset expectations to the "
160                  "generated results in their existing location."),
161         optparse.make_option("--no-new-test-results", action="store_false",
162             dest="new_test_results", default=True,
163             help="Don't create new baselines when no expected results exist"),
164         optparse.make_option("--treat-ref-tests-as-pixel-tests", action="store_true", default=False,
165             help="Run ref tests, but treat them as if they were traditional pixel tests"),
166
167         #FIXME: we should support a comma separated list with --pixel-test-directory as well.
168         optparse.make_option("--pixel-test-directory", action="append", default=[], dest="pixel_test_directories",
169             help="A directory where it is allowed to execute tests as pixel tests. "
170                  "Specify multiple times to add multiple directories. "
171                  "This option implies --pixel-tests. If specified, only those tests "
172                  "will be executed as pixel tests that are located in one of the "
173                  "directories enumerated with the option. Some ports may ignore this "
174                  "option while others can have a default value that can be overridden here."),
175
176         optparse.make_option("--skip-failing-tests", action="store_true",
177             default=False, help="Skip tests that are expected to fail. "
178                  "Note: When using this option, you might miss new crashes "
179                  "in these tests."),
180         optparse.make_option("--additional-drt-flag", action="append",
181             default=[], help="Additional command line flag to pass to DumpRenderTree "
182                  "Specify multiple times to add multiple flags."),
183         optparse.make_option("--driver-name", type="string",
184             help="Alternative DumpRenderTree binary to use"),
185         optparse.make_option("--additional-platform-directory", action="append",
186             default=[], help="Additional directory where to look for test "
187                  "baselines (will take precendence over platform baselines). "
188                  "Specify multiple times to add multiple search path entries."),
189         optparse.make_option("--additional-expectations", action="append", default=[],
190             help="Path to a test_expectations file that will override previous expectations. "
191                  "Specify multiple times for multiple sets of overrides."),
192         optparse.make_option("--compare-port", action="store", default=None,
193             help="Use the specified port's baselines first"),
194         optparse.make_option("--no-show-results", action="store_false",
195             default=True, dest="show_results",
196             help="Don't launch a browser with results after the tests "
197                  "are done"),
198         optparse.make_option("--full-results-html", action="store_true",
199             default=False,
200             help="Show all failures in results.html, rather than only regressions"),
201         optparse.make_option("--clobber-old-results", action="store_true",
202             default=False, help="Clobbers test results from previous runs."),
203         optparse.make_option("--http", action="store_true", dest="http",
204             default=True, help="Run HTTP and WebSocket tests (default)"),
205         optparse.make_option("--no-http", action="store_false", dest="http",
206             help="Don't run HTTP and WebSocket tests"),
207         optparse.make_option("--ignore-metrics", action="store_true", dest="ignore_metrics",
208             default=False, help="Ignore rendering metrics related information from test "
209             "output, only compare the structure of the rendertree."),
210         optparse.make_option("--nocheck-sys-deps", action="store_true",
211             default=False,
212             help="Don't check the system dependencies (themes)"),
213         optparse.make_option("--java", action="store_true",
214             default=False,
215             help="Build java support files"),
216         optparse.make_option("--layout-tests-directory", action="store", default=None,
217             help="Override the default layout test directory.", dest="layout_tests_dir")
218     ]))
219
220     option_group_definitions.append(("Testing Options", [
221         optparse.make_option("--build", dest="build",
222             action="store_true", default=True,
223             help="Check to ensure the DumpRenderTree build is up-to-date "
224                  "(default)."),
225         optparse.make_option("--no-build", dest="build",
226             action="store_false", help="Don't check to see if the "
227                                        "DumpRenderTree build is up-to-date."),
228         optparse.make_option("-n", "--dry-run", action="store_true",
229             default=False,
230             help="Do everything but actually run the tests or upload results."),
231         optparse.make_option("--wrapper",
232             help="wrapper command to insert before invocations of "
233                  "DumpRenderTree or WebKitTestRunner; option is split on whitespace before "
234                  "running. (Example: --wrapper='valgrind --smc-check=all')"),
235         optparse.make_option("-i", "--ignore-tests", action="append", default=[],
236             help="directories or test to ignore (may specify multiple times)"),
237         optparse.make_option("--test-list", action="append",
238             help="read list of tests to run from file", metavar="FILE"),
239         optparse.make_option("--skipped", action="store", default="default",
240             help=("control how tests marked SKIP are run. "
241                  "'default' == Skip tests unless explicitly listed on the command line, "
242                  "'ignore' == Run them anyway, "
243                  "'only' == only run the SKIP tests, "
244                  "'always' == always skip, even if listed on the command line.")),
245         optparse.make_option("--force", action="store_true", default=False,
246             help="Run all tests with PASS as expected result, even those marked SKIP in the test list (implies --skipped=ignore)"),
247         optparse.make_option("--time-out-ms",
248             help="Set the timeout for each test"),
249         optparse.make_option("--order", action="store", default="natural",
250             help=("determine the order in which the test cases will be run. "
251                   "'none' == use the order in which the tests were listed either in arguments or test list, "
252                   "'natural' == use the natural order (default), "
253                   "'random' == randomize the test order.")),
254         optparse.make_option("--run-chunk",
255             help=("Run a specified chunk (n:l), the nth of len l, "
256                  "of the layout tests")),
257         optparse.make_option("--run-part", help=("Run a specified part (n:m), "
258                   "the nth of m parts, of the layout tests")),
259         optparse.make_option("--batch-size",
260             help=("Run a the tests in batches (n), after every n tests, "
261                   "DumpRenderTree is relaunched."), type="int", default=None),
262         optparse.make_option("--run-singly", action="store_true",
263             default=False, help="run a separate DumpRenderTree for each test (implies --verbose)"),
264         optparse.make_option("--child-processes",
265             help="Number of DumpRenderTrees to run in parallel."),
266         # FIXME: Display default number of child processes that will run.
267         optparse.make_option("-f", "--fully-parallel", action="store_true",
268             help="run all tests in parallel"),
269         optparse.make_option("--exit-after-n-failures", type="int", default=None,
270             help="Exit after the first N failures instead of running all "
271             "tests"),
272         optparse.make_option("--exit-after-n-crashes-or-timeouts", type="int",
273             default=None, help="Exit after the first N crashes instead of "
274             "running all tests"),
275         optparse.make_option("--iterations", type="int", default=1, help="Number of times to run the set of tests (e.g. ABCABCABC)"),
276         optparse.make_option("--repeat-each", type="int", default=1, help="Number of times to run each test (e.g. AAABBBCCC)"),
277         optparse.make_option("--retry-failures", action="store_true",
278             default=True,
279             help="Re-try any tests that produce unexpected results (default)"),
280         optparse.make_option("--no-retry-failures", action="store_false",
281             dest="retry_failures",
282             help="Don't re-try any tests that produce unexpected results."),
283         optparse.make_option("--max-locked-shards", type="int", default=0,
284             help="Set the maximum number of locked shards"),
285         optparse.make_option("--additional-env-var", type="string", action="append", default=[],
286             help="Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)"),
287         optparse.make_option("--profile", action="store_true",
288             help="Output per-test profile information."),
289         optparse.make_option("--profiler", action="store",
290             help="Output per-test profile information, using the specified profiler."),
291         optparse.make_option("--no-timeout", action="store_true", default=False, help="Disable test timeouts"),
292         optparse.make_option("--wayland",  action="store_true", default=False,
293             help="Run the layout tests inside a (virtualized) weston compositor (GTK only)."),
294     ]))
295
296     option_group_definitions.append(("iOS Simulator Options", [
297         optparse.make_option('--runtime', help='iOS Simulator runtime identifier (default: latest runtime)'),
298         optparse.make_option('--device-type', help='iOS Simulator device type identifier (default: i386 -> iPhone 5, x86_64 -> iPhone 5s)'),
299         optparse.make_option('--dedicated-simulators', action="store_true", default=False,
300             help="If set, dedicated iOS simulators will always be created.  If not set, the script will attempt to use any currently running simulator."),
301     ]))
302
303     option_group_definitions.append(("Miscellaneous Options", [
304         optparse.make_option("--lint-test-files", action="store_true",
305         default=False, help=("Makes sure the test files parse for all "
306                             "configurations. Does not run any tests.")),
307         optparse.make_option("--print-expectations", action="store_true",
308         default=False, help=("Print the expected outcome for the given test, or all tests listed in TestExpectations. "
309                             "Does not run any tests.")),
310     ]))
311
312     option_group_definitions.append(("Web Platform Test Server Options", [
313         optparse.make_option("--wptserver-doc-root", type="string", help=("Set web platform server document root, relative to LayoutTests directory")),
314     ]))
315
316     # FIXME: Move these into json_results_generator.py
317     option_group_definitions.append(("Result JSON Options", [
318         optparse.make_option("--master-name", help="The name of the buildbot master."),
319         optparse.make_option("--builder-name", default="",
320             help=("The name of the builder shown on the waterfall running this script. e.g. Apple MountainLion Release WK2 (Tests).")),
321         optparse.make_option("--build-name", default="DUMMY_BUILD_NAME",
322             help=("The name of the builder used in its path, e.g. webkit-rel.")),
323         optparse.make_option("--build-slave", default="DUMMY_BUILD_SLAVE",
324             help=("The name of the buildslave used. e.g. apple-macpro-6.")),
325         optparse.make_option("--build-number", default="DUMMY_BUILD_NUMBER",
326             help=("The build number of the builder running this script.")),
327         optparse.make_option("--test-results-server", default="",
328             help=("If specified, upload results json files to this appengine server.")),
329         optparse.make_option("--results-server-host", default="",
330             help=("If specified, upload results JSON file to this results server.")),
331         optparse.make_option("--additional-repository-name",
332             help=("The name of an additional subversion or git checkout")),
333         optparse.make_option("--additional-repository-path",
334             help=("The path to an additional subversion or git checkout (requires --additional-repository-name)")),
335         optparse.make_option("--allowed-host", type="string", action="append", default=[],
336             help=("If specified, tests are allowed to make requests to the specified hostname."))
337     ]))
338
339     option_parser = optparse.OptionParser()
340
341     for group_name, group_options in option_group_definitions:
342         option_group = optparse.OptionGroup(option_parser, group_name)
343         option_group.add_options(group_options)
344         option_parser.add_option_group(option_group)
345
346     return option_parser.parse_args(args)
347
348
349 def _print_expectations(port, options, args, logging_stream):
350     logger = logging.getLogger()
351     logger.setLevel(logging.DEBUG if options.debug_rwt_logging else logging.INFO)
352     try:
353         printer = printing.Printer(port, options, logging_stream, logger=logger)
354
355         _set_up_derived_options(port, options)
356         manager = Manager(port, options, printer)
357
358         exit_code = manager.print_expectations(args)
359         _log.debug("Printing expectations completed, Exit status: %d", exit_code)
360         return exit_code
361     except Exception as error:
362         _log.error('Error printing expectations: {}'.format(error))
363     finally:
364         printer.cleanup()
365         return -1
366
367 def _set_up_derived_options(port, options):
368     """Sets the options values that depend on other options values."""
369     if not options.child_processes:
370         options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES",
371                                                  str(port.default_child_processes()))
372
373     if not options.configuration:
374         options.configuration = port.default_configuration()
375
376     if options.pixel_tests is None:
377         options.pixel_tests = port.default_pixel_tests()
378
379     if not options.time_out_ms:
380         options.time_out_ms = str(port.default_timeout_ms())
381
382     options.slow_time_out_ms = str(5 * int(options.time_out_ms))
383
384     if options.additional_platform_directory:
385         additional_platform_directories = []
386         for path in options.additional_platform_directory:
387             additional_platform_directories.append(port.host.filesystem.abspath(path))
388         options.additional_platform_directory = additional_platform_directories
389
390     if options.force:
391         if options.skipped not in ('ignore', 'default'):
392             _log.warning("--force overrides --skipped=%s" % (options.skipped))
393         options.skipped = 'ignore'
394
395     if not options.http and options.skipped in ('ignore', 'only'):
396         _log.warning("--force/--skipped=%s overrides --no-http." % (options.skipped))
397         options.http = True
398
399     if options.ignore_metrics and (options.new_baseline or options.reset_results):
400         _log.warning("--ignore-metrics has no effect with --new-baselines or with --reset-results")
401
402     if options.new_baseline:
403         options.reset_results = True
404         options.add_platform_exceptions = True
405
406     if options.pixel_test_directories:
407         options.pixel_tests = True
408         varified_dirs = set()
409         pixel_test_directories = options.pixel_test_directories
410         for directory in pixel_test_directories:
411             # FIXME: we should support specifying the directories all the ways we support it for additional
412             # arguments specifying which tests and directories to run. We should also move the logic for that
413             # to Port.
414             filesystem = port.host.filesystem
415             if not filesystem.isdir(filesystem.join(port.layout_tests_dir(), directory)):
416                 _log.warning("'%s' was passed to --pixel-test-directories, which doesn't seem to be a directory" % str(directory))
417             else:
418                 varified_dirs.add(directory)
419
420         options.pixel_test_directories = list(varified_dirs)
421
422     if options.run_singly:
423         options.verbose = True
424
425     # The GTK+ and EFL ports only support WebKit2 so they always use WKTR.
426     if options.platform == "gtk" or options.platform == "efl":
427         options.webkit_test_runner = True
428
429
430 def run(port, options, args, logging_stream):
431     logger = logging.getLogger()
432     logger.setLevel(logging.DEBUG if options.debug_rwt_logging else logging.INFO)
433
434     try:
435         printer = printing.Printer(port, options, logging_stream, logger=logger)
436
437         _set_up_derived_options(port, options)
438         manager = Manager(port, options, printer)
439         printer.print_config(port.results_directory())
440
441         run_details = manager.run(args)
442         _log.debug("Testing completed, Exit status: %d" % run_details.exit_code)
443         return run_details
444     finally:
445         printer.cleanup()
446
447 if __name__ == '__main__':
448     sys.exit(main(sys.argv[1:], sys.stdout, sys.stderr))