Chromium Code Reviews| Index: tools/run-tests.py |
| diff --git a/tools/run-tests.py b/tools/run-tests.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..52177a6031ec1df0f9aab9cded6194ad344eb0b1 |
| --- /dev/null |
| +++ b/tools/run-tests.py |
| @@ -0,0 +1,357 @@ |
| +#!/usr/bin/env python |
| +# |
| +# Copyright 2012 the V8 project authors. All rights reserved. |
| +# Redistribution and use in source and binary forms, with or without |
| +# modification, are permitted provided that the following conditions are |
| +# met: |
| +# |
| +# * Redistributions of source code must retain the above copyright |
| +# notice, this list of conditions and the following disclaimer. |
| +# * Redistributions in binary form must reproduce the above |
| +# copyright notice, this list of conditions and the following |
| +# disclaimer in the documentation and/or other materials provided |
| +# with the distribution. |
| +# * Neither the name of Google Inc. nor the names of its |
| +# contributors may be used to endorse or promote products derived |
| +# from this software without specific prior written permission. |
| +# |
| +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + |
| + |
| +import multiprocessing |
| +import optparse |
| +import os |
| +from os.path import join |
| +import subprocess |
| +import sys |
| +import time |
| + |
| +from testrunner.local import execution |
| +from testrunner.local import progress |
| +from testrunner.local import testsuite |
| +from testrunner.local import utils |
| +from testrunner.local import verbose |
| +from testrunner.network import network_execution |
| +from testrunner.objects import context |
| + |
| + |
| +ARCH_GUESS = utils.DefaultArch() |
| +DEFAULT_TESTS = ["mjsunit", "cctest", "message", "preparser"] |
| +TIMEOUT_DEFAULT = 60 |
| +TIMEOUT_SCALEFACTOR = {"debug" : 4, |
| + "release" : 1 } |
| + |
| +# Use this to run several variants of the tests. |
| +VARIANT_FLAGS = [[], |
| + ["--stress-opt", "--always-opt"], |
| + ["--nocrankshaft"]] |
| +MODE_FLAGS = { |
| + "debug" : ["--nobreak-on-abort", "--enable-slow-asserts", |
| + "--debug-code", "--verify-heap"], |
| + "release" : ["--nobreak-on-abort"]} |
| + |
| + |
| +def BuildOptions(): |
| + result = optparse.OptionParser() |
| + result.add_option("--arch", |
| + help=("The architecture to run tests for, " |
| + "'auto' or 'native' for auto-detect"), |
| + default="ia32,x64,arm") |
| + result.add_option("--arch-and-mode", |
| + help="Architecture and mode in the format 'arch.mode'", |
| + default=None) |
| + result.add_option("--buildbot", |
| + help="Adapt to path structure used on buildbots", |
| + default=False, action="store_true") |
| + result.add_option("--cat", help="Print the source of the tests", |
| + default=False, action="store_true") |
| + result.add_option("--command-prefix", |
| + help="Prepended to each shell command used to run a test", |
| + default="") |
| + result.add_option("--download-data", help="Download missing test suite data", |
| + default=False, action="store_true") |
| + result.add_option("--extra-flags", |
| + help="Additional flags to pass to each test command", |
| + default="") |
| + result.add_option("--isolates", help="Whether to test isolates", |
| + default=False, action="store_true") |
| + result.add_option("-j", help="The number of parallel tasks to run", |
| + default=0, type="int") |
| + result.add_option("-m", "--mode", |
| + help="The test modes in which to run (comma-separated)", |
| + default="release,debug") |
| + result.add_option("--no-network", "--nonetwork", |
| + help="Don't distribute tests on the network", |
| + default=(utils.GuessOS() != "linux"), |
| + dest="no_network", action="store_true") |
| + result.add_option("--no-presubmit", "--nopresubmit", |
| + help='Skip presubmit checks', |
| + default=False, dest="no_presubmit", action="store_true") |
| + result.add_option("--no-stress", "--nostress", |
| + help="Don't run crankshaft --always-opt --stress-op test", |
| + default=False, dest="no_stress", action="store_true") |
| + result.add_option("--outdir", help="Base directory with compile output", |
| + default="out") |
| + result.add_option("-p", "--progress", |
| + help=("The style of progress indicator" |
| + " (verbose, dots, color, mono)"), |
| + choices=progress.PROGRESS_INDICATORS.keys(), default="mono") |
| + result.add_option("--report", help="Print a summary of the tests to be run", |
| + default=False, action="store_true") |
| + result.add_option("--shard-count", |
| + help="Split testsuites into this number of shards", |
| + default=1, type="int") |
| + result.add_option("--shard-run", |
| + help="Run this shard from the split up tests.", |
| + default=1, type="int") |
| + result.add_option("--shell", help="DEPRECATED! use --shell-dir", default="") |
| + result.add_option("--shell-dir", help="Directory containing executables", |
| + default="") |
| + result.add_option("--stress-only", |
| + help="Only run tests with --always-opt --stress-opt", |
| + default=False, action="store_true") |
| + result.add_option("--time", help="Print timing information after running", |
| + default=False, action="store_true") |
| + result.add_option("-t", "--timeout", help="Timeout in seconds", |
| + default= -1, type="int") |
| + result.add_option("-v", "--verbose", help="Verbose output", |
| + default=False, action="store_true") |
| + result.add_option("--valgrind", help="Run tests through valgrind", |
| + default=False, action="store_true") |
| + result.add_option("--warn-unused", help="Report unused rules", |
| + default=False, action="store_true") |
| + return result |
| + |
| + |
| +def ProcessOptions(options): |
| + global VARIANT_FLAGS |
| + |
| + # Architecture and mode related stuff. |
| + if options.arch_and_mode: |
| + tokens = options.arch_and_mode.split(".") |
| + options.arch = tokens[0] |
| + options.mode = tokens[1] |
| + options.mode = options.mode.split(",") |
| + for mode in options.mode: |
| + if not mode in ["debug", "release"]: |
| + print "Unknown mode %s" % mode |
| + return False |
| + if options.arch in ["auto", "native"]: |
| + options.arch = ARCH_GUESS |
| + options.arch = options.arch.split(",") |
| + for arch in options.arch: |
| + if not arch in ['ia32', 'x64', 'arm', 'mips']: |
|
Michael Starzinger
2012/09/21 13:20:40
Shouldn't that be "mipsel"?
Jakob Kummerow
2012/09/21 17:02:24
Done.
|
| + print "Unknown architecture %s" % arch |
| + return False |
| + |
| + # Special processing of other options, sorted alphabetically. |
| + |
| + if options.buildbot: |
| + # Buildbots run presubmit tests as a separate step. |
| + options.no_presubmit = True |
| + options.no_network = True |
| + if options.command_prefix: |
| + print("Specifying --command-prefix disables network distribution, " |
| + "running tests locally.") |
| + options.no_network = True |
| + if options.j == 0: |
| + options.j = multiprocessing.cpu_count() |
| + if options.no_stress: |
| + VARIANT_FLAGS = [[], ["--nocrankshaft"]] |
| + if not options.shell_dir: |
| + if options.shell: |
| + print "Warning: --shell is deprecated, use --shell-dir instead." |
| + options.shell_dir = os.path.dirname(options.shell) |
| + if options.stress_only: |
| + VARIANT_FLAGS = [["--stress-opt", "--always-opt"]] |
| + # Simulators are slow, therefore allow a longer default timeout. |
| + if options.timeout == -1: |
| + if options.arch == "arm" or options.arch == "mips": |
|
Michael Starzinger
2012/09/21 13:20:40
Likewise.
Jakob Kummerow
2012/09/21 17:02:24
Done.
|
| + options.timeout = 2 * TIMEOUT_DEFAULT; |
| + else: |
| + options.timeout = TIMEOUT_DEFAULT; |
| + if options.valgrind: |
| + run_valgrind = os.path.join("tools", "run-valgrind.py") |
| + # This is OK for distributed running, so we don't need to set no_network. |
| + options.command_prefix = ("python -u " + run_valgrind + |
| + options.command_prefix) |
| + return True |
| + |
| + |
| +def ShardTests(tests, shard_count, shard_run): |
| + if shard_count < 2: |
| + return tests |
| + if shard_run < 1 or shard_run > shard_count: |
| + print "shard-run not a valid number, should be in [1:shard-count]" |
| + print "defaulting back to running all tests" |
| + return tests |
| + count = 0 |
| + shard = [] |
| + for test in tests: |
| + if count % shard_count == shard_run - 1: |
| + shard.append(test) |
| + count += 1 |
| + return shard |
| + |
| + |
| +def Main(): |
| + parser = BuildOptions() |
| + (options, args) = parser.parse_args() |
| + if not ProcessOptions(options): |
| + parser.print_help() |
| + return 1 |
| + |
| + exit_code = 0 |
| + workspace = os.path.abspath(join(os.path.dirname(sys.argv[0]), "..")) |
| + if not options.no_presubmit: |
| + print ">>> running presubmit tests" |
| + code = subprocess.call( |
| + [sys.executable, join(workspace, "tools", "presubmit.py")]) |
| + exit_code = code |
| + |
| + suite_paths = utils.GetSuitePaths(join(workspace, "test")) |
| + print "all suite_paths:", suite_paths |
| + |
| + if len(args) == 0: |
| + suite_paths = [ s for s in suite_paths if s in DEFAULT_TESTS ] |
| + else: |
| + args_suites = set() |
| + for arg in args: |
| + suite = arg.split(os.path.sep)[0] |
| + if not suite in args_suites: |
| + args_suites.add(suite) |
| + suite_paths = [ s for s in suite_paths if s in args_suites ] |
| + |
| + suites = [] |
| + for root in suite_paths: |
| + suite = testsuite.TestSuite.LoadTestSuite( |
| + os.path.join(workspace, "test", root)) |
| + if suite: |
| + suites.append(suite) |
| + |
| + if options.download_data: |
| + for s in suites: |
| + s.DownloadData() |
| + |
| + for mode in options.mode: |
| + for arch in options.arch: |
| + code = Execute(arch, mode, args, options, suites, workspace) |
| + exit_code = exit_code or code |
| + return exit_code |
| + |
| + |
| +def Execute(arch, mode, args, options, suites, workspace): |
| + print(">>> Running tests for %s.%s" % (arch, mode)) |
| + |
| + if not options.shell_dir: |
| + if options.buildbot: |
| + default_dir = os.path.join(workspace, options.outdir, mode) |
| + mode = mode.lower() |
| + else: |
| + default_dir = os.path.join(workspace, |
| + "%s" % options.outdir, |
| + "%s.%s" % (arch, mode)) |
| + options.shell_dir = os.path.relpath(default_dir) |
| + |
| + # Populate context object. |
| + mode_flags = MODE_FLAGS[mode] |
| + options.timeout *= TIMEOUT_SCALEFACTOR[mode] |
| + ctx = context.Context(arch, mode, |
| + os.path.relpath(options.shell_dir), |
| + mode_flags, options.verbose, |
| + options.timeout, options.isolates, |
| + options.command_prefix, |
| + options.extra_flags) |
| + |
| + # Find available test suites and read test cases from them. |
| + variables = { |
| + "mode": mode, |
| + "arch": arch, |
| + "system": utils.GuessOS(), |
| + "isolates": options.isolates |
| + } |
| + all_tests = [] |
| + num_tests = 0 |
| + test_id = 0 |
| + for s in suites: |
| + s.ReadStatusFile(variables) |
| + s.ReadTestCases(ctx) |
| + all_tests += s.tests |
| + if len(args) > 0: |
| + s.FilterTestCasesByArgs(args) |
| + s.FilterTestCasesByStatus(options.warn_unused) |
| + if options.cat: |
| + verbose.PrintTestSource(s.tests) |
| + continue |
| + variant_flags = s.VariantFlags() or VARIANT_FLAGS |
| + s.tests = [ t.CopyAddingFlags(v) for t in s.tests for v in variant_flags ] |
| + s.tests = ShardTests(s.tests, options.shard_count, options.shard_run) |
| + num_tests += len(s.tests) |
| + for t in s.tests: |
| + t.id = test_id |
| + test_id += 1 |
| + |
| + if options.cat: |
| + return 0 # We're done here. |
| + |
| + if options.report: |
| + verbose.PrintReport(all_tests) |
| + |
| + if num_tests == 0: |
| + print "No tests to run." |
| + return 0 |
| + |
| + # Run the tests, either locally or distributed on the network. |
| + try: |
| + start_time = time.time() |
| + progress_indicator = progress.PROGRESS_INDICATORS[options.progress]() |
| + |
| + run_networked = not options.no_network |
| + if not run_networked: |
| + print("Network distribution disabled, running tests locally.") |
| + elif utils.GuessOS() != "linux": |
| + print("Network distribution is only supported on Linux, sorry!") |
| + run_networked = False |
| + peers = [] |
| + if run_networked: |
| + peers = network_execution.GetPeers() |
| + if not peers: |
| + print("No connection to distribution server; running tests locally.") |
| + run_networked = False |
| + elif len(peers) == 1: |
| + print("No other peers on the network; running tests locally.") |
| + run_networked = False |
| + elif num_tests <= 100: |
| + print("Less than 100 tests, running them locally.") |
| + run_networked = False |
| + |
| + if run_networked: |
| + runner = network_execution.NetworkedRunner(suites, progress_indicator, |
| + ctx, peers, workspace) |
| + else: |
| + runner = execution.Runner(suites, progress_indicator, ctx) |
| + |
| + exit_code = runner.Run(options.j) |
| + if runner.terminate: |
| + return exit_code |
| + overall_duration = time.time() - start_time |
| + except KeyboardInterrupt: |
| + return 1 |
| + |
| + if options.time: |
| + verbose.PrintTestDurations(suites, overall_duration) |
| + return exit_code |
| + |
| + |
| +if __name__ == "__main__": |
| + sys.exit(Main()) |