| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609 | 
							- #!/usr/bin/env python
 
- # Copyright 2015 gRPC authors.
 
- #
 
- # Licensed under the Apache License, Version 2.0 (the "License");
 
- # you may not use this file except in compliance with the License.
 
- # You may obtain a copy of the License at
 
- #
 
- #     http://www.apache.org/licenses/LICENSE-2.0
 
- #
 
- # Unless required by applicable law or agreed to in writing, software
 
- # distributed under the License is distributed on an "AS IS" BASIS,
 
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
- # See the License for the specific language governing permissions and
 
- # limitations under the License.
 
- """Run tests in parallel."""
 
- from __future__ import print_function
 
- import argparse
 
- import ast
 
- import collections
 
- import glob
 
- import itertools
 
- import json
 
- import logging
 
- import multiprocessing
 
- import os
 
- import os.path
 
- import pipes
 
- import platform
 
- import random
 
- import re
 
- import socket
 
- import subprocess
 
- import sys
 
- import tempfile
 
- import traceback
 
- import time
 
- from six.moves import urllib
 
- import uuid
 
- import six
 
- import python_utils.jobset as jobset
 
- import python_utils.report_utils as report_utils
 
- import python_utils.watch_dirs as watch_dirs
 
- import python_utils.start_port_server as start_port_server
 
- try:
 
-   from python_utils.upload_test_results import upload_results_to_bq
 
- except (ImportError):
 
-   pass # It's ok to not import because this is only necessary to upload results to BQ.
 
- gcp_utils_dir = os.path.abspath(os.path.join(
 
-         os.path.dirname(__file__), '../gcp/utils'))
 
- sys.path.append(gcp_utils_dir)
 
- _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': ['epollsig', 'poll', 'poll-cv'],
 
- # TODO(ctiller, sreecha): enable epoll1, epollex, epoll-thread-pool
 
-   'mac': ['poll'],
 
- }
 
- def get_flaky_tests(limit=None):
 
-   import big_query_utils
 
-   bq = big_query_utils.create_big_query()
 
-   query = """
 
-     SELECT
 
-       test_name,
 
-       SUM(result != 'PASSED'
 
-         AND result != 'SKIPPED') AS count_failed,
 
-     FROM
 
-       [grpc-testing:jenkins_test_results.aggregate_results]
 
-     WHERE
 
-       timestamp >= DATE_ADD(CURRENT_DATE(), -1, "WEEK")
 
-       AND NOT REGEXP_MATCH(job_name, '.*portability.*')
 
-     GROUP BY
 
-       test_name
 
-     HAVING
 
-       count_failed > 0"""
 
-   if limit:
 
-     query += " limit {}".format(limit)
 
-   query_job = big_query_utils.sync_query_job(bq, 'grpc-testing', query)
 
-   page = bq.jobs().getQueryResults(
 
-       pageToken=None,
 
-       **query_job['jobReference']).execute(num_retries=3)
 
-   flake_names = [row['f'][0]['v'] for row in page['rows']]
 
-   return flake_names
 
- def platform_string():
 
-   return jobset.platform_string()
 
- _DEFAULT_TIMEOUT_SECONDS = 5 * 60
 
- def 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 test
 
- class 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
 
-     if not flaky and shortname and shortname in flaky_tests:
 
-       print('Setting %s to flaky' % shortname)
 
-       flaky = True
 
-     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 -DGRPC_UV_THREAD_CHECK'
 
-       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,
 
-             'check_epollexclusive']
 
-   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_target
 
- class 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', 'node8',
 
-                                          'electron1.3', 'electron1.6'])
 
-     if self.args.compiler == 'default':
 
-       self.runtime = 'node'
 
-       self.node_version = '8'
 
-     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 (python35_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 under
 
- with 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.5
 
- def 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 line
 
- argp = 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', 'node8',
 
-                            '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.')
 
- argp.add_argument('--auto_set_flakes', default=True, type=bool,
 
-                   help='Set flakiness data from historic data')
 
- args = argp.parse_args()
 
- flaky_tests = set()
 
- if args.auto_set_flakes:
 
-   try:
 
-     flaky_tests = set(get_flaky_tests())
 
-   except:
 
-     print("Unexpected error getting flaky tests:", sys.exc_info()[0])
 
- if args.force_default_poller:
 
-   _POLLING_STRATEGIES = {}
 
- jobset.measure_cpu_costs = args.measure_cpu_costs
 
- # update submodules if necessary
 
- need_to_regenerate_projects = False
 
- for 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 = True
 
- if 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 config
 
- run_config = _CONFIGS[args.config]
 
- build_config = run_config.build_config
 
- if 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 languages
 
- if '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 environ
 
- build_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_test
 
- forever = args.forever
 
- def _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 results
 
- class 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
 
-   except OSError, e:
 
-     # For languages other than C and Windows the binary won't exist
 
-     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 platform_string() in _POLLING_STRATEGIES 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 out
 
- if 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)
 
 
  |