Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 #
      3 # Copyright 2012 the V8 project authors. All rights reserved.
      4 # Redistribution and use in source and binary forms, with or without
      5 # modification, are permitted provided that the following conditions are
      6 # met:
      7 #
      8 #     * Redistributions of source code must retain the above copyright
      9 #       notice, this list of conditions and the following disclaimer.
     10 #     * Redistributions in binary form must reproduce the above
     11 #       copyright notice, this list of conditions and the following
     12 #       disclaimer in the documentation and/or other materials provided
     13 #       with the distribution.
     14 #     * Neither the name of Google Inc. nor the names of its
     15 #       contributors may be used to endorse or promote products derived
     16 #       from this software without specific prior written permission.
     17 #
     18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29 
     30 
     31 import itertools
     32 import multiprocessing
     33 import optparse
     34 import os
     35 from os.path import join
     36 import platform
     37 import random
     38 import shlex
     39 import subprocess
     40 import sys
     41 import time
     42 
     43 from testrunner.local import execution
     44 from testrunner.local import progress
     45 from testrunner.local import testsuite
     46 from testrunner.local import utils
     47 from testrunner.local import verbose
     48 from testrunner.network import network_execution
     49 from testrunner.objects import context
     50 
     51 
     52 ARCH_GUESS = utils.DefaultArch()
     53 DEFAULT_TESTS = ["mjsunit", "fuzz-natives", "cctest", "message", "preparser"]
     54 TIMEOUT_DEFAULT = 60
     55 TIMEOUT_SCALEFACTOR = {"debug"   : 4,
     56                        "release" : 1 }
     57 
     58 # Use this to run several variants of the tests.
     59 VARIANT_FLAGS = {
     60     "default": [],
     61     "stress": ["--stress-opt", "--always-opt"],
     62     "nocrankshaft": ["--nocrankshaft"]}
     63 
     64 VARIANTS = ["default", "stress", "nocrankshaft"]
     65 
     66 MODE_FLAGS = {
     67     "debug"   : ["--nohard-abort", "--nodead-code-elimination",
     68                  "--nofold-constants", "--enable-slow-asserts",
     69                  "--debug-code", "--verify-heap"],
     70     "release" : ["--nohard-abort", "--nodead-code-elimination",
     71                  "--nofold-constants"]}
     72 
     73 GC_STRESS_FLAGS = ["--gc-interval=500", "--stress-compaction",
     74                    "--concurrent-recompilation-queue-length=64",
     75                    "--concurrent-recompilation-delay=500",
     76                    "--concurrent-recompilation"]
     77 
     78 SUPPORTED_ARCHS = ["android_arm",
     79                    "android_arm64",
     80                    "android_ia32",
     81                    "arm",
     82                    "ia32",
     83                    "x87",
     84                    "mips",
     85                    "mipsel",
     86                    "nacl_ia32",
     87                    "nacl_x64",
     88                    "x64",
     89                    "arm64"]
     90 # Double the timeout for these:
     91 SLOW_ARCHS = ["android_arm",
     92               "android_arm64",
     93               "android_ia32",
     94               "arm",
     95               "mips",
     96               "mipsel",
     97               "nacl_ia32",
     98               "nacl_x64",
     99               "x87",
    100               "arm64"]
    101 
    102 
    103 def BuildOptions():
    104   result = optparse.OptionParser()
    105   result.add_option("--arch",
    106                     help=("The architecture to run tests for, "
    107                           "'auto' or 'native' for auto-detect"),
    108                     default="ia32,x64,arm")
    109   result.add_option("--arch-and-mode",
    110                     help="Architecture and mode in the format 'arch.mode'",
    111                     default=None)
    112   result.add_option("--asan",
    113                     help="Regard test expectations for ASAN",
    114                     default=False, action="store_true")
    115   result.add_option("--buildbot",
    116                     help="Adapt to path structure used on buildbots",
    117                     default=False, action="store_true")
    118   result.add_option("--cat", help="Print the source of the tests",
    119                     default=False, action="store_true")
    120   result.add_option("--flaky-tests",
    121                     help="Regard tests marked as flaky (run|skip|dontcare)",
    122                     default="dontcare")
    123   result.add_option("--slow-tests",
    124                     help="Regard slow tests (run|skip|dontcare)",
    125                     default="dontcare")
    126   result.add_option("--pass-fail-tests",
    127                     help="Regard pass|fail tests (run|skip|dontcare)",
    128                     default="dontcare")
    129   result.add_option("--gc-stress",
    130                     help="Switch on GC stress mode",
    131                     default=False, action="store_true")
    132   result.add_option("--command-prefix",
    133                     help="Prepended to each shell command used to run a test",
    134                     default="")
    135   result.add_option("--download-data", help="Download missing test suite data",
    136                     default=False, action="store_true")
    137   result.add_option("--extra-flags",
    138                     help="Additional flags to pass to each test command",
    139                     default="")
    140   result.add_option("--isolates", help="Whether to test isolates",
    141                     default=False, action="store_true")
    142   result.add_option("-j", help="The number of parallel tasks to run",
    143                     default=0, type="int")
    144   result.add_option("-m", "--mode",
    145                     help="The test modes in which to run (comma-separated)",
    146                     default="release,debug")
    147   result.add_option("--no-i18n", "--noi18n",
    148                     help="Skip internationalization tests",
    149                     default=False, action="store_true")
    150   result.add_option("--no-network", "--nonetwork",
    151                     help="Don't distribute tests on the network",
    152                     default=(utils.GuessOS() != "linux"),
    153                     dest="no_network", action="store_true")
    154   result.add_option("--no-presubmit", "--nopresubmit",
    155                     help='Skip presubmit checks',
    156                     default=False, dest="no_presubmit", action="store_true")
    157   result.add_option("--no-snap", "--nosnap",
    158                     help='Test a build compiled without snapshot.',
    159                     default=False, dest="no_snap", action="store_true")
    160   result.add_option("--no-sorting", "--nosorting",
    161                     help="Don't sort tests according to duration of last run.",
    162                     default=False, dest="no_sorting", action="store_true")
    163   result.add_option("--no-stress", "--nostress",
    164                     help="Don't run crankshaft --always-opt --stress-op test",
    165                     default=False, dest="no_stress", action="store_true")
    166   result.add_option("--no-variants", "--novariants",
    167                     help="Don't run any testing variants",
    168                     default=False, dest="no_variants", action="store_true")
    169   result.add_option("--variants",
    170                     help="Comma-separated list of testing variants")
    171   result.add_option("--outdir", help="Base directory with compile output",
    172                     default="out")
    173   result.add_option("-p", "--progress",
    174                     help=("The style of progress indicator"
    175                           " (verbose, dots, color, mono)"),
    176                     choices=progress.PROGRESS_INDICATORS.keys(), default="mono")
    177   result.add_option("--quickcheck", default=False, action="store_true",
    178                     help=("Quick check mode (skip slow/flaky tests)"))
    179   result.add_option("--report", help="Print a summary of the tests to be run",
    180                     default=False, action="store_true")
    181   result.add_option("--json-test-results",
    182                     help="Path to a file for storing json results.")
    183   result.add_option("--shard-count",
    184                     help="Split testsuites into this number of shards",
    185                     default=1, type="int")
    186   result.add_option("--shard-run",
    187                     help="Run this shard from the split up tests.",
    188                     default=1, type="int")
    189   result.add_option("--shell", help="DEPRECATED! use --shell-dir", default="")
    190   result.add_option("--shell-dir", help="Directory containing executables",
    191                     default="")
    192   result.add_option("--dont-skip-slow-simulator-tests",
    193                     help="Don't skip more slow tests when using a simulator.",
    194                     default=False, action="store_true",
    195                     dest="dont_skip_simulator_slow_tests")
    196   result.add_option("--stress-only",
    197                     help="Only run tests with --always-opt --stress-opt",
    198                     default=False, action="store_true")
    199   result.add_option("--time", help="Print timing information after running",
    200                     default=False, action="store_true")
    201   result.add_option("-t", "--timeout", help="Timeout in seconds",
    202                     default= -1, type="int")
    203   result.add_option("-v", "--verbose", help="Verbose output",
    204                     default=False, action="store_true")
    205   result.add_option("--valgrind", help="Run tests through valgrind",
    206                     default=False, action="store_true")
    207   result.add_option("--warn-unused", help="Report unused rules",
    208                     default=False, action="store_true")
    209   result.add_option("--junitout", help="File name of the JUnit output")
    210   result.add_option("--junittestsuite",
    211                     help="The testsuite name in the JUnit output file",
    212                     default="v8tests")
    213   result.add_option("--random-seed", default=0, dest="random_seed",
    214                     help="Default seed for initializing random generator")
    215   return result
    216 
    217 
    218 def ProcessOptions(options):
    219   global VARIANT_FLAGS
    220   global VARIANTS
    221 
    222   # Architecture and mode related stuff.
    223   if options.arch_and_mode:
    224     options.arch_and_mode = [arch_and_mode.split(".")
    225         for arch_and_mode in options.arch_and_mode.split(",")]
    226     options.arch = ",".join([tokens[0] for tokens in options.arch_and_mode])
    227     options.mode = ",".join([tokens[1] for tokens in options.arch_and_mode])
    228   options.mode = options.mode.split(",")
    229   for mode in options.mode:
    230     if not mode.lower() in ["debug", "release", "optdebug"]:
    231       print "Unknown mode %s" % mode
    232       return False
    233   if options.arch in ["auto", "native"]:
    234     options.arch = ARCH_GUESS
    235   options.arch = options.arch.split(",")
    236   for arch in options.arch:
    237     if not arch in SUPPORTED_ARCHS:
    238       print "Unknown architecture %s" % arch
    239       return False
    240 
    241   # Store the final configuration in arch_and_mode list. Don't overwrite
    242   # predefined arch_and_mode since it is more expressive than arch and mode.
    243   if not options.arch_and_mode:
    244     options.arch_and_mode = itertools.product(options.arch, options.mode)
    245 
    246   # Special processing of other options, sorted alphabetically.
    247 
    248   if options.buildbot:
    249     # Buildbots run presubmit tests as a separate step.
    250     options.no_presubmit = True
    251     options.no_network = True
    252   if options.command_prefix:
    253     print("Specifying --command-prefix disables network distribution, "
    254           "running tests locally.")
    255     options.no_network = True
    256   options.command_prefix = shlex.split(options.command_prefix)
    257   options.extra_flags = shlex.split(options.extra_flags)
    258 
    259   if options.gc_stress:
    260     options.extra_flags += GC_STRESS_FLAGS
    261 
    262   if options.asan:
    263     options.extra_flags.append("--invoke-weak-callbacks")
    264 
    265   if options.j == 0:
    266     options.j = multiprocessing.cpu_count()
    267 
    268   while options.random_seed == 0:
    269     options.random_seed = random.SystemRandom().randint(-2147483648, 2147483647)
    270 
    271   def excl(*args):
    272     """Returns true if zero or one of multiple arguments are true."""
    273     return reduce(lambda x, y: x + y, args) <= 1
    274 
    275   if not excl(options.no_stress, options.stress_only, options.no_variants,
    276               bool(options.variants), options.quickcheck):
    277     print("Use only one of --no-stress, --stress-only, --no-variants, "
    278           "--variants, or --quickcheck.")
    279     return False
    280   if options.no_stress:
    281     VARIANTS = ["default", "nocrankshaft"]
    282   if options.no_variants:
    283     VARIANTS = ["default"]
    284   if options.stress_only:
    285     VARIANTS = ["stress"]
    286   if options.variants:
    287     VARIANTS = options.variants.split(",")
    288     if not set(VARIANTS).issubset(VARIANT_FLAGS.keys()):
    289       print "All variants must be in %s" % str(VARIANT_FLAGS.keys())
    290       return False
    291   if options.quickcheck:
    292     VARIANTS = ["default", "stress"]
    293     options.flaky_tests = "skip"
    294     options.slow_tests = "skip"
    295     options.pass_fail_tests = "skip"
    296 
    297   if not options.shell_dir:
    298     if options.shell:
    299       print "Warning: --shell is deprecated, use --shell-dir instead."
    300       options.shell_dir = os.path.dirname(options.shell)
    301   if options.valgrind:
    302     run_valgrind = os.path.join("tools", "run-valgrind.py")
    303     # This is OK for distributed running, so we don't need to set no_network.
    304     options.command_prefix = (["python", "-u", run_valgrind] +
    305                               options.command_prefix)
    306   def CheckTestMode(name, option):
    307     if not option in ["run", "skip", "dontcare"]:
    308       print "Unknown %s mode %s" % (name, option)
    309       return False
    310     return True
    311   if not CheckTestMode("flaky test", options.flaky_tests):
    312     return False
    313   if not CheckTestMode("slow test", options.slow_tests):
    314     return False
    315   if not CheckTestMode("pass|fail test", options.pass_fail_tests):
    316     return False
    317   if not options.no_i18n:
    318     DEFAULT_TESTS.append("intl")
    319   return True
    320 
    321 
    322 def ShardTests(tests, shard_count, shard_run):
    323   if shard_count < 2:
    324     return tests
    325   if shard_run < 1 or shard_run > shard_count:
    326     print "shard-run not a valid number, should be in [1:shard-count]"
    327     print "defaulting back to running all tests"
    328     return tests
    329   count = 0
    330   shard = []
    331   for test in tests:
    332     if count % shard_count == shard_run - 1:
    333       shard.append(test)
    334     count += 1
    335   return shard
    336 
    337 
    338 def Main():
    339   parser = BuildOptions()
    340   (options, args) = parser.parse_args()
    341   if not ProcessOptions(options):
    342     parser.print_help()
    343     return 1
    344 
    345   exit_code = 0
    346   workspace = os.path.abspath(join(os.path.dirname(sys.argv[0]), ".."))
    347   if not options.no_presubmit:
    348     print ">>> running presubmit tests"
    349     exit_code = subprocess.call(
    350         [sys.executable, join(workspace, "tools", "presubmit.py")])
    351 
    352   suite_paths = utils.GetSuitePaths(join(workspace, "test"))
    353 
    354   if len(args) == 0:
    355     suite_paths = [ s for s in DEFAULT_TESTS if s in suite_paths ]
    356   else:
    357     args_suites = set()
    358     for arg in args:
    359       suite = arg.split(os.path.sep)[0]
    360       if not suite in args_suites:
    361         args_suites.add(suite)
    362     suite_paths = [ s for s in args_suites if s in suite_paths ]
    363 
    364   suites = []
    365   for root in suite_paths:
    366     suite = testsuite.TestSuite.LoadTestSuite(
    367         os.path.join(workspace, "test", root))
    368     if suite:
    369       suites.append(suite)
    370 
    371   if options.download_data:
    372     for s in suites:
    373       s.DownloadData()
    374 
    375   for (arch, mode) in options.arch_and_mode:
    376     try:
    377       code = Execute(arch, mode, args, options, suites, workspace)
    378     except KeyboardInterrupt:
    379       return 2
    380     exit_code = exit_code or code
    381   return exit_code
    382 
    383 
    384 def Execute(arch, mode, args, options, suites, workspace):
    385   print(">>> Running tests for %s.%s" % (arch, mode))
    386 
    387   shell_dir = options.shell_dir
    388   if not shell_dir:
    389     if options.buildbot:
    390       shell_dir = os.path.join(workspace, options.outdir, mode)
    391       mode = mode.lower()
    392     else:
    393       shell_dir = os.path.join(workspace, options.outdir,
    394                                "%s.%s" % (arch, mode))
    395   shell_dir = os.path.relpath(shell_dir)
    396 
    397   if mode == "optdebug":
    398     mode = "debug"  # "optdebug" is just an alias.
    399 
    400   # Populate context object.
    401   mode_flags = MODE_FLAGS[mode]
    402   timeout = options.timeout
    403   if timeout == -1:
    404     # Simulators are slow, therefore allow a longer default timeout.
    405     if arch in SLOW_ARCHS:
    406       timeout = 2 * TIMEOUT_DEFAULT;
    407     else:
    408       timeout = TIMEOUT_DEFAULT;
    409 
    410   timeout *= TIMEOUT_SCALEFACTOR[mode]
    411   ctx = context.Context(arch, mode, shell_dir,
    412                         mode_flags, options.verbose,
    413                         timeout, options.isolates,
    414                         options.command_prefix,
    415                         options.extra_flags,
    416                         options.no_i18n,
    417                         options.random_seed,
    418                         options.no_sorting)
    419 
    420   # TODO(all): Combine "simulator" and "simulator_run".
    421   simulator_run = not options.dont_skip_simulator_slow_tests and \
    422       arch in ['arm64', 'arm', 'mips'] and ARCH_GUESS and arch != ARCH_GUESS
    423   # Find available test suites and read test cases from them.
    424   variables = {
    425     "arch": arch,
    426     "asan": options.asan,
    427     "deopt_fuzzer": False,
    428     "gc_stress": options.gc_stress,
    429     "isolates": options.isolates,
    430     "mode": mode,
    431     "no_i18n": options.no_i18n,
    432     "no_snap": options.no_snap,
    433     "simulator_run": simulator_run,
    434     "simulator": utils.UseSimulator(arch),
    435     "system": utils.GuessOS(),
    436   }
    437   all_tests = []
    438   num_tests = 0
    439   test_id = 0
    440   for s in suites:
    441     s.ReadStatusFile(variables)
    442     s.ReadTestCases(ctx)
    443     if len(args) > 0:
    444       s.FilterTestCasesByArgs(args)
    445     all_tests += s.tests
    446     s.FilterTestCasesByStatus(options.warn_unused, options.flaky_tests,
    447                               options.slow_tests, options.pass_fail_tests)
    448     if options.cat:
    449       verbose.PrintTestSource(s.tests)
    450       continue
    451     variant_flags = [VARIANT_FLAGS[var] for var in VARIANTS]
    452     s.tests = [ t.CopyAddingFlags(v)
    453                 for t in s.tests
    454                 for v in s.VariantFlags(t, variant_flags) ]
    455     s.tests = ShardTests(s.tests, options.shard_count, options.shard_run)
    456     num_tests += len(s.tests)
    457     for t in s.tests:
    458       t.id = test_id
    459       test_id += 1
    460 
    461   if options.cat:
    462     return 0  # We're done here.
    463 
    464   if options.report:
    465     verbose.PrintReport(all_tests)
    466 
    467   if num_tests == 0:
    468     print "No tests to run."
    469     return 0
    470 
    471   # Run the tests, either locally or distributed on the network.
    472   start_time = time.time()
    473   progress_indicator = progress.PROGRESS_INDICATORS[options.progress]()
    474   if options.junitout:
    475     progress_indicator = progress.JUnitTestProgressIndicator(
    476         progress_indicator, options.junitout, options.junittestsuite)
    477   if options.json_test_results:
    478     progress_indicator = progress.JsonTestProgressIndicator(
    479         progress_indicator, options.json_test_results, arch, mode)
    480 
    481   run_networked = not options.no_network
    482   if not run_networked:
    483     print("Network distribution disabled, running tests locally.")
    484   elif utils.GuessOS() != "linux":
    485     print("Network distribution is only supported on Linux, sorry!")
    486     run_networked = False
    487   peers = []
    488   if run_networked:
    489     peers = network_execution.GetPeers()
    490     if not peers:
    491       print("No connection to distribution server; running tests locally.")
    492       run_networked = False
    493     elif len(peers) == 1:
    494       print("No other peers on the network; running tests locally.")
    495       run_networked = False
    496     elif num_tests <= 100:
    497       print("Less than 100 tests, running them locally.")
    498       run_networked = False
    499 
    500   if run_networked:
    501     runner = network_execution.NetworkedRunner(suites, progress_indicator,
    502                                                ctx, peers, workspace)
    503   else:
    504     runner = execution.Runner(suites, progress_indicator, ctx)
    505 
    506   exit_code = runner.Run(options.j)
    507   overall_duration = time.time() - start_time
    508 
    509   if options.time:
    510     verbose.PrintTestDurations(suites, overall_duration)
    511   return exit_code
    512 
    513 
    514 if __name__ == "__main__":
    515   sys.exit(Main())
    516