2 # Copyright (C) 2010 Google Inc. All rights reserved.
3 # Copyright (C) 2010 Gabor Rapcsanyi (rgabor@inf.u-szeged.hu), University of Szeged
4 # Copyright (C) 2011 Apple Inc. All rights reserved.
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following disclaimer
14 # in the documentation and/or other materials provided with the
16 # * Neither the name of Google Inc. nor the names of its
17 # contributors may be used to endorse or promote products derived from
18 # this software without specific prior written permission.
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 from webkitpy.common.host import Host
41 from webkitpy.common.system import stack_utils
42 from webkitpy.layout_tests.controllers.manager import Manager, WorkerException, TestRunInterruptedException
43 from webkitpy.layout_tests.models import test_expectations
44 from webkitpy.layout_tests.port import port_options
45 from webkitpy.layout_tests.views import printing
48 _log = logging.getLogger(__name__)
51 # This mirrors what the shell normally does.
52 INTERRUPTED_EXIT_STATUS = signal.SIGINT + 128
54 # This is a randomly chosen exit code that can be tested against to
55 # indicate that an unexpected exception occurred.
56 EXCEPTIONAL_EXIT_STATUS = 254
59 def lint(port, options):
62 ports_to_lint = [port]
64 ports_to_lint = [host.port_factory.get(name) for name in host.port_factory.all_port_names()]
69 for port_to_lint in ports_to_lint:
70 expectations_file = port_to_lint.path_to_test_expectations_file()
71 if expectations_file in files_linted:
75 test_expectations.TestExpectations(port_to_lint, is_lint_mode=True)
76 except test_expectations.ParseError, e:
79 for warning in e.warnings:
82 files_linted.add(expectations_file)
85 _log.error('Lint failed.')
87 _log.info('Lint succeeded.')
91 def run(port, options, args, regular_output=sys.stderr, buildbot_output=sys.stdout):
93 warnings = _set_up_derived_options(port, options)
95 printer = printing.Printer(port, options, regular_output, buildbot_output, logger=logging.getLogger())
97 for warning in warnings:
100 if options.lint_test_files:
101 return lint(port, options)
103 # We wrap any parts of the run that are slow or likely to raise exceptions
104 # in a try/finally to ensure that we clean up the logging configuration.
105 unexpected_result_count = -1
107 manager = Manager(port, options, printer)
108 printer.print_config()
110 unexpected_result_count = manager.run(args)
111 _log.debug("Testing completed, Exit status: %d" % unexpected_result_count)
113 exception_type, exception_value, exception_traceback = sys.exc_info()
114 if exception_type not in (KeyboardInterrupt, TestRunInterruptedException, WorkerException):
115 print >> sys.stderr, '\n%s raised: %s' % (exception_type.__name__, exception_value)
116 stack_utils.log_traceback(_log.error, exception_traceback)
121 return unexpected_result_count
124 def _set_up_derived_options(port, options):
125 """Sets the options values that depend on other options values."""
126 # We return a list of warnings to print after the printer is initialized.
129 if not options.child_processes:
130 options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES",
131 str(port.default_child_processes()))
133 if not options.configuration:
134 options.configuration = port.default_configuration()
136 if options.pixel_tests is None:
137 options.pixel_tests = port.default_pixel_tests()
139 if not options.time_out_ms:
140 options.time_out_ms = str(port.default_timeout_ms())
142 options.slow_time_out_ms = str(5 * int(options.time_out_ms))
144 if options.additional_platform_directory:
145 normalized_platform_directories = []
146 for path in options.additional_platform_directory:
147 if not port.host.filesystem.isabs(path):
148 warnings.append("--additional-platform-directory=%s is ignored since it is not absolute" % path)
150 normalized_platform_directories.append(port.host.filesystem.normpath(path))
151 options.additional_platform_directory = normalized_platform_directories
153 if not options.http and options.skipped in ('ignore', 'only'):
154 warnings.append("--force/--skipped=%s overrides --no-http." % (options.skipped))
157 if options.ignore_metrics and (options.new_baseline or options.reset_results):
158 warnings.append("--ignore-metrics has no effect with --new-baselines or with --reset-results")
160 if options.new_baseline:
161 options.reset_results = True
162 options.add_platform_exceptions = True
164 if options.pixel_test_directories:
165 options.pixel_tests = True
166 varified_dirs = set()
167 pixel_test_directories = options.pixel_test_directories
168 for directory in pixel_test_directories:
169 # FIXME: we should support specifying the directories all the ways we support it for additional
170 # arguments specifying which tests and directories to run. We should also move the logic for that
172 filesystem = port.host.filesystem
173 if not filesystem.isdir(filesystem.join(port.layout_tests_dir(), directory)):
174 warnings.append("'%s' was passed to --pixel-test-directories, which doesn't seem to be a directory" % str(directory))
176 varified_dirs.add(directory)
178 options.pixel_test_directories = list(varified_dirs)
180 if options.run_singly:
181 options.verbose = True
186 def _compat_shim_callback(option, opt_str, value, parser):
187 print "Ignoring unsupported option: %s" % opt_str
190 def _compat_shim_option(option_name, **kwargs):
191 return optparse.make_option(option_name, action="callback",
192 callback=_compat_shim_callback,
193 help="Ignored, for old-run-webkit-tests compat only.", **kwargs)
196 def parse_args(args=None):
197 """Provides a default set of command line args.
199 Returns a tuple of options, args from optparse"""
201 option_group_definitions = []
203 option_group_definitions.append(("Configuration options", port_options()))
204 option_group_definitions.append(("Printing Options", printing.print_options()))
206 # FIXME: These options should move onto the ChromiumPort.
207 option_group_definitions.append(("Chromium-specific Options", [
208 optparse.make_option("--startup-dialog", action="store_true",
209 default=False, help="create a dialog on DumpRenderTree startup"),
210 optparse.make_option("--gp-fault-error-box", action="store_true",
211 default=False, help="enable Windows GP fault error box"),
212 optparse.make_option("--js-flags",
213 type="string", help="JavaScript flags to pass to tests"),
214 optparse.make_option("--stress-opt", action="store_true",
216 help="Enable additional stress test to JavaScript optimization"),
217 optparse.make_option("--stress-deopt", action="store_true",
219 help="Enable additional stress test to JavaScript optimization"),
220 optparse.make_option("--nocheck-sys-deps", action="store_true",
222 help="Don't check the system dependencies (themes)"),
223 optparse.make_option("--accelerated-video",
225 help="Use hardware-accelerated compositing for video"),
226 optparse.make_option("--no-accelerated-video",
227 action="store_false",
228 dest="accelerated_video",
229 help="Don't use hardware-accelerated compositing for video"),
230 optparse.make_option("--threaded-compositing",
232 help="Use threaded compositing for rendering"),
233 optparse.make_option("--accelerated-2d-canvas",
235 help="Use hardware-accelerated 2D Canvas calls"),
236 optparse.make_option("--no-accelerated-2d-canvas",
237 action="store_false",
238 dest="accelerated_2d_canvas",
239 help="Don't use hardware-accelerated 2D Canvas calls"),
240 optparse.make_option("--accelerated-painting",
243 help="Use hardware accelerated painting of composited pages"),
244 optparse.make_option("--per-tile-painting",
246 help="Use per-tile painting of composited pages"),
247 optparse.make_option("--adb-device",
248 action="append", default=[],
249 help="Run Android layout tests on these devices."),
252 option_group_definitions.append(("EFL-specific Options", [
253 optparse.make_option("--webprocess-cmd-prefix", type="string",
254 default=False, help="Prefix used when spawning the Web process (Debug mode only)"),
257 option_group_definitions.append(("WebKit Options", [
258 optparse.make_option("--gc-between-tests", action="store_true", default=False,
259 help="Force garbage collection between each test"),
260 optparse.make_option("--complex-text", action="store_true", default=False,
261 help="Use the complex text code path for all text (Mac OS X and Windows only)"),
262 optparse.make_option("-l", "--leaks", action="store_true", default=False,
263 help="Enable leaks checking (Mac OS X only)"),
264 optparse.make_option("-g", "--guard-malloc", action="store_true", default=False,
265 help="Enable Guard Malloc (Mac OS X only)"),
266 optparse.make_option("--threaded", action="store_true", default=False,
267 help="Run a concurrent JavaScript thread with each test"),
268 optparse.make_option("--webkit-test-runner", "-2", action="store_true",
269 help="Use WebKitTestRunner rather than DumpRenderTree."),
270 # FIXME: We should merge this w/ --build-directory and only have one flag.
271 optparse.make_option("--root", action="store",
272 help="Path to a directory containing the executables needed to run tests."),
275 option_group_definitions.append(("ORWT Compatibility Options", [
276 # FIXME: Remove this option once the bots don't refer to it.
277 # results.html is smart enough to figure this out itself.
278 _compat_shim_option("--use-remote-links-to-tests"),
281 option_group_definitions.append(("Results Options", [
282 optparse.make_option("-p", "--pixel-tests", action="store_true",
283 dest="pixel_tests", help="Enable pixel-to-pixel PNG comparisons"),
284 optparse.make_option("--no-pixel-tests", action="store_false",
285 dest="pixel_tests", help="Disable pixel-to-pixel PNG comparisons"),
286 optparse.make_option("--no-sample-on-timeout", action="store_false",
287 dest="sample_on_timeout", help="Don't run sample on timeout (Mac OS X only)"),
288 optparse.make_option("--no-ref-tests", action="store_true",
289 dest="no_ref_tests", help="Skip all ref tests"),
290 optparse.make_option("--tolerance",
291 help="Ignore image differences less than this percentage (some "
292 "ports may ignore this option)", type="float"),
293 optparse.make_option("--results-directory", help="Location of test results"),
294 optparse.make_option("--build-directory",
295 help="Path to the directory under which build files are kept (should not include configuration)"),
296 optparse.make_option("--add-platform-exceptions", action="store_true", default=False,
297 help="Save generated results into the *most-specific-platform* directory rather than the *generic-platform* directory"),
298 optparse.make_option("--new-baseline", action="store_true",
299 default=False, help="Save generated results as new baselines "
300 "into the *most-specific-platform* directory, overwriting whatever's "
301 "already there. Equivalent to --reset-results --add-platform-exceptions"),
302 optparse.make_option("--reset-results", action="store_true",
303 default=False, help="Reset expectations to the "
304 "generated results in their existing location."),
305 optparse.make_option("--no-new-test-results", action="store_false",
306 dest="new_test_results", default=True,
307 help="Don't create new baselines when no expected results exist"),
309 #FIXME: we should support a comma separated list with --pixel-test-directory as well.
310 optparse.make_option("--pixel-test-directory", action="append", default=[], dest="pixel_test_directories",
311 help="A directory where it is allowed to execute tests as pixel tests. "
312 "Specify multiple times to add multiple directories. "
313 "This option implies --pixel-tests. If specified, only those tests "
314 "will be executed as pixel tests that are located in one of the "
315 "directories enumerated with the option. Some ports may ignore this "
316 "option while others can have a default value that can be overridden here."),
318 optparse.make_option("--skip-failing-tests", action="store_true",
319 default=False, help="Skip tests that are expected to fail. "
320 "Note: When using this option, you might miss new crashes "
322 optparse.make_option("--additional-drt-flag", action="append",
323 default=[], help="Additional command line flag to pass to DumpRenderTree "
324 "Specify multiple times to add multiple flags."),
325 optparse.make_option("--driver-name", type="string",
326 help="Alternative DumpRenderTree binary to use"),
327 optparse.make_option("--additional-platform-directory", action="append",
328 default=[], help="Additional directory where to look for test "
329 "baselines (will take precendence over platform baselines). "
330 "Specify multiple times to add multiple search path entries."),
331 optparse.make_option("--additional-expectations", action="append", default=[],
332 help="Path to a test_expectations file that will override previous expectations. "
333 "Specify multiple times for multiple sets of overrides."),
334 optparse.make_option("--compare-port", action="store", default=None,
335 help="Use the specified port's baselines first"),
336 optparse.make_option("--no-show-results", action="store_false",
337 default=True, dest="show_results",
338 help="Don't launch a browser with results after the tests "
340 # FIXME: We should have a helper function to do this sort of
341 # deprectated mapping and automatically log, etc.
342 optparse.make_option("--noshow-results", action="store_false", dest="show_results", help="Deprecated, same as --no-show-results."),
343 optparse.make_option("--no-launch-safari", action="store_false", dest="show_results", help="Deprecated, same as --no-show-results."),
344 optparse.make_option("--full-results-html", action="store_true",
346 help="Show all failures in results.html, rather than only regressions"),
347 optparse.make_option("--clobber-old-results", action="store_true",
348 default=False, help="Clobbers test results from previous runs."),
349 optparse.make_option("--no-record-results", action="store_false",
350 default=True, dest="record_results",
351 help="Don't record the results."),
352 optparse.make_option("--http", action="store_true", dest="http",
353 default=True, help="Run HTTP and WebSocket tests (default)"),
354 optparse.make_option("--no-http", action="store_false", dest="http",
355 help="Don't run HTTP and WebSocket tests"),
356 optparse.make_option("--ignore-metrics", action="store_true", dest="ignore_metrics",
357 default=False, help="Ignore rendering metrics related information from test "
358 "output, only compare the structure of the rendertree."),
361 option_group_definitions.append(("Testing Options", [
362 optparse.make_option("--build", dest="build",
363 action="store_true", default=True,
364 help="Check to ensure the DumpRenderTree build is up-to-date "
366 optparse.make_option("--no-build", dest="build",
367 action="store_false", help="Don't check to see if the "
368 "DumpRenderTree build is up-to-date."),
369 optparse.make_option("-n", "--dry-run", action="store_true",
371 help="Do everything but actually run the tests or upload results."),
372 optparse.make_option("--wrapper",
373 help="wrapper command to insert before invocations of "
374 "DumpRenderTree; option is split on whitespace before "
375 "running. (Example: --wrapper='valgrind --smc-check=all')"),
376 optparse.make_option("-i", "--ignore-tests", action="append", default=[],
377 help="directories or test to ignore (may specify multiple times)"),
378 optparse.make_option("--test-list", action="append",
379 help="read list of tests to run from file", metavar="FILE"),
380 optparse.make_option("--skipped", action="store", default="default",
381 help="control how tests marked SKIP are run. 'default' == Skip, 'ignore' == Run them anyway, 'only' == only run the SKIP tests."),
382 optparse.make_option("--force", dest="skipped", action="store_const", const='ignore',
383 help="Run all tests, even those marked SKIP in the test list (same as --skipped=ignore)"),
384 optparse.make_option("--time-out-ms",
385 help="Set the timeout for each test"),
386 optparse.make_option("--randomize-order", action="store_true",
387 default=False, help=("Run tests in random order (useful "
388 "for tracking down corruption)")),
389 optparse.make_option("--run-chunk",
390 help=("Run a specified chunk (n:l), the nth of len l, "
391 "of the layout tests")),
392 optparse.make_option("--run-part", help=("Run a specified part (n:m), "
393 "the nth of m parts, of the layout tests")),
394 optparse.make_option("--batch-size",
395 help=("Run a the tests in batches (n), after every n tests, "
396 "DumpRenderTree is relaunched."), type="int", default=None),
397 optparse.make_option("--run-singly", action="store_true",
398 default=False, help="run a separate DumpRenderTree for each test (implies --verbose)"),
399 optparse.make_option("--child-processes",
400 help="Number of DumpRenderTrees to run in parallel."),
401 # FIXME: Display default number of child processes that will run.
402 optparse.make_option("-f", "--fully-parallel", action="store_true",
403 help="run all tests in parallel"),
404 optparse.make_option("--exit-after-n-failures", type="int", default=None,
405 help="Exit after the first N failures instead of running all "
407 optparse.make_option("--exit-after-n-crashes-or-timeouts", type="int",
408 default=None, help="Exit after the first N crashes instead of "
409 "running all tests"),
410 optparse.make_option("--iterations", type="int", default=1, help="Number of times to run the set of tests (e.g. ABCABCABC)"),
411 optparse.make_option("--repeat-each", type="int", default=1, help="Number of times to run each test (e.g. AAABBBCCC)"),
412 optparse.make_option("--retry-failures", action="store_true",
414 help="Re-try any tests that produce unexpected results (default)"),
415 optparse.make_option("--no-retry-failures", action="store_false",
416 dest="retry_failures",
417 help="Don't re-try any tests that produce unexpected results."),
418 optparse.make_option("--max-locked-shards", type="int", default=1,
419 help="Set the maximum number of locked shards"),
420 optparse.make_option("--additional-env-var", type="string", action="append", default=[],
421 help="Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)"),
424 option_group_definitions.append(("Miscellaneous Options", [
425 optparse.make_option("--lint-test-files", action="store_true",
426 default=False, help=("Makes sure the test files parse for all "
427 "configurations. Does not run any tests.")),
430 # FIXME: Move these into json_results_generator.py
431 option_group_definitions.append(("Result JSON Options", [
432 optparse.make_option("--master-name", help="The name of the buildbot master."),
433 optparse.make_option("--builder-name", default="",
434 help=("The name of the builder shown on the waterfall running "
435 "this script e.g. WebKit.")),
436 optparse.make_option("--build-name", default="DUMMY_BUILD_NAME",
437 help=("The name of the builder used in its path, e.g. "
439 optparse.make_option("--build-number", default="DUMMY_BUILD_NUMBER",
440 help=("The build number of the builder running this script.")),
441 optparse.make_option("--test-results-server", default="",
442 help=("If specified, upload results json files to this appengine "
446 option_parser = optparse.OptionParser()
448 for group_name, group_options in option_group_definitions:
449 option_group = optparse.OptionGroup(option_parser, group_name)
450 option_group.add_options(group_options)
451 option_parser.add_option_group(option_group)
453 return option_parser.parse_args(args)
458 options, args = parse_args(argv)
459 if options.platform and 'test' in options.platform:
460 # It's a bit lame to import mocks into real code, but this allows the user
461 # to run tests against the test platform interactively, which is useful for
462 # debugging test failures.
463 from webkitpy.common.host_mock import MockHost
467 port = host.port_factory.get(options.platform, options)
468 except NotImplementedError, e:
469 # FIXME: is this the best way to handle unsupported port names?
470 print >> sys.stderr, str(e)
471 return EXCEPTIONAL_EXIT_STATUS
473 print >> sys.stderr, '\n%s raised: %s' % (e.__class__.__name__, str(e))
474 traceback.print_exc(file=sys.stderr)
477 logging.getLogger().setLevel(logging.DEBUG if options.debug_rwt_logging else logging.INFO)
478 return run(port, options, args)
481 if '__main__' == __name__:
484 except BaseException, e:
485 if e.__class__ in (KeyboardInterrupt, TestRunInterruptedException):
486 sys.exit(INTERRUPTED_EXIT_STATUS)
487 sys.exit(EXCEPTIONAL_EXIT_STATUS)
489 sys.exit(return_code)