| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575 | #!/usr/bin/env python# Copyright 2015, Google Inc.# All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions are# met:##     * Redistributions of source code must retain the above copyright# notice, this list of conditions and the following disclaimer.#     * Redistributions in binary form must reproduce the above# copyright notice, this list of conditions and the following disclaimer# in the documentation and/or other materials provided with the# distribution.#     * Neither the name of Google Inc. nor the names of its# contributors may be used to endorse or promote products derived from# this software without specific prior written permission.## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."""Run tests in parallel."""from __future__ import print_functionimport argparseimport astimport collectionsimport globimport itertoolsimport jsonimport loggingimport multiprocessingimport osimport os.pathimport pipesimport platformimport randomimport reimport socketimport subprocessimport sysimport tempfileimport tracebackimport timefrom six.moves import urllibimport uuidimport siximport python_utils.jobset as jobsetimport python_utils.report_utils as report_utilsimport python_utils.watch_dirs as watch_dirsimport python_utils.start_port_server as start_port_servertry:  from python_utils.upload_test_results import upload_results_to_bqexcept (ImportError):  pass # It's ok to not import because this is only necessary to upload results to BQ._ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), '../..'))os.chdir(_ROOT)_FORCE_ENVIRON_FOR_WRAPPERS = {  'GRPC_VERBOSITY': 'DEBUG',}_POLLING_STRATEGIES = {  'linux': ['epollex', 'epollsig', 'poll', 'poll-cv']# TODO(ctiller, sreecha): enable epoll1, epollex, epoll-thread-pool}def platform_string():  return jobset.platform_string()_DEFAULT_TIMEOUT_SECONDS = 5 * 60def run_shell_command(cmd, env=None, cwd=None):  try:    subprocess.check_output(cmd, shell=True, env=env, cwd=cwd)  except subprocess.CalledProcessError as e:    logging.exception("Error while running command '%s'. Exit status %d. Output:\n%s",                       e.cmd, e.returncode, e.output)    raise# SimpleConfig: just compile with CONFIG=config, and run the binary to testclass Config(object):  def __init__(self, config, environ=None, timeout_multiplier=1, tool_prefix=[], iomgr_platform='native'):    if environ is None:      environ = {}    self.build_config = config    self.environ = environ    self.environ['CONFIG'] = config    self.tool_prefix = tool_prefix    self.timeout_multiplier = timeout_multiplier    self.iomgr_platform = iomgr_platform  def job_spec(self, cmdline, timeout_seconds=_DEFAULT_TIMEOUT_SECONDS,               shortname=None, environ={}, cpu_cost=1.0, flaky=False):    """Construct a jobset.JobSpec for a test under this config       Args:         cmdline:      a list of strings specifying the command line the test                       would like to run    """    actual_environ = self.environ.copy()    for k, v in environ.items():      actual_environ[k] = v    return jobset.JobSpec(cmdline=self.tool_prefix + cmdline,                          shortname=shortname,                          environ=actual_environ,                          cpu_cost=cpu_cost,                          timeout_seconds=(self.timeout_multiplier * timeout_seconds if timeout_seconds else None),                          flake_retries=5 if flaky or args.allow_flakes else 0,                          timeout_retries=3 if args.allow_flakes else 0)def get_c_tests(travis, test_lang) :  out = []  platforms_str = 'ci_platforms' if travis else 'platforms'  with open('tools/run_tests/generated/tests.json') as f:    js = json.load(f)    return [tgt            for tgt in js            if tgt['language'] == test_lang and                platform_string() in tgt[platforms_str] and                not (travis and tgt['flaky'])]def _check_compiler(compiler, supported_compilers):  if compiler not in supported_compilers:    raise Exception('Compiler %s not supported (on this platform).' % compiler)def _check_arch(arch, supported_archs):  if arch not in supported_archs:    raise Exception('Architecture %s not supported.' % arch)def _is_use_docker_child():  """Returns True if running running as a --use_docker child."""  return True if os.getenv('RUN_TESTS_COMMAND') else False_PythonConfigVars = collections.namedtuple(  '_ConfigVars', ['shell', 'builder', 'builder_prefix_arguments',                  'venv_relative_python', 'toolchain', 'runner'])def _python_config_generator(name, major, minor, bits, config_vars):  return PythonConfig(    name,    config_vars.shell + config_vars.builder + config_vars.builder_prefix_arguments + [      _python_pattern_function(major=major, minor=minor, bits=bits)] + [      name] + config_vars.venv_relative_python + config_vars.toolchain,    config_vars.shell + config_vars.runner + [      os.path.join(name, config_vars.venv_relative_python[0])])def _pypy_config_generator(name, major, config_vars):  return PythonConfig(    name,    config_vars.shell + config_vars.builder + config_vars.builder_prefix_arguments + [      _pypy_pattern_function(major=major)] + [      name] + config_vars.venv_relative_python + config_vars.toolchain,    config_vars.shell + config_vars.runner + [      os.path.join(name, config_vars.venv_relative_python[0])])def _python_pattern_function(major, minor, bits):  # Bit-ness is handled by the test machine's environment  if os.name == "nt":    if bits == "64":      return '/c/Python{major}{minor}/python.exe'.format(        major=major, minor=minor, bits=bits)    else:      return '/c/Python{major}{minor}_{bits}bits/python.exe'.format(        major=major, minor=minor, bits=bits)  else:    return 'python{major}.{minor}'.format(major=major, minor=minor)def _pypy_pattern_function(major):  if major == '2':    return 'pypy'  elif major == '3':    return 'pypy3'  else:    raise ValueError("Unknown PyPy major version")class CLanguage(object):  def __init__(self, make_target, test_lang):    self.make_target = make_target    self.platform = platform_string()    self.test_lang = test_lang  def configure(self, config, args):    self.config = config    self.args = args    if self.args.compiler == 'cmake':      _check_arch(self.args.arch, ['default'])      self._use_cmake = True      self._docker_distro = 'jessie'      self._make_options = []    elif self.platform == 'windows':      self._use_cmake = False      self._make_options = [_windows_toolset_option(self.args.compiler),                            _windows_arch_option(self.args.arch)]    else:      self._use_cmake = False      self._docker_distro, self._make_options = self._compiler_options(self.args.use_docker,                                                                       self.args.compiler)    if args.iomgr_platform == "uv":      cflags = '-DGRPC_UV '      try:        cflags += subprocess.check_output(['pkg-config', '--cflags', 'libuv']).strip() + ' '      except (subprocess.CalledProcessError, OSError):        pass      try:        ldflags = subprocess.check_output(['pkg-config', '--libs', 'libuv']).strip() + ' '      except (subprocess.CalledProcessError, OSError):        ldflags = '-luv '      self._make_options += ['EXTRA_CPPFLAGS={}'.format(cflags),                             'EXTRA_LDLIBS={}'.format(ldflags)]  def test_specs(self):    out = []    binaries = get_c_tests(self.args.travis, self.test_lang)    for target in binaries:      if self._use_cmake and target.get('boringssl', False):        # cmake doesn't build boringssl tests        continue      polling_strategies = (_POLLING_STRATEGIES.get(self.platform, ['all'])                            if target.get('uses_polling', True)                            else ['all'])      if self.args.iomgr_platform == 'uv':        polling_strategies = ['all']      for polling_strategy in polling_strategies:        env={'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':                 _ROOT + '/src/core/tsi/test_creds/ca.pem',             'GRPC_POLL_STRATEGY': polling_strategy,             'GRPC_VERBOSITY': 'DEBUG'}        resolver = os.environ.get('GRPC_DNS_RESOLVER', None);        if resolver:          env['GRPC_DNS_RESOLVER'] = resolver        shortname_ext = '' if polling_strategy=='all' else ' GRPC_POLL_STRATEGY=%s' % polling_strategy        timeout_scaling = 1        if polling_strategy == 'poll-cv':          timeout_scaling *= 5        if polling_strategy in target.get('excluded_poll_engines', []):          continue        # Scale overall test timeout if running under various sanitizers.        config = self.args.config        if ('asan' in config            or config == 'msan'            or config == 'tsan'            or config == 'ubsan'            or config == 'helgrind'            or config == 'memcheck'):          timeout_scaling *= 20        if self.config.build_config in target['exclude_configs']:          continue        if self.args.iomgr_platform in target.get('exclude_iomgrs', []):          continue        if self.platform == 'windows':          if self._use_cmake:            binary = 'cmake/build/%s/%s.exe' % (_MSBUILD_CONFIG[self.config.build_config], target['name'])          else:            binary = 'vsprojects/%s%s/%s.exe' % (                'x64/' if self.args.arch == 'x64' else '',                _MSBUILD_CONFIG[self.config.build_config],                target['name'])        else:          if self._use_cmake:            binary = 'cmake/build/%s' % target['name']          else:            binary = 'bins/%s/%s' % (self.config.build_config, target['name'])        cpu_cost = target['cpu_cost']        if cpu_cost == 'capacity':          cpu_cost = multiprocessing.cpu_count()        if os.path.isfile(binary):          if 'gtest' in target and target['gtest']:            # here we parse the output of --gtest_list_tests to build up a            # complete list of the tests contained in a binary            # for each test, we then add a job to run, filtering for just that            # test            with open(os.devnull, 'w') as fnull:              tests = subprocess.check_output([binary, '--gtest_list_tests'],                                              stderr=fnull)            base = None            for line in tests.split('\n'):              i = line.find('#')              if i >= 0: line = line[:i]              if not line: continue              if line[0] != ' ':                base = line.strip()              else:                assert base is not None                assert line[1] == ' '                test = base + line.strip()                cmdline = [binary, '--gtest_filter=%s' % test] + target['args']                out.append(self.config.job_spec(cmdline,                                                shortname='%s %s' % (' '.join(cmdline), shortname_ext),                                                cpu_cost=cpu_cost,                                                timeout_seconds=_DEFAULT_TIMEOUT_SECONDS * timeout_scaling,                                                environ=env))          else:            cmdline = [binary] + target['args']            out.append(self.config.job_spec(cmdline,                                            shortname=' '.join(                                                          pipes.quote(arg)                                                          for arg in cmdline) +                                                      shortname_ext,                                            cpu_cost=cpu_cost,                                            flaky=target.get('flaky', False),                                            timeout_seconds=target.get('timeout_seconds', _DEFAULT_TIMEOUT_SECONDS) * timeout_scaling,                                            environ=env))        elif self.args.regex == '.*' or self.platform == 'windows':          print('\nWARNING: binary not found, skipping', binary)    return sorted(out)  def make_targets(self):    if self.platform == 'windows':      # don't build tools on windows just yet      return ['buildtests_%s' % self.make_target]    return ['buildtests_%s' % self.make_target, 'tools_%s' % self.make_target]  def make_options(self):    return self._make_options;  def pre_build_steps(self):    if self._use_cmake:      if self.platform == 'windows':        return [['tools\\run_tests\\helper_scripts\\pre_build_cmake.bat']]      else:        return [['tools/run_tests/helper_scripts/pre_build_cmake.sh']]    else:      if self.platform == 'windows':        return [['tools\\run_tests\\helper_scripts\\pre_build_c.bat']]      else:        return []  def build_steps(self):    return []  def post_tests_steps(self):    if self.platform == 'windows':      return []    else:      return [['tools/run_tests/helper_scripts/post_tests_c.sh']]  def makefile_name(self):    if self._use_cmake:      return 'cmake/build/Makefile'    else:      return 'Makefile'  def _clang_make_options(self, version_suffix=''):    return ['CC=clang%s' % version_suffix,            'CXX=clang++%s' % version_suffix,            'LD=clang%s' % version_suffix,            'LDXX=clang++%s' % version_suffix]  def _gcc_make_options(self, version_suffix):    return ['CC=gcc%s' % version_suffix,            'CXX=g++%s' % version_suffix,            'LD=gcc%s' % version_suffix,            'LDXX=g++%s' % version_suffix]  def _compiler_options(self, use_docker, compiler):    """Returns docker distro and make options to use for given compiler."""    if not use_docker and not _is_use_docker_child():      _check_compiler(compiler, ['default'])    if compiler == 'gcc4.9' or compiler == 'default':      return ('jessie', [])    elif compiler == 'gcc4.8':      return ('jessie', self._gcc_make_options(version_suffix='-4.8'))    elif compiler == 'gcc5.3':      return ('ubuntu1604', [])    elif compiler == 'gcc_musl':      return ('alpine', [])    elif compiler == 'clang3.4':      # on ubuntu1404, clang-3.4 alias doesn't exist, just use 'clang'      return ('ubuntu1404', self._clang_make_options())    elif compiler == 'clang3.5':      return ('jessie', self._clang_make_options(version_suffix='-3.5'))    elif compiler == 'clang3.6':      return ('ubuntu1604', self._clang_make_options(version_suffix='-3.6'))    elif compiler == 'clang3.7':      return ('ubuntu1604', self._clang_make_options(version_suffix='-3.7'))    else:      raise Exception('Compiler %s not supported.' % compiler)  def dockerfile_dir(self):    return 'tools/dockerfile/test/cxx_%s_%s' % (self._docker_distro,                                                _docker_arch_suffix(self.args.arch))  def __str__(self):    return self.make_targetclass NodeLanguage(object):  def __init__(self):    self.platform = platform_string()  def configure(self, config, args):    self.config = config    self.args = args    # Note: electron ABI only depends on major and minor version, so that's all    # we should specify in the compiler argument    _check_compiler(self.args.compiler, ['default', 'node0.12',                                         'node4', 'node5', 'node6',                                         'node7', 'electron1.3', 'electron1.6'])    if self.args.compiler == 'default':      self.runtime = 'node'      self.node_version = '7'    else:      if self.args.compiler.startswith('electron'):        self.runtime = 'electron'        self.node_version = self.args.compiler[8:]      else:        self.runtime = 'node'        # Take off the word "node"        self.node_version = self.args.compiler[4:]  def test_specs(self):    if self.platform == 'windows':      return [self.config.job_spec(['tools\\run_tests\\helper_scripts\\run_node.bat'])]    else:      run_script = 'run_node'      if self.runtime == 'electron':        run_script += '_electron'      return [self.config.job_spec(['tools/run_tests/helper_scripts/{}.sh'.format(run_script),                                    self.node_version],                                   None,                                   environ=_FORCE_ENVIRON_FOR_WRAPPERS)]  def pre_build_steps(self):    if self.platform == 'windows':      return [['tools\\run_tests\\helper_scripts\\pre_build_node.bat']]    else:      build_script = 'pre_build_node'      if self.runtime == 'electron':        build_script += '_electron'      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),               self.node_version]]  def make_targets(self):    return []  def make_options(self):    return []  def build_steps(self):    if self.platform == 'windows':      if self.config == 'dbg':        config_flag = '--debug'      else:        config_flag = '--release'      return [['tools\\run_tests\\helper_scripts\\build_node.bat',               config_flag]]    else:      build_script = 'build_node'      if self.runtime == 'electron':        build_script += '_electron'        # building for electron requires a patch version        self.node_version += '.0'      return [['tools/run_tests/helper_scripts/{}.sh'.format(build_script),               self.node_version]]  def post_tests_steps(self):    return []  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/node_jessie_%s' % _docker_arch_suffix(self.args.arch)  def __str__(self):    return 'node'class PhpLanguage(object):  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default'])  def test_specs(self):    return [self.config.job_spec(['src/php/bin/run_tests.sh'],                                  environ=_FORCE_ENVIRON_FOR_WRAPPERS)]  def pre_build_steps(self):    return []  def make_targets(self):    return ['static_c', 'shared_c']  def make_options(self):    return []  def build_steps(self):    return [['tools/run_tests/helper_scripts/build_php.sh']]  def post_tests_steps(self):    return [['tools/run_tests/helper_scripts/post_tests_php.sh']]  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/php_jessie_%s' % _docker_arch_suffix(self.args.arch)  def __str__(self):    return 'php'class Php7Language(object):  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default'])  def test_specs(self):    return [self.config.job_spec(['src/php/bin/run_tests.sh'],                                  environ=_FORCE_ENVIRON_FOR_WRAPPERS)]  def pre_build_steps(self):    return []  def make_targets(self):    return ['static_c', 'shared_c']  def make_options(self):    return []  def build_steps(self):    return [['tools/run_tests/helper_scripts/build_php.sh']]  def post_tests_steps(self):    return [['tools/run_tests/helper_scripts/post_tests_php.sh']]  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/php7_jessie_%s' % _docker_arch_suffix(self.args.arch)  def __str__(self):    return 'php7'class PythonConfig(collections.namedtuple('PythonConfig', [    'name', 'build', 'run'])):  """Tuple of commands (named s.t. 'what it says on the tin' applies)"""class PythonLanguage(object):  def configure(self, config, args):    self.config = config    self.args = args    self.pythons = self._get_pythons(self.args)  def test_specs(self):    # load list of known test suites    with open('src/python/grpcio_tests/tests/tests.json') as tests_json_file:      tests_json = json.load(tests_json_file)    environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)    return [self.config.job_spec(        config.run,        timeout_seconds=5*60,        environ=dict(list(environment.items()) +                     [('GRPC_PYTHON_TESTRUNNER_FILTER', str(suite_name))]),        shortname='%s.test.%s' % (config.name, suite_name),)        for suite_name in tests_json        for config in self.pythons]  def pre_build_steps(self):    return []  def make_targets(self):    return []  def make_options(self):    return []  def build_steps(self):    return [config.build for config in self.pythons]  def post_tests_steps(self):    if self.config != 'gcov':      return []    else:      return [['tools/run_tests/helper_scripts/post_tests_python.sh']]  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/python_%s_%s' % (self.python_manager_name(), _docker_arch_suffix(self.args.arch))  def python_manager_name(self):    if self.args.compiler in ['python3.5', 'python3.6']:      return 'pyenv'    elif self.args.compiler == 'python_alpine':      return 'alpine'    else:      return 'jessie'  def _get_pythons(self, args):    if args.arch == 'x86':      bits = '32'    else:      bits = '64'    if os.name == 'nt':      shell = ['bash']      builder = [os.path.abspath('tools/run_tests/helper_scripts/build_python_msys2.sh')]      builder_prefix_arguments = ['MINGW{}'.format(bits)]      venv_relative_python = ['Scripts/python.exe']      toolchain = ['mingw32']    else:      shell = []      builder = [os.path.abspath('tools/run_tests/helper_scripts/build_python.sh')]      builder_prefix_arguments = []      venv_relative_python = ['bin/python']      toolchain = ['unix']    runner = [os.path.abspath('tools/run_tests/helper_scripts/run_python.sh')]    config_vars = _PythonConfigVars(shell, builder, builder_prefix_arguments,                              venv_relative_python, toolchain, runner)    python27_config = _python_config_generator(name='py27', major='2',                                               minor='7', bits=bits,                                               config_vars=config_vars)    python34_config = _python_config_generator(name='py34', major='3',                                               minor='4', bits=bits,                                               config_vars=config_vars)    python35_config = _python_config_generator(name='py35', major='3',                                               minor='5', bits=bits,                                               config_vars=config_vars)    python36_config = _python_config_generator(name='py36', major='3',                                               minor='6', bits=bits,                                               config_vars=config_vars)    pypy27_config = _pypy_config_generator(name='pypy', major='2',                                           config_vars=config_vars)    pypy32_config = _pypy_config_generator(name='pypy3', major='3',                                           config_vars=config_vars)    if args.compiler == 'default':      if os.name == 'nt':        return (python27_config,)      else:        return (python27_config, python34_config,)    elif args.compiler == 'python2.7':      return (python27_config,)    elif args.compiler == 'python3.4':      return (python34_config,)    elif args.compiler == 'python3.5':      return (python35_config,)    elif args.compiler == 'python3.6':      return (python36_config,)    elif args.compiler == 'pypy':      return (pypy27_config,)    elif args.compiler == 'pypy3':      return (pypy32_config,)    elif args.compiler == 'python_alpine':      return (python27_config,)    else:      raise Exception('Compiler %s not supported.' % args.compiler)  def __str__(self):    return 'python'class RubyLanguage(object):  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default'])  def test_specs(self):    tests = [self.config.job_spec(['tools/run_tests/helper_scripts/run_ruby.sh'],                                  timeout_seconds=10*60,                                  environ=_FORCE_ENVIRON_FOR_WRAPPERS)]    tests.append(self.config.job_spec(['tools/run_tests/helper_scripts/run_ruby_end2end_tests.sh'],                 timeout_seconds=10*60,                 environ=_FORCE_ENVIRON_FOR_WRAPPERS))    return tests  def pre_build_steps(self):    return [['tools/run_tests/helper_scripts/pre_build_ruby.sh']]  def make_targets(self):    return []  def make_options(self):    return []  def build_steps(self):    return [['tools/run_tests/helper_scripts/build_ruby.sh']]  def post_tests_steps(self):    return [['tools/run_tests/helper_scripts/post_tests_ruby.sh']]  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/ruby_jessie_%s' % _docker_arch_suffix(self.args.arch)  def __str__(self):    return 'ruby'class CSharpLanguage(object):  def __init__(self):    self.platform = platform_string()  def configure(self, config, args):    self.config = config    self.args = args    if self.platform == 'windows':      _check_compiler(self.args.compiler, ['coreclr', 'default'])      _check_arch(self.args.arch, ['default'])      self._cmake_arch_option = 'x64'      self._make_options = []    else:      _check_compiler(self.args.compiler, ['default', 'coreclr'])      self._docker_distro = 'jessie'      if self.platform == 'mac':        # TODO(jtattermusch): EMBED_ZLIB=true currently breaks the mac build        self._make_options = ['EMBED_OPENSSL=true']        if self.args.compiler != 'coreclr':          # On Mac, official distribution of mono is 32bit.          self._make_options += ['ARCH_FLAGS=-m32', 'LDFLAGS=-m32']      else:        self._make_options = ['EMBED_OPENSSL=true', 'EMBED_ZLIB=true']  def test_specs(self):    with open('src/csharp/tests.json') as f:      tests_by_assembly = json.load(f)    msbuild_config = _MSBUILD_CONFIG[self.config.build_config]    nunit_args = ['--labels=All', '--noresult', '--workers=1']    assembly_subdir = 'bin/%s' % msbuild_config    assembly_extension = '.exe'    if self.args.compiler == 'coreclr':      assembly_subdir += '/netcoreapp1.0'      runtime_cmd = ['dotnet', 'exec']      assembly_extension = '.dll'    else:      assembly_subdir += '/net45'      if self.platform == 'windows':        runtime_cmd = []      else:        runtime_cmd = ['mono']    specs = []    for assembly in six.iterkeys(tests_by_assembly):      assembly_file = 'src/csharp/%s/%s/%s%s' % (assembly,                                                 assembly_subdir,                                                 assembly,                                                 assembly_extension)      if self.config.build_config != 'gcov' or self.platform != 'windows':        # normally, run each test as a separate process        for test in tests_by_assembly[assembly]:          cmdline = runtime_cmd + [assembly_file, '--test=%s' % test] + nunit_args          specs.append(self.config.job_spec(cmdline,                                            shortname='csharp.%s' % test,                                            environ=_FORCE_ENVIRON_FOR_WRAPPERS))      else:        # For C# test coverage, run all tests from the same assembly at once        # using OpenCover.Console (only works on Windows).        cmdline = ['src\\csharp\\packages\\OpenCover.4.6.519\\tools\\OpenCover.Console.exe',                   '-target:%s' % assembly_file,                   '-targetdir:src\\csharp',                   '-targetargs:%s' % ' '.join(nunit_args),                   '-filter:+[Grpc.Core]*',                   '-register:user',                   '-output:src\\csharp\\coverage_csharp_%s.xml' % assembly]        # set really high cpu_cost to make sure instances of OpenCover.Console run exclusively        # to prevent problems with registering the profiler.        run_exclusive = 1000000        specs.append(self.config.job_spec(cmdline,                                          shortname='csharp.coverage.%s' % assembly,                                          cpu_cost=run_exclusive,                                          environ=_FORCE_ENVIRON_FOR_WRAPPERS))    return specs  def pre_build_steps(self):    if self.platform == 'windows':      return [['tools\\run_tests\\helper_scripts\\pre_build_csharp.bat', self._cmake_arch_option]]    else:      return [['tools/run_tests/helper_scripts/pre_build_csharp.sh']]  def make_targets(self):    return ['grpc_csharp_ext']  def make_options(self):    return self._make_options;  def build_steps(self):    if self.platform == 'windows':      return [['tools\\run_tests\\helper_scripts\\build_csharp.bat']]    else:      return [['tools/run_tests/helper_scripts/build_csharp.sh']]  def post_tests_steps(self):    if self.platform == 'windows':      return [['tools\\run_tests\\helper_scripts\\post_tests_csharp.bat']]    else:      return [['tools/run_tests/helper_scripts/post_tests_csharp.sh']]  def makefile_name(self):    if self.platform == 'windows':      return 'cmake/build/%s/Makefile' % self._cmake_arch_option    else:      return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/csharp_%s_%s' % (self._docker_distro,                                                   _docker_arch_suffix(self.args.arch))  def __str__(self):    return 'csharp'class ObjCLanguage(object):  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default'])  def test_specs(self):    return [        self.config.job_spec(['src/objective-c/tests/run_tests.sh'],                              timeout_seconds=60*60,                              shortname='objc-tests',                              environ=_FORCE_ENVIRON_FOR_WRAPPERS),        self.config.job_spec(['src/objective-c/tests/build_example_test.sh'],                              timeout_seconds=30*60,                              shortname='objc-examples-build',                              environ=_FORCE_ENVIRON_FOR_WRAPPERS),    ]  def pre_build_steps(self):    return []  def make_targets(self):    return ['interop_server']  def make_options(self):    return []  def build_steps(self):    return [['src/objective-c/tests/build_tests.sh']]  def post_tests_steps(self):    return []  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return None  def __str__(self):    return 'objc'class Sanity(object):  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default'])  def test_specs(self):    import yaml    with open('tools/run_tests/sanity/sanity_tests.yaml', 'r') as f:      environ={'TEST': 'true'}      if _is_use_docker_child():        environ['CLANG_FORMAT_SKIP_DOCKER'] = 'true'      return [self.config.job_spec(cmd['script'].split(),                                   timeout_seconds=30*60,                                   environ=environ,                                   cpu_cost=cmd.get('cpu_cost', 1))              for cmd in yaml.load(f)]  def pre_build_steps(self):    return []  def make_targets(self):    return ['run_dep_checks']  def make_options(self):    return []  def build_steps(self):    return []  def post_tests_steps(self):    return []  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/sanity'  def __str__(self):    return 'sanity'class NodeExpressLanguage(object):  """Dummy Node express test target to enable running express performance  benchmarks"""  def __init__(self):    self.platform = platform_string()  def configure(self, config, args):    self.config = config    self.args = args    _check_compiler(self.args.compiler, ['default', 'node0.12',                                         'node4', 'node5', 'node6'])    if self.args.compiler == 'default':      self.node_version = '4'    else:      # Take off the word "node"      self.node_version = self.args.compiler[4:]  def test_specs(self):    return []  def pre_build_steps(self):    if self.platform == 'windows':      return [['tools\\run_tests\\helper_scripts\\pre_build_node.bat']]    else:      return [['tools/run_tests/helper_scripts/pre_build_node.sh', self.node_version]]  def make_targets(self):    return []  def make_options(self):    return []  def build_steps(self):    return []  def post_tests_steps(self):    return []  def makefile_name(self):    return 'Makefile'  def dockerfile_dir(self):    return 'tools/dockerfile/test/node_jessie_%s' % _docker_arch_suffix(self.args.arch)  def __str__(self):    return 'node_express'# different configurations we can run underwith open('tools/run_tests/generated/configs.json') as f:  _CONFIGS = dict((cfg['config'], Config(**cfg)) for cfg in ast.literal_eval(f.read()))_LANGUAGES = {    'c++': CLanguage('cxx', 'c++'),    'c': CLanguage('c', 'c'),    'node': NodeLanguage(),    'node_express': NodeExpressLanguage(),    'php': PhpLanguage(),    'php7': Php7Language(),    'python': PythonLanguage(),    'ruby': RubyLanguage(),    'csharp': CSharpLanguage(),    'objc' : ObjCLanguage(),    'sanity': Sanity()    }_MSBUILD_CONFIG = {    'dbg': 'Debug',    'opt': 'Release',    'gcov': 'Debug',    }def _windows_arch_option(arch):  """Returns msbuild cmdline option for selected architecture."""  if arch == 'default' or arch == 'x86':    return '/p:Platform=Win32'  elif arch == 'x64':    return '/p:Platform=x64'  else:    print('Architecture %s not supported.' % arch)    sys.exit(1)def _check_arch_option(arch):  """Checks that architecture option is valid."""  if platform_string() == 'windows':    _windows_arch_option(arch)  elif platform_string() == 'linux':    # On linux, we need to be running under docker with the right architecture.    runtime_arch = platform.architecture()[0]    if arch == 'default':      return    elif runtime_arch == '64bit' and arch == 'x64':      return    elif runtime_arch == '32bit' and arch == 'x86':      return    else:      print('Architecture %s does not match current runtime architecture.' % arch)      sys.exit(1)  else:    if args.arch != 'default':      print('Architecture %s not supported on current platform.' % args.arch)      sys.exit(1)def _windows_build_bat(compiler):  """Returns name of build.bat for selected compiler."""  # For CoreCLR, fall back to the default compiler for C core  if compiler == 'default' or compiler == 'vs2013':    return 'vsprojects\\build_vs2013.bat'  elif compiler == 'vs2015':    return 'vsprojects\\build_vs2015.bat'  else:    print('Compiler %s not supported.' % compiler)    sys.exit(1)def _windows_toolset_option(compiler):  """Returns msbuild PlatformToolset for selected compiler."""  # For CoreCLR, fall back to the default compiler for C core  if compiler == 'default' or compiler == 'vs2013' or compiler == 'coreclr':    return '/p:PlatformToolset=v120'  elif compiler == 'vs2015':    return '/p:PlatformToolset=v140'  else:    print('Compiler %s not supported.' % compiler)    sys.exit(1)def _docker_arch_suffix(arch):  """Returns suffix to dockerfile dir to use."""  if arch == 'default' or arch == 'x64':    return 'x64'  elif arch == 'x86':    return 'x86'  else:    print('Architecture %s not supported with current settings.' % arch)    sys.exit(1)def runs_per_test_type(arg_str):    """Auxilary function to parse the "runs_per_test" flag.       Returns:           A positive integer or 0, the latter indicating an infinite number of           runs.       Raises:           argparse.ArgumentTypeError: Upon invalid input.    """    if arg_str == 'inf':        return 0    try:        n = int(arg_str)        if n <= 0: raise ValueError        return n    except:        msg = '\'{}\' is not a positive integer or \'inf\''.format(arg_str)        raise argparse.ArgumentTypeError(msg)def percent_type(arg_str):  pct = float(arg_str)  if pct > 100 or pct < 0:    raise argparse.ArgumentTypeError(        "'%f' is not a valid percentage in the [0, 100] range" % pct)  return pct# This is math.isclose in python >= 3.5def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):      return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)# parse command lineargp = argparse.ArgumentParser(description='Run grpc tests.')argp.add_argument('-c', '--config',                  choices=sorted(_CONFIGS.keys()),                  default='opt')argp.add_argument('-n', '--runs_per_test', default=1, type=runs_per_test_type,        help='A positive integer or "inf". If "inf", all tests will run in an '             'infinite loop. Especially useful in combination with "-f"')argp.add_argument('-r', '--regex', default='.*', type=str)argp.add_argument('--regex_exclude', default='', type=str)argp.add_argument('-j', '--jobs', default=multiprocessing.cpu_count(), type=int)argp.add_argument('-s', '--slowdown', default=1.0, type=float)argp.add_argument('-p', '--sample_percent', default=100.0, type=percent_type,                  help='Run a random sample with that percentage of tests')argp.add_argument('-f', '--forever',                  default=False,                  action='store_const',                  const=True)argp.add_argument('-t', '--travis',                  default=False,                  action='store_const',                  const=True)argp.add_argument('--newline_on_success',                  default=False,                  action='store_const',                  const=True)argp.add_argument('-l', '--language',                  choices=['all'] + sorted(_LANGUAGES.keys()),                  nargs='+',                  default=['all'])argp.add_argument('-S', '--stop_on_failure',                  default=False,                  action='store_const',                  const=True)argp.add_argument('--use_docker',                  default=False,                  action='store_const',                  const=True,                  help='Run all the tests under docker. That provides ' +                  'additional isolation and prevents the need to install ' +                  'language specific prerequisites. Only available on Linux.')argp.add_argument('--allow_flakes',                  default=False,                  action='store_const',                  const=True,                  help='Allow flaky tests to show as passing (re-runs failed tests up to five times)')argp.add_argument('--arch',                  choices=['default', 'x86', 'x64'],                  default='default',                  help='Selects architecture to target. For some platforms "default" is the only supported choice.')argp.add_argument('--compiler',                  choices=['default',                           'gcc4.4', 'gcc4.6', 'gcc4.8', 'gcc4.9', 'gcc5.3', 'gcc_musl',                           'clang3.4', 'clang3.5', 'clang3.6', 'clang3.7',                           'vs2013', 'vs2015',                           'python2.7', 'python3.4', 'python3.5', 'python3.6', 'pypy', 'pypy3', 'python_alpine',                           'node0.12', 'node4', 'node5', 'node6', 'node7',                           'electron1.3', 'electron1.6',                           'coreclr',                           'cmake'],                  default='default',                  help='Selects compiler to use. Allowed values depend on the platform and language.')argp.add_argument('--iomgr_platform',                  choices=['native', 'uv'],                  default='native',                  help='Selects iomgr platform to build on')argp.add_argument('--build_only',                  default=False,                  action='store_const',                  const=True,                  help='Perform all the build steps but don\'t run any tests.')argp.add_argument('--measure_cpu_costs', default=False, action='store_const', const=True,                  help='Measure the cpu costs of tests')argp.add_argument('--update_submodules', default=[], nargs='*',                  help='Update some submodules before building. If any are updated, also run generate_projects. ' +                       'Submodules are specified as SUBMODULE_NAME:BRANCH; if BRANCH is omitted, master is assumed.')argp.add_argument('-a', '--antagonists', default=0, type=int)argp.add_argument('-x', '--xml_report', default=None, type=str,        help='Generates a JUnit-compatible XML report')argp.add_argument('--report_suite_name', default='tests', type=str,        help='Test suite name to use in generated JUnit XML report')argp.add_argument('--quiet_success',                  default=False,                  action='store_const',                  const=True,                  help='Don\'t print anything when a test passes. Passing tests also will not be reported in XML report. ' +                       'Useful when running many iterations of each test (argument -n).')argp.add_argument('--force_default_poller', default=False, action='store_const', const=True,                  help='Don\'t try to iterate over many polling strategies when they exist')argp.add_argument('--max_time', default=-1, type=int, help='Maximum test runtime in seconds')argp.add_argument('--bq_result_table',                  default='',                  type=str,                  nargs='?',                  help='Upload test results to a specified BQ table.')args = argp.parse_args()if args.force_default_poller:  _POLLING_STRATEGIES = {}jobset.measure_cpu_costs = args.measure_cpu_costs# update submodules if necessaryneed_to_regenerate_projects = Falsefor spec in args.update_submodules:  spec = spec.split(':', 1)  if len(spec) == 1:    submodule = spec[0]    branch = 'master'  elif len(spec) == 2:    submodule = spec[0]    branch = spec[1]  cwd = 'third_party/%s' % submodule  def git(cmd, cwd=cwd):    print('in %s: git %s' % (cwd, cmd))    run_shell_command('git %s' % cmd, cwd=cwd)  git('fetch')  git('checkout %s' % branch)  git('pull origin %s' % branch)  if os.path.exists('src/%s/gen_build_yaml.py' % submodule):    need_to_regenerate_projects = Trueif need_to_regenerate_projects:  if jobset.platform_string() == 'linux':    run_shell_command('tools/buildgen/generate_projects.sh')  else:    print('WARNING: may need to regenerate projects, but since we are not on')    print('         Linux this step is being skipped. Compilation MAY fail.')# grab configrun_config = _CONFIGS[args.config]build_config = run_config.build_configif args.travis:  _FORCE_ENVIRON_FOR_WRAPPERS = {'GRPC_TRACE': 'api'}if 'all' in args.language:  lang_list = _LANGUAGES.keys()else:  lang_list = args.language# We don't support code coverage on some languagesif 'gcov' in args.config:  for bad in ['objc', 'sanity']:    if bad in lang_list:      lang_list.remove(bad)languages = set(_LANGUAGES[l] for l in lang_list)for l in languages:  l.configure(run_config, args)language_make_options=[]if any(language.make_options() for language in languages):  if not 'gcov' in args.config and len(languages) != 1:    print('languages with custom make options cannot be built simultaneously with other languages')    sys.exit(1)  else:    # Combining make options is not clean and just happens to work. It allows C/C++ and C# to build    # together, and is only used under gcov. All other configs should build languages individually.    language_make_options = list(set([make_option for lang in languages for make_option in lang.make_options()]))if args.use_docker:  if not args.travis:    print('Seen --use_docker flag, will run tests under docker.')    print('')    print('IMPORTANT: The changes you are testing need to be locally committed')    print('because only the committed changes in the current branch will be')    print('copied to the docker environment.')    time.sleep(5)  dockerfile_dirs = set([l.dockerfile_dir() for l in languages])  if len(dockerfile_dirs) > 1:    if 'gcov' in args.config:      dockerfile_dir = 'tools/dockerfile/test/multilang_jessie_x64'      print ('Using multilang_jessie_x64 docker image for code coverage for '             'all languages.')    else:      print ('Languages to be tested require running under different docker '             'images.')      sys.exit(1)  else:    dockerfile_dir = next(iter(dockerfile_dirs))  child_argv = [ arg for arg in sys.argv if not arg == '--use_docker' ]  run_tests_cmd = 'python tools/run_tests/run_tests.py %s' % ' '.join(child_argv[1:])  env = os.environ.copy()  env['RUN_TESTS_COMMAND'] = run_tests_cmd  env['DOCKERFILE_DIR'] = dockerfile_dir  env['DOCKER_RUN_SCRIPT'] = 'tools/run_tests/dockerize/docker_run_tests.sh'  if args.xml_report:    env['XML_REPORT'] = args.xml_report  if not args.travis:    env['TTY_FLAG'] = '-t'  # enables Ctrl-C when not on Jenkins.  subprocess.check_call('tools/run_tests/dockerize/build_docker_and_run_tests.sh',                        shell=True,                        env=env)  sys.exit(0)_check_arch_option(args.arch)def make_jobspec(cfg, targets, makefile='Makefile'):  if platform_string() == 'windows':    if makefile.startswith('cmake/build/'):      return [jobset.JobSpec(['cmake', '--build', '.',                              '--target', '%s' % target,                              '--config', _MSBUILD_CONFIG[cfg]],                             cwd=os.path.dirname(makefile),                             timeout_seconds=None) for target in targets]    extra_args = []    # better do parallel compilation    # empirically /m:2 gives the best performance/price and should prevent    # overloading the windows workers.    extra_args.extend(['/m:2'])    # disable PDB generation: it's broken, and we don't need it during CI    extra_args.extend(['/p:Jenkins=true'])    return [      jobset.JobSpec([_windows_build_bat(args.compiler),                      'vsprojects\\%s.sln' % target,                      '/p:Configuration=%s' % _MSBUILD_CONFIG[cfg]] +                      extra_args +                      language_make_options,                      shell=True, timeout_seconds=None)      for target in targets]  else:    if targets and makefile.startswith('cmake/build/'):      # With cmake, we've passed all the build configuration in the pre-build step already      return [jobset.JobSpec([os.getenv('MAKE', 'make'),                              '-j', '%d' % args.jobs] +                             targets,                             cwd='cmake/build',                             timeout_seconds=None)]    if targets:      return [jobset.JobSpec([os.getenv('MAKE', 'make'),                              '-f', makefile,                              '-j', '%d' % args.jobs,                              'EXTRA_DEFINES=GRPC_TEST_SLOWDOWN_MACHINE_FACTOR=%f' % args.slowdown,                              'CONFIG=%s' % cfg,                              'Q='] +                              language_make_options +                             ([] if not args.travis else ['JENKINS_BUILD=1']) +                             targets,                             timeout_seconds=None)]    else:      return []make_targets = {}for l in languages:  makefile = l.makefile_name()  make_targets[makefile] = make_targets.get(makefile, set()).union(      set(l.make_targets()))def build_step_environ(cfg):  environ = {'CONFIG': cfg}  msbuild_cfg = _MSBUILD_CONFIG.get(cfg)  if msbuild_cfg:    environ['MSBUILD_CONFIG'] = msbuild_cfg  return environbuild_steps = list(set(                   jobset.JobSpec(cmdline, environ=build_step_environ(build_config), flake_retries=5)                   for l in languages                   for cmdline in l.pre_build_steps()))if make_targets:  make_commands = itertools.chain.from_iterable(make_jobspec(build_config, list(targets), makefile) for (makefile, targets) in make_targets.items())  build_steps.extend(set(make_commands))build_steps.extend(set(                   jobset.JobSpec(cmdline, environ=build_step_environ(build_config), timeout_seconds=None)                   for l in languages                   for cmdline in l.build_steps()))post_tests_steps = list(set(                        jobset.JobSpec(cmdline, environ=build_step_environ(build_config))                        for l in languages                        for cmdline in l.post_tests_steps()))runs_per_test = args.runs_per_testforever = args.foreverdef _shut_down_legacy_server(legacy_server_port):  try:    version = int(urllib.request.urlopen(        'http://localhost:%d/version_number' % legacy_server_port,        timeout=10).read())  except:    pass  else:    urllib.request.urlopen(        'http://localhost:%d/quitquitquit' % legacy_server_port).read()def _calculate_num_runs_failures(list_of_results):  """Caculate number of runs and failures for a particular test.  Args:    list_of_results: (List) of JobResult object.  Returns:    A tuple of total number of runs and failures.  """  num_runs = len(list_of_results)  # By default, there is 1 run per JobResult.  num_failures = 0  for jobresult in list_of_results:    if jobresult.retries > 0:      num_runs += jobresult.retries    if jobresult.num_failures > 0:      num_failures += jobresult.num_failures  return num_runs, num_failures# _build_and_run resultsclass BuildAndRunError(object):  BUILD = object()  TEST = object()  POST_TEST = object()def _has_epollexclusive():  try:    subprocess.check_call('bins/%s/check_epollexclusive' % args.config)    return True  except subprocess.CalledProcessError, e:    return False# returns a list of things that failed (or an empty list on success)def _build_and_run(    check_cancelled, newline_on_success, xml_report=None, build_only=False):  """Do one pass of building & running tests."""  # build latest sequentially  num_failures, resultset = jobset.run(      build_steps, maxjobs=1, stop_on_failure=True,      newline_on_success=newline_on_success, travis=args.travis)  if num_failures:    return [BuildAndRunError.BUILD]  if build_only:    if xml_report:      report_utils.render_junit_xml_report(resultset, xml_report,                                           suite_name=args.report_suite_name)    return []  if not args.travis and not _has_epollexclusive() and 'epollex' in _POLLING_STRATEGIES[platform_string()]:    print('\n\nOmitting EPOLLEXCLUSIVE tests\n\n')    _POLLING_STRATEGIES[platform_string()].remove('epollex')  # start antagonists  antagonists = [subprocess.Popen(['tools/run_tests/python_utils/antagonist.py'])                 for _ in range(0, args.antagonists)]  start_port_server.start_port_server()  resultset = None  num_test_failures = 0  try:    infinite_runs = runs_per_test == 0    one_run = set(      spec      for language in languages      for spec in language.test_specs()      if (re.search(args.regex, spec.shortname) and          (args.regex_exclude == '' or           not re.search(args.regex_exclude, spec.shortname))))    # When running on travis, we want out test runs to be as similar as possible    # for reproducibility purposes.    if args.travis and args.max_time <= 0:      massaged_one_run = sorted(one_run, key=lambda x: x.shortname)    else:      # whereas otherwise, we want to shuffle things up to give all tests a      # chance to run.      massaged_one_run = list(one_run)  # random.sample needs an indexable seq.      num_jobs = len(massaged_one_run)      # for a random sample, get as many as indicated by the 'sample_percent'      # argument. By default this arg is 100, resulting in a shuffle of all      # jobs.      sample_size = int(num_jobs * args.sample_percent/100.0)      massaged_one_run = random.sample(massaged_one_run, sample_size)      if not isclose(args.sample_percent, 100.0):        assert args.runs_per_test == 1, "Can't do sampling (-p) over multiple runs (-n)."        print("Running %d tests out of %d (~%d%%)" %              (sample_size, num_jobs, args.sample_percent))    if infinite_runs:      assert len(massaged_one_run) > 0, 'Must have at least one test for a -n inf run'    runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs                     else itertools.repeat(massaged_one_run, runs_per_test))    all_runs = itertools.chain.from_iterable(runs_sequence)    if args.quiet_success:      jobset.message('START', 'Running tests quietly, only failing tests will be reported', do_newline=True)    num_test_failures, resultset = jobset.run(        all_runs, check_cancelled, newline_on_success=newline_on_success,        travis=args.travis, maxjobs=args.jobs,        stop_on_failure=args.stop_on_failure,        quiet_success=args.quiet_success, max_time=args.max_time)    if resultset:      for k, v in sorted(resultset.items()):        num_runs, num_failures = _calculate_num_runs_failures(v)        if num_failures > 0:          if num_failures == num_runs:  # what about infinite_runs???            jobset.message('FAILED', k, do_newline=True)          else:            jobset.message(                'FLAKE', '%s [%d/%d runs flaked]' % (k, num_failures, num_runs),                do_newline=True)  finally:    for antagonist in antagonists:      antagonist.kill()    if args.bq_result_table and resultset:      upload_results_to_bq(resultset, args.bq_result_table, args, platform_string())    if xml_report and resultset:      report_utils.render_junit_xml_report(resultset, xml_report,                                           suite_name=args.report_suite_name)  number_failures, _ = jobset.run(      post_tests_steps, maxjobs=1, stop_on_failure=True,      newline_on_success=newline_on_success, travis=args.travis)  out = []  if number_failures:    out.append(BuildAndRunError.POST_TEST)  if num_test_failures:    out.append(BuildAndRunError.TEST)  return outif forever:  success = True  while True:    dw = watch_dirs.DirWatcher(['src', 'include', 'test', 'examples'])    initial_time = dw.most_recent_change()    have_files_changed = lambda: dw.most_recent_change() != initial_time    previous_success = success    errors = _build_and_run(check_cancelled=have_files_changed,                            newline_on_success=False,                            build_only=args.build_only) == 0    if not previous_success and not errors:      jobset.message('SUCCESS',                     'All tests are now passing properly',                     do_newline=True)    jobset.message('IDLE', 'No change detected')    while not have_files_changed():      time.sleep(1)else:  errors = _build_and_run(check_cancelled=lambda: False,                          newline_on_success=args.newline_on_success,                          xml_report=args.xml_report,                          build_only=args.build_only)  if not errors:    jobset.message('SUCCESS', 'All tests passed', do_newline=True)  else:    jobset.message('FAILED', 'Some tests failed', do_newline=True)  exit_code = 0  if BuildAndRunError.BUILD in errors:    exit_code |= 1  if BuildAndRunError.TEST in errors:    exit_code |= 2  if BuildAndRunError.POST_TEST in errors:    exit_code |= 4  sys.exit(exit_code)
 |