15 """Run tests in parallel."""
17 from __future__
import print_function
26 import multiprocessing
42 from six.moves
import urllib
54 gcp_utils_dir = os.path.abspath(
55 os.path.join(os.path.dirname(__file__),
'../gcp/utils'))
56 sys.path.append(gcp_utils_dir)
58 _ROOT = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]),
'../..'))
61 _FORCE_ENVIRON_FOR_WRAPPERS = {
62 'GRPC_VERBOSITY':
'DEBUG',
65 _POLLING_STRATEGIES = {
66 'linux': [
'epoll1',
'poll'],
72 return jobset.platform_string()
75 _DEFAULT_TIMEOUT_SECONDS = 5 * 60
76 _PRE_BUILD_STEP_TIMEOUT_SECONDS = 10 * 60
81 subprocess.check_output(cmd, shell=
True, env=env, cwd=cwd)
82 except subprocess.CalledProcessError
as e:
84 "Error while running command '%s'. Exit status %d. Output:\n%s",
85 e.cmd, e.returncode, e.output)
92 if jobset.platform_string() ==
'windows':
98 """Use to print useful info for debug/repro just before exiting."""
100 print(
'=== run_tests.py DEBUG INFO ===')
101 print(
'command: \"%s\"' %
' '.join(sys.argv))
103 print(
'dockerfile: %s' % dockerfile_dir)
104 kokoro_job_name = os.getenv(
'KOKORO_JOB_NAME')
106 print(
'kokoro job name: %s' % kokoro_job_name)
107 print(
'===============================')
116 timeout_multiplier=1,
118 iomgr_platform='native'):
123 self.
environ[
'CONFIG'] = config
130 timeout_seconds=_DEFAULT_TIMEOUT_SECONDS,
135 """Construct a jobset.JobSpec for a test under this config
138 cmdline: a list of strings specifying the command line the test
142 for k, v
in environ.items():
143 actual_environ[k] = v
144 if not flaky
and shortname
and shortname
in flaky_tests:
146 if shortname
in shortname_to_cpu:
147 cpu_cost = shortname_to_cpu[shortname]
148 return jobset.JobSpec(
151 environ=actual_environ,
154 timeout_seconds
if timeout_seconds
else None),
155 flake_retries=4
if flaky
or args.allow_flakes
else 0,
156 timeout_retries=1
if flaky
or args.allow_flakes
else 0)
161 platforms_str =
'ci_platforms' if travis
else 'platforms'
162 with open(
'tools/run_tests/generated/tests.json')
as f:
167 tgt[platforms_str]
and not (travis
and tgt[
'flaky'])
172 if compiler
not in supported_compilers:
173 raise Exception(
'Compiler %s not supported (on this platform).' %
178 if arch
not in supported_archs:
179 raise Exception(
'Architecture %s not supported.' % arch)
183 """Returns True if running running as a --use_docker child."""
184 return True if os.getenv(
'DOCKER_RUN_SCRIPT_COMMAND')
else False
187 _PythonConfigVars = collections.namedtuple(
'_ConfigVars', [
190 'builder_prefix_arguments',
191 'venv_relative_python',
198 build = (config_vars.shell + config_vars.builder +
199 config_vars.builder_prefix_arguments +
201 [name] + config_vars.venv_relative_python + config_vars.toolchain)
202 run = (config_vars.shell + config_vars.runner + [
203 os.path.join(name, config_vars.venv_relative_python[0]),
210 name, config_vars.shell + config_vars.builder +
211 config_vars.builder_prefix_arguments +
213 config_vars.venv_relative_python + config_vars.toolchain,
214 config_vars.shell + config_vars.runner +
215 [os.path.join(name, config_vars.venv_relative_python[0])])
222 return '/c/Python{major}{minor}/python.exe'.
format(major=major,
226 return '/c/Python{major}{minor}_{bits}bits/python.exe'.
format(
227 major=major, minor=minor, bits=bits)
229 return 'python{major}.{minor}'.
format(major=major, minor=minor)
238 raise ValueError(
"Unknown PyPy major version")
255 'cmake_ninja_vs2017',
261 activate_vs_tools =
''
262 if self.
args.compiler ==
'cmake_ninja_vs2017' or self.
args.compiler ==
'cmake' or self.
args.compiler ==
'default':
265 cmake_generator =
'Ninja'
266 activate_vs_tools =
'2017'
267 elif self.
args.compiler ==
'cmake_vs2017':
268 cmake_generator =
'Visual Studio 15 2017'
269 elif self.
args.compiler ==
'cmake_vs2019':
270 cmake_generator =
'Visual Studio 16 2019'
272 print(
'should never reach here.')
292 self.
args.use_docker, self.
args.compiler)
294 if self.
args.arch ==
'x86':
302 for target
in binaries:
303 if target.get(
'boringssl',
False):
306 auto_timeout_scaling = target.get(
'auto_timeout_scaling',
True)
307 polling_strategies = (_POLLING_STRATEGIES.get(
308 self.
platform, [
'all'])
if target.get(
'uses_polling',
True)
else
310 for polling_strategy
in polling_strategies:
312 'GRPC_DEFAULT_SSL_ROOTS_FILE_PATH':
313 _ROOT +
'/src/core/tsi/test_creds/ca.pem',
314 'GRPC_POLL_STRATEGY':
319 resolver = os.environ.get(
'GRPC_DNS_RESOLVER',
None)
321 env[
'GRPC_DNS_RESOLVER'] = resolver
322 shortname_ext =
'' if polling_strategy ==
'all' else ' GRPC_POLL_STRATEGY=%s' % polling_strategy
323 if polling_strategy
in target.get(
'excluded_poll_engines', []):
327 if auto_timeout_scaling:
328 config = self.
args.config
329 if (
'asan' in config
or config ==
'msan' or
330 config ==
'tsan' or config ==
'ubsan' or
331 config ==
'helgrind' or config ==
'memcheck'):
336 if self.
config.build_config
in target[
'exclude_configs']:
338 if self.
args.iomgr_platform
in target.get(
'exclude_iomgrs', []):
341 binary =
'cmake/build/%s/%s.exe' % (_MSBUILD_CONFIG[
342 self.
config.build_config], target[
'name'])
344 binary =
'cmake/build/%s' % target[
'name']
346 cpu_cost = target[
'cpu_cost']
347 if cpu_cost ==
'capacity':
348 cpu_cost = multiprocessing.cpu_count()
349 if os.path.isfile(binary):
350 list_test_command =
None
351 filter_test_command =
None
356 if 'benchmark' in target
and target[
'benchmark']:
357 with open(os.devnull,
'w')
as fnull:
358 tests = subprocess.check_output(
359 [binary,
'--benchmark_list_tests'],
361 for line
in tests.decode().
split(
'\n'):
366 '--benchmark_filter=%s$' % test
372 (
' '.join(cmdline), shortname_ext),
374 timeout_seconds=target.get(
376 _DEFAULT_TIMEOUT_SECONDS) *
379 elif 'gtest' in target
and target[
'gtest']:
383 with open(os.devnull,
'w')
as fnull:
384 tests = subprocess.check_output(
385 [binary,
'--gtest_list_tests'], stderr=fnull)
387 for line
in tests.decode().
split(
'\n'):
396 assert base
is not None
397 assert line[1] ==
' '
398 test = base + line.strip()
400 '--gtest_filter=%s' % test
406 (
' '.join(cmdline), shortname_ext),
408 timeout_seconds=target.get(
410 _DEFAULT_TIMEOUT_SECONDS) *
414 cmdline = [binary] + target[
'args']
415 shortname = target.get(
417 ' '.join(pipes.quote(arg)
for arg
in cmdline))
418 shortname += shortname_ext
424 flaky=target.get(
'flaky',
False),
425 timeout_seconds=target.get(
427 _DEFAULT_TIMEOUT_SECONDS) * timeout_scaling,
429 elif self.
args.regex ==
'.*' or self.
platform ==
'windows':
430 print(
'\nWARNING: binary not found, skipping', binary)
439 'tools\\run_tests\\helper_scripts\\build_cxx.bat',
440 '-DgRPC_BUILD_MSVC_MP_COUNT=%d' % self.
args.jobs
443 return [[
'tools/run_tests/helper_scripts/build_cxx.sh'] +
447 """Extra environment variables set for pre_build_steps and build_steps jobs."""
448 environ = {
'GRPC_RUN_TESTS_CXX_LANGUAGE_SUFFIX': self.
lang_suffix}
463 return [[
'tools/run_tests/helper_scripts/post_tests_c.sh']]
467 '-DCMAKE_C_COMPILER=clang%s' % version_suffix,
468 '-DCMAKE_CXX_COMPILER=clang++%s' % version_suffix,
472 """Returns docker distro and cmake configure args to use for given compiler."""
478 if compiler ==
'default' or compiler ==
'cmake':
479 return (
'debian11', [])
480 elif compiler ==
'gcc6':
482 elif compiler ==
'gcc10.2':
483 return (
'debian11', [])
484 elif compiler ==
'gcc10.2_openssl102':
485 return (
'debian11_openssl102', [
486 "-DgRPC_SSL_PROVIDER=package",
488 elif compiler ==
'gcc11':
489 return (
'gcc_11', [])
490 elif compiler ==
'gcc_musl':
491 return (
'alpine', [])
492 elif compiler ==
'clang6':
494 elif compiler ==
'clang13':
497 raise Exception(
'Compiler %s not supported.' % compiler)
500 return 'tools/dockerfile/test/cxx_%s_%s' % (
519 'default',
'node0.12',
'node4',
'node5',
'node6',
'node7',
'node8',
520 'electron1.3',
'electron1.6'
522 if self.
args.compiler ==
'default':
526 if self.
args.compiler.startswith(
'electron'):
539 [
'tools\\run_tests\\helper_scripts\\run_node.bat'])
544 [
'tools/run_tests/helper_scripts/run_grpc-node.sh'],
546 environ=_FORCE_ENVIRON_FOR_WRAPPERS)
556 """Extra environment variables set for pre_build_steps and build_steps jobs."""
579 self.
config.job_spec([
'src/php/bin/run_tests.sh'],
580 environ=_FORCE_ENVIRON_FOR_WRAPPERS)
587 return [[
'tools/run_tests/helper_scripts/build_php.sh']]
590 """Extra environment variables set for pre_build_steps and build_steps jobs."""
594 return [[
'tools/run_tests/helper_scripts/post_tests_php.sh']]
605 collections.namedtuple(
'PythonConfig', [
'name',
'build',
'run'])):
606 """Tuple of commands (named s.t. 'what it says on the tin' applies)"""
612 'native': [
'src/python/grpcio_tests/tests/tests.json'],
614 'src/python/grpcio_tests/tests/tests.json',
615 'src/python/grpcio_tests/tests_gevent/tests.json',
617 'asyncio': [
'src/python/grpcio_tests/tests_aio/tests.json'],
621 'native':
'test_lite',
622 'gevent':
'test_gevent',
623 'asyncio':
'test_aio',
637 with open(tests_json_file_name)
as tests_json_file:
638 test_cases.extend(json.load(tests_json_file))
640 environment = dict(_FORCE_ENVIRON_FOR_WRAPPERS)
644 if io_platform !=
'native':
645 environment[
'GRPC_ENABLE_FORK_SUPPORT'] =
'0'
646 for python_config
in self.
pythons:
649 if io_platform ==
'gevent' and python_config.name !=
'py36':
654 timeout_seconds=8 * 60,
656 GRPC_PYTHON_TESTRUNNER_FILTER=
str(test_case),
658 shortname=
'%s.%s.%s' %
659 (python_config.name, io_platform, test_case),
660 )
for test_case
in test_cases
668 return [config.build
for config
in self.
pythons]
671 """Extra environment variables set for pre_build_steps and build_steps jobs."""
675 if self.
config.build_config !=
'gcov':
678 return [[
'tools/run_tests/helper_scripts/post_tests_python.sh']]
681 return 'tools/dockerfile/test/python_%s_%s' % (
686 """Choose the docker image to use based on python version."""
687 if self.
args.compiler ==
'python_alpine':
690 return 'debian11_default'
693 """Get python runtimes to test with, based on current platform, architecture, compiler etc."""
694 if args.iomgr_platform !=
'native':
696 'Python builds no longer differentiate IO Manager platforms, please use "native"'
699 if args.arch ==
'x86':
708 'tools/run_tests/helper_scripts/build_python_msys2.sh')
710 builder_prefix_arguments = [
'MINGW{}'.
format(bits)]
711 venv_relative_python = [
'Scripts/python.exe']
712 toolchain = [
'mingw32']
717 'tools/run_tests/helper_scripts/build_python.sh')
719 builder_prefix_arguments = []
720 venv_relative_python = [
'bin/python']
724 os.path.abspath(
'tools/run_tests/helper_scripts/run_python.sh')
728 builder_prefix_arguments,
729 venv_relative_python, toolchain, runner)
734 config_vars=config_vars)
739 config_vars=config_vars)
744 config_vars=config_vars)
749 config_vars=config_vars)
754 config_vars=config_vars)
757 config_vars=config_vars)
760 config_vars=config_vars)
762 if args.compiler ==
'default':
764 return (python38_config,)
765 elif os.uname()[0] ==
'Darwin':
769 return (python38_config,)
770 elif platform.machine() ==
'aarch64':
774 return (python39_config,)
780 elif args.compiler ==
'python3.6':
781 return (python36_config,)
782 elif args.compiler ==
'python3.7':
783 return (python37_config,)
784 elif args.compiler ==
'python3.8':
785 return (python38_config,)
786 elif args.compiler ==
'python3.9':
787 return (python39_config,)
788 elif args.compiler ==
'python3.10':
789 return (python310_config,)
790 elif args.compiler ==
'pypy':
791 return (pypy27_config,)
792 elif args.compiler ==
'pypy3':
793 return (pypy32_config,)
794 elif args.compiler ==
'python_alpine':
795 return (python38_config,)
796 elif args.compiler ==
'all_the_cpythons':
805 raise Exception(
'Compiler %s not supported.' % args.compiler)
820 self.
config.job_spec([
'tools/run_tests/helper_scripts/run_ruby.sh'],
821 timeout_seconds=10 * 60,
822 environ=_FORCE_ENVIRON_FOR_WRAPPERS)
825 'src/ruby/end2end/sig_handling_test.rb',
826 'src/ruby/end2end/channel_state_test.rb',
827 'src/ruby/end2end/channel_closing_test.rb',
828 'src/ruby/end2end/sig_int_during_channel_watch_test.rb',
829 'src/ruby/end2end/killed_client_thread_test.rb',
830 'src/ruby/end2end/forking_client_test.rb',
831 'src/ruby/end2end/grpc_class_init_test.rb',
832 'src/ruby/end2end/multiple_killed_watching_threads_test.rb',
833 'src/ruby/end2end/load_grpc_with_gc_stress_test.rb',
834 'src/ruby/end2end/client_memory_usage_test.rb',
835 'src/ruby/end2end/package_with_underscore_test.rb',
836 'src/ruby/end2end/graceful_sig_handling_test.rb',
837 'src/ruby/end2end/graceful_sig_stop_test.rb',
838 'src/ruby/end2end/errors_load_before_grpc_lib_test.rb',
839 'src/ruby/end2end/logger_load_before_grpc_lib_test.rb',
840 'src/ruby/end2end/status_codes_load_before_grpc_lib_test.rb',
841 'src/ruby/end2end/call_credentials_timeout_test.rb',
842 'src/ruby/end2end/call_credentials_returning_bad_metadata_doesnt_kill_background_thread_test.rb'
845 self.
config.job_spec([
'ruby', test],
847 timeout_seconds=20 * 60,
848 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
852 return [[
'tools/run_tests/helper_scripts/pre_build_ruby.sh']]
855 return [[
'tools/run_tests/helper_scripts/build_ruby.sh']]
858 """Extra environment variables set for pre_build_steps and build_steps jobs."""
862 return [[
'tools/run_tests/helper_scripts/post_tests_ruby.sh']]
881 if self.
args.compiler ==
'default':
895 with open(
'src/csharp/tests.json')
as f:
896 tests_by_assembly = json.load(f)
898 msbuild_config = _MSBUILD_CONFIG[self.
config.build_config]
899 nunit_args = [
'--labels=All',
'--noresult',
'--workers=1']
903 if test_runtime ==
'coreclr':
904 assembly_extension =
'.dll'
905 assembly_subdir =
'bin/%s/netcoreapp3.1' % msbuild_config
906 runtime_cmd = [
'dotnet',
'exec']
907 elif test_runtime ==
'mono':
908 assembly_extension =
'.exe'
909 assembly_subdir =
'bin/%s/net45' % msbuild_config
914 runtime_cmd = [
'mono',
'--arch=64']
916 runtime_cmd = [
'mono']
918 raise Exception(
'Illegal runtime "%s" was specified.')
920 for assembly
in six.iterkeys(tests_by_assembly):
921 assembly_file =
'src/csharp/%s/%s/%s%s' % (
922 assembly, assembly_subdir, assembly, assembly_extension)
925 for test
in tests_by_assembly[assembly]:
926 cmdline = runtime_cmd + [assembly_file,
927 '--test=%s' % test] + nunit_args
931 shortname=
'csharp.%s.%s' % (test_runtime, test),
932 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
937 return [[
'tools\\run_tests\\helper_scripts\\pre_build_csharp.bat']]
939 return [[
'tools/run_tests/helper_scripts/pre_build_csharp.sh']]
943 return [[
'tools\\run_tests\\helper_scripts\\build_csharp.bat']]
945 return [[
'tools/run_tests/helper_scripts/build_csharp.sh']]
948 """Extra environment variables set for pre_build_steps and build_steps jobs."""
956 return [[
'tools\\run_tests\\helper_scripts\\post_tests_csharp.bat']]
958 return [[
'tools/run_tests/helper_scripts/post_tests_csharp.sh']]
961 return 'tools/dockerfile/test/csharp_%s_%s' % (
981 [
'src/objective-c/tests/build_one_example.sh'],
982 timeout_seconds=20 * 60,
983 shortname=
'ios-buildtest-example-sample-frameworks',
987 'EXAMPLE_PATH':
'src/objective-c/examples/Sample',
993 [
'src/objective-c/tests/build_one_example.sh'],
994 timeout_seconds=20 * 60,
995 shortname=
'ios-buildtest-example-switftsample',
998 'SCHEME':
'SwiftSample',
999 'EXAMPLE_PATH':
'src/objective-c/examples/SwiftSample'
1019 [
'test/core/iomgr/ios/CFStreamTests/build_and_run_tests.sh'],
1020 timeout_seconds=60 * 60,
1021 shortname=
'ios-test-cfstream-tests',
1023 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
1027 self.
config.job_spec([
'src/objective-c/tests/run_one_test.sh'],
1028 timeout_seconds=60 * 60,
1029 shortname=
'ios-test-cronettests',
1031 environ={
'SCHEME':
'CronetTests'}))
1034 self.
config.job_spec([
'src/objective-c/tests/run_one_test.sh'],
1035 timeout_seconds=30 * 60,
1036 shortname=
'ios-perf-test',
1038 environ={
'SCHEME':
'PerfTests'}))
1042 self.
config.job_spec([
'src/objective-c/tests/run_one_test.sh'],
1043 timeout_seconds=30 * 60,
1044 shortname=
'ios-perf-test-posix',
1046 environ={
'SCHEME':
'PerfTestsPosix'}))
1050 self.
config.job_spec([
'test/cpp/ios/build_and_run_tests.sh'],
1051 timeout_seconds=60 * 60,
1052 shortname=
'ios-cpp-test-cronet',
1054 environ=_FORCE_ENVIRON_FOR_WRAPPERS))
1057 self.
config.job_spec([
'src/objective-c/tests/run_one_test.sh'],
1058 timeout_seconds=30 * 60,
1059 shortname=
'tvos-test-basictests',
1062 'SCHEME':
'TvTests',
1075 """Extra environment variables set for pre_build_steps and build_steps jobs."""
1097 with open(
'tools/run_tests/sanity/sanity_tests.yaml',
'r')
as f:
1098 environ = {
'TEST':
'true'}
1100 environ[
'CLANG_FORMAT_SKIP_DOCKER'] =
'true'
1101 environ[
'CLANG_TIDY_SKIP_DOCKER'] =
'true'
1102 environ[
'IWYU_SKIP_DOCKER'] =
'true'
1107 environ[
'DISABLE_BAZEL_WRAPPER'] =
'true'
1110 timeout_seconds=30 * 60,
1112 cpu_cost=cmd.get(
'cpu_cost', 1))
1113 for cmd
in yaml.load(f)
1123 """Extra environment variables set for pre_build_steps and build_steps jobs."""
1130 return 'tools/dockerfile/test/sanity'
1137 with open(
'tools/run_tests/generated/configs.json')
as f:
1139 (cfg[
'config'],
Config(**cfg))
for cfg
in ast.literal_eval(f.read()))
1161 """Environment variables set for each build step."""
1162 environ = {
'CONFIG': cfg,
'GRPC_RUN_TESTS_JOBS':
str(args.jobs)}
1163 msbuild_cfg = _MSBUILD_CONFIG.get(cfg)
1165 environ[
'MSBUILD_CONFIG'] = msbuild_cfg
1166 environ.update(extra_env)
1171 """Returns msbuild cmdline option for selected architecture."""
1172 if arch ==
'default' or arch ==
'x86':
1173 return '/p:Platform=Win32'
1175 return '/p:Platform=x64'
1177 print(
'Architecture %s not supported.' % arch)
1182 """Checks that architecture option is valid."""
1187 runtime_machine = platform.machine()
1188 runtime_arch = platform.architecture()[0]
1189 if arch ==
'default':
1191 elif runtime_machine ==
'x86_64' and runtime_arch ==
'64bit' and arch ==
'x64':
1193 elif runtime_machine ==
'x86_64' and runtime_arch ==
'32bit' and arch ==
'x86':
1195 elif runtime_machine ==
'aarch64' and runtime_arch ==
'64bit' and arch ==
'arm64':
1199 'Architecture %s does not match current runtime architecture.' %
1203 if args.arch !=
'default':
1204 print(
'Architecture %s not supported on current platform.' %
1210 """Returns suffix to dockerfile dir to use."""
1211 if arch ==
'default' or arch ==
'x64':
1215 elif arch ==
'arm64':
1218 print(
'Architecture %s not supported with current settings.' % arch)
1223 """Auxiliary function to parse the "runs_per_test" flag.
1226 A positive integer or 0, the latter indicating an infinite number of
1230 argparse.ArgumentTypeError: Upon invalid input.
1232 if arg_str ==
'inf':
1240 msg =
'\'{}\' is not a positive integer or \'inf\''.
format(arg_str)
1241 raise argparse.ArgumentTypeError(msg)
1245 pct = float(arg_str)
1246 if pct > 100
or pct < 0:
1247 raise argparse.ArgumentTypeError(
1248 "'%f' is not a valid percentage in the [0, 100] range" % pct)
1254 return abs(a - b) <=
max(rel_tol *
max(abs(a), abs(b)), abs_tol)
1258 """Shut down legacy version of port server."""
1261 urllib.request.urlopen(
'http://localhost:%d/version_number' %
1267 urllib.request.urlopen(
'http://localhost:%d/quitquitquit' %
1268 legacy_server_port).
read()
1272 """Calculate number of runs and failures for a particular test.
1275 list_of_results: (List) of JobResult object.
1277 A tuple of total number of runs and failures.
1279 num_runs =
len(list_of_results)
1281 for jobresult
in list_of_results:
1282 if jobresult.retries > 0:
1283 num_runs += jobresult.retries
1284 if jobresult.num_failures > 0:
1285 num_failures += jobresult.num_failures
1286 return num_runs, num_failures
1290 """Represents error type in _build_and_run."""
1294 POST_TEST = object()
1302 """Do one pass of building & running tests."""
1304 num_failures, resultset = jobset.run(build_steps,
1306 stop_on_failure=
True,
1307 newline_on_success=newline_on_success,
1310 return [BuildAndRunError.BUILD]
1314 report_utils.render_junit_xml_report(
1315 resultset, xml_report, suite_name=args.report_suite_name)
1320 subprocess.Popen([
'tools/run_tests/python_utils/antagonist.py'])
1321 for _
in range(0, args.antagonists)
1323 start_port_server.start_port_server()
1325 num_test_failures = 0
1327 infinite_runs = runs_per_test == 0
1328 one_run =
set(spec
for language
in languages
1329 for spec
in language.test_specs()
1330 if (re.search(args.regex, spec.shortname)
and
1331 (args.regex_exclude ==
'' or
1332 not re.search(args.regex_exclude, spec.shortname))))
1335 if args.travis
and args.max_time <= 0:
1336 massaged_one_run = sorted(one_run, key=
lambda x: x.cpu_cost)
1340 massaged_one_run = list(
1342 num_jobs =
len(massaged_one_run)
1346 sample_size =
int(num_jobs * args.sample_percent / 100.0)
1347 massaged_one_run = random.sample(massaged_one_run, sample_size)
1348 if not isclose(args.sample_percent, 100.0):
1349 assert args.runs_per_test == 1,
"Can't do sampling (-p) over multiple runs (-n)."
1350 print(
"Running %d tests out of %d (~%d%%)" %
1351 (sample_size, num_jobs, args.sample_percent))
1353 assert len(massaged_one_run
1354 ) > 0,
'Must have at least one test for a -n inf run'
1355 runs_sequence = (itertools.repeat(massaged_one_run)
if infinite_runs
1356 else itertools.repeat(massaged_one_run, runs_per_test))
1357 all_runs = itertools.chain.from_iterable(runs_sequence)
1359 if args.quiet_success:
1362 'Running tests quietly, only failing tests will be reported',
1364 num_test_failures, resultset = jobset.run(
1367 newline_on_success=newline_on_success,
1371 stop_on_failure=args.stop_on_failure,
1372 quiet_success=args.quiet_success,
1373 max_time=args.max_time)
1375 for k, v
in sorted(resultset.items()):
1377 if num_failures > 0:
1378 if num_failures == num_runs:
1379 jobset.message(
'FAILED', k, do_newline=
True)
1381 jobset.message(
'FLAKE',
1382 '%s [%d/%d runs flaked]' %
1383 (k, num_failures, num_runs),
1386 for antagonist
in antagonists:
1388 if args.bq_result_table
and resultset:
1389 upload_extra_fields = {
1390 'compiler': args.compiler,
1391 'config': args.config,
1392 'iomgr_platform': args.iomgr_platform,
1393 'language': args.language[
1400 upload_extra_fields)
1401 except NameError
as e:
1404 if xml_report
and resultset:
1405 report_utils.render_junit_xml_report(
1408 suite_name=args.report_suite_name,
1409 multi_target=args.report_multi_target)
1411 number_failures, _ = jobset.run(post_tests_steps,
1413 stop_on_failure=
False,
1414 newline_on_success=newline_on_success,
1419 out.append(BuildAndRunError.POST_TEST)
1420 if num_test_failures:
1421 out.append(BuildAndRunError.TEST)
1427 argp = argparse.ArgumentParser(description=
'Run grpc tests.')
1428 argp.add_argument(
'-c',
1430 choices=sorted(_CONFIGS.keys()),
1436 type=runs_per_test_type,
1437 help=
'A positive integer or "inf". If "inf", all tests will run in an '
1438 'infinite loop. Especially useful in combination with "-f"')
1439 argp.add_argument(
'-r',
'--regex', default=
'.*', type=str)
1440 argp.add_argument(
'--regex_exclude', default=
'', type=str)
1441 argp.add_argument(
'-j',
'--jobs', default=multiprocessing.cpu_count(), type=int)
1442 argp.add_argument(
'-s',
'--slowdown', default=1.0, type=float)
1443 argp.add_argument(
'-p',
1447 help=
'Run a random sample with that percentage of tests')
1452 action=
'store_const',
1454 help=
'When set, indicates that the script is running on CI (= not locally).'
1456 argp.add_argument(
'--newline_on_success',
1458 action=
'store_const',
1460 argp.add_argument(
'-l',
1462 choices=sorted(_LANGUAGES.keys()),
1465 argp.add_argument(
'-S',
1466 '--stop_on_failure',
1468 action=
'store_const',
1470 argp.add_argument(
'--use_docker',
1472 action=
'store_const',
1474 help=
'Run all the tests under docker. That provides ' +
1475 'additional isolation and prevents the need to install ' +
1476 'language specific prerequisites. Only available on Linux.')
1480 action=
'store_const',
1483 'Allow flaky tests to show as passing (re-runs failed tests up to five times)'
1487 choices=[
'default',
'x86',
'x64',
'arm64'],
1490 'Selects architecture to target. For some platforms "default" is the only supported choice.'
1498 'gcc10.2_openssl102',
1517 'cmake_ninja_vs2017',
1524 'Selects compiler to use. Allowed values depend on the platform and language.'
1526 argp.add_argument(
'--iomgr_platform',
1527 choices=[
'native',
'gevent',
'asyncio'],
1529 help=
'Selects iomgr platform to build on')
1530 argp.add_argument(
'--build_only',
1532 action=
'store_const',
1534 help=
'Perform all the build steps but don\'t run any tests.')
1535 argp.add_argument(
'--measure_cpu_costs',
1537 action=
'store_const',
1539 help=
'Measure the cpu costs of tests')
1540 argp.add_argument(
'-a',
'--antagonists', default=0, type=int)
1541 argp.add_argument(
'-x',
1545 help=
'Generates a JUnit-compatible XML report')
1546 argp.add_argument(
'--report_suite_name',
1549 help=
'Test suite name to use in generated JUnit XML report')
1551 '--report_multi_target',
1554 action=
'store_const',
1555 help=
'Generate separate XML report for each test job (Looks better in UIs).'
1560 action=
'store_const',
1563 'Don\'t print anything when a test passes. Passing tests also will not be reported in XML report. '
1564 +
'Useful when running many iterations of each test (argument -n).')
1566 '--force_default_poller',
1568 action=
'store_const',
1570 help=
'Don\'t try to iterate over many polling strategies when they exist')
1572 '--force_use_pollers',
1575 help=
'Only use the specified comma-delimited list of polling engines. '
1576 'Example: --force_use_pollers epoll1,poll '
1577 ' (This flag has no effect if --force_default_poller flag is also used)')
1578 argp.add_argument(
'--max_time',
1581 help=
'Maximum test runtime in seconds')
1582 argp.add_argument(
'--bq_result_table',
1586 help=
'Upload test results to a specified BQ table.')
1587 args = argp.parse_args()
1590 shortname_to_cpu = {}
1592 if args.force_default_poller:
1593 _POLLING_STRATEGIES = {}
1594 elif args.force_use_pollers:
1595 _POLLING_STRATEGIES[
platform_string()] = args.force_use_pollers.split(
',')
1597 jobset.measure_cpu_costs = args.measure_cpu_costs
1600 run_config = _CONFIGS[args.config]
1601 build_config = run_config.build_config
1605 _FORCE_ENVIRON_FOR_WRAPPERS = {
'GRPC_TRACE':
'api'}
1607 languages =
set(_LANGUAGES[l]
for l
in args.language)
1609 l.configure(run_config, args)
1611 if len(languages) != 1:
1612 print(
'Building multiple languages simultaneously is not supported!')
1619 print(
'Seen --use_docker flag, will run tests under docker.')
1622 'IMPORTANT: The changes you are testing need to be locally committed'
1625 'because only the committed changes in the current branch will be')
1626 print(
'copied to the docker environment.')
1629 dockerfile_dirs =
set([l.dockerfile_dir()
for l
in languages])
1630 if len(dockerfile_dirs) > 1:
1631 print(
'Languages to be tested require running under different docker '
1637 child_argv = [arg
for arg
in sys.argv
if not arg ==
'--use_docker']
1638 run_tests_cmd =
'python3 tools/run_tests/run_tests.py %s' %
' '.join(
1641 env = os.environ.copy()
1642 env[
'DOCKERFILE_DIR'] = dockerfile_dir
1643 env[
'DOCKER_RUN_SCRIPT'] =
'tools/run_tests/dockerize/docker_run.sh'
1644 env[
'DOCKER_RUN_SCRIPT_COMMAND'] = run_tests_cmd
1646 retcode = subprocess.call(
1647 'tools/run_tests/dockerize/build_and_run_docker.sh',
1659 jobset.JobSpec(cmdline,
1661 build_config, extra_env=l.build_steps_environ()),
1662 timeout_seconds=_PRE_BUILD_STEP_TIMEOUT_SECONDS,
1665 for cmdline
in l.pre_build_steps()))
1670 jobset.JobSpec(cmdline,
1672 build_config, extra_env=l.build_steps_environ()),
1673 timeout_seconds=
None)
1675 for cmdline
in l.build_steps()))
1678 post_tests_steps = list(
1680 jobset.JobSpec(cmdline,
1682 build_config, extra_env=l.build_steps_environ()))
1684 for cmdline
in l.post_tests_steps()))
1685 runs_per_test = args.runs_per_test
1688 newline_on_success=args.newline_on_success,
1689 xml_report=args.xml_report,
1690 build_only=args.build_only)
1692 jobset.message(
'SUCCESS',
'All tests passed', do_newline=
True)
1694 jobset.message(
'FAILED',
'Some tests failed', do_newline=
True)
1702 if BuildAndRunError.BUILD
in errors:
1704 if BuildAndRunError.TEST
in errors:
1706 if BuildAndRunError.POST_TEST
in errors: