• 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
5
6from collections import OrderedDict
7import json
8import multiprocessing
9import optparse
10import os
11import shlex
12import sys
13
14
15# Add testrunner to the path.
16sys.path.insert(
17  0,
18  os.path.dirname(
19    os.path.dirname(os.path.abspath(__file__))))
20
21
22from testrunner.local import command
23from testrunner.local import testsuite
24from testrunner.local import utils
25from testrunner.test_config import TestConfig
26from testrunner.testproc import progress
27from testrunner.testproc.rerun import RerunProc
28from testrunner.testproc.shard import ShardProc
29from testrunner.testproc.sigproc import SignalProc
30from testrunner.testproc.timeout import TimeoutProc
31
32
33BASE_DIR = (
34    os.path.dirname(
35      os.path.dirname(
36        os.path.dirname(
37          os.path.abspath(__file__)))))
38
39DEFAULT_OUT_GN = 'out.gn'
40
41# Map of test name synonyms to lists of test suites. Should be ordered by
42# expected runtimes (suites with slow test cases first). These groups are
43# invoked in separate steps on the bots.
44TEST_MAP = {
45  # This needs to stay in sync with test/bot_default.isolate.
46  "bot_default": [
47    "debugger",
48    "mjsunit",
49    "cctest",
50    "wasm-spec-tests",
51    "inspector",
52    "webkit",
53    "mkgrokdump",
54    "fuzzer",
55    "message",
56    "preparser",
57    "intl",
58    "unittests",
59  ],
60  # This needs to stay in sync with test/default.isolate.
61  "default": [
62    "debugger",
63    "mjsunit",
64    "cctest",
65    "wasm-spec-tests",
66    "inspector",
67    "mkgrokdump",
68    "fuzzer",
69    "message",
70    "preparser",
71    "intl",
72    "unittests",
73  ],
74  # This needs to stay in sync with test/d8_default.isolate.
75  "d8_default": [
76    # TODO(machenbach): uncomment after infra side lands.
77    #"debugger",
78    "mjsunit",
79    "webkit",
80    #"message",
81    #"preparser",
82    #"intl",
83  ],
84  # This needs to stay in sync with test/optimize_for_size.isolate.
85  "optimize_for_size": [
86    "debugger",
87    "mjsunit",
88    "cctest",
89    "inspector",
90    "webkit",
91    "intl",
92  ],
93  "unittests": [
94    "unittests",
95  ],
96}
97
98# Double the timeout for these:
99SLOW_ARCHS = ["arm",
100              "mips",
101              "mipsel",
102              "mips64",
103              "mips64el",
104              "s390",
105              "s390x",
106              "arm64"]
107
108
109class ModeConfig(object):
110  def __init__(self, flags, timeout_scalefactor, status_mode, execution_mode):
111    self.flags = flags
112    self.timeout_scalefactor = timeout_scalefactor
113    self.status_mode = status_mode
114    self.execution_mode = execution_mode
115
116
117DEBUG_FLAGS = ["--nohard-abort", "--enable-slow-asserts", "--verify-heap"]
118RELEASE_FLAGS = ["--nohard-abort"]
119MODES = {
120  "debug": ModeConfig(
121    flags=DEBUG_FLAGS,
122    timeout_scalefactor=4,
123    status_mode="debug",
124    execution_mode="debug",
125  ),
126  "optdebug": ModeConfig(
127    flags=DEBUG_FLAGS,
128    timeout_scalefactor=4,
129    status_mode="debug",
130    execution_mode="debug",
131  ),
132  "release": ModeConfig(
133    flags=RELEASE_FLAGS,
134    timeout_scalefactor=1,
135    status_mode="release",
136    execution_mode="release",
137  ),
138  # Normal trybot release configuration. There, dchecks are always on which
139  # implies debug is set. Hence, the status file needs to assume debug-like
140  # behavior/timeouts.
141  "tryrelease": ModeConfig(
142    flags=RELEASE_FLAGS,
143    timeout_scalefactor=1,
144    status_mode="debug",
145    execution_mode="release",
146  ),
147  # This mode requires v8 to be compiled with dchecks and slow dchecks.
148  "slowrelease": ModeConfig(
149    flags=RELEASE_FLAGS + ["--enable-slow-asserts"],
150    timeout_scalefactor=2,
151    status_mode="debug",
152    execution_mode="release",
153  ),
154}
155
156PROGRESS_INDICATORS = {
157  'verbose': progress.VerboseProgressIndicator,
158  'dots': progress.DotsProgressIndicator,
159  'color': progress.ColorProgressIndicator,
160  'mono': progress.MonochromeProgressIndicator,
161}
162
163class TestRunnerError(Exception):
164  pass
165
166
167class BuildConfig(object):
168  def __init__(self, build_config):
169    # In V8 land, GN's x86 is called ia32.
170    if build_config['v8_target_cpu'] == 'x86':
171      self.arch = 'ia32'
172    else:
173      self.arch = build_config['v8_target_cpu']
174
175    self.asan = build_config['is_asan']
176    self.cfi_vptr = build_config['is_cfi']
177    self.dcheck_always_on = build_config['dcheck_always_on']
178    self.gcov_coverage = build_config['is_gcov_coverage']
179    self.is_android = build_config['is_android']
180    self.is_debug = build_config['is_debug']
181    self.msan = build_config['is_msan']
182    self.no_i18n = not build_config['v8_enable_i18n_support']
183    self.no_snap = not build_config['v8_use_snapshot']
184    self.predictable = build_config['v8_enable_verify_predictable']
185    self.tsan = build_config['is_tsan']
186    self.ubsan_vptr = build_config['is_ubsan_vptr']
187    # Export only for MIPS target
188    if self.arch in ['mips', 'mipsel', 'mips64', 'mips64el']:
189      self.mips_arch_variant = build_config['mips_arch_variant']
190      self.mips_use_msa = build_config['mips_use_msa']
191
192  def __str__(self):
193    detected_options = []
194
195    if self.asan:
196      detected_options.append('asan')
197    if self.cfi_vptr:
198      detected_options.append('cfi_vptr')
199    if self.dcheck_always_on:
200      detected_options.append('dcheck_always_on')
201    if self.gcov_coverage:
202      detected_options.append('gcov_coverage')
203    if self.msan:
204      detected_options.append('msan')
205    if self.no_i18n:
206      detected_options.append('no_i18n')
207    if self.no_snap:
208      detected_options.append('no_snap')
209    if self.predictable:
210      detected_options.append('predictable')
211    if self.tsan:
212      detected_options.append('tsan')
213    if self.ubsan_vptr:
214      detected_options.append('ubsan_vptr')
215
216    return '\n'.join(detected_options)
217
218
219class BaseTestRunner(object):
220  def __init__(self, basedir=None):
221    self.basedir = basedir or BASE_DIR
222    self.outdir = None
223    self.build_config = None
224    self.mode_name = None
225    self.mode_options = None
226    self.target_os = None
227
228  def execute(self, sys_args=None):
229    if sys_args is None:  # pragma: no cover
230      sys_args = sys.argv[1:]
231    try:
232      parser = self._create_parser()
233      options, args = self._parse_args(parser, sys_args)
234      if options.swarming:
235        # Swarming doesn't print how isolated commands are called. Lets make
236        # this less cryptic by printing it ourselves.
237        print ' '.join(sys.argv)
238
239      self._load_build_config(options)
240      command.setup(self.target_os)
241
242      try:
243        self._process_default_options(options)
244        self._process_options(options)
245      except TestRunnerError:
246        parser.print_help()
247        raise
248
249      args = self._parse_test_args(args)
250      suites = self._get_suites(args, options)
251      self._prepare_suites(suites, options)
252
253      self._setup_env()
254
255      print(">>> Running tests for %s.%s" % (self.build_config.arch,
256                                            self.mode_name))
257      tests = [t for s in suites for t in s.tests]
258      return self._do_execute(tests, args, options)
259    except TestRunnerError:
260      return utils.EXIT_CODE_INTERNAL_ERROR
261    except KeyboardInterrupt:
262      return utils.EXIT_CODE_INTERRUPTED
263    finally:
264      command.tear_down()
265
266  def _create_parser(self):
267    parser = optparse.OptionParser()
268    parser.usage = '%prog [options] [tests]'
269    parser.description = """TESTS: %s""" % (TEST_MAP["default"])
270    self._add_parser_default_options(parser)
271    self._add_parser_options(parser)
272    return parser
273
274  def _add_parser_default_options(self, parser):
275    parser.add_option("--gn", help="Scan out.gn for the last built"
276                      " configuration",
277                      default=False, action="store_true")
278    parser.add_option("--outdir", help="Base directory with compile output",
279                      default="out")
280    parser.add_option("--buildbot", help="DEPRECATED!",
281                      default=False, action="store_true")
282    parser.add_option("--arch",
283                      help="The architecture to run tests for")
284    parser.add_option("-m", "--mode",
285                      help="The test mode in which to run (uppercase for ninja"
286                      " and buildbot builds): %s" % MODES.keys())
287    parser.add_option("--shell-dir", help="DEPRECATED! Executables from build "
288                      "directory will be used")
289    parser.add_option("--test-root", help="Root directory of the test suites",
290                      default=os.path.join(self.basedir, 'test'))
291    parser.add_option("--total-timeout-sec", default=0, type="int",
292                      help="How long should fuzzer run")
293    parser.add_option("--swarming", default=False, action="store_true",
294                      help="Indicates running test driver on swarming.")
295
296    parser.add_option("-j", help="The number of parallel tasks to run",
297                      default=0, type=int)
298
299    # Shard
300    parser.add_option("--shard-count", default=1, type=int,
301                      help="Split tests into this number of shards")
302    parser.add_option("--shard-run", default=1, type=int,
303                      help="Run this shard from the split up tests.")
304
305    # Progress
306    parser.add_option("-p", "--progress",
307                      choices=PROGRESS_INDICATORS.keys(), default="mono",
308                      help="The style of progress indicator (verbose, dots, "
309                           "color, mono)")
310    parser.add_option("--json-test-results",
311                      help="Path to a file for storing json results.")
312    parser.add_option("--junitout", help="File name of the JUnit output")
313    parser.add_option("--junittestsuite", default="v8tests",
314                      help="The testsuite name in the JUnit output file")
315
316    # Rerun
317    parser.add_option("--rerun-failures-count", default=0, type=int,
318                      help="Number of times to rerun each failing test case. "
319                           "Very slow tests will be rerun only once.")
320    parser.add_option("--rerun-failures-max", default=100, type=int,
321                      help="Maximum number of failing test cases to rerun")
322
323    # Test config
324    parser.add_option("--command-prefix", default="",
325                      help="Prepended to each shell command used to run a test")
326    parser.add_option("--extra-flags", action="append", default=[],
327                      help="Additional flags to pass to each test command")
328    parser.add_option("--isolates", action="store_true", default=False,
329                      help="Whether to test isolates")
330    parser.add_option("--no-harness", "--noharness",
331                      default=False, action="store_true",
332                      help="Run without test harness of a given suite")
333    parser.add_option("--random-seed", default=0, type=int,
334                      help="Default seed for initializing random generator")
335    parser.add_option("-t", "--timeout", default=60, type=int,
336                      help="Timeout for single test in seconds")
337    parser.add_option("-v", "--verbose", default=False, action="store_true",
338                      help="Verbose output")
339
340    # TODO(machenbach): Temporary options for rolling out new test runner
341    # features.
342    parser.add_option("--mastername", default='',
343                      help="Mastername property from infrastructure. Not "
344                           "setting this option indicates manual usage.")
345    parser.add_option("--buildername", default='',
346                      help="Buildername property from infrastructure. Not "
347                           "setting this option indicates manual usage.")
348
349  def _add_parser_options(self, parser):
350    pass
351
352  def _parse_args(self, parser, sys_args):
353    options, args = parser.parse_args(sys_args)
354
355    if any(map(lambda v: v and ',' in v,
356                [options.arch, options.mode])):  # pragma: no cover
357      print 'Multiple arch/mode are deprecated'
358      raise TestRunnerError()
359
360    return options, args
361
362  def _load_build_config(self, options):
363    for outdir in self._possible_outdirs(options):
364      try:
365        self.build_config = self._do_load_build_config(outdir, options.verbose)
366      except TestRunnerError:
367        pass
368
369    if not self.build_config:  # pragma: no cover
370      print 'Failed to load build config'
371      raise TestRunnerError
372
373    print 'Build found: %s' % self.outdir
374    if str(self.build_config):
375      print '>>> Autodetected:'
376      print self.build_config
377
378    # Represents the OS where tests are run on. Same as host OS except for
379    # Android, which is determined by build output.
380    if self.build_config.is_android:
381      self.target_os = 'android'
382    else:
383      self.target_os = utils.GuessOS()
384
385  # Returns possible build paths in order:
386  # gn
387  # outdir
388  # outdir/arch.mode
389  # Each path is provided in two versions: <path> and <path>/mode for buildbot.
390  def _possible_outdirs(self, options):
391    def outdirs():
392      if options.gn:
393        yield self._get_gn_outdir()
394        return
395
396      yield options.outdir
397      if options.arch and options.mode:
398        yield os.path.join(options.outdir,
399                          '%s.%s' % (options.arch, options.mode))
400
401    for outdir in outdirs():
402      yield os.path.join(self.basedir, outdir)
403
404      # buildbot option
405      if options.mode:
406        yield os.path.join(self.basedir, outdir, options.mode)
407
408  def _get_gn_outdir(self):
409    gn_out_dir = os.path.join(self.basedir, DEFAULT_OUT_GN)
410    latest_timestamp = -1
411    latest_config = None
412    for gn_config in os.listdir(gn_out_dir):
413      gn_config_dir = os.path.join(gn_out_dir, gn_config)
414      if not os.path.isdir(gn_config_dir):
415        continue
416      if os.path.getmtime(gn_config_dir) > latest_timestamp:
417        latest_timestamp = os.path.getmtime(gn_config_dir)
418        latest_config = gn_config
419    if latest_config:
420      print(">>> Latest GN build found: %s" % latest_config)
421      return os.path.join(DEFAULT_OUT_GN, latest_config)
422
423  def _do_load_build_config(self, outdir, verbose=False):
424    build_config_path = os.path.join(outdir, "v8_build_config.json")
425    if not os.path.exists(build_config_path):
426      if verbose:
427        print("Didn't find build config: %s" % build_config_path)
428      raise TestRunnerError()
429
430    with open(build_config_path) as f:
431      try:
432        build_config_json = json.load(f)
433      except Exception:  # pragma: no cover
434        print("%s exists but contains invalid json. Is your build up-to-date?"
435              % build_config_path)
436        raise TestRunnerError()
437
438    # In auto-detect mode the outdir is always where we found the build config.
439    # This ensures that we'll also take the build products from there.
440    self.outdir = os.path.dirname(build_config_path)
441
442    return BuildConfig(build_config_json)
443
444  def _process_default_options(self, options):
445    # We don't use the mode for more path-magic.
446    # Therefore transform the buildbot mode here to fix build_config value.
447    if options.mode:
448      options.mode = self._buildbot_to_v8_mode(options.mode)
449
450    build_config_mode = 'debug' if self.build_config.is_debug else 'release'
451    if options.mode:
452      if options.mode not in MODES:  # pragma: no cover
453        print '%s mode is invalid' % options.mode
454        raise TestRunnerError()
455      if MODES[options.mode].execution_mode != build_config_mode:
456        print ('execution mode (%s) for %s is inconsistent with build config '
457               '(%s)' % (
458            MODES[options.mode].execution_mode,
459            options.mode,
460            build_config_mode))
461        raise TestRunnerError()
462
463      self.mode_name = options.mode
464    else:
465      self.mode_name = build_config_mode
466
467    self.mode_options = MODES[self.mode_name]
468
469    if options.arch and options.arch != self.build_config.arch:
470      print('--arch value (%s) inconsistent with build config (%s).' % (
471        options.arch, self.build_config.arch))
472      raise TestRunnerError()
473
474    if options.shell_dir:  # pragma: no cover
475      print('Warning: --shell-dir is deprecated. Searching for executables in '
476            'build directory (%s) instead.' % self.outdir)
477
478    if options.j == 0:
479      if self.build_config.is_android:
480        # Adb isn't happy about multi-processed file pushing.
481        options.j = 1
482      else:
483        options.j = multiprocessing.cpu_count()
484
485    options.command_prefix = shlex.split(options.command_prefix)
486    options.extra_flags = sum(map(shlex.split, options.extra_flags), [])
487
488  def _buildbot_to_v8_mode(self, config):
489    """Convert buildbot build configs to configs understood by the v8 runner.
490
491    V8 configs are always lower case and without the additional _x64 suffix
492    for 64 bit builds on windows with ninja.
493    """
494    mode = config[:-4] if config.endswith('_x64') else config
495    return mode.lower()
496
497  def _process_options(self, options):
498    pass
499
500  def _setup_env(self):
501    # Use the v8 root as cwd as some test cases use "load" with relative paths.
502    os.chdir(self.basedir)
503
504    # Many tests assume an English interface.
505    os.environ['LANG'] = 'en_US.UTF-8'
506
507    symbolizer_option = self._get_external_symbolizer_option()
508
509    if self.build_config.asan:
510      asan_options = [
511          symbolizer_option,
512          'allow_user_segv_handler=1',
513          'allocator_may_return_null=1',
514      ]
515      if not utils.GuessOS() in ['macos', 'windows']:
516        # LSAN is not available on mac and windows.
517        asan_options.append('detect_leaks=1')
518      else:
519        asan_options.append('detect_leaks=0')
520      os.environ['ASAN_OPTIONS'] = ":".join(asan_options)
521
522    if self.build_config.cfi_vptr:
523      os.environ['UBSAN_OPTIONS'] = ":".join([
524        'print_stacktrace=1',
525        'print_summary=1',
526        'symbolize=1',
527        symbolizer_option,
528      ])
529
530    if self.build_config.ubsan_vptr:
531      os.environ['UBSAN_OPTIONS'] = ":".join([
532        'print_stacktrace=1',
533        symbolizer_option,
534      ])
535
536    if self.build_config.msan:
537      os.environ['MSAN_OPTIONS'] = symbolizer_option
538
539    if self.build_config.tsan:
540      suppressions_file = os.path.join(
541          self.basedir,
542          'tools',
543          'sanitizers',
544          'tsan_suppressions.txt')
545      os.environ['TSAN_OPTIONS'] = " ".join([
546        symbolizer_option,
547        'suppressions=%s' % suppressions_file,
548        'exit_code=0',
549        'report_thread_leaks=0',
550        'history_size=7',
551        'report_destroy_locked=0',
552      ])
553
554  def _get_external_symbolizer_option(self):
555    external_symbolizer_path = os.path.join(
556        self.basedir,
557        'third_party',
558        'llvm-build',
559        'Release+Asserts',
560        'bin',
561        'llvm-symbolizer',
562    )
563
564    if utils.IsWindows():
565      # Quote, because sanitizers might confuse colon as option separator.
566      external_symbolizer_path = '"%s.exe"' % external_symbolizer_path
567
568    return 'external_symbolizer_path=%s' % external_symbolizer_path
569
570  def _parse_test_args(self, args):
571    if not args:
572      args = self._get_default_suite_names()
573
574    # Expand arguments with grouped tests. The args should reflect the list
575    # of suites as otherwise filters would break.
576    def expand_test_group(name):
577      return TEST_MAP.get(name, [name])
578
579    return reduce(list.__add__, map(expand_test_group, args), [])
580
581  def _get_suites(self, args, options):
582    names = self._args_to_suite_names(args, options.test_root)
583    return self._load_suites(names, options)
584
585  def _args_to_suite_names(self, args, test_root):
586    # Use default tests if no test configuration was provided at the cmd line.
587    all_names = set(utils.GetSuitePaths(test_root))
588    args_names = OrderedDict([(arg.split('/')[0], None) for arg in args]) # set
589    return [name for name in args_names if name in all_names]
590
591  def _get_default_suite_names(self):
592    return []
593
594  def _load_suites(self, names, options):
595    test_config = self._create_test_config(options)
596    def load_suite(name):
597      if options.verbose:
598        print '>>> Loading test suite: %s' % name
599      return testsuite.TestSuite.LoadTestSuite(
600          os.path.join(options.test_root, name),
601          test_config)
602    return map(load_suite, names)
603
604  def _prepare_suites(self, suites, options):
605    self._load_status_files(suites, options)
606    for s in suites:
607      s.ReadTestCases()
608
609  def _load_status_files(self, suites, options):
610    # simd_mips is true if SIMD is fully supported on MIPS
611    variables = self._get_statusfile_variables(options)
612    for s in suites:
613      s.ReadStatusFile(variables)
614
615  def _get_statusfile_variables(self, options):
616    simd_mips = (
617      self.build_config.arch in ['mipsel', 'mips', 'mips64', 'mips64el'] and
618      self.build_config.mips_arch_variant == "r6" and
619      self.build_config.mips_use_msa)
620
621    mips_arch_variant = (
622      self.build_config.arch in ['mipsel', 'mips', 'mips64', 'mips64el'] and
623      self.build_config.mips_arch_variant)
624
625    # TODO(all): Combine "simulator" and "simulator_run".
626    # TODO(machenbach): In GN we can derive simulator run from
627    # target_arch != v8_target_arch in the dumped build config.
628    return {
629      "arch": self.build_config.arch,
630      "asan": self.build_config.asan,
631      "byteorder": sys.byteorder,
632      "dcheck_always_on": self.build_config.dcheck_always_on,
633      "deopt_fuzzer": False,
634      "endurance_fuzzer": False,
635      "gc_fuzzer": False,
636      "gc_stress": False,
637      "gcov_coverage": self.build_config.gcov_coverage,
638      "isolates": options.isolates,
639      "mips_arch_variant": mips_arch_variant,
640      "mode": self.mode_options.status_mode,
641      "msan": self.build_config.msan,
642      "no_harness": options.no_harness,
643      "no_i18n": self.build_config.no_i18n,
644      "no_snap": self.build_config.no_snap,
645      "novfp3": False,
646      "predictable": self.build_config.predictable,
647      "simd_mips": simd_mips,
648      "simulator": utils.UseSimulator(self.build_config.arch),
649      "simulator_run": False,
650      "system": self.target_os,
651      "tsan": self.build_config.tsan,
652      "ubsan_vptr": self.build_config.ubsan_vptr,
653    }
654
655  def _create_test_config(self, options):
656    timeout = options.timeout * self._timeout_scalefactor(options)
657    return TestConfig(
658        command_prefix=options.command_prefix,
659        extra_flags=options.extra_flags,
660        isolates=options.isolates,
661        mode_flags=self.mode_options.flags,
662        no_harness=options.no_harness,
663        noi18n=self.build_config.no_i18n,
664        random_seed=options.random_seed,
665        shell_dir=self.outdir,
666        timeout=timeout,
667        verbose=options.verbose,
668    )
669
670  def _timeout_scalefactor(self, options):
671    factor = self.mode_options.timeout_scalefactor
672
673    # Simulators are slow, therefore allow a longer timeout.
674    if self.build_config.arch in SLOW_ARCHS:
675      factor *= 2
676
677    # Predictable mode is slower.
678    if self.build_config.predictable:
679      factor *= 2
680
681    return factor
682
683  # TODO(majeski): remove options & args parameters
684  def _do_execute(self, suites, args, options):
685    raise NotImplementedError()
686
687  def _prepare_procs(self, procs):
688    procs = filter(None, procs)
689    for i in xrange(0, len(procs) - 1):
690      procs[i].connect_to(procs[i + 1])
691    procs[0].setup()
692
693  def _create_shard_proc(self, options):
694    myid, count = self._get_shard_info(options)
695    if count == 1:
696      return None
697    return ShardProc(myid - 1, count)
698
699  def _get_shard_info(self, options):
700    """
701    Returns pair:
702      (id of the current shard [1; number of shards], number of shards)
703    """
704    # Read gtest shard configuration from environment (e.g. set by swarming).
705    # If none is present, use values passed on the command line.
706    shard_count = int(
707      os.environ.get('GTEST_TOTAL_SHARDS', options.shard_count))
708    shard_run = os.environ.get('GTEST_SHARD_INDEX')
709    if shard_run is not None:
710      # The v8 shard_run starts at 1, while GTEST_SHARD_INDEX starts at 0.
711      shard_run = int(shard_run) + 1
712    else:
713      shard_run = options.shard_run
714
715    if options.shard_count > 1:
716      # Log if a value was passed on the cmd line and it differs from the
717      # environment variables.
718      if options.shard_count != shard_count:  # pragma: no cover
719        print("shard_count from cmd line differs from environment variable "
720              "GTEST_TOTAL_SHARDS")
721      if (options.shard_run > 1 and
722          options.shard_run != shard_run):  # pragma: no cover
723        print("shard_run from cmd line differs from environment variable "
724              "GTEST_SHARD_INDEX")
725
726    if shard_run < 1 or shard_run > shard_count:
727      # TODO(machenbach): Turn this into an assert. If that's wrong on the
728      # bots, printing will be quite useless. Or refactor this code to make
729      # sure we get a return code != 0 after testing if we got here.
730      print "shard-run not a valid number, should be in [1:shard-count]"
731      print "defaulting back to running all tests"
732      return 1, 1
733
734    return shard_run, shard_count
735
736  def _create_progress_indicators(self, options):
737    procs = [PROGRESS_INDICATORS[options.progress]()]
738    if options.junitout:
739      procs.append(progress.JUnitTestProgressIndicator(options.junitout,
740                                                       options.junittestsuite))
741    if options.json_test_results:
742      procs.append(progress.JsonTestProgressIndicator(
743        options.json_test_results,
744        self.build_config.arch,
745        self.mode_options.execution_mode))
746    return procs
747
748  def _create_timeout_proc(self, options):
749    if not options.total_timeout_sec:
750      return None
751    return TimeoutProc(options.total_timeout_sec)
752
753  def _create_signal_proc(self):
754    return SignalProc()
755
756  def _create_rerun_proc(self, options):
757    if not options.rerun_failures_count:
758      return None
759    return RerunProc(options.rerun_failures_count,
760                     options.rerun_failures_max)
761