1#!/usr/bin/env vpython3 2# 3# Copyright 2013 The Chromium Authors 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Runs all types of tests from one unified interface.""" 8 9from __future__ import absolute_import 10import argparse 11import collections 12import contextlib 13import io 14import itertools 15import logging 16import os 17import re 18import shlex 19import shutil 20import signal 21import sys 22import tempfile 23import threading 24import traceback 25import unittest 26 27# Import _strptime before threaded code. datetime.datetime.strptime is 28# threadsafe except for the initial import of the _strptime module. 29# See http://crbug.com/724524 and https://bugs.python.org/issue7980. 30import _strptime # pylint: disable=unused-import 31 32# pylint: disable=ungrouped-imports 33from pylib.constants import host_paths 34 35if host_paths.DEVIL_PATH not in sys.path: 36 sys.path.append(host_paths.DEVIL_PATH) 37 38from devil import base_error 39from devil.utils import reraiser_thread 40from devil.utils import run_tests_helper 41 42from pylib import constants 43from pylib.base import base_test_result 44from pylib.base import environment_factory 45from pylib.base import output_manager 46from pylib.base import output_manager_factory 47from pylib.base import test_instance_factory 48from pylib.base import test_run_factory 49from pylib.results import json_results 50from pylib.results import report_results 51from pylib.results.presentation import test_results_presentation 52from pylib.utils import local_utils 53from pylib.utils import logdog_helper 54from pylib.utils import logging_utils 55from pylib.utils import test_filter 56 57from py_utils import contextlib_ext 58 59from lib.results import result_sink # pylint: disable=import-error 60 61_DEVIL_STATIC_CONFIG_FILE = os.path.abspath(os.path.join( 62 host_paths.DIR_SOURCE_ROOT, 'build', 'android', 'devil_config.json')) 63 64_RERUN_FAILED_TESTS_FILE = 'rerun_failed_tests.filter' 65 66 67def _RealPath(arg): 68 if arg.startswith('//'): 69 arg = os.path.abspath(os.path.join(host_paths.DIR_SOURCE_ROOT, 70 arg[2:].replace('/', os.sep))) 71 return os.path.realpath(arg) 72 73 74def AddTestLauncherOptions(parser): 75 """Adds arguments mirroring //base/test/launcher. 76 77 Args: 78 parser: The parser to which arguments should be added. 79 Returns: 80 The given parser. 81 """ 82 parser.add_argument( 83 '--test-launcher-retry-limit', 84 '--test_launcher_retry_limit', 85 '--num_retries', '--num-retries', 86 '--isolated-script-test-launcher-retry-limit', 87 dest='num_retries', type=int, default=2, 88 help='Number of retries for a test before ' 89 'giving up (default: %(default)s).') 90 parser.add_argument( 91 '--test-launcher-summary-output', 92 '--json-results-file', 93 dest='json_results_file', type=os.path.realpath, 94 help='If set, will dump results in JSON form to the specified file. ' 95 'Note that this will also trigger saving per-test logcats to ' 96 'logdog.') 97 parser.add_argument( 98 '--test-launcher-shard-index', 99 type=int, default=os.environ.get('GTEST_SHARD_INDEX', 0), 100 help='Index of the external shard to run.') 101 parser.add_argument( 102 '--test-launcher-total-shards', 103 type=int, default=os.environ.get('GTEST_TOTAL_SHARDS', 1), 104 help='Total number of external shards.') 105 106 test_filter.AddFilterOptions(parser) 107 108 return parser 109 110 111def AddCommandLineOptions(parser): 112 """Adds arguments to support passing command-line flags to the device.""" 113 parser.add_argument( 114 '--device-flags-file', 115 type=os.path.realpath, 116 help='The relative filepath to a file containing ' 117 'command-line flags to set on the device') 118 parser.add_argument( 119 '--use-apk-under-test-flags-file', 120 action='store_true', 121 help='Wether to use the flags file for the apk under test. If set, ' 122 "the filename will be looked up in the APK's PackageInfo.") 123 parser.add_argument('--variations-test-seed-path', 124 type=os.path.relpath, 125 default=None, 126 help='Path to variations seed file.') 127 parser.add_argument('--webview-variations-test-seed-path', 128 type=os.path.relpath, 129 default=None, 130 help='Path to variations seed file for WebView.') 131 132 parser.set_defaults(allow_unknown=True) 133 parser.set_defaults(command_line_flags=None) 134 135 136def AddTracingOptions(parser): 137 # TODO(shenghuazhang): Move this into AddCommonOptions once it's supported 138 # for all test types. 139 parser.add_argument( 140 '--trace-output', 141 metavar='FILENAME', type=os.path.realpath, 142 help='Path to save test_runner trace json output to.') 143 144 parser.add_argument( 145 '--trace-all', 146 action='store_true', 147 help='Whether to trace all function calls.') 148 149 150def AddCommonOptions(parser): 151 """Adds all common options to |parser|.""" 152 153 default_build_type = os.environ.get('BUILDTYPE', 'Debug') 154 155 debug_or_release_group = parser.add_mutually_exclusive_group() 156 debug_or_release_group.add_argument( 157 '--debug', 158 action='store_const', const='Debug', dest='build_type', 159 default=default_build_type, 160 help='If set, run test suites under out/Debug. ' 161 'Default is env var BUILDTYPE or Debug.') 162 debug_or_release_group.add_argument( 163 '--release', 164 action='store_const', const='Release', dest='build_type', 165 help='If set, run test suites under out/Release. ' 166 'Default is env var BUILDTYPE or Debug.') 167 168 parser.add_argument( 169 '--break-on-failure', '--break_on_failure', 170 dest='break_on_failure', action='store_true', 171 help='Whether to break on failure.') 172 173 # TODO(jbudorick): Remove this once everything has switched to platform 174 # mode. 175 parser.add_argument( 176 '--enable-platform-mode', 177 action='store_true', 178 help='Run the test scripts in platform mode, which ' 179 'conceptually separates the test runner from the ' 180 '"device" (local or remote, real or emulated) on ' 181 'which the tests are running. [experimental]') 182 183 parser.add_argument( 184 '-e', '--environment', 185 default='local', choices=constants.VALID_ENVIRONMENTS, 186 help='Test environment to run in (default: %(default)s).') 187 188 parser.add_argument( 189 '--local-output', 190 action='store_true', 191 help='Whether to archive test output locally and generate ' 192 'a local results detail page.') 193 194 parser.add_argument('--list-tests', 195 action='store_true', 196 help='List available tests and exit.') 197 198 parser.add_argument('--wrapper-script-args', 199 help='A string of args that were passed to the wrapper ' 200 'script. This should probably not be edited by a ' 201 'user as it is passed by the wrapper itself.') 202 203 class FastLocalDevAction(argparse.Action): 204 def __call__(self, parser, namespace, values, option_string=None): 205 namespace.enable_concurrent_adb = True 206 namespace.enable_device_cache = True 207 namespace.extract_test_list_from_filter = True 208 namespace.local_output = True 209 namespace.num_retries = 0 210 namespace.skip_clear_data = True 211 namespace.use_persistent_shell = True 212 213 parser.add_argument( 214 '--fast-local-dev', 215 type=bool, 216 nargs=0, 217 action=FastLocalDevAction, 218 help='Alias for: --num-retries=0 --enable-device-cache ' 219 '--enable-concurrent-adb --skip-clear-data ' 220 '--extract-test-list-from-filter --use-persistent-shell --local-output') 221 222 # TODO(jbudorick): Remove this once downstream bots have switched to 223 # api.test_results. 224 parser.add_argument( 225 '--flakiness-dashboard-server', 226 dest='flakiness_dashboard_server', 227 help=argparse.SUPPRESS) 228 parser.add_argument( 229 '--gs-results-bucket', 230 help='Google Storage bucket to upload results to.') 231 232 parser.add_argument( 233 '--output-directory', 234 dest='output_directory', type=os.path.realpath, 235 help='Path to the directory in which build files are' 236 ' located (must include build type). This will take' 237 ' precedence over --debug and --release') 238 parser.add_argument( 239 '-v', '--verbose', 240 dest='verbose_count', default=0, action='count', 241 help='Verbose level (multiple times for more)') 242 243 parser.add_argument( 244 '--repeat', '--gtest_repeat', '--gtest-repeat', 245 '--isolated-script-test-repeat', 246 dest='repeat', type=int, default=0, 247 help='Number of times to repeat the specified set of tests.') 248 249 # Not useful for junit tests. 250 parser.add_argument( 251 '--use-persistent-shell', 252 action='store_true', 253 help='Uses a persistent shell connection for the adb connection.') 254 255 parser.add_argument('--disable-test-server', 256 action='store_true', 257 help='Disables SpawnedTestServer which doesn' 258 't work with remote adb. ' 259 'WARNING: Will break tests which require the server.') 260 261 # This is currently only implemented for gtests and instrumentation tests. 262 parser.add_argument( 263 '--gtest_also_run_disabled_tests', '--gtest-also-run-disabled-tests', 264 '--isolated-script-test-also-run-disabled-tests', 265 dest='run_disabled', action='store_true', 266 help='Also run disabled tests if applicable.') 267 268 # These are currently only implemented for gtests. 269 parser.add_argument('--isolated-script-test-output', 270 help='If present, store test results on this path.') 271 parser.add_argument('--isolated-script-test-perf-output', 272 help='If present, store chartjson results on this path.') 273 274 AddTestLauncherOptions(parser) 275 276 277def ProcessCommonOptions(args): 278 """Processes and handles all common options.""" 279 run_tests_helper.SetLogLevel(args.verbose_count, add_handler=False) 280 if args.verbose_count > 0: 281 handler = logging_utils.ColorStreamHandler() 282 else: 283 handler = logging.StreamHandler(sys.stdout) 284 handler.setFormatter(run_tests_helper.CustomFormatter()) 285 logging.getLogger().addHandler(handler) 286 287 constants.SetBuildType(args.build_type) 288 if args.output_directory: 289 constants.SetOutputDirectory(args.output_directory) 290 291 292def AddDeviceOptions(parser): 293 """Adds device options to |parser|.""" 294 295 parser = parser.add_argument_group('device arguments') 296 297 parser.add_argument( 298 '--adb-path', 299 type=os.path.realpath, 300 help='Specify the absolute path of the adb binary that ' 301 'should be used.') 302 parser.add_argument( 303 '--use-local-devil-tools', 304 action='store_true', 305 help='Use locally built versions of tools used by devil_chromium.') 306 parser.add_argument('--denylist-file', 307 type=os.path.realpath, 308 help='Device denylist file.') 309 parser.add_argument( 310 '-d', '--device', nargs='+', 311 dest='test_devices', 312 help='Target device(s) for the test suite to run on.') 313 parser.add_argument( 314 '--enable-concurrent-adb', 315 action='store_true', 316 help='Run multiple adb commands at the same time, even ' 317 'for the same device.') 318 parser.add_argument( 319 '--enable-device-cache', 320 action='store_true', 321 help='Cache device state to disk between runs') 322 parser.add_argument( 323 '--skip-clear-data', 324 action='store_true', 325 help='Do not wipe app data between tests. Use this to ' 326 'speed up local development and never on bots ' 327 '(increases flakiness)') 328 parser.add_argument( 329 '--recover-devices', 330 action='store_true', 331 help='Attempt to recover devices prior to the final retry. Warning: ' 332 'this will cause all devices to reboot.') 333 parser.add_argument( 334 '--tool', 335 dest='tool', 336 help='Run the test under a tool ' 337 '(use --tool help to list them)') 338 339 parser.add_argument( 340 '--upload-logcats-file', 341 action='store_true', 342 dest='upload_logcats_file', 343 help='Whether to upload logcat file to logdog.') 344 345 logcat_output_group = parser.add_mutually_exclusive_group() 346 logcat_output_group.add_argument( 347 '--logcat-output-dir', type=os.path.realpath, 348 help='If set, will dump logcats recorded during test run to directory. ' 349 'File names will be the device ids with timestamps.') 350 logcat_output_group.add_argument( 351 '--logcat-output-file', type=os.path.realpath, 352 help='If set, will merge logcats recorded during test run and dump them ' 353 'to the specified file.') 354 355 parser.add_argument( 356 '--force-main-user', 357 action='store_true', 358 help='Force the applicable adb commands to run with "--user" param set ' 359 'to the id of the main user on device. Only use when the main user is a ' 360 'secondary user, e.g. Android Automotive OS.') 361 362 363def AddEmulatorOptions(parser): 364 """Adds emulator-specific options to |parser|.""" 365 parser = parser.add_argument_group('emulator arguments') 366 367 parser.add_argument( 368 '--avd-config', 369 type=os.path.realpath, 370 help='Path to the avd config textpb. ' 371 '(See //tools/android/avd/proto/ for message definition' 372 ' and existing textpb files.)') 373 parser.add_argument( 374 '--emulator-count', 375 type=int, 376 default=1, 377 help='Number of emulators to use.') 378 parser.add_argument( 379 '--emulator-window', 380 action='store_true', 381 default=False, 382 help='Enable graphical window display on the emulator.') 383 parser.add_argument( 384 '--emulator-debug-tags', 385 help='Comma-separated list of debug tags. This can be used to enable or ' 386 'disable debug messages from specific parts of the emulator, e.g. ' 387 'init,snapshot. See "emulator -help-debug-tags" ' 388 'for a full list of tags.') 389 parser.add_argument( 390 '--emulator-enable-network', 391 action='store_true', 392 help='Enable the network (WiFi and mobile data) on the emulator.') 393 394 395def AddGTestOptions(parser): 396 """Adds gtest options to |parser|.""" 397 398 parser = parser.add_argument_group('gtest arguments') 399 400 parser.add_argument( 401 '--additional-apk', 402 action='append', dest='additional_apks', default=[], 403 type=_RealPath, 404 help='Additional apk that must be installed on ' 405 'the device when the tests are run.') 406 parser.add_argument( 407 '--app-data-file', 408 action='append', dest='app_data_files', 409 help='A file path relative to the app data directory ' 410 'that should be saved to the host.') 411 parser.add_argument( 412 '--app-data-file-dir', 413 help='Host directory to which app data files will be' 414 ' saved. Used with --app-data-file.') 415 parser.add_argument( 416 '--enable-xml-result-parsing', 417 action='store_true', help=argparse.SUPPRESS) 418 parser.add_argument( 419 '--executable-dist-dir', 420 type=os.path.realpath, 421 help="Path to executable's dist directory for native" 422 " (non-apk) tests.") 423 parser.add_argument( 424 '--extract-test-list-from-filter', 425 action='store_true', 426 help='When a test filter is specified, and the list of ' 427 'tests can be determined from it, skip querying the ' 428 'device for the list of all tests. Speeds up local ' 429 'development, but is not safe to use on bots (' 430 'http://crbug.com/549214') 431 parser.add_argument( 432 '--gs-test-artifacts-bucket', 433 help=('If present, test artifacts will be uploaded to this Google ' 434 'Storage bucket.')) 435 parser.add_argument( 436 '--render-test-output-dir', 437 help='If present, store rendering artifacts in this path.') 438 parser.add_argument( 439 '--runtime-deps-path', 440 dest='runtime_deps_path', type=os.path.realpath, 441 help='Runtime data dependency file from GN.') 442 parser.add_argument( 443 '-t', '--shard-timeout', 444 dest='shard_timeout', type=int, default=120, 445 help='Timeout to wait for each test (default: %(default)s).') 446 parser.add_argument( 447 '--store-tombstones', 448 dest='store_tombstones', action='store_true', 449 help='Add tombstones in results if crash.') 450 parser.add_argument( 451 '-s', '--suite', 452 dest='suite_name', nargs='+', metavar='SUITE_NAME', required=True, 453 help='Executable name of the test suite to run.') 454 parser.add_argument( 455 '--test-apk-incremental-install-json', 456 type=os.path.realpath, 457 help='Path to install json for the test apk.') 458 parser.add_argument('--test-launcher-batch-limit', 459 dest='test_launcher_batch_limit', 460 type=int, 461 help='The max number of tests to run in a shard. ' 462 'Ignores non-positive ints and those greater than ' 463 'MAX_SHARDS') 464 parser.add_argument( 465 '-w', '--wait-for-java-debugger', action='store_true', 466 help='Wait for java debugger to attach before running any application ' 467 'code. Also disables test timeouts and sets retries=0.') 468 parser.add_argument( 469 '--coverage-dir', 470 type=os.path.realpath, 471 help='Directory in which to place all generated coverage files.') 472 parser.add_argument( 473 '--use-existing-test-data', 474 action='store_true', 475 help='Do not push new files to the device, instead using existing APK ' 476 'and test data. Only use when running the same test for multiple ' 477 'iterations.') 478 # This is currently only implemented for gtests tests. 479 parser.add_argument('--gtest_also_run_pre_tests', 480 '--gtest-also-run-pre-tests', 481 dest='run_pre_tests', 482 action='store_true', 483 help='Also run PRE_ tests if applicable.') 484 485 486def AddInstrumentationTestOptions(parser): 487 """Adds Instrumentation test options to |parser|.""" 488 489 parser = parser.add_argument_group('instrumentation arguments') 490 491 parser.add_argument('--additional-apex', 492 action='append', 493 dest='additional_apexs', 494 default=[], 495 type=_RealPath, 496 help='Additional apex that must be installed on ' 497 'the device when the tests are run') 498 parser.add_argument( 499 '--additional-apk', 500 action='append', dest='additional_apks', default=[], 501 type=_RealPath, 502 help='Additional apk that must be installed on ' 503 'the device when the tests are run') 504 parser.add_argument('--forced-queryable-additional-apk', 505 action='append', 506 dest='forced_queryable_additional_apks', 507 default=[], 508 type=_RealPath, 509 help='Configures an additional-apk to be forced ' 510 'to be queryable by other APKs.') 511 parser.add_argument('--instant-additional-apk', 512 action='append', 513 dest='instant_additional_apks', 514 default=[], 515 type=_RealPath, 516 help='Configures an additional-apk to be an instant APK') 517 parser.add_argument( 518 '-A', '--annotation', 519 dest='annotation_str', 520 help='Comma-separated list of annotations. Run only tests with any of ' 521 'the given annotations. An annotation can be either a key or a ' 522 'key-values pair. A test that has no annotation is considered ' 523 '"SmallTest".') 524 # TODO(jbudorick): Remove support for name-style APK specification once 525 # bots are no longer doing it. 526 parser.add_argument( 527 '--apk-under-test', 528 help='Path or name of the apk under test.') 529 parser.add_argument( 530 '--store-data-dependencies-in-temp', 531 action='store_true', 532 help='Store data dependencies in /data/local/tmp/chromium_tests_root') 533 parser.add_argument( 534 '--module', 535 action='append', 536 dest='modules', 537 help='Specify Android App Bundle modules to install in addition to the ' 538 'base module.') 539 parser.add_argument( 540 '--fake-module', 541 action='append', 542 dest='fake_modules', 543 help='Specify Android App Bundle modules to fake install in addition to ' 544 'the real modules.') 545 parser.add_argument( 546 '--additional-locale', 547 action='append', 548 dest='additional_locales', 549 help='Specify locales in addition to the device locale to install splits ' 550 'for when --apk-under-test is an Android App Bundle.') 551 parser.add_argument( 552 '--coverage-dir', 553 type=os.path.realpath, 554 help='Directory in which to place all generated ' 555 'Jacoco coverage files.') 556 parser.add_argument( 557 '--disable-dalvik-asserts', 558 dest='set_asserts', action='store_false', default=True, 559 help='Removes the dalvik.vm.enableassertions property') 560 parser.add_argument( 561 '--proguard-mapping-path', 562 help='.mapping file to use to Deobfuscate java stack traces in test ' 563 'output and logcat.') 564 parser.add_argument( 565 '-E', '--exclude-annotation', 566 dest='exclude_annotation_str', 567 help='Comma-separated list of annotations. Exclude tests with these ' 568 'annotations.') 569 parser.add_argument( 570 '--enable-breakpad-dump', 571 action='store_true', 572 help='Stores any breakpad dumps till the end of the test.') 573 parser.add_argument( 574 '--replace-system-package', 575 type=_RealPath, 576 default=None, 577 help='Use this apk to temporarily replace a system package with the same ' 578 'package name.') 579 parser.add_argument( 580 '--remove-system-package', 581 default=[], 582 action='append', 583 dest='system_packages_to_remove', 584 help='Specifies a system package to remove before testing if it exists ' 585 'on the system. WARNING: THIS WILL PERMANENTLY REMOVE THE SYSTEM APP. ' 586 'Unlike --replace-system-package, the app will not be restored after ' 587 'tests are finished.') 588 parser.add_argument( 589 '--use-voice-interaction-service', 590 help='This can be used to update the voice interaction service to be a ' 591 'custom one. This is useful for mocking assistants. eg: ' 592 'android.assist.service/.MainInteractionService') 593 parser.add_argument( 594 '--use-webview-provider', 595 type=_RealPath, default=None, 596 help='Use this apk as the webview provider during test. ' 597 'The original provider will be restored if possible, ' 598 "on Nougat the provider can't be determined and so " 599 'the system will choose the default provider.') 600 parser.add_argument( 601 '--webview-command-line-arg', 602 default=[], 603 action='append', 604 help="Specifies command line arguments to add to WebView's flag file") 605 parser.add_argument( 606 '--run-setup-command', 607 default=[], 608 action='append', 609 dest='run_setup_commands', 610 help='This can be used to run a custom shell command on the device as a ' 611 'setup step') 612 parser.add_argument( 613 '--run-teardown-command', 614 default=[], 615 action='append', 616 dest='run_teardown_commands', 617 help='This can be used to run a custom shell command on the device as a ' 618 'teardown step') 619 parser.add_argument( 620 '--runtime-deps-path', 621 dest='runtime_deps_path', type=os.path.realpath, 622 help='Runtime data dependency file from GN.') 623 parser.add_argument( 624 '--screenshot-directory', 625 dest='screenshot_dir', type=os.path.realpath, 626 help='Capture screenshots of test failures') 627 parser.add_argument( 628 '--store-tombstones', 629 action='store_true', dest='store_tombstones', 630 help='Add tombstones in results if crash.') 631 parser.add_argument( 632 '--strict-mode', 633 dest='strict_mode', default='testing', 634 help='StrictMode command-line flag set on the device, ' 635 'death/testing to kill the process, off to stop ' 636 'checking, flash to flash only. (default: %(default)s)') 637 parser.add_argument( 638 '--test-apk', 639 required=True, 640 help='Path or name of the apk containing the tests.') 641 parser.add_argument( 642 '--test-apk-as-instant', 643 action='store_true', 644 help='Install the test apk as an instant app. ' 645 'Instant apps run in a more restrictive execution environment.') 646 parser.add_argument( 647 '--test-launcher-batch-limit', 648 dest='test_launcher_batch_limit', 649 type=int, 650 help=('Not actually used for instrumentation tests, but can be used as ' 651 'a proxy for determining if the current run is a retry without ' 652 'patch.')) 653 parser.add_argument( 654 '--timeout-scale', 655 type=float, 656 help='Factor by which timeouts should be scaled.') 657 parser.add_argument( 658 '--is-unit-test', 659 action='store_true', 660 help=('Specify the test suite as composed of unit tests, blocking ' 661 'certain operations.')) 662 parser.add_argument( 663 '-w', '--wait-for-java-debugger', action='store_true', 664 help='Wait for java debugger to attach before running any application ' 665 'code. Also disables test timeouts and sets retries=0.') 666 667 # WPR record mode. 668 parser.add_argument('--wpr-enable-record', 669 action='store_true', 670 default=False, 671 help='If true, WPR server runs in record mode.' 672 'otherwise, runs in replay mode.') 673 674 parser.add_argument( 675 '--approve-app-links', 676 help='Force enables Digital Asset Link verification for the provided ' 677 'package and domain, example usage: --approve-app-links ' 678 'com.android.package:www.example.com') 679 680 # These arguments are suppressed from the help text because they should 681 # only ever be specified by an intermediate script. 682 parser.add_argument( 683 '--apk-under-test-incremental-install-json', 684 help=argparse.SUPPRESS) 685 parser.add_argument( 686 '--test-apk-incremental-install-json', 687 type=os.path.realpath, 688 help=argparse.SUPPRESS) 689 690 691def AddSkiaGoldTestOptions(parser): 692 """Adds Skia Gold test options to |parser|.""" 693 parser = parser.add_argument_group("Skia Gold arguments") 694 parser.add_argument( 695 '--code-review-system', 696 help='A non-default code review system to pass to pass to Gold, if ' 697 'applicable') 698 parser.add_argument( 699 '--continuous-integration-system', 700 help='A non-default continuous integration system to pass to Gold, if ' 701 'applicable') 702 parser.add_argument( 703 '--git-revision', help='The git commit currently being tested.') 704 parser.add_argument( 705 '--gerrit-issue', 706 help='The Gerrit issue this test is being run on, if applicable.') 707 parser.add_argument( 708 '--gerrit-patchset', 709 help='The Gerrit patchset this test is being run on, if applicable.') 710 parser.add_argument( 711 '--buildbucket-id', 712 help='The Buildbucket build ID that this test was triggered from, if ' 713 'applicable.') 714 local_group = parser.add_mutually_exclusive_group() 715 local_group.add_argument( 716 '--local-pixel-tests', 717 action='store_true', 718 default=None, 719 help='Specifies to run the Skia Gold pixel tests in local mode. When run ' 720 'in local mode, uploading to Gold is disabled and traditional ' 721 'generated/golden/diff images are output instead of triage links. ' 722 'Running in local mode also implies --no-luci-auth. If both this ' 723 'and --no-local-pixel-tests are left unset, the test harness will ' 724 'attempt to detect whether it is running on a workstation or not ' 725 'and set the options accordingly.') 726 local_group.add_argument( 727 '--no-local-pixel-tests', 728 action='store_false', 729 dest='local_pixel_tests', 730 help='Specifies to run the Skia Gold pixel tests in non-local (bot) ' 731 'mode. When run in this mode, data is actually uploaded to Gold and ' 732 'triage links are generated. If both this and --local-pixel-tests ' 733 'are left unset, the test harness will attempt to detect whether ' 734 'it is running on a workstation or not and set the options ' 735 'accordingly.') 736 parser.add_argument( 737 '--no-luci-auth', 738 action='store_true', 739 default=False, 740 help="Don't use the serve account provided by LUCI for authentication " 741 'with Skia Gold, instead relying on gsutil to be pre-authenticated. ' 742 'Meant for testing locally instead of on the bots.') 743 parser.add_argument( 744 '--bypass-skia-gold-functionality', 745 action='store_true', 746 default=False, 747 help='Bypass all interaction with Skia Gold, effectively disabling the ' 748 'image comparison portion of any tests that use Gold. Only meant to be ' 749 'used in case a Gold outage occurs and cannot be fixed quickly.') 750 751 752def AddHostsideTestOptions(parser): 753 """Adds hostside test options to |parser|.""" 754 755 parser = parser.add_argument_group('hostside arguments') 756 757 parser.add_argument( 758 '-s', '--test-suite', required=True, 759 help='Hostside test suite to run.') 760 parser.add_argument( 761 '--test-apk-as-instant', 762 action='store_true', 763 help='Install the test apk as an instant app. ' 764 'Instant apps run in a more restrictive execution environment.') 765 parser.add_argument( 766 '--additional-apk', 767 action='append', 768 dest='additional_apks', 769 default=[], 770 type=_RealPath, 771 help='Additional apk that must be installed on ' 772 'the device when the tests are run') 773 parser.add_argument( 774 '--use-webview-provider', 775 type=_RealPath, default=None, 776 help='Use this apk as the webview provider during test. ' 777 'The original provider will be restored if possible, ' 778 "on Nougat the provider can't be determined and so " 779 'the system will choose the default provider.') 780 parser.add_argument( 781 '--tradefed-executable', 782 type=_RealPath, default=None, 783 help='Location of the cts-tradefed script') 784 parser.add_argument( 785 '--tradefed-aapt-path', 786 type=_RealPath, default=None, 787 help='Location of the directory containing aapt binary') 788 parser.add_argument( 789 '--tradefed-adb-path', 790 type=_RealPath, default=None, 791 help='Location of the directory containing adb binary') 792 # The below arguments are not used, but allow us to pass the same arguments 793 # from run_cts.py regardless of type of run (instrumentation/hostside) 794 parser.add_argument( 795 '--apk-under-test', 796 help=argparse.SUPPRESS) 797 parser.add_argument( 798 '--use-apk-under-test-flags-file', 799 action='store_true', 800 help=argparse.SUPPRESS) 801 parser.add_argument( 802 '-E', '--exclude-annotation', 803 dest='exclude_annotation_str', 804 help=argparse.SUPPRESS) 805 806 807def AddJUnitTestOptions(parser): 808 """Adds junit test options to |parser|.""" 809 810 parser = parser.add_argument_group('junit arguments') 811 812 parser.add_argument( 813 '--coverage-on-the-fly', 814 action='store_true', 815 help='Generate coverage data by Jacoco on-the-fly instrumentation.') 816 parser.add_argument( 817 '--coverage-dir', type=os.path.realpath, 818 help='Directory to store coverage info.') 819 parser.add_argument( 820 '--package-filter', 821 help='Filters tests by package.') 822 parser.add_argument( 823 '--runner-filter', 824 help='Filters tests by runner class. Must be fully qualified.') 825 parser.add_argument('--json-config', 826 help='Runs only tests listed in this config.') 827 parser.add_argument( 828 '--shards', 829 type=int, 830 help='Number of shards to run junit tests in parallel on. Only 1 shard ' 831 'is supported when test-filter is specified. Values less than 1 will ' 832 'use auto select.') 833 parser.add_argument('--shard-filter', 834 help='Comma separated list of shard indices to run.') 835 parser.add_argument( 836 '-s', '--test-suite', required=True, 837 help='JUnit test suite to run.') 838 debug_group = parser.add_mutually_exclusive_group() 839 debug_group.add_argument( 840 '-w', '--wait-for-java-debugger', action='store_const', const='8701', 841 dest='debug_socket', help='Alias for --debug-socket=8701') 842 debug_group.add_argument( 843 '--debug-socket', 844 help='Wait for java debugger to attach at specified socket address ' 845 'before running any application code. Also disables test timeouts ' 846 'and sets retries=0.') 847 848 # These arguments are for Android Robolectric tests. 849 parser.add_argument( 850 '--robolectric-runtime-deps-dir', 851 help='Path to runtime deps for Robolectric.') 852 parser.add_argument('--native-libs-dir', 853 help='Path to search for native libraries.') 854 parser.add_argument( 855 '--resource-apk', 856 required=True, 857 help='Path to .ap_ containing binary resources for Robolectric.') 858 859 860def AddLinkerTestOptions(parser): 861 862 parser = parser.add_argument_group('linker arguments') 863 864 parser.add_argument( 865 '--test-apk', 866 type=os.path.realpath, 867 help='Path to the linker test APK.') 868 869 870def AddMonkeyTestOptions(parser): 871 """Adds monkey test options to |parser|.""" 872 873 parser = parser.add_argument_group('monkey arguments') 874 875 parser.add_argument('--browser', 876 required=True, 877 choices=list(constants.PACKAGE_INFO.keys()), 878 metavar='BROWSER', 879 help='Browser under test.') 880 parser.add_argument( 881 '--category', 882 nargs='*', dest='categories', default=[], 883 help='A list of allowed categories. Monkey will only visit activities ' 884 'that are listed with one of the specified categories.') 885 parser.add_argument( 886 '--event-count', 887 default=10000, type=int, 888 help='Number of events to generate (default: %(default)s).') 889 parser.add_argument( 890 '--seed', 891 type=int, 892 help='Seed value for pseudo-random generator. Same seed value generates ' 893 'the same sequence of events. Seed is randomized by default.') 894 parser.add_argument( 895 '--throttle', 896 default=100, type=int, 897 help='Delay between events (ms) (default: %(default)s). ') 898 899 900def AddPythonTestOptions(parser): 901 902 parser = parser.add_argument_group('python arguments') 903 904 parser.add_argument('-s', 905 '--suite', 906 dest='suite_name', 907 metavar='SUITE_NAME', 908 choices=list(constants.PYTHON_UNIT_TEST_SUITES.keys()), 909 help='Name of the test suite to run.') 910 911 912def _CreateClassToFileNameDict(test_apk): 913 """Creates a dict mapping classes to file names from size-info apk.""" 914 constants.CheckOutputDirectory() 915 test_apk_size_info = os.path.join(constants.GetOutDirectory(), 'size-info', 916 os.path.basename(test_apk) + '.jar.info') 917 918 class_to_file_dict = {} 919 # Some tests such as webview_cts_tests use a separately downloaded apk to run 920 # tests. This means the apk may not have been built by the system and hence 921 # no size info file exists. 922 if not os.path.exists(test_apk_size_info): 923 logging.debug('Apk size file not found. %s', test_apk_size_info) 924 return class_to_file_dict 925 926 with open(test_apk_size_info, 'r') as f: 927 for line in f: 928 file_class, file_name = line.rstrip().split(',', 1) 929 # Only want files that are not prebuilt. 930 if file_name.startswith('../../'): 931 class_to_file_dict[file_class] = str( 932 file_name.replace('../../', '//', 1)) 933 934 return class_to_file_dict 935 936 937def _RunPythonTests(args): 938 """Subcommand of RunTestsCommand which runs python unit tests.""" 939 suite_vars = constants.PYTHON_UNIT_TEST_SUITES[args.suite_name] 940 suite_path = suite_vars['path'] 941 suite_test_modules = suite_vars['test_modules'] 942 943 sys.path = [suite_path] + sys.path 944 try: 945 suite = unittest.TestSuite() 946 suite.addTests(unittest.defaultTestLoader.loadTestsFromName(m) 947 for m in suite_test_modules) 948 runner = unittest.TextTestRunner(verbosity=1+args.verbose_count) 949 return 0 if runner.run(suite).wasSuccessful() else 1 950 finally: 951 sys.path = sys.path[1:] 952 953 954_DEFAULT_PLATFORM_MODE_TESTS = [ 955 'gtest', 'hostside', 'instrumentation', 'junit', 'linker', 'monkey' 956] 957 958 959def RunTestsCommand(args, result_sink_client=None): 960 """Checks test type and dispatches to the appropriate function. 961 962 Args: 963 args: argparse.Namespace object. 964 result_sink_client: A ResultSinkClient object. 965 966 Returns: 967 Integer indicated exit code. 968 969 Raises: 970 Exception: Unknown command name passed in, or an exception from an 971 individual test runner. 972 """ 973 command = args.command 974 975 ProcessCommonOptions(args) 976 logging.info('command: %s', ' '.join(sys.argv)) 977 if args.enable_platform_mode or command in _DEFAULT_PLATFORM_MODE_TESTS: 978 return RunTestsInPlatformMode(args, result_sink_client) 979 980 if command == 'python': 981 return _RunPythonTests(args) 982 raise Exception('Unknown test type.') 983 984 985def _SinkTestResult(test_result, test_file_name, result_sink_client): 986 """Upload test result to result_sink. 987 988 Args: 989 test_result: A BaseTestResult object 990 test_file_name: A string representing the file location of the test 991 result_sink_client: A ResultSinkClient object 992 993 Returns: 994 N/A 995 """ 996 # Some tests put in non utf-8 char as part of the test 997 # which breaks uploads, so need to decode and re-encode. 998 log_decoded = test_result.GetLog() 999 if isinstance(log_decoded, bytes): 1000 log_decoded = log_decoded.decode('utf-8', 'replace') 1001 html_artifact = '' 1002 https_artifacts = [] 1003 for link_name, link_url in sorted(test_result.GetLinks().items()): 1004 if link_url.startswith('https:'): 1005 https_artifacts.append('<li><a target="_blank" href=%s>%s</a></li>' % 1006 (link_url, link_name)) 1007 else: 1008 logging.info('Skipping non-https link %r (%s) for test %s.', link_name, 1009 link_url, test_result.GetName()) 1010 if https_artifacts: 1011 html_artifact += '<ul>%s</ul>' % '\n'.join(https_artifacts) 1012 result_sink_client.Post(test_result.GetNameForResultSink(), 1013 test_result.GetType(), 1014 test_result.GetDuration(), 1015 log_decoded, 1016 test_file_name, 1017 variant=test_result.GetVariantForResultSink(), 1018 failure_reason=test_result.GetFailureReason(), 1019 html_artifact=html_artifact) 1020 1021 1022_SUPPORTED_IN_PLATFORM_MODE = [ 1023 # TODO(jbudorick): Add support for more test types. 1024 'gtest', 1025 'hostside', 1026 'instrumentation', 1027 'junit', 1028 'linker', 1029 'monkey', 1030] 1031 1032 1033def RunTestsInPlatformMode(args, result_sink_client=None): 1034 1035 def infra_error(message): 1036 logging.fatal(message) 1037 sys.exit(constants.INFRA_EXIT_CODE) 1038 1039 if args.command not in _SUPPORTED_IN_PLATFORM_MODE: 1040 infra_error('%s is not yet supported in platform mode' % args.command) 1041 1042 ### Set up sigterm handler. 1043 1044 contexts_to_notify_on_sigterm = [] 1045 def unexpected_sigterm(_signum, _frame): 1046 msg = [ 1047 'Received SIGTERM. Shutting down.', 1048 ] 1049 for live_thread in threading.enumerate(): 1050 # pylint: disable=protected-access 1051 thread_stack = ''.join(traceback.format_stack( 1052 sys._current_frames()[live_thread.ident])) 1053 msg.extend([ 1054 'Thread "%s" (ident: %s) is currently running:' % ( 1055 live_thread.name, live_thread.ident), 1056 thread_stack]) 1057 1058 for context in contexts_to_notify_on_sigterm: 1059 context.ReceivedSigterm() 1060 1061 infra_error('\n'.join(msg)) 1062 1063 signal.signal(signal.SIGTERM, unexpected_sigterm) 1064 1065 ### Set up results handling. 1066 # TODO(jbudorick): Rewrite results handling. 1067 1068 # all_raw_results is a list of lists of 1069 # base_test_result.TestRunResults objects. Each instance of 1070 # TestRunResults contains all test results produced by a single try, 1071 # while each list of TestRunResults contains all tries in a single 1072 # iteration. 1073 all_raw_results = [] 1074 1075 # all_iteration_results is a list of base_test_result.TestRunResults 1076 # objects. Each instance of TestRunResults contains the last test 1077 # result for each test run in that iteration. 1078 all_iteration_results = [] 1079 1080 global_results_tags = set() 1081 1082 json_file = tempfile.NamedTemporaryFile(delete=False) 1083 json_file.close() 1084 1085 @contextlib.contextmanager 1086 def json_finalizer(): 1087 try: 1088 yield 1089 finally: 1090 if args.json_results_file and os.path.exists(json_file.name): 1091 shutil.move(json_file.name, args.json_results_file) 1092 elif args.isolated_script_test_output and os.path.exists(json_file.name): 1093 shutil.move(json_file.name, args.isolated_script_test_output) 1094 else: 1095 os.remove(json_file.name) 1096 1097 @contextlib.contextmanager 1098 def json_writer(): 1099 try: 1100 yield 1101 except Exception: 1102 global_results_tags.add('UNRELIABLE_RESULTS') 1103 raise 1104 finally: 1105 if args.isolated_script_test_output: 1106 interrupted = 'UNRELIABLE_RESULTS' in global_results_tags 1107 json_results.GenerateJsonTestResultFormatFile(all_raw_results, 1108 interrupted, 1109 json_file.name, 1110 indent=2) 1111 else: 1112 json_results.GenerateJsonResultsFile( 1113 all_raw_results, 1114 json_file.name, 1115 global_tags=list(global_results_tags), 1116 indent=2) 1117 1118 test_class_to_file_name_dict = {} 1119 # Test Location is only supported for instrumentation tests as it 1120 # requires the size-info file. 1121 if test_instance.TestType() == 'instrumentation': 1122 test_class_to_file_name_dict = _CreateClassToFileNameDict(args.test_apk) 1123 1124 if result_sink_client: 1125 for run in all_raw_results: 1126 for results in run: 1127 for r in results.GetAll(): 1128 # Matches chrome.page_info.PageInfoViewTest#testChromePage 1129 match = re.search(r'^(.+\..+)#', r.GetName()) 1130 test_file_name = test_class_to_file_name_dict.get( 1131 match.group(1)) if match else None 1132 _SinkTestResult(r, test_file_name, result_sink_client) 1133 1134 @contextlib.contextmanager 1135 def upload_logcats_file(): 1136 try: 1137 yield 1138 finally: 1139 if not args.logcat_output_file: 1140 logging.critical('Cannot upload logcat file: no file specified.') 1141 elif not os.path.exists(args.logcat_output_file): 1142 logging.critical("Cannot upload logcat file: file doesn't exist.") 1143 else: 1144 with open(args.logcat_output_file) as src: 1145 dst = logdog_helper.open_text('unified_logcats') 1146 if dst: 1147 shutil.copyfileobj(src, dst) 1148 dst.close() 1149 logging.critical( 1150 'Logcat: %s', logdog_helper.get_viewer_url('unified_logcats')) 1151 1152 1153 logcats_uploader = contextlib_ext.Optional( 1154 upload_logcats_file(), 1155 'upload_logcats_file' in args and args.upload_logcats_file) 1156 1157 save_detailed_results = (args.local_output or not local_utils.IsOnSwarming() 1158 ) and not args.isolated_script_test_output 1159 1160 ### Set up test objects. 1161 1162 out_manager = output_manager_factory.CreateOutputManager(args) 1163 env = environment_factory.CreateEnvironment( 1164 args, out_manager, infra_error) 1165 test_instance = test_instance_factory.CreateTestInstance(args, infra_error) 1166 test_run = test_run_factory.CreateTestRun(env, test_instance, infra_error) 1167 1168 contexts_to_notify_on_sigterm.append(env) 1169 contexts_to_notify_on_sigterm.append(test_run) 1170 1171 if args.list_tests: 1172 try: 1173 with out_manager, env, test_instance, test_run: 1174 test_names = test_run.GetTestsForListing() 1175 print('There are {} tests:'.format(len(test_names))) 1176 for n in test_names: 1177 print(n) 1178 return 0 1179 except NotImplementedError: 1180 sys.stderr.write('Test does not support --list-tests (type={}).\n'.format( 1181 args.command)) 1182 return 1 1183 1184 ### Run. 1185 with out_manager, json_finalizer(): 1186 # |raw_logs_fh| is only used by Robolectric tests. 1187 raw_logs_fh = io.StringIO() if save_detailed_results else None 1188 1189 with json_writer(), logcats_uploader, env, test_instance, test_run: 1190 1191 repetitions = (range(args.repeat + 1192 1) if args.repeat >= 0 else itertools.count()) 1193 result_counts = collections.defaultdict( 1194 lambda: collections.defaultdict(int)) 1195 iteration_count = 0 1196 for _ in repetitions: 1197 # raw_results will be populated with base_test_result.TestRunResults by 1198 # test_run.RunTests(). It is immediately added to all_raw_results so 1199 # that in the event of an exception, all_raw_results will already have 1200 # the up-to-date results and those can be written to disk. 1201 raw_results = [] 1202 all_raw_results.append(raw_results) 1203 1204 test_run.RunTests(raw_results, raw_logs_fh=raw_logs_fh) 1205 if not raw_results: 1206 all_raw_results.pop() 1207 continue 1208 1209 iteration_results = base_test_result.TestRunResults() 1210 for r in reversed(raw_results): 1211 iteration_results.AddTestRunResults(r) 1212 all_iteration_results.append(iteration_results) 1213 iteration_count += 1 1214 1215 for r in iteration_results.GetAll(): 1216 result_counts[r.GetName()][r.GetType()] += 1 1217 1218 report_results.LogFull( 1219 results=iteration_results, 1220 test_type=test_instance.TestType(), 1221 test_package=test_run.TestPackage(), 1222 annotation=getattr(args, 'annotations', None), 1223 flakiness_server=getattr(args, 'flakiness_dashboard_server', 1224 None)) 1225 1226 failed_tests = (iteration_results.GetNotPass() - 1227 iteration_results.GetSkip()) 1228 if failed_tests: 1229 _LogRerunStatement(failed_tests, args.wrapper_script_args) 1230 1231 if args.break_on_failure and not iteration_results.DidRunPass(): 1232 break 1233 1234 if iteration_count > 1: 1235 # display summary results 1236 # only display results for a test if at least one test did not pass 1237 all_pass = 0 1238 tot_tests = 0 1239 for test_name in result_counts: 1240 tot_tests += 1 1241 if any(result_counts[test_name][x] for x in ( 1242 base_test_result.ResultType.FAIL, 1243 base_test_result.ResultType.CRASH, 1244 base_test_result.ResultType.TIMEOUT, 1245 base_test_result.ResultType.UNKNOWN)): 1246 logging.critical( 1247 '%s: %s', 1248 test_name, 1249 ', '.join('%s %s' % (str(result_counts[test_name][i]), i) 1250 for i in base_test_result.ResultType.GetTypes())) 1251 else: 1252 all_pass += 1 1253 1254 logging.critical('%s of %s tests passed in all %s runs', 1255 str(all_pass), 1256 str(tot_tests), 1257 str(iteration_count)) 1258 1259 if save_detailed_results: 1260 assert raw_logs_fh 1261 raw_logs_fh.seek(0) 1262 raw_logs = raw_logs_fh.read() 1263 if raw_logs: 1264 with out_manager.ArchivedTempfile( 1265 'raw_logs.txt', 'raw_logs', 1266 output_manager.Datatype.TEXT) as raw_logs_file: 1267 raw_logs_file.write(raw_logs) 1268 logging.critical('RAW LOGS: %s', raw_logs_file.Link()) 1269 1270 with out_manager.ArchivedTempfile( 1271 'test_results_presentation.html', 1272 'test_results_presentation', 1273 output_manager.Datatype.HTML) as results_detail_file: 1274 result_html_string, _, _ = test_results_presentation.result_details( 1275 json_path=json_file.name, 1276 test_name=args.command, 1277 cs_base_url='http://cs.chromium.org', 1278 local_output=True) 1279 results_detail_file.write(result_html_string) 1280 results_detail_file.flush() 1281 logging.critical('TEST RESULTS: %s', results_detail_file.Link()) 1282 1283 ui_screenshots = test_results_presentation.ui_screenshot_set( 1284 json_file.name) 1285 if ui_screenshots: 1286 with out_manager.ArchivedTempfile( 1287 'ui_screenshots.json', 1288 'ui_capture', 1289 output_manager.Datatype.JSON) as ui_screenshot_file: 1290 ui_screenshot_file.write(ui_screenshots) 1291 logging.critical('UI Screenshots: %s', ui_screenshot_file.Link()) 1292 1293 return (0 if all(r.DidRunPass() for r in all_iteration_results) 1294 else constants.ERROR_EXIT_CODE) 1295 1296 1297def _LogRerunStatement(failed_tests, wrapper_arg_str): 1298 """Logs a message that can rerun the failed tests. 1299 1300 Logs a copy/pasteable message that filters tests so just the failing tests 1301 are run. 1302 1303 Args: 1304 failed_tests: A set of test results that did not pass. 1305 wrapper_arg_str: A string of args that were passed to the called wrapper 1306 script. 1307 """ 1308 rerun_arg_list = [] 1309 try: 1310 constants.CheckOutputDirectory() 1311 # constants.CheckOutputDirectory throws bare exceptions. 1312 except: # pylint: disable=bare-except 1313 logging.exception('Output directory not found. Unable to generate failing ' 1314 'test filter file.') 1315 return 1316 1317 output_directory = constants.GetOutDirectory() 1318 if not os.path.exists(output_directory): 1319 logging.error('Output directory not found. Unable to generate failing ' 1320 'test filter file.') 1321 return 1322 1323 test_filter_file = os.path.join(os.path.relpath(output_directory), 1324 _RERUN_FAILED_TESTS_FILE) 1325 arg_list = shlex.split(wrapper_arg_str) if wrapper_arg_str else sys.argv 1326 index = 0 1327 while index < len(arg_list): 1328 arg = arg_list[index] 1329 # Skip adding the filter=<file> and/or the filter arg as we're replacing 1330 # it with the new filter arg. 1331 # This covers --test-filter=, --test-launcher-filter-file=, --gtest-filter=, 1332 # --test-filter *Foobar.baz, -f *foobar, --package-filter <package>, 1333 # --runner-filter <runner>. 1334 if 'filter' in arg or arg == '-f': 1335 index += 1 if '=' in arg else 2 1336 continue 1337 1338 rerun_arg_list.append(arg) 1339 index += 1 1340 1341 failed_test_list = [str(t) for t in failed_tests] 1342 with open(test_filter_file, 'w') as fp: 1343 for t in failed_test_list: 1344 # Test result names can have # in them that don't match when applied as 1345 # a test name filter. 1346 fp.write('%s\n' % t.replace('#', '.')) 1347 1348 rerun_arg_list.append('--test-launcher-filter-file=%s' % test_filter_file) 1349 msg = """ 1350 %d Test(s) failed. 1351 Rerun failed tests with copy and pastable command: 1352 %s 1353 """ 1354 logging.critical(msg, len(failed_tests), shlex.join(rerun_arg_list)) 1355 1356 1357def DumpThreadStacks(_signal, _frame): 1358 for thread in threading.enumerate(): 1359 reraiser_thread.LogThreadStack(thread) 1360 1361 1362def main(): 1363 signal.signal(signal.SIGUSR1, DumpThreadStacks) 1364 1365 parser = argparse.ArgumentParser() 1366 command_parsers = parser.add_subparsers( 1367 title='test types', dest='command') 1368 1369 subp = command_parsers.add_parser( 1370 'gtest', 1371 help='googletest-based C++ tests') 1372 AddCommonOptions(subp) 1373 AddDeviceOptions(subp) 1374 AddEmulatorOptions(subp) 1375 AddGTestOptions(subp) 1376 AddTracingOptions(subp) 1377 AddCommandLineOptions(subp) 1378 1379 subp = command_parsers.add_parser( 1380 'hostside', 1381 help='Webview CTS host-side tests') 1382 AddCommonOptions(subp) 1383 AddDeviceOptions(subp) 1384 AddEmulatorOptions(subp) 1385 AddHostsideTestOptions(subp) 1386 1387 subp = command_parsers.add_parser( 1388 'instrumentation', 1389 help='InstrumentationTestCase-based Java tests') 1390 AddCommonOptions(subp) 1391 AddDeviceOptions(subp) 1392 AddEmulatorOptions(subp) 1393 AddInstrumentationTestOptions(subp) 1394 AddSkiaGoldTestOptions(subp) 1395 AddTracingOptions(subp) 1396 AddCommandLineOptions(subp) 1397 1398 subp = command_parsers.add_parser( 1399 'junit', 1400 help='JUnit4-based Java tests') 1401 AddCommonOptions(subp) 1402 AddJUnitTestOptions(subp) 1403 1404 subp = command_parsers.add_parser( 1405 'linker', 1406 help='linker tests') 1407 AddCommonOptions(subp) 1408 AddDeviceOptions(subp) 1409 AddEmulatorOptions(subp) 1410 AddLinkerTestOptions(subp) 1411 1412 subp = command_parsers.add_parser( 1413 'monkey', 1414 help="tests based on Android's monkey command") 1415 AddCommonOptions(subp) 1416 AddDeviceOptions(subp) 1417 AddEmulatorOptions(subp) 1418 AddMonkeyTestOptions(subp) 1419 1420 subp = command_parsers.add_parser( 1421 'python', 1422 help='python tests based on unittest.TestCase') 1423 AddCommonOptions(subp) 1424 AddPythonTestOptions(subp) 1425 1426 args, unknown_args = parser.parse_known_args() 1427 if unknown_args: 1428 if hasattr(args, 'allow_unknown') and args.allow_unknown: 1429 args.command_line_flags = unknown_args 1430 else: 1431 parser.error('unrecognized arguments: %s' % ' '.join(unknown_args)) 1432 1433 # --replace-system-package/--remove-system-package has the potential to cause 1434 # issues if --enable-concurrent-adb is set, so disallow that combination. 1435 concurrent_adb_enabled = (hasattr(args, 'enable_concurrent_adb') 1436 and args.enable_concurrent_adb) 1437 replacing_system_packages = (hasattr(args, 'replace_system_package') 1438 and args.replace_system_package) 1439 removing_system_packages = (hasattr(args, 'system_packages_to_remove') 1440 and args.system_packages_to_remove) 1441 if (concurrent_adb_enabled 1442 and (replacing_system_packages or removing_system_packages)): 1443 parser.error('--enable-concurrent-adb cannot be used with either ' 1444 '--replace-system-package or --remove-system-package') 1445 1446 # --use-webview-provider has the potential to cause issues if 1447 # --enable-concurrent-adb is set, so disallow that combination 1448 if (hasattr(args, 'use_webview_provider') and 1449 hasattr(args, 'enable_concurrent_adb') and args.use_webview_provider and 1450 args.enable_concurrent_adb): 1451 parser.error('--use-webview-provider and --enable-concurrent-adb cannot ' 1452 'be used together') 1453 1454 if (getattr(args, 'coverage_on_the_fly', False) 1455 and not getattr(args, 'coverage_dir', '')): 1456 parser.error('--coverage-on-the-fly requires --coverage-dir') 1457 1458 if (hasattr(args, 'debug_socket') or 1459 (hasattr(args, 'wait_for_java_debugger') and 1460 args.wait_for_java_debugger)): 1461 args.num_retries = 0 1462 1463 # Result-sink may not exist in the environment if rdb stream is not enabled. 1464 result_sink_client = result_sink.TryInitClient() 1465 1466 try: 1467 return RunTestsCommand(args, result_sink_client) 1468 except base_error.BaseError as e: 1469 logging.exception('Error occurred.') 1470 if e.is_infra_error: 1471 return constants.INFRA_EXIT_CODE 1472 return constants.ERROR_EXIT_CODE 1473 except Exception: # pylint: disable=W0703 1474 logging.exception('Unrecognized error occurred.') 1475 return constants.ERROR_EXIT_CODE 1476 1477 1478if __name__ == '__main__': 1479 exit_code = main() 1480 if exit_code == constants.INFRA_EXIT_CODE: 1481 # This exit code is returned in case of missing, unreachable, 1482 # or otherwise not fit for purpose test devices. 1483 # When this happens, the graceful cleanup triggered by sys.exit() 1484 # hangs indefinitely (on swarming - until it hits 20min timeout). 1485 # Skip cleanup (other than flushing output streams) and exit forcefully 1486 # to avoid the hang. 1487 sys.stdout.flush() 1488 sys.stderr.flush() 1489 os._exit(exit_code) # pylint: disable=protected-access 1490 else: 1491 sys.exit(exit_code) 1492