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 '--app-data-file', 402 action='append', dest='app_data_files', 403 help='A file path relative to the app data directory ' 404 'that should be saved to the host.') 405 parser.add_argument( 406 '--app-data-file-dir', 407 help='Host directory to which app data files will be' 408 ' saved. Used with --app-data-file.') 409 parser.add_argument( 410 '--enable-xml-result-parsing', 411 action='store_true', help=argparse.SUPPRESS) 412 parser.add_argument( 413 '--executable-dist-dir', 414 type=os.path.realpath, 415 help="Path to executable's dist directory for native" 416 " (non-apk) tests.") 417 parser.add_argument( 418 '--extract-test-list-from-filter', 419 action='store_true', 420 help='When a test filter is specified, and the list of ' 421 'tests can be determined from it, skip querying the ' 422 'device for the list of all tests. Speeds up local ' 423 'development, but is not safe to use on bots (' 424 'http://crbug.com/549214') 425 parser.add_argument( 426 '--gs-test-artifacts-bucket', 427 help=('If present, test artifacts will be uploaded to this Google ' 428 'Storage bucket.')) 429 parser.add_argument( 430 '--render-test-output-dir', 431 help='If present, store rendering artifacts in this path.') 432 parser.add_argument( 433 '--runtime-deps-path', 434 dest='runtime_deps_path', type=os.path.realpath, 435 help='Runtime data dependency file from GN.') 436 parser.add_argument( 437 '-t', '--shard-timeout', 438 dest='shard_timeout', type=int, default=120, 439 help='Timeout to wait for each test (default: %(default)s).') 440 parser.add_argument( 441 '--store-tombstones', 442 dest='store_tombstones', action='store_true', 443 help='Add tombstones in results if crash.') 444 parser.add_argument( 445 '-s', '--suite', 446 dest='suite_name', nargs='+', metavar='SUITE_NAME', required=True, 447 help='Executable name of the test suite to run.') 448 parser.add_argument( 449 '--test-apk-incremental-install-json', 450 type=os.path.realpath, 451 help='Path to install json for the test apk.') 452 parser.add_argument('--test-launcher-batch-limit', 453 dest='test_launcher_batch_limit', 454 type=int, 455 help='The max number of tests to run in a shard. ' 456 'Ignores non-positive ints and those greater than ' 457 'MAX_SHARDS') 458 parser.add_argument( 459 '-w', '--wait-for-java-debugger', action='store_true', 460 help='Wait for java debugger to attach before running any application ' 461 'code. Also disables test timeouts and sets retries=0.') 462 parser.add_argument( 463 '--coverage-dir', 464 type=os.path.realpath, 465 help='Directory in which to place all generated coverage files.') 466 parser.add_argument( 467 '--use-existing-test-data', 468 action='store_true', 469 help='Do not push new files to the device, instead using existing APK ' 470 'and test data. Only use when running the same test for multiple ' 471 'iterations.') 472 473 474def AddInstrumentationTestOptions(parser): 475 """Adds Instrumentation test options to |parser|.""" 476 477 parser = parser.add_argument_group('instrumentation arguments') 478 479 parser.add_argument('--additional-apex', 480 action='append', 481 dest='additional_apexs', 482 default=[], 483 type=_RealPath, 484 help='Additional apex that must be installed on ' 485 'the device when the tests are run') 486 parser.add_argument( 487 '--additional-apk', 488 action='append', dest='additional_apks', default=[], 489 type=_RealPath, 490 help='Additional apk that must be installed on ' 491 'the device when the tests are run') 492 parser.add_argument('--forced-queryable-additional-apk', 493 action='append', 494 dest='forced_queryable_additional_apks', 495 default=[], 496 type=_RealPath, 497 help='Configures an additional-apk to be forced ' 498 'to be queryable by other APKs.') 499 parser.add_argument('--instant-additional-apk', 500 action='append', 501 dest='instant_additional_apks', 502 default=[], 503 type=_RealPath, 504 help='Configures an additional-apk to be an instant APK') 505 parser.add_argument( 506 '-A', '--annotation', 507 dest='annotation_str', 508 help='Comma-separated list of annotations. Run only tests with any of ' 509 'the given annotations. An annotation can be either a key or a ' 510 'key-values pair. A test that has no annotation is considered ' 511 '"SmallTest".') 512 # TODO(jbudorick): Remove support for name-style APK specification once 513 # bots are no longer doing it. 514 parser.add_argument( 515 '--apk-under-test', 516 help='Path or name of the apk under test.') 517 parser.add_argument( 518 '--store-data-dependencies-in-temp', 519 action='store_true', 520 help='Store data dependencies in /data/local/tmp/chromium_tests_root') 521 parser.add_argument( 522 '--module', 523 action='append', 524 dest='modules', 525 help='Specify Android App Bundle modules to install in addition to the ' 526 'base module.') 527 parser.add_argument( 528 '--fake-module', 529 action='append', 530 dest='fake_modules', 531 help='Specify Android App Bundle modules to fake install in addition to ' 532 'the real modules.') 533 parser.add_argument( 534 '--additional-locale', 535 action='append', 536 dest='additional_locales', 537 help='Specify locales in addition to the device locale to install splits ' 538 'for when --apk-under-test is an Android App Bundle.') 539 parser.add_argument( 540 '--coverage-dir', 541 type=os.path.realpath, 542 help='Directory in which to place all generated ' 543 'Jacoco coverage files.') 544 parser.add_argument( 545 '--disable-dalvik-asserts', 546 dest='set_asserts', action='store_false', default=True, 547 help='Removes the dalvik.vm.enableassertions property') 548 parser.add_argument( 549 '--proguard-mapping-path', 550 help='.mapping file to use to Deobfuscate java stack traces in test ' 551 'output and logcat.') 552 parser.add_argument( 553 '-E', '--exclude-annotation', 554 dest='exclude_annotation_str', 555 help='Comma-separated list of annotations. Exclude tests with these ' 556 'annotations.') 557 parser.add_argument( 558 '--enable-breakpad-dump', 559 action='store_true', 560 help='Stores any breakpad dumps till the end of the test.') 561 parser.add_argument( 562 '--replace-system-package', 563 type=_RealPath, 564 default=None, 565 help='Use this apk to temporarily replace a system package with the same ' 566 'package name.') 567 parser.add_argument( 568 '--remove-system-package', 569 default=[], 570 action='append', 571 dest='system_packages_to_remove', 572 help='Specifies a system package to remove before testing if it exists ' 573 'on the system. WARNING: THIS WILL PERMANENTLY REMOVE THE SYSTEM APP. ' 574 'Unlike --replace-system-package, the app will not be restored after ' 575 'tests are finished.') 576 parser.add_argument( 577 '--use-voice-interaction-service', 578 help='This can be used to update the voice interaction service to be a ' 579 'custom one. This is useful for mocking assistants. eg: ' 580 'android.assist.service/.MainInteractionService') 581 parser.add_argument( 582 '--use-webview-provider', 583 type=_RealPath, default=None, 584 help='Use this apk as the webview provider during test. ' 585 'The original provider will be restored if possible, ' 586 "on Nougat the provider can't be determined and so " 587 'the system will choose the default provider.') 588 parser.add_argument( 589 '--webview-command-line-arg', 590 default=[], 591 action='append', 592 help="Specifies command line arguments to add to WebView's flag file") 593 parser.add_argument( 594 '--run-setup-command', 595 default=[], 596 action='append', 597 dest='run_setup_commands', 598 help='This can be used to run a custom shell command on the device as a ' 599 'setup step') 600 parser.add_argument( 601 '--run-teardown-command', 602 default=[], 603 action='append', 604 dest='run_teardown_commands', 605 help='This can be used to run a custom shell command on the device as a ' 606 'teardown step') 607 parser.add_argument( 608 '--runtime-deps-path', 609 dest='runtime_deps_path', type=os.path.realpath, 610 help='Runtime data dependency file from GN.') 611 parser.add_argument( 612 '--screenshot-directory', 613 dest='screenshot_dir', type=os.path.realpath, 614 help='Capture screenshots of test failures') 615 parser.add_argument( 616 '--shared-prefs-file', 617 dest='shared_prefs_file', type=_RealPath, 618 help='The relative path to a file containing JSON list of shared ' 619 'preference files to edit and how to do so. Example list: ' 620 '[{' 621 ' "package": "com.package.example",' 622 ' "filename": "ExampleSettings.xml",' 623 ' "set": {' 624 ' "boolean_key_in_xml": true,' 625 ' "string_key_in_xml": "string_value"' 626 ' },' 627 ' "remove": [' 628 ' "key_in_xml_to_remove"' 629 ' ]' 630 '}]') 631 parser.add_argument( 632 '--store-tombstones', 633 action='store_true', dest='store_tombstones', 634 help='Add tombstones in results if crash.') 635 parser.add_argument( 636 '--strict-mode', 637 dest='strict_mode', default='testing', 638 help='StrictMode command-line flag set on the device, ' 639 'death/testing to kill the process, off to stop ' 640 'checking, flash to flash only. (default: %(default)s)') 641 parser.add_argument( 642 '--test-apk', 643 required=True, 644 help='Path or name of the apk containing the tests.') 645 parser.add_argument( 646 '--test-apk-as-instant', 647 action='store_true', 648 help='Install the test apk as an instant app. ' 649 'Instant apps run in a more restrictive execution environment.') 650 parser.add_argument( 651 '--test-launcher-batch-limit', 652 dest='test_launcher_batch_limit', 653 type=int, 654 help=('Not actually used for instrumentation tests, but can be used as ' 655 'a proxy for determining if the current run is a retry without ' 656 'patch.')) 657 parser.add_argument( 658 '--timeout-scale', 659 type=float, 660 help='Factor by which timeouts should be scaled.') 661 parser.add_argument( 662 '--is-unit-test', 663 action='store_true', 664 help=('Specify the test suite as composed of unit tests, blocking ' 665 'certain operations.')) 666 parser.add_argument( 667 '-w', '--wait-for-java-debugger', action='store_true', 668 help='Wait for java debugger to attach before running any application ' 669 'code. Also disables test timeouts and sets retries=0.') 670 671 # WPR record mode. 672 parser.add_argument('--wpr-enable-record', 673 action='store_true', 674 default=False, 675 help='If true, WPR server runs in record mode.' 676 'otherwise, runs in replay mode.') 677 678 parser.add_argument( 679 '--approve-app-links', 680 help='Force enables Digital Asset Link verification for the provided ' 681 'package and domain, example usage: --approve-app-links ' 682 'com.android.package:www.example.com') 683 684 # These arguments are suppressed from the help text because they should 685 # only ever be specified by an intermediate script. 686 parser.add_argument( 687 '--apk-under-test-incremental-install-json', 688 help=argparse.SUPPRESS) 689 parser.add_argument( 690 '--test-apk-incremental-install-json', 691 type=os.path.realpath, 692 help=argparse.SUPPRESS) 693 694 695def AddSkiaGoldTestOptions(parser): 696 """Adds Skia Gold test options to |parser|.""" 697 parser = parser.add_argument_group("Skia Gold arguments") 698 parser.add_argument( 699 '--code-review-system', 700 help='A non-default code review system to pass to pass to Gold, if ' 701 'applicable') 702 parser.add_argument( 703 '--continuous-integration-system', 704 help='A non-default continuous integration system to pass to Gold, if ' 705 'applicable') 706 parser.add_argument( 707 '--git-revision', help='The git commit currently being tested.') 708 parser.add_argument( 709 '--gerrit-issue', 710 help='The Gerrit issue this test is being run on, if applicable.') 711 parser.add_argument( 712 '--gerrit-patchset', 713 help='The Gerrit patchset this test is being run on, if applicable.') 714 parser.add_argument( 715 '--buildbucket-id', 716 help='The Buildbucket build ID that this test was triggered from, if ' 717 'applicable.') 718 local_group = parser.add_mutually_exclusive_group() 719 local_group.add_argument( 720 '--local-pixel-tests', 721 action='store_true', 722 default=None, 723 help='Specifies to run the Skia Gold pixel tests in local mode. When run ' 724 'in local mode, uploading to Gold is disabled and traditional ' 725 'generated/golden/diff images are output instead of triage links. ' 726 'Running in local mode also implies --no-luci-auth. If both this ' 727 'and --no-local-pixel-tests are left unset, the test harness will ' 728 'attempt to detect whether it is running on a workstation or not ' 729 'and set the options accordingly.') 730 local_group.add_argument( 731 '--no-local-pixel-tests', 732 action='store_false', 733 dest='local_pixel_tests', 734 help='Specifies to run the Skia Gold pixel tests in non-local (bot) ' 735 'mode. When run in this mode, data is actually uploaded to Gold and ' 736 'triage links are generated. If both this and --local-pixel-tests ' 737 'are left unset, the test harness will attempt to detect whether ' 738 'it is running on a workstation or not and set the options ' 739 'accordingly.') 740 parser.add_argument( 741 '--no-luci-auth', 742 action='store_true', 743 default=False, 744 help="Don't use the serve account provided by LUCI for authentication " 745 'with Skia Gold, instead relying on gsutil to be pre-authenticated. ' 746 'Meant for testing locally instead of on the bots.') 747 parser.add_argument( 748 '--bypass-skia-gold-functionality', 749 action='store_true', 750 default=False, 751 help='Bypass all interaction with Skia Gold, effectively disabling the ' 752 'image comparison portion of any tests that use Gold. Only meant to be ' 753 'used in case a Gold outage occurs and cannot be fixed quickly.') 754 755 756def AddJUnitTestOptions(parser): 757 """Adds junit test options to |parser|.""" 758 759 parser = parser.add_argument_group('junit arguments') 760 761 parser.add_argument( 762 '--coverage-on-the-fly', 763 action='store_true', 764 help='Generate coverage data by Jacoco on-the-fly instrumentation.') 765 parser.add_argument( 766 '--coverage-dir', type=os.path.realpath, 767 help='Directory to store coverage info.') 768 parser.add_argument( 769 '--package-filter', 770 help='Filters tests by package.') 771 parser.add_argument( 772 '--runner-filter', 773 help='Filters tests by runner class. Must be fully qualified.') 774 parser.add_argument('--json-config', 775 help='Runs only tests listed in this config.') 776 parser.add_argument( 777 '--shards', 778 type=int, 779 help='Number of shards to run junit tests in parallel on. Only 1 shard ' 780 'is supported when test-filter is specified. Values less than 1 will ' 781 'use auto select.') 782 parser.add_argument('--shard-filter', 783 help='Comma separated list of shard indices to run.') 784 parser.add_argument( 785 '-s', '--test-suite', required=True, 786 help='JUnit test suite to run.') 787 debug_group = parser.add_mutually_exclusive_group() 788 debug_group.add_argument( 789 '-w', '--wait-for-java-debugger', action='store_const', const='8701', 790 dest='debug_socket', help='Alias for --debug-socket=8701') 791 debug_group.add_argument( 792 '--debug-socket', 793 help='Wait for java debugger to attach at specified socket address ' 794 'before running any application code. Also disables test timeouts ' 795 'and sets retries=0.') 796 797 # These arguments are for Android Robolectric tests. 798 parser.add_argument( 799 '--robolectric-runtime-deps-dir', 800 help='Path to runtime deps for Robolectric.') 801 parser.add_argument('--native-libs-dir', 802 help='Path to search for native libraries.') 803 parser.add_argument( 804 '--resource-apk', 805 required=True, 806 help='Path to .ap_ containing binary resources for Robolectric.') 807 808 809def AddLinkerTestOptions(parser): 810 811 parser = parser.add_argument_group('linker arguments') 812 813 parser.add_argument( 814 '--test-apk', 815 type=os.path.realpath, 816 help='Path to the linker test APK.') 817 818 819def AddMonkeyTestOptions(parser): 820 """Adds monkey test options to |parser|.""" 821 822 parser = parser.add_argument_group('monkey arguments') 823 824 parser.add_argument('--browser', 825 required=True, 826 choices=list(constants.PACKAGE_INFO.keys()), 827 metavar='BROWSER', 828 help='Browser under test.') 829 parser.add_argument( 830 '--category', 831 nargs='*', dest='categories', default=[], 832 help='A list of allowed categories. Monkey will only visit activities ' 833 'that are listed with one of the specified categories.') 834 parser.add_argument( 835 '--event-count', 836 default=10000, type=int, 837 help='Number of events to generate (default: %(default)s).') 838 parser.add_argument( 839 '--seed', 840 type=int, 841 help='Seed value for pseudo-random generator. Same seed value generates ' 842 'the same sequence of events. Seed is randomized by default.') 843 parser.add_argument( 844 '--throttle', 845 default=100, type=int, 846 help='Delay between events (ms) (default: %(default)s). ') 847 848 849def AddPythonTestOptions(parser): 850 851 parser = parser.add_argument_group('python arguments') 852 853 parser.add_argument('-s', 854 '--suite', 855 dest='suite_name', 856 metavar='SUITE_NAME', 857 choices=list(constants.PYTHON_UNIT_TEST_SUITES.keys()), 858 help='Name of the test suite to run.') 859 860 861def _CreateClassToFileNameDict(test_apk): 862 """Creates a dict mapping classes to file names from size-info apk.""" 863 constants.CheckOutputDirectory() 864 test_apk_size_info = os.path.join(constants.GetOutDirectory(), 'size-info', 865 os.path.basename(test_apk) + '.jar.info') 866 867 class_to_file_dict = {} 868 # Some tests such as webview_cts_tests use a separately downloaded apk to run 869 # tests. This means the apk may not have been built by the system and hence 870 # no size info file exists. 871 if not os.path.exists(test_apk_size_info): 872 logging.debug('Apk size file not found. %s', test_apk_size_info) 873 return class_to_file_dict 874 875 with open(test_apk_size_info, 'r') as f: 876 for line in f: 877 file_class, file_name = line.rstrip().split(',', 1) 878 # Only want files that are not prebuilt. 879 if file_name.startswith('../../'): 880 class_to_file_dict[file_class] = str( 881 file_name.replace('../../', '//', 1)) 882 883 return class_to_file_dict 884 885 886def _RunPythonTests(args): 887 """Subcommand of RunTestsCommand which runs python unit tests.""" 888 suite_vars = constants.PYTHON_UNIT_TEST_SUITES[args.suite_name] 889 suite_path = suite_vars['path'] 890 suite_test_modules = suite_vars['test_modules'] 891 892 sys.path = [suite_path] + sys.path 893 try: 894 suite = unittest.TestSuite() 895 suite.addTests(unittest.defaultTestLoader.loadTestsFromName(m) 896 for m in suite_test_modules) 897 runner = unittest.TextTestRunner(verbosity=1+args.verbose_count) 898 return 0 if runner.run(suite).wasSuccessful() else 1 899 finally: 900 sys.path = sys.path[1:] 901 902 903_DEFAULT_PLATFORM_MODE_TESTS = [ 904 'gtest', 'instrumentation', 'junit', 'linker', 'monkey' 905] 906 907 908def RunTestsCommand(args, result_sink_client=None): 909 """Checks test type and dispatches to the appropriate function. 910 911 Args: 912 args: argparse.Namespace object. 913 result_sink_client: A ResultSinkClient object. 914 915 Returns: 916 Integer indicated exit code. 917 918 Raises: 919 Exception: Unknown command name passed in, or an exception from an 920 individual test runner. 921 """ 922 command = args.command 923 924 ProcessCommonOptions(args) 925 logging.info('command: %s', ' '.join(sys.argv)) 926 if args.enable_platform_mode or command in _DEFAULT_PLATFORM_MODE_TESTS: 927 return RunTestsInPlatformMode(args, result_sink_client) 928 929 if command == 'python': 930 return _RunPythonTests(args) 931 raise Exception('Unknown test type.') 932 933 934def _SinkTestResult(test_result, test_file_name, result_sink_client): 935 """Upload test result to result_sink. 936 937 Args: 938 test_result: A BaseTestResult object 939 test_file_name: A string representing the file location of the test 940 result_sink_client: A ResultSinkClient object 941 942 Returns: 943 N/A 944 """ 945 # Some tests put in non utf-8 char as part of the test 946 # which breaks uploads, so need to decode and re-encode. 947 log_decoded = test_result.GetLog() 948 if isinstance(log_decoded, bytes): 949 log_decoded = log_decoded.decode('utf-8', 'replace') 950 html_artifact = '' 951 https_artifacts = [] 952 for link_name, link_url in sorted(test_result.GetLinks().items()): 953 if link_url.startswith('https:'): 954 https_artifacts.append('<li><a target="_blank" href=%s>%s</a></li>' % 955 (link_url, link_name)) 956 else: 957 logging.info('Skipping non-https link %r (%s) for test %s.', link_name, 958 link_url, test_result.GetName()) 959 if https_artifacts: 960 html_artifact += '<ul>%s</ul>' % '\n'.join(https_artifacts) 961 result_sink_client.Post(test_result.GetNameForResultSink(), 962 test_result.GetType(), 963 test_result.GetDuration(), 964 log_decoded, 965 test_file_name, 966 variant=test_result.GetVariantForResultSink(), 967 failure_reason=test_result.GetFailureReason(), 968 html_artifact=html_artifact) 969 970 971_SUPPORTED_IN_PLATFORM_MODE = [ 972 # TODO(jbudorick): Add support for more test types. 973 'gtest', 974 'instrumentation', 975 'junit', 976 'linker', 977 'monkey', 978] 979 980 981def RunTestsInPlatformMode(args, result_sink_client=None): 982 983 def infra_error(message): 984 logging.fatal(message) 985 sys.exit(constants.INFRA_EXIT_CODE) 986 987 if args.command not in _SUPPORTED_IN_PLATFORM_MODE: 988 infra_error('%s is not yet supported in platform mode' % args.command) 989 990 ### Set up sigterm handler. 991 992 contexts_to_notify_on_sigterm = [] 993 def unexpected_sigterm(_signum, _frame): 994 msg = [ 995 'Received SIGTERM. Shutting down.', 996 ] 997 for live_thread in threading.enumerate(): 998 # pylint: disable=protected-access 999 thread_stack = ''.join(traceback.format_stack( 1000 sys._current_frames()[live_thread.ident])) 1001 msg.extend([ 1002 'Thread "%s" (ident: %s) is currently running:' % ( 1003 live_thread.name, live_thread.ident), 1004 thread_stack]) 1005 1006 for context in contexts_to_notify_on_sigterm: 1007 context.ReceivedSigterm() 1008 1009 infra_error('\n'.join(msg)) 1010 1011 signal.signal(signal.SIGTERM, unexpected_sigterm) 1012 1013 ### Set up results handling. 1014 # TODO(jbudorick): Rewrite results handling. 1015 1016 # all_raw_results is a list of lists of 1017 # base_test_result.TestRunResults objects. Each instance of 1018 # TestRunResults contains all test results produced by a single try, 1019 # while each list of TestRunResults contains all tries in a single 1020 # iteration. 1021 all_raw_results = [] 1022 1023 # all_iteration_results is a list of base_test_result.TestRunResults 1024 # objects. Each instance of TestRunResults contains the last test 1025 # result for each test run in that iteration. 1026 all_iteration_results = [] 1027 1028 global_results_tags = set() 1029 1030 json_file = tempfile.NamedTemporaryFile(delete=False) 1031 json_file.close() 1032 1033 @contextlib.contextmanager 1034 def json_finalizer(): 1035 try: 1036 yield 1037 finally: 1038 if args.json_results_file and os.path.exists(json_file.name): 1039 shutil.move(json_file.name, args.json_results_file) 1040 elif args.isolated_script_test_output and os.path.exists(json_file.name): 1041 shutil.move(json_file.name, args.isolated_script_test_output) 1042 else: 1043 os.remove(json_file.name) 1044 1045 @contextlib.contextmanager 1046 def json_writer(): 1047 try: 1048 yield 1049 except Exception: 1050 global_results_tags.add('UNRELIABLE_RESULTS') 1051 raise 1052 finally: 1053 if args.isolated_script_test_output: 1054 interrupted = 'UNRELIABLE_RESULTS' in global_results_tags 1055 json_results.GenerateJsonTestResultFormatFile(all_raw_results, 1056 interrupted, 1057 json_file.name, 1058 indent=2) 1059 else: 1060 json_results.GenerateJsonResultsFile( 1061 all_raw_results, 1062 json_file.name, 1063 global_tags=list(global_results_tags), 1064 indent=2) 1065 1066 test_class_to_file_name_dict = {} 1067 # Test Location is only supported for instrumentation tests as it 1068 # requires the size-info file. 1069 if test_instance.TestType() == 'instrumentation': 1070 test_class_to_file_name_dict = _CreateClassToFileNameDict(args.test_apk) 1071 1072 if result_sink_client: 1073 for run in all_raw_results: 1074 for results in run: 1075 for r in results.GetAll(): 1076 # Matches chrome.page_info.PageInfoViewTest#testChromePage 1077 match = re.search(r'^(.+\..+)#', r.GetName()) 1078 test_file_name = test_class_to_file_name_dict.get( 1079 match.group(1)) if match else None 1080 _SinkTestResult(r, test_file_name, result_sink_client) 1081 1082 @contextlib.contextmanager 1083 def upload_logcats_file(): 1084 try: 1085 yield 1086 finally: 1087 if not args.logcat_output_file: 1088 logging.critical('Cannot upload logcat file: no file specified.') 1089 elif not os.path.exists(args.logcat_output_file): 1090 logging.critical("Cannot upload logcat file: file doesn't exist.") 1091 else: 1092 with open(args.logcat_output_file) as src: 1093 dst = logdog_helper.open_text('unified_logcats') 1094 if dst: 1095 shutil.copyfileobj(src, dst) 1096 dst.close() 1097 logging.critical( 1098 'Logcat: %s', logdog_helper.get_viewer_url('unified_logcats')) 1099 1100 1101 logcats_uploader = contextlib_ext.Optional( 1102 upload_logcats_file(), 1103 'upload_logcats_file' in args and args.upload_logcats_file) 1104 1105 save_detailed_results = (args.local_output or not local_utils.IsOnSwarming() 1106 ) and not args.isolated_script_test_output 1107 1108 ### Set up test objects. 1109 1110 out_manager = output_manager_factory.CreateOutputManager(args) 1111 env = environment_factory.CreateEnvironment( 1112 args, out_manager, infra_error) 1113 test_instance = test_instance_factory.CreateTestInstance(args, infra_error) 1114 test_run = test_run_factory.CreateTestRun(env, test_instance, infra_error) 1115 1116 contexts_to_notify_on_sigterm.append(env) 1117 contexts_to_notify_on_sigterm.append(test_run) 1118 1119 if args.list_tests: 1120 try: 1121 with out_manager, env, test_instance, test_run: 1122 test_names = test_run.GetTestsForListing() 1123 print('There are {} tests:'.format(len(test_names))) 1124 for n in test_names: 1125 print(n) 1126 return 0 1127 except NotImplementedError: 1128 sys.stderr.write('Test does not support --list-tests (type={}).\n'.format( 1129 args.command)) 1130 return 1 1131 1132 ### Run. 1133 with out_manager, json_finalizer(): 1134 # |raw_logs_fh| is only used by Robolectric tests. 1135 raw_logs_fh = io.StringIO() if save_detailed_results else None 1136 1137 with json_writer(), logcats_uploader, env, test_instance, test_run: 1138 1139 repetitions = (range(args.repeat + 1140 1) if args.repeat >= 0 else itertools.count()) 1141 result_counts = collections.defaultdict( 1142 lambda: collections.defaultdict(int)) 1143 iteration_count = 0 1144 for _ in repetitions: 1145 # raw_results will be populated with base_test_result.TestRunResults by 1146 # test_run.RunTests(). It is immediately added to all_raw_results so 1147 # that in the event of an exception, all_raw_results will already have 1148 # the up-to-date results and those can be written to disk. 1149 raw_results = [] 1150 all_raw_results.append(raw_results) 1151 1152 test_run.RunTests(raw_results, raw_logs_fh=raw_logs_fh) 1153 if not raw_results: 1154 all_raw_results.pop() 1155 continue 1156 1157 iteration_results = base_test_result.TestRunResults() 1158 for r in reversed(raw_results): 1159 iteration_results.AddTestRunResults(r) 1160 all_iteration_results.append(iteration_results) 1161 iteration_count += 1 1162 1163 for r in iteration_results.GetAll(): 1164 result_counts[r.GetName()][r.GetType()] += 1 1165 1166 report_results.LogFull( 1167 results=iteration_results, 1168 test_type=test_instance.TestType(), 1169 test_package=test_run.TestPackage(), 1170 annotation=getattr(args, 'annotations', None), 1171 flakiness_server=getattr(args, 'flakiness_dashboard_server', 1172 None)) 1173 1174 failed_tests = (iteration_results.GetNotPass() - 1175 iteration_results.GetSkip()) 1176 if failed_tests: 1177 _LogRerunStatement(failed_tests, args.wrapper_script_args) 1178 1179 if args.break_on_failure and not iteration_results.DidRunPass(): 1180 break 1181 1182 if iteration_count > 1: 1183 # display summary results 1184 # only display results for a test if at least one test did not pass 1185 all_pass = 0 1186 tot_tests = 0 1187 for test_name in result_counts: 1188 tot_tests += 1 1189 if any(result_counts[test_name][x] for x in ( 1190 base_test_result.ResultType.FAIL, 1191 base_test_result.ResultType.CRASH, 1192 base_test_result.ResultType.TIMEOUT, 1193 base_test_result.ResultType.UNKNOWN)): 1194 logging.critical( 1195 '%s: %s', 1196 test_name, 1197 ', '.join('%s %s' % (str(result_counts[test_name][i]), i) 1198 for i in base_test_result.ResultType.GetTypes())) 1199 else: 1200 all_pass += 1 1201 1202 logging.critical('%s of %s tests passed in all %s runs', 1203 str(all_pass), 1204 str(tot_tests), 1205 str(iteration_count)) 1206 1207 if save_detailed_results: 1208 assert raw_logs_fh 1209 raw_logs_fh.seek(0) 1210 raw_logs = raw_logs_fh.read() 1211 if raw_logs: 1212 with out_manager.ArchivedTempfile( 1213 'raw_logs.txt', 'raw_logs', 1214 output_manager.Datatype.TEXT) as raw_logs_file: 1215 raw_logs_file.write(raw_logs) 1216 logging.critical('RAW LOGS: %s', raw_logs_file.Link()) 1217 1218 with out_manager.ArchivedTempfile( 1219 'test_results_presentation.html', 1220 'test_results_presentation', 1221 output_manager.Datatype.HTML) as results_detail_file: 1222 result_html_string, _, _ = test_results_presentation.result_details( 1223 json_path=json_file.name, 1224 test_name=args.command, 1225 cs_base_url='http://cs.chromium.org', 1226 local_output=True) 1227 results_detail_file.write(result_html_string) 1228 results_detail_file.flush() 1229 logging.critical('TEST RESULTS: %s', results_detail_file.Link()) 1230 1231 ui_screenshots = test_results_presentation.ui_screenshot_set( 1232 json_file.name) 1233 if ui_screenshots: 1234 with out_manager.ArchivedTempfile( 1235 'ui_screenshots.json', 1236 'ui_capture', 1237 output_manager.Datatype.JSON) as ui_screenshot_file: 1238 ui_screenshot_file.write(ui_screenshots) 1239 logging.critical('UI Screenshots: %s', ui_screenshot_file.Link()) 1240 1241 return (0 if all(r.DidRunPass() for r in all_iteration_results) 1242 else constants.ERROR_EXIT_CODE) 1243 1244 1245def _LogRerunStatement(failed_tests, wrapper_arg_str): 1246 """Logs a message that can rerun the failed tests. 1247 1248 Logs a copy/pasteable message that filters tests so just the failing tests 1249 are run. 1250 1251 Args: 1252 failed_tests: A set of test results that did not pass. 1253 wrapper_arg_str: A string of args that were passed to the called wrapper 1254 script. 1255 """ 1256 rerun_arg_list = [] 1257 try: 1258 constants.CheckOutputDirectory() 1259 # constants.CheckOutputDirectory throws bare exceptions. 1260 except: # pylint: disable=bare-except 1261 logging.exception('Output directory not found. Unable to generate failing ' 1262 'test filter file.') 1263 return 1264 1265 output_directory = constants.GetOutDirectory() 1266 if not os.path.exists(output_directory): 1267 logging.error('Output directory not found. Unable to generate failing ' 1268 'test filter file.') 1269 return 1270 1271 test_filter_file = os.path.join(os.path.relpath(output_directory), 1272 _RERUN_FAILED_TESTS_FILE) 1273 arg_list = shlex.split(wrapper_arg_str) if wrapper_arg_str else sys.argv 1274 index = 0 1275 while index < len(arg_list): 1276 arg = arg_list[index] 1277 # Skip adding the filter=<file> and/or the filter arg as we're replacing 1278 # it with the new filter arg. 1279 # This covers --test-filter=, --test-launcher-filter-file=, --gtest-filter=, 1280 # --test-filter *Foobar.baz, -f *foobar, --package-filter <package>, 1281 # --runner-filter <runner>. 1282 if 'filter' in arg or arg == '-f': 1283 index += 1 if '=' in arg else 2 1284 continue 1285 1286 rerun_arg_list.append(arg) 1287 index += 1 1288 1289 failed_test_list = [str(t) for t in failed_tests] 1290 with open(test_filter_file, 'w') as fp: 1291 for t in failed_test_list: 1292 # Test result names can have # in them that don't match when applied as 1293 # a test name filter. 1294 fp.write('%s\n' % t.replace('#', '.')) 1295 1296 rerun_arg_list.append('--test-launcher-filter-file=%s' % test_filter_file) 1297 msg = """ 1298 %d Test(s) failed. 1299 Rerun failed tests with copy and pastable command: 1300 %s 1301 """ 1302 logging.critical(msg, len(failed_tests), shlex.join(rerun_arg_list)) 1303 1304 1305def DumpThreadStacks(_signal, _frame): 1306 for thread in threading.enumerate(): 1307 reraiser_thread.LogThreadStack(thread) 1308 1309 1310def main(): 1311 signal.signal(signal.SIGUSR1, DumpThreadStacks) 1312 1313 parser = argparse.ArgumentParser() 1314 command_parsers = parser.add_subparsers( 1315 title='test types', dest='command') 1316 1317 subp = command_parsers.add_parser( 1318 'gtest', 1319 help='googletest-based C++ tests') 1320 AddCommonOptions(subp) 1321 AddDeviceOptions(subp) 1322 AddEmulatorOptions(subp) 1323 AddGTestOptions(subp) 1324 AddTracingOptions(subp) 1325 AddCommandLineOptions(subp) 1326 1327 subp = command_parsers.add_parser( 1328 'instrumentation', 1329 help='InstrumentationTestCase-based Java tests') 1330 AddCommonOptions(subp) 1331 AddDeviceOptions(subp) 1332 AddEmulatorOptions(subp) 1333 AddInstrumentationTestOptions(subp) 1334 AddSkiaGoldTestOptions(subp) 1335 AddTracingOptions(subp) 1336 AddCommandLineOptions(subp) 1337 1338 subp = command_parsers.add_parser( 1339 'junit', 1340 help='JUnit4-based Java tests') 1341 AddCommonOptions(subp) 1342 AddJUnitTestOptions(subp) 1343 1344 subp = command_parsers.add_parser( 1345 'linker', 1346 help='linker tests') 1347 AddCommonOptions(subp) 1348 AddDeviceOptions(subp) 1349 AddEmulatorOptions(subp) 1350 AddLinkerTestOptions(subp) 1351 1352 subp = command_parsers.add_parser( 1353 'monkey', 1354 help="tests based on Android's monkey command") 1355 AddCommonOptions(subp) 1356 AddDeviceOptions(subp) 1357 AddEmulatorOptions(subp) 1358 AddMonkeyTestOptions(subp) 1359 1360 subp = command_parsers.add_parser( 1361 'python', 1362 help='python tests based on unittest.TestCase') 1363 AddCommonOptions(subp) 1364 AddPythonTestOptions(subp) 1365 1366 args, unknown_args = parser.parse_known_args() 1367 if unknown_args: 1368 if hasattr(args, 'allow_unknown') and args.allow_unknown: 1369 args.command_line_flags = unknown_args 1370 else: 1371 parser.error('unrecognized arguments: %s' % ' '.join(unknown_args)) 1372 1373 # --replace-system-package/--remove-system-package has the potential to cause 1374 # issues if --enable-concurrent-adb is set, so disallow that combination. 1375 concurrent_adb_enabled = (hasattr(args, 'enable_concurrent_adb') 1376 and args.enable_concurrent_adb) 1377 replacing_system_packages = (hasattr(args, 'replace_system_package') 1378 and args.replace_system_package) 1379 removing_system_packages = (hasattr(args, 'system_packages_to_remove') 1380 and args.system_packages_to_remove) 1381 if (concurrent_adb_enabled 1382 and (replacing_system_packages or removing_system_packages)): 1383 parser.error('--enable-concurrent-adb cannot be used with either ' 1384 '--replace-system-package or --remove-system-package') 1385 1386 # --use-webview-provider has the potential to cause issues if 1387 # --enable-concurrent-adb is set, so disallow that combination 1388 if (hasattr(args, 'use_webview_provider') and 1389 hasattr(args, 'enable_concurrent_adb') and args.use_webview_provider and 1390 args.enable_concurrent_adb): 1391 parser.error('--use-webview-provider and --enable-concurrent-adb cannot ' 1392 'be used together') 1393 1394 if (getattr(args, 'coverage_on_the_fly', False) 1395 and not getattr(args, 'coverage_dir', '')): 1396 parser.error('--coverage-on-the-fly requires --coverage-dir') 1397 1398 if (hasattr(args, 'debug_socket') or 1399 (hasattr(args, 'wait_for_java_debugger') and 1400 args.wait_for_java_debugger)): 1401 args.num_retries = 0 1402 1403 # Result-sink may not exist in the environment if rdb stream is not enabled. 1404 result_sink_client = result_sink.TryInitClient() 1405 1406 try: 1407 return RunTestsCommand(args, result_sink_client) 1408 except base_error.BaseError as e: 1409 logging.exception('Error occurred.') 1410 if e.is_infra_error: 1411 return constants.INFRA_EXIT_CODE 1412 return constants.ERROR_EXIT_CODE 1413 except: # pylint: disable=W0702 1414 logging.exception('Unrecognized error occurred.') 1415 return constants.ERROR_EXIT_CODE 1416 1417 1418if __name__ == '__main__': 1419 sys.exit(main()) 1420