• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2017 the V8 project authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from functools import reduce
6
7from collections import OrderedDict, namedtuple
8import json
9import multiprocessing
10import optparse
11import os
12import shlex
13import sys
14import traceback
15
16
17
18# Add testrunner to the path.
19sys.path.insert(
20  0,
21  os.path.dirname(
22    os.path.dirname(os.path.abspath(__file__))))
23
24
25from testrunner.local import command
26from testrunner.local import testsuite
27from testrunner.local import utils
28from testrunner.test_config import TestConfig
29from testrunner.testproc import progress
30from testrunner.testproc.rerun import RerunProc
31from testrunner.testproc.shard import ShardProc
32from testrunner.testproc.sigproc import SignalProc
33from testrunner.testproc.timeout import TimeoutProc
34from testrunner.testproc import util
35
36
37BASE_DIR = (
38    os.path.dirname(
39      os.path.dirname(
40        os.path.dirname(
41          os.path.abspath(__file__)))))
42
43DEFAULT_OUT_GN = 'out.gn'
44
45# Map of test name synonyms to lists of test suites. Should be ordered by
46# expected runtimes (suites with slow test cases first). These groups are
47# invoked in separate steps on the bots.
48# The mapping from names used here to GN targets (which must stay in sync)
49# is defined in infra/mb/gn_isolate_map.pyl.
50TEST_MAP = {
51  # This needs to stay in sync with group("v8_bot_default") in test/BUILD.gn.
52  "bot_default": [
53    "debugger",
54    "mjsunit",
55    "cctest",
56    "wasm-spec-tests",
57    "inspector",
58    "webkit",
59    "mkgrokdump",
60    "wasm-js",
61    "fuzzer",
62    "message",
63    "intl",
64    "unittests",
65    "wasm-api-tests",
66  ],
67  # This needs to stay in sync with group("v8_default") in test/BUILD.gn.
68  "default": [
69    "debugger",
70    "mjsunit",
71    "cctest",
72    "wasm-spec-tests",
73    "inspector",
74    "mkgrokdump",
75    "wasm-js",
76    "fuzzer",
77    "message",
78    "intl",
79    "unittests",
80    "wasm-api-tests",
81  ],
82  # This needs to stay in sync with group("v8_d8_default") in test/BUILD.gn.
83  "d8_default": [
84    "debugger",
85    "mjsunit",
86    "webkit",
87    "message",
88    "intl",
89  ],
90  # This needs to stay in sync with "v8_optimize_for_size" in test/BUILD.gn.
91  "optimize_for_size": [
92    "debugger",
93    "mjsunit",
94    "cctest",
95    "inspector",
96    "webkit",
97    "intl",
98  ],
99  "unittests": [
100    "unittests",
101  ],
102}
103
104# Increase the timeout for these:
105SLOW_ARCHS = [
106  "arm",
107  "arm64",
108  "mips",
109  "mipsel",
110  "mips64",
111  "mips64el",
112  "s390",
113  "s390x",
114  "riscv64",
115  "loong64"
116]
117
118
119ModeConfig = namedtuple(
120    'ModeConfig', 'label flags timeout_scalefactor status_mode')
121
122DEBUG_FLAGS = ["--nohard-abort", "--enable-slow-asserts", "--verify-heap"]
123RELEASE_FLAGS = ["--nohard-abort"]
124
125DEBUG_MODE = ModeConfig(
126    label='debug',
127    flags=DEBUG_FLAGS,
128    timeout_scalefactor=4,
129    status_mode="debug",
130)
131
132RELEASE_MODE = ModeConfig(
133    label='release',
134    flags=RELEASE_FLAGS,
135    timeout_scalefactor=1,
136    status_mode="release",
137)
138
139# Normal trybot release configuration. There, dchecks are always on which
140# implies debug is set. Hence, the status file needs to assume debug-like
141# behavior/timeouts.
142TRY_RELEASE_MODE = ModeConfig(
143    label='release+dchecks',
144    flags=RELEASE_FLAGS,
145    timeout_scalefactor=4,
146    status_mode="debug",
147)
148
149PROGRESS_INDICATORS = {
150  'verbose': progress.VerboseProgressIndicator,
151  'ci': progress.CIProgressIndicator,
152  'dots': progress.DotsProgressIndicator,
153  'color': progress.ColorProgressIndicator,
154  'mono': progress.MonochromeProgressIndicator,
155  'stream': progress.StreamProgressIndicator,
156}
157
158class TestRunnerError(Exception):
159  pass
160
161
162class BuildConfig(object):
163  def __init__(self, build_config):
164    # In V8 land, GN's x86 is called ia32.
165    if build_config['v8_target_cpu'] == 'x86':
166      self.arch = 'ia32'
167    else:
168      self.arch = build_config['v8_target_cpu']
169
170    self.asan = build_config['is_asan']
171    self.cfi_vptr = build_config['is_cfi']
172    self.control_flow_integrity = build_config['v8_control_flow_integrity']
173    self.concurrent_marking = build_config['v8_enable_concurrent_marking']
174    self.single_generation = build_config['v8_enable_single_generation']
175    self.dcheck_always_on = build_config['dcheck_always_on']
176    self.gcov_coverage = build_config['is_gcov_coverage']
177    self.is_android = build_config['is_android']
178    self.is_clang = build_config['is_clang']
179    self.is_debug = build_config['is_debug']
180    self.is_full_debug = build_config['is_full_debug']
181    self.msan = build_config['is_msan']
182    self.no_i18n = not build_config['v8_enable_i18n_support']
183    self.predictable = build_config['v8_enable_verify_predictable']
184    self.simulator_run = (build_config['target_cpu'] !=
185                          build_config['v8_target_cpu'])
186    self.tsan = build_config['is_tsan']
187    # TODO(machenbach): We only have ubsan not ubsan_vptr.
188    self.ubsan_vptr = build_config['is_ubsan_vptr']
189    self.verify_csa = build_config['v8_enable_verify_csa']
190    self.lite_mode = build_config['v8_enable_lite_mode']
191    self.pointer_compression = build_config['v8_enable_pointer_compression']
192    self.pointer_compression_shared_cage = build_config['v8_enable_pointer_compression_shared_cage']
193    self.shared_ro_heap = build_config['v8_enable_shared_ro_heap']
194    self.sandbox = build_config['v8_enable_sandbox']
195    self.third_party_heap = build_config['v8_enable_third_party_heap']
196    self.webassembly = build_config['v8_enable_webassembly']
197    self.dict_property_const_tracking = build_config['v8_dict_property_const_tracking']
198    # Export only for MIPS target
199    if self.arch in ['mips', 'mipsel', 'mips64', 'mips64el']:
200      self.mips_arch_variant = build_config['mips_arch_variant']
201      self.mips_use_msa = build_config['mips_use_msa']
202
203  @property
204  def use_sanitizer(self):
205    return (self.asan or self.cfi_vptr or self.msan or self.tsan or
206            self.ubsan_vptr)
207
208  def __str__(self):
209    detected_options = []
210
211    if self.asan:
212      detected_options.append('asan')
213    if self.cfi_vptr:
214      detected_options.append('cfi_vptr')
215    if self.control_flow_integrity:
216      detected_options.append('control_flow_integrity')
217    if self.dcheck_always_on:
218      detected_options.append('dcheck_always_on')
219    if self.gcov_coverage:
220      detected_options.append('gcov_coverage')
221    if self.msan:
222      detected_options.append('msan')
223    if self.no_i18n:
224      detected_options.append('no_i18n')
225    if self.predictable:
226      detected_options.append('predictable')
227    if self.tsan:
228      detected_options.append('tsan')
229    if self.ubsan_vptr:
230      detected_options.append('ubsan_vptr')
231    if self.verify_csa:
232      detected_options.append('verify_csa')
233    if self.lite_mode:
234      detected_options.append('lite_mode')
235    if self.pointer_compression:
236      detected_options.append('pointer_compression')
237    if self.pointer_compression_shared_cage:
238      detected_options.append('pointer_compression_shared_cage')
239    if self.sandbox:
240      detected_options.append('sandbox')
241    if self.third_party_heap:
242      detected_options.append('third_party_heap')
243    if self.webassembly:
244      detected_options.append('webassembly')
245    if self.dict_property_const_tracking:
246      detected_options.append('dict_property_const_tracking')
247
248    return '\n'.join(detected_options)
249
250
251def _do_load_build_config(outdir, verbose=False):
252  build_config_path = os.path.join(outdir, "v8_build_config.json")
253  if not os.path.exists(build_config_path):
254    if verbose:
255      print("Didn't find build config: %s" % build_config_path)
256    raise TestRunnerError()
257
258  with open(build_config_path) as f:
259    try:
260      build_config_json = json.load(f)
261    except Exception:  # pragma: no cover
262      print("%s exists but contains invalid json. Is your build up-to-date?"
263            % build_config_path)
264      raise TestRunnerError()
265
266  return BuildConfig(build_config_json)
267
268
269class BaseTestRunner(object):
270  def __init__(self, basedir=None):
271    self.basedir = basedir or BASE_DIR
272    self.outdir = None
273    self.build_config = None
274    self.mode_options = None
275    self.target_os = None
276    self.infra_staging = False
277
278  @property
279  def framework_name(self):
280    """String name of the base-runner subclass, used in test results."""
281    raise NotImplementedError()
282
283  def execute(self, sys_args=None):
284    if sys_args is None:  # pragma: no cover
285      sys_args = sys.argv[1:]
286    try:
287      parser = self._create_parser()
288      options, args = self._parse_args(parser, sys_args)
289      self.infra_staging = options.infra_staging
290      if options.swarming:
291        # Swarming doesn't print how isolated commands are called. Lets make
292        # this less cryptic by printing it ourselves.
293        print(' '.join(sys.argv))
294
295        # TODO(machenbach): Print used Python version until we have switched to
296        # Python3 everywhere.
297        print('Running with:')
298        print(sys.version)
299
300        # Kill stray processes from previous tasks on swarming.
301        util.kill_processes_linux()
302
303      self._load_build_config(options)
304      command.setup(self.target_os, options.device)
305
306      try:
307        self._process_default_options(options)
308        self._process_options(options)
309      except TestRunnerError:
310        parser.print_help()
311        raise
312
313      args = self._parse_test_args(args)
314      tests = self._load_testsuite_generators(args, options)
315      self._setup_env()
316      print(">>> Running tests for %s.%s" % (self.build_config.arch,
317                                             self.mode_options.label))
318      exit_code = self._do_execute(tests, args, options)
319      if exit_code == utils.EXIT_CODE_FAILURES and options.json_test_results:
320        print("Force exit code 0 after failures. Json test results file "
321              "generated with failure information.")
322        exit_code = utils.EXIT_CODE_PASS
323      return exit_code
324    except TestRunnerError:
325      traceback.print_exc()
326      return utils.EXIT_CODE_INTERNAL_ERROR
327    except KeyboardInterrupt:
328      return utils.EXIT_CODE_INTERRUPTED
329    except Exception:
330      traceback.print_exc()
331      return utils.EXIT_CODE_INTERNAL_ERROR
332    finally:
333      command.tear_down()
334
335  def _create_parser(self):
336    parser = optparse.OptionParser()
337    parser.usage = '%prog [options] [tests]'
338    parser.description = """TESTS: %s""" % (TEST_MAP["default"])
339    self._add_parser_default_options(parser)
340    self._add_parser_options(parser)
341    return parser
342
343  def _add_parser_default_options(self, parser):
344    parser.add_option("--gn", help="Scan out.gn for the last built"
345                      " configuration",
346                      default=False, action="store_true")
347    parser.add_option("--outdir", help="Base directory with compile output",
348                      default="out")
349    parser.add_option("--arch",
350                      help="The architecture to run tests for")
351    parser.add_option("--shell-dir", help="DEPRECATED! Executables from build "
352                      "directory will be used")
353    parser.add_option("--test-root", help="Root directory of the test suites",
354                      default=os.path.join(self.basedir, 'test'))
355    parser.add_option("--total-timeout-sec", default=0, type="int",
356                      help="How long should fuzzer run")
357    parser.add_option("--swarming", default=False, action="store_true",
358                      help="Indicates running test driver on swarming.")
359    parser.add_option('--infra-staging', help='Use new test runner features',
360                      dest='infra_staging', default=None,
361                      action='store_true')
362    parser.add_option('--no-infra-staging',
363                      help='Opt out of new test runner features',
364                      dest='infra_staging', default=None,
365                      action='store_false')
366
367    parser.add_option("-j", help="The number of parallel tasks to run",
368                      default=0, type=int)
369    parser.add_option("-d", "--device",
370                      help="The device ID to run Android tests on. If not "
371                           "given it will be autodetected.")
372
373    # Shard
374    parser.add_option("--shard-count", default=1, type=int,
375                      help="Split tests into this number of shards")
376    parser.add_option("--shard-run", default=1, type=int,
377                      help="Run this shard from the split up tests.")
378
379    # Progress
380    parser.add_option("-p", "--progress",
381                      choices=list(PROGRESS_INDICATORS.keys()), default="mono",
382                      help="The style of progress indicator (verbose, dots, "
383                           "color, mono)")
384    parser.add_option("--json-test-results",
385                      help="Path to a file for storing json results.")
386    parser.add_option('--slow-tests-cutoff', type="int", default=100,
387                      help='Collect N slowest tests')
388    parser.add_option("--junitout", help="File name of the JUnit output")
389    parser.add_option("--junittestsuite", default="v8tests",
390                      help="The testsuite name in the JUnit output file")
391    parser.add_option("--exit-after-n-failures", type="int", default=100,
392                      help="Exit after the first N failures instead of "
393                           "running all tests. Pass 0 to disable this feature.")
394    parser.add_option("--ci-test-completion",
395                      help="Path to a file for logging test completion in the "
396                           "context of CI progress indicator. Ignored if "
397                           "progress indicator is other than 'ci'.")
398
399    # Rerun
400    parser.add_option("--rerun-failures-count", default=0, type=int,
401                      help="Number of times to rerun each failing test case. "
402                           "Very slow tests will be rerun only once.")
403    parser.add_option("--rerun-failures-max", default=100, type=int,
404                      help="Maximum number of failing test cases to rerun")
405
406    # Test config
407    parser.add_option("--command-prefix", default="",
408                      help="Prepended to each shell command used to run a test")
409    parser.add_option('--dont-skip-slow-simulator-tests',
410                      help='Don\'t skip more slow tests when using a'
411                      ' simulator.', default=False, action='store_true',
412                      dest='dont_skip_simulator_slow_tests')
413    parser.add_option("--extra-flags", action="append", default=[],
414                      help="Additional flags to pass to each test command")
415    parser.add_option("--isolates", action="store_true", default=False,
416                      help="Whether to test isolates")
417    parser.add_option("--no-harness", "--noharness",
418                      default=False, action="store_true",
419                      help="Run without test harness of a given suite")
420    parser.add_option("--random-seed", default=0, type=int,
421                      help="Default seed for initializing random generator")
422    parser.add_option("--run-skipped", help="Also run skipped tests.",
423                      default=False, action="store_true")
424    parser.add_option("-t", "--timeout", default=60, type=int,
425                      help="Timeout for single test in seconds")
426    parser.add_option("-v", "--verbose", default=False, action="store_true",
427                      help="Verbose output")
428    parser.add_option('--regenerate-expected-files', default=False, action='store_true',
429                      help='Regenerate expected files')
430
431    # TODO(machenbach): Temporary options for rolling out new test runner
432    # features.
433    parser.add_option("--mastername", default='',
434                      help="Mastername property from infrastructure. Not "
435                           "setting this option indicates manual usage.")
436    parser.add_option("--buildername", default='',
437                      help="Buildername property from infrastructure. Not "
438                           "setting this option indicates manual usage.")
439
440  def _add_parser_options(self, parser):
441    pass
442
443  def _parse_args(self, parser, sys_args):
444    options, args = parser.parse_args(sys_args)
445
446    if options.arch and ',' in options.arch:  # pragma: no cover
447      print('Multiple architectures are deprecated')
448      raise TestRunnerError()
449
450    return options, args
451
452  def _load_build_config(self, options):
453    for outdir in self._possible_outdirs(options):
454      try:
455        self.build_config = _do_load_build_config(outdir, options.verbose)
456
457        # In auto-detect mode the outdir is always where we found the build config.
458        # This ensures that we'll also take the build products from there.
459        self.outdir = outdir
460        break
461      except TestRunnerError:
462        pass
463
464    if not self.build_config:  # pragma: no cover
465      print('Failed to load build config')
466      raise TestRunnerError
467
468    print('Build found: %s' % self.outdir)
469    if str(self.build_config):
470      print('>>> Autodetected:')
471      print(self.build_config)
472
473    # Represents the OS where tests are run on. Same as host OS except for
474    # Android, which is determined by build output.
475    if self.build_config.is_android:
476      self.target_os = 'android'
477    else:
478      self.target_os = utils.GuessOS()
479
480  # Returns possible build paths in order:
481  # gn
482  # outdir
483  # outdir on bots
484  def _possible_outdirs(self, options):
485    def outdirs():
486      if options.gn:
487        yield self._get_gn_outdir()
488        return
489
490      yield options.outdir
491
492      if os.path.basename(options.outdir) != 'build':
493        yield os.path.join(options.outdir, 'build')
494
495    for outdir in outdirs():
496      yield os.path.join(self.basedir, outdir)
497
498  def _get_gn_outdir(self):
499    gn_out_dir = os.path.join(self.basedir, DEFAULT_OUT_GN)
500    latest_timestamp = -1
501    latest_config = None
502    for gn_config in os.listdir(gn_out_dir):
503      gn_config_dir = os.path.join(gn_out_dir, gn_config)
504      if not os.path.isdir(gn_config_dir):
505        continue
506      if os.path.getmtime(gn_config_dir) > latest_timestamp:
507        latest_timestamp = os.path.getmtime(gn_config_dir)
508        latest_config = gn_config
509    if latest_config:
510      print(">>> Latest GN build found: %s" % latest_config)
511      return os.path.join(DEFAULT_OUT_GN, latest_config)
512
513  def _process_default_options(self, options):
514    if self.build_config.is_debug:
515      self.mode_options = DEBUG_MODE
516    elif self.build_config.dcheck_always_on:
517      self.mode_options = TRY_RELEASE_MODE
518    else:
519      self.mode_options = RELEASE_MODE
520
521    if options.arch and options.arch != self.build_config.arch:
522      print('--arch value (%s) inconsistent with build config (%s).' % (
523        options.arch, self.build_config.arch))
524      raise TestRunnerError()
525
526    if options.shell_dir:  # pragma: no cover
527      print('Warning: --shell-dir is deprecated. Searching for executables in '
528            'build directory (%s) instead.' % self.outdir)
529
530    if options.j == 0:
531      if self.build_config.is_android:
532        # Adb isn't happy about multi-processed file pushing.
533        options.j = 1
534      else:
535        options.j = multiprocessing.cpu_count()
536
537    options.command_prefix = shlex.split(options.command_prefix)
538    options.extra_flags = sum(list(map(shlex.split, options.extra_flags)), [])
539
540  def _process_options(self, options):
541    pass
542
543  def _setup_env(self):
544    # Use the v8 root as cwd as some test cases use "load" with relative paths.
545    os.chdir(self.basedir)
546
547    # Many tests assume an English interface.
548    os.environ['LANG'] = 'en_US.UTF-8'
549
550    symbolizer_option = self._get_external_symbolizer_option()
551
552    if self.build_config.asan:
553      asan_options = [
554          symbolizer_option,
555          'allow_user_segv_handler=1',
556          'allocator_may_return_null=1',
557      ]
558      if not utils.GuessOS() in ['macos', 'windows']:
559        # LSAN is not available on mac and windows.
560        asan_options.append('detect_leaks=1')
561      else:
562        asan_options.append('detect_leaks=0')
563      if utils.GuessOS() == 'windows':
564        # https://crbug.com/967663
565        asan_options.append('detect_stack_use_after_return=0')
566      os.environ['ASAN_OPTIONS'] = ":".join(asan_options)
567
568    if self.build_config.cfi_vptr:
569      os.environ['UBSAN_OPTIONS'] = ":".join([
570        'print_stacktrace=1',
571        'print_summary=1',
572        'symbolize=1',
573        symbolizer_option,
574      ])
575
576    if self.build_config.ubsan_vptr:
577      os.environ['UBSAN_OPTIONS'] = ":".join([
578        'print_stacktrace=1',
579        symbolizer_option,
580      ])
581
582    if self.build_config.msan:
583      os.environ['MSAN_OPTIONS'] = symbolizer_option
584
585    if self.build_config.tsan:
586      suppressions_file = os.path.join(
587          self.basedir,
588          'tools',
589          'sanitizers',
590          'tsan_suppressions.txt')
591      os.environ['TSAN_OPTIONS'] = " ".join([
592        symbolizer_option,
593        'suppressions=%s' % suppressions_file,
594        'exit_code=0',
595        'report_thread_leaks=0',
596        'history_size=7',
597        'report_destroy_locked=0',
598      ])
599
600  def _get_external_symbolizer_option(self):
601    external_symbolizer_path = os.path.join(
602        self.basedir,
603        'third_party',
604        'llvm-build',
605        'Release+Asserts',
606        'bin',
607        'llvm-symbolizer',
608    )
609
610    if utils.IsWindows():
611      # Quote, because sanitizers might confuse colon as option separator.
612      external_symbolizer_path = '"%s.exe"' % external_symbolizer_path
613
614    return 'external_symbolizer_path=%s' % external_symbolizer_path
615
616  def _parse_test_args(self, args):
617    if not args:
618      args = self._get_default_suite_names()
619
620    # Expand arguments with grouped tests. The args should reflect the list
621    # of suites as otherwise filters would break.
622    def expand_test_group(name):
623      return TEST_MAP.get(name, [name])
624
625    return reduce(list.__add__, list(map(expand_test_group, args)), [])
626
627  def _args_to_suite_names(self, args, test_root):
628    # Use default tests if no test configuration was provided at the cmd line.
629    all_names = set(utils.GetSuitePaths(test_root))
630    args_names = OrderedDict([(arg.split('/')[0], None) for arg in args]) # set
631    return [name for name in args_names if name in all_names]
632
633  def _get_default_suite_names(self):
634    return []
635
636  def _load_testsuite_generators(self, args, options):
637    names = self._args_to_suite_names(args, options.test_root)
638    test_config = self._create_test_config(options)
639    variables = self._get_statusfile_variables(options)
640
641    # Head generator with no elements
642    test_chain = testsuite.TestGenerator(0, [], [])
643    for name in names:
644      if options.verbose:
645        print('>>> Loading test suite: %s' % name)
646      suite = testsuite.TestSuite.Load(
647          os.path.join(options.test_root, name), test_config,
648          self.framework_name)
649
650      if self._is_testsuite_supported(suite, options):
651        tests = suite.load_tests_from_disk(variables)
652        test_chain.merge(tests)
653
654    return test_chain
655
656  def _is_testsuite_supported(self, suite, options):
657    """A predicate that can be overridden to filter out unsupported TestSuite
658    instances (see NumFuzzer for usage)."""
659    return True
660
661  def _get_statusfile_variables(self, options):
662    simd_mips = (
663      self.build_config.arch in ['mipsel', 'mips', 'mips64', 'mips64el'] and
664      self.build_config.mips_arch_variant == "r6" and
665      self.build_config.mips_use_msa)
666
667    mips_arch_variant = (
668      self.build_config.arch in ['mipsel', 'mips', 'mips64', 'mips64el'] and
669      self.build_config.mips_arch_variant)
670
671    no_simd_hardware = any(
672        i in options.extra_flags for i in ['--noenable-sse3',
673                                           '--no-enable-sse3',
674                                           '--noenable-ssse3',
675                                           '--no-enable-ssse3',
676                                           '--noenable-sse4-1',
677                                           '--no-enable-sse4_1'])
678
679    # Set no_simd_hardware on architectures without Simd enabled.
680    if self.build_config.arch == 'mips64el' or \
681       self.build_config.arch == 'mipsel':
682       no_simd_hardware = not simd_mips
683
684    if self.build_config.arch == 'loong64':
685       no_simd_hardware = True
686
687    # S390 hosts without VEF1 do not support Simd.
688    if self.build_config.arch == 's390x' and \
689       not self.build_config.simulator_run and \
690       not utils.IsS390SimdSupported():
691       no_simd_hardware = True
692
693    # Ppc64 processors earlier than POWER9 do not support Simd instructions
694    if self.build_config.arch == 'ppc64' and \
695       not self.build_config.simulator_run and \
696       utils.GuessPowerProcessorVersion() < 9:
697       no_simd_hardware = True
698
699    return {
700      "arch": self.build_config.arch,
701      "asan": self.build_config.asan,
702      "byteorder": sys.byteorder,
703      "cfi_vptr": self.build_config.cfi_vptr,
704      "control_flow_integrity": self.build_config.control_flow_integrity,
705      "concurrent_marking": self.build_config.concurrent_marking,
706      "single_generation": self.build_config.single_generation,
707      "dcheck_always_on": self.build_config.dcheck_always_on,
708      "deopt_fuzzer": False,
709      "endurance_fuzzer": False,
710      "gc_fuzzer": False,
711      "gc_stress": False,
712      "gcov_coverage": self.build_config.gcov_coverage,
713      "has_webassembly": self.build_config.webassembly,
714      "isolates": options.isolates,
715      "is_clang": self.build_config.is_clang,
716      "is_full_debug": self.build_config.is_full_debug,
717      "mips_arch_variant": mips_arch_variant,
718      "mode": self.mode_options.status_mode,
719      "msan": self.build_config.msan,
720      "no_harness": options.no_harness,
721      "no_i18n": self.build_config.no_i18n,
722      "no_simd_hardware": no_simd_hardware,
723      "novfp3": False,
724      "optimize_for_size": "--optimize-for-size" in options.extra_flags,
725      "predictable": self.build_config.predictable,
726      "simd_mips": simd_mips,
727      "simulator_run": self.build_config.simulator_run and
728                       not options.dont_skip_simulator_slow_tests,
729      "system": self.target_os,
730      "third_party_heap": self.build_config.third_party_heap,
731      "tsan": self.build_config.tsan,
732      "ubsan_vptr": self.build_config.ubsan_vptr,
733      "verify_csa": self.build_config.verify_csa,
734      "lite_mode": self.build_config.lite_mode,
735      "pointer_compression": self.build_config.pointer_compression,
736      "pointer_compression_shared_cage": self.build_config.pointer_compression_shared_cage,
737      "no_js_shared_memory": (not self.build_config.shared_ro_heap) or
738                             (self.build_config.pointer_compression and
739                              not self.build_config.pointer_compression_shared_cage),
740      "sandbox": self.build_config.sandbox,
741      "dict_property_const_tracking": self.build_config.dict_property_const_tracking,
742    }
743
744  def _runner_flags(self):
745    """Extra default flags specific to the test runner implementation."""
746    return []
747
748  def _create_test_config(self, options):
749    timeout = options.timeout * self._timeout_scalefactor(options)
750    return TestConfig(
751        command_prefix=options.command_prefix,
752        extra_flags=options.extra_flags,
753        isolates=options.isolates,
754        mode_flags=self.mode_options.flags + self._runner_flags(),
755        no_harness=options.no_harness,
756        noi18n=self.build_config.no_i18n,
757        random_seed=options.random_seed,
758        run_skipped=options.run_skipped,
759        shell_dir=self.outdir,
760        timeout=timeout,
761        verbose=options.verbose,
762        regenerate_expected_files=options.regenerate_expected_files,
763    )
764
765  def _timeout_scalefactor(self, options):
766    """Increases timeout for slow build configurations."""
767    factor = self.mode_options.timeout_scalefactor
768    if self.build_config.arch in SLOW_ARCHS:
769      factor *= 4.5
770    if self.build_config.lite_mode:
771      factor *= 2
772    if self.build_config.predictable:
773      factor *= 4
774    if self.build_config.tsan:
775      factor *= 2
776    if self.build_config.use_sanitizer:
777      factor *= 1.5
778    if self.build_config.is_full_debug:
779      factor *= 4
780
781    return factor
782
783  # TODO(majeski): remove options & args parameters
784  def _do_execute(self, suites, args, options):
785    raise NotImplementedError()
786
787  def _prepare_procs(self, procs):
788    procs = list([_f for _f in procs if _f])
789    for i in range(0, len(procs) - 1):
790      procs[i].connect_to(procs[i + 1])
791    procs[0].setup()
792
793  def _create_shard_proc(self, options):
794    myid, count = self._get_shard_info(options)
795    if count == 1:
796      return None
797    return ShardProc(myid - 1, count)
798
799  def _get_shard_info(self, options):
800    """
801    Returns pair:
802      (id of the current shard [1; number of shards], number of shards)
803    """
804    # Read gtest shard configuration from environment (e.g. set by swarming).
805    # If none is present, use values passed on the command line.
806    shard_count = int(
807      os.environ.get('GTEST_TOTAL_SHARDS', options.shard_count))
808    shard_run = os.environ.get('GTEST_SHARD_INDEX')
809    if shard_run is not None:
810      # The v8 shard_run starts at 1, while GTEST_SHARD_INDEX starts at 0.
811      shard_run = int(shard_run) + 1
812    else:
813      shard_run = options.shard_run
814
815    if options.shard_count > 1:
816      # Log if a value was passed on the cmd line and it differs from the
817      # environment variables.
818      if options.shard_count != shard_count:  # pragma: no cover
819        print("shard_count from cmd line differs from environment variable "
820              "GTEST_TOTAL_SHARDS")
821      if (options.shard_run > 1 and
822          options.shard_run != shard_run):  # pragma: no cover
823        print("shard_run from cmd line differs from environment variable "
824              "GTEST_SHARD_INDEX")
825
826    if shard_run < 1 or shard_run > shard_count:
827      # TODO(machenbach): Turn this into an assert. If that's wrong on the
828      # bots, printing will be quite useless. Or refactor this code to make
829      # sure we get a return code != 0 after testing if we got here.
830      print("shard-run not a valid number, should be in [1:shard-count]")
831      print("defaulting back to running all tests")
832      return 1, 1
833
834    return shard_run, shard_count
835
836  def _create_progress_indicators(self, test_count, options):
837    procs = [PROGRESS_INDICATORS[options.progress]()]
838    if options.junitout:
839      procs.append(progress.JUnitTestProgressIndicator(options.junitout,
840                                                       options.junittestsuite))
841    if options.json_test_results:
842      procs.append(progress.JsonTestProgressIndicator(self.framework_name))
843
844    for proc in procs:
845      proc.configure(options)
846
847    for proc in procs:
848      try:
849        proc.set_test_count(test_count)
850      except AttributeError:
851        pass
852
853    return procs
854
855  def _create_result_tracker(self, options):
856    return progress.ResultsTracker(options.exit_after_n_failures)
857
858  def _create_timeout_proc(self, options):
859    if not options.total_timeout_sec:
860      return None
861    return TimeoutProc(options.total_timeout_sec)
862
863  def _create_signal_proc(self):
864    return SignalProc()
865
866  def _create_rerun_proc(self, options):
867    if not options.rerun_failures_count:
868      return None
869    return RerunProc(options.rerun_failures_count,
870                     options.rerun_failures_max)
871