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 multiprocessing
     32 import optparse
     33 import os
     34 from os.path import join
     35 import shlex
     36 import subprocess
     37 import sys
     38 import time
     39 
     40 from testrunner.local import execution
     41 from testrunner.local import progress
     42 from testrunner.local import testsuite
     43 from testrunner.local import utils
     44 from testrunner.local import verbose
     45 from testrunner.network import network_execution
     46 from testrunner.objects import context
     47 
     48 
     49 ARCH_GUESS = utils.DefaultArch()
     50 DEFAULT_TESTS = ["mjsunit", "cctest", "message", "preparser"]
     51 TIMEOUT_DEFAULT = 60
     52 TIMEOUT_SCALEFACTOR = {"debug"   : 4,
     53                        "release" : 1 }
     54 
     55 # Use this to run several variants of the tests.
     56 VARIANT_FLAGS = [[],
     57                  ["--stress-opt", "--always-opt"],
     58                  ["--nocrankshaft"]]
     59 MODE_FLAGS = {
     60     "debug"   : ["--nobreak-on-abort", "--nodead-code-elimination",
     61                  "--nofold-constants", "--enable-slow-asserts",
     62                  "--debug-code", "--verify-heap"],
     63     "release" : ["--nobreak-on-abort", "--nodead-code-elimination",
     64                  "--nofold-constants"]}
     65 
     66 SUPPORTED_ARCHS = ["android_arm",
     67                    "android_ia32",
     68                    "arm",
     69                    "ia32",
     70                    "mipsel",
     71                    "nacl_ia32",
     72                    "nacl_x64",
     73                    "x64"]
     74 # Double the timeout for these:
     75 SLOW_ARCHS = ["android_arm",
     76               "android_ia32",
     77               "arm",
     78               "mipsel",
     79               "nacl_ia32",
     80               "nacl_x64"]
     81 
     82 
     83 def BuildOptions():
     84   result = optparse.OptionParser()
     85   result.add_option("--arch",
     86                     help=("The architecture to run tests for, "
     87                           "'auto' or 'native' for auto-detect"),
     88                     default="ia32,x64,arm")
     89   result.add_option("--arch-and-mode",
     90                     help="Architecture and mode in the format 'arch.mode'",
     91                     default=None)
     92   result.add_option("--buildbot",
     93                     help="Adapt to path structure used on buildbots",
     94                     default=False, action="store_true")
     95   result.add_option("--cat", help="Print the source of the tests",
     96                     default=False, action="store_true")
     97   result.add_option("--flaky-tests",
     98                     help="Regard tests marked as flaky (run|skip|dontcare)",
     99                     default="dontcare")
    100   result.add_option("--command-prefix",
    101                     help="Prepended to each shell command used to run a test",
    102                     default="")
    103   result.add_option("--download-data", help="Download missing test suite data",
    104                     default=False, action="store_true")
    105   result.add_option("--extra-flags",
    106                     help="Additional flags to pass to each test command",
    107                     default="")
    108   result.add_option("--isolates", help="Whether to test isolates",
    109                     default=False, action="store_true")
    110   result.add_option("-j", help="The number of parallel tasks to run",
    111                     default=0, type="int")
    112   result.add_option("-m", "--mode",
    113                     help="The test modes in which to run (comma-separated)",
    114                     default="release,debug")
    115   result.add_option("--no-network", "--nonetwork",
    116                     help="Don't distribute tests on the network",
    117                     default=(utils.GuessOS() != "linux"),
    118                     dest="no_network", action="store_true")
    119   result.add_option("--no-presubmit", "--nopresubmit",
    120                     help='Skip presubmit checks',
    121                     default=False, dest="no_presubmit", action="store_true")
    122   result.add_option("--no-stress", "--nostress",
    123                     help="Don't run crankshaft --always-opt --stress-op test",
    124                     default=False, dest="no_stress", action="store_true")
    125   result.add_option("--outdir", help="Base directory with compile output",
    126                     default="out")
    127   result.add_option("-p", "--progress",
    128                     help=("The style of progress indicator"
    129                           " (verbose, dots, color, mono)"),
    130                     choices=progress.PROGRESS_INDICATORS.keys(), default="mono")
    131   result.add_option("--report", help="Print a summary of the tests to be run",
    132                     default=False, action="store_true")
    133   result.add_option("--shard-count",
    134                     help="Split testsuites into this number of shards",
    135                     default=1, type="int")
    136   result.add_option("--shard-run",
    137                     help="Run this shard from the split up tests.",
    138                     default=1, type="int")
    139   result.add_option("--shell", help="DEPRECATED! use --shell-dir", default="")
    140   result.add_option("--shell-dir", help="Directory containing executables",
    141                     default="")
    142   result.add_option("--stress-only",
    143                     help="Only run tests with --always-opt --stress-opt",
    144                     default=False, action="store_true")
    145   result.add_option("--time", help="Print timing information after running",
    146                     default=False, action="store_true")
    147   result.add_option("-t", "--timeout", help="Timeout in seconds",
    148                     default= -1, type="int")
    149   result.add_option("-v", "--verbose", help="Verbose output",
    150                     default=False, action="store_true")
    151   result.add_option("--valgrind", help="Run tests through valgrind",
    152                     default=False, action="store_true")
    153   result.add_option("--warn-unused", help="Report unused rules",
    154                     default=False, action="store_true")
    155   result.add_option("--junitout", help="File name of the JUnit output")
    156   result.add_option("--junittestsuite",
    157                     help="The testsuite name in the JUnit output file",
    158                     default="v8tests")
    159   return result
    160 
    161 
    162 def ProcessOptions(options):
    163   global VARIANT_FLAGS
    164 
    165   # Architecture and mode related stuff.
    166   if options.arch_and_mode:
    167     tokens = options.arch_and_mode.split(".")
    168     options.arch = tokens[0]
    169     options.mode = tokens[1]
    170   options.mode = options.mode.split(",")
    171   for mode in options.mode:
    172     if not mode.lower() in ["debug", "release"]:
    173       print "Unknown mode %s" % mode
    174       return False
    175   if options.arch in ["auto", "native"]:
    176     options.arch = ARCH_GUESS
    177   options.arch = options.arch.split(",")
    178   for arch in options.arch:
    179     if not arch in SUPPORTED_ARCHS:
    180       print "Unknown architecture %s" % arch
    181       return False
    182 
    183   # Special processing of other options, sorted alphabetically.
    184 
    185   if options.buildbot:
    186     # Buildbots run presubmit tests as a separate step.
    187     options.no_presubmit = True
    188     options.no_network = True
    189   if options.command_prefix:
    190     print("Specifying --command-prefix disables network distribution, "
    191           "running tests locally.")
    192     options.no_network = True
    193   options.command_prefix = shlex.split(options.command_prefix)
    194   options.extra_flags = shlex.split(options.extra_flags)
    195   if options.j == 0:
    196     options.j = multiprocessing.cpu_count()
    197   if options.no_stress:
    198     VARIANT_FLAGS = [[], ["--nocrankshaft"]]
    199   if not options.shell_dir:
    200     if options.shell:
    201       print "Warning: --shell is deprecated, use --shell-dir instead."
    202       options.shell_dir = os.path.dirname(options.shell)
    203   if options.stress_only:
    204     VARIANT_FLAGS = [["--stress-opt", "--always-opt"]]
    205   if options.valgrind:
    206     run_valgrind = os.path.join("tools", "run-valgrind.py")
    207     # This is OK for distributed running, so we don't need to set no_network.
    208     options.command_prefix = (["python", "-u", run_valgrind] +
    209                               options.command_prefix)
    210   if not options.flaky_tests in ["run", "skip", "dontcare"]:
    211     print "Unknown flaky test mode %s" % options.flaky_tests
    212     return False
    213   return True
    214 
    215 
    216 def ShardTests(tests, shard_count, shard_run):
    217   if shard_count < 2:
    218     return tests
    219   if shard_run < 1 or shard_run > shard_count:
    220     print "shard-run not a valid number, should be in [1:shard-count]"
    221     print "defaulting back to running all tests"
    222     return tests
    223   count = 0
    224   shard = []
    225   for test in tests:
    226     if count % shard_count == shard_run - 1:
    227       shard.append(test)
    228     count += 1
    229   return shard
    230 
    231 
    232 def Main():
    233   parser = BuildOptions()
    234   (options, args) = parser.parse_args()
    235   if not ProcessOptions(options):
    236     parser.print_help()
    237     return 1
    238 
    239   exit_code = 0
    240   workspace = os.path.abspath(join(os.path.dirname(sys.argv[0]), ".."))
    241   if not options.no_presubmit:
    242     print ">>> running presubmit tests"
    243     code = subprocess.call(
    244         [sys.executable, join(workspace, "tools", "presubmit.py")])
    245     exit_code = code
    246 
    247   suite_paths = utils.GetSuitePaths(join(workspace, "test"))
    248 
    249   if len(args) == 0:
    250     suite_paths = [ s for s in suite_paths if s in DEFAULT_TESTS ]
    251   else:
    252     args_suites = set()
    253     for arg in args:
    254       suite = arg.split(os.path.sep)[0]
    255       if not suite in args_suites:
    256         args_suites.add(suite)
    257     suite_paths = [ s for s in suite_paths if s in args_suites ]
    258 
    259   suites = []
    260   for root in suite_paths:
    261     suite = testsuite.TestSuite.LoadTestSuite(
    262         os.path.join(workspace, "test", root))
    263     if suite:
    264       suites.append(suite)
    265 
    266   if options.download_data:
    267     for s in suites:
    268       s.DownloadData()
    269 
    270   for mode in options.mode:
    271     for arch in options.arch:
    272       code = Execute(arch, mode, args, options, suites, workspace)
    273       exit_code = exit_code or code
    274   return exit_code
    275 
    276 
    277 def Execute(arch, mode, args, options, suites, workspace):
    278   print(">>> Running tests for %s.%s" % (arch, mode))
    279 
    280   shell_dir = options.shell_dir
    281   if not shell_dir:
    282     if options.buildbot:
    283       shell_dir = os.path.join(workspace, options.outdir, mode)
    284       mode = mode.lower()
    285     else:
    286       shell_dir = os.path.join(workspace, options.outdir,
    287                                "%s.%s" % (arch, mode))
    288   shell_dir = os.path.relpath(shell_dir)
    289 
    290   # Populate context object.
    291   mode_flags = MODE_FLAGS[mode]
    292   timeout = options.timeout
    293   if timeout == -1:
    294     # Simulators are slow, therefore allow a longer default timeout.
    295     if arch in SLOW_ARCHS:
    296       timeout = 2 * TIMEOUT_DEFAULT;
    297     else:
    298       timeout = TIMEOUT_DEFAULT;
    299 
    300   timeout *= TIMEOUT_SCALEFACTOR[mode]
    301   ctx = context.Context(arch, mode, shell_dir,
    302                         mode_flags, options.verbose,
    303                         timeout, options.isolates,
    304                         options.command_prefix,
    305                         options.extra_flags)
    306 
    307   # Find available test suites and read test cases from them.
    308   variables = {
    309     "mode": mode,
    310     "arch": arch,
    311     "system": utils.GuessOS(),
    312     "isolates": options.isolates,
    313     "deopt_fuzzer": False,
    314   }
    315   all_tests = []
    316   num_tests = 0
    317   test_id = 0
    318   for s in suites:
    319     s.ReadStatusFile(variables)
    320     s.ReadTestCases(ctx)
    321     if len(args) > 0:
    322       s.FilterTestCasesByArgs(args)
    323     all_tests += s.tests
    324     s.FilterTestCasesByStatus(options.warn_unused, options.flaky_tests)
    325     if options.cat:
    326       verbose.PrintTestSource(s.tests)
    327       continue
    328     variant_flags = s.VariantFlags() or VARIANT_FLAGS
    329     s.tests = [ t.CopyAddingFlags(v) for t in s.tests for v in variant_flags ]
    330     s.tests = ShardTests(s.tests, options.shard_count, options.shard_run)
    331     num_tests += len(s.tests)
    332     for t in s.tests:
    333       t.id = test_id
    334       test_id += 1
    335 
    336   if options.cat:
    337     return 0  # We're done here.
    338 
    339   if options.report:
    340     verbose.PrintReport(all_tests)
    341 
    342   if num_tests == 0:
    343     print "No tests to run."
    344     return 0
    345 
    346   # Run the tests, either locally or distributed on the network.
    347   try:
    348     start_time = time.time()
    349     progress_indicator = progress.PROGRESS_INDICATORS[options.progress]()
    350     if options.junitout:
    351       progress_indicator = progress.JUnitTestProgressIndicator(
    352           progress_indicator, options.junitout, options.junittestsuite)
    353 
    354     run_networked = not options.no_network
    355     if not run_networked:
    356       print("Network distribution disabled, running tests locally.")
    357     elif utils.GuessOS() != "linux":
    358       print("Network distribution is only supported on Linux, sorry!")
    359       run_networked = False
    360     peers = []
    361     if run_networked:
    362       peers = network_execution.GetPeers()
    363       if not peers:
    364         print("No connection to distribution server; running tests locally.")
    365         run_networked = False
    366       elif len(peers) == 1:
    367         print("No other peers on the network; running tests locally.")
    368         run_networked = False
    369       elif num_tests <= 100:
    370         print("Less than 100 tests, running them locally.")
    371         run_networked = False
    372 
    373     if run_networked:
    374       runner = network_execution.NetworkedRunner(suites, progress_indicator,
    375                                                  ctx, peers, workspace)
    376     else:
    377       runner = execution.Runner(suites, progress_indicator, ctx)
    378 
    379     exit_code = runner.Run(options.j)
    380     if runner.terminate:
    381       return exit_code
    382     overall_duration = time.time() - start_time
    383   except KeyboardInterrupt:
    384     return 1
    385 
    386   if options.time:
    387     verbose.PrintTestDurations(suites, overall_duration)
    388   return exit_code
    389 
    390 
    391 if __name__ == "__main__":
    392   sys.exit(Main())
    393