Format python files using yapf
Change-Id: I8b7b97efb6bfdcceef9efc533cdaa0675ab7db40
diff --git a/tools/test.py b/tools/test.py
index b106d33..efbe5a1 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -23,23 +23,14 @@
import utils
if utils.is_python3():
- import threading
+ import threading
else:
- import thread
+ import thread
ALL_ART_VMS = [
- "default",
- "14.0.0",
- "13.0.0",
- "12.0.0",
- "10.0.0",
- "9.0.0",
- "8.1.0",
- "7.0.0",
- "6.0.1",
- "5.1.1",
- "4.4.4",
- "4.0.4"]
+ "default", "14.0.0", "13.0.0", "12.0.0", "10.0.0", "9.0.0", "8.1.0",
+ "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"
+]
# How often do we check for progress on the bots:
# Should be long enough that a normal run would always have med progress
@@ -55,562 +46,666 @@
REPORTS_PATH = os.path.join(utils.BUILD, 'reports')
REPORT_INDEX = ['tests', 'test', 'index.html']
VALID_RUNTIMES = [
- 'none',
- 'jdk8',
- 'jdk9',
- 'jdk11',
- 'jdk17',
- 'jdk20',
-] + [ 'dex-%s' % dexvm for dexvm in ALL_ART_VMS ]
+ 'none',
+ 'jdk8',
+ 'jdk9',
+ 'jdk11',
+ 'jdk17',
+ 'jdk20',
+] + ['dex-%s' % dexvm for dexvm in ALL_ART_VMS]
+
def ParseOptions():
- result = argparse.ArgumentParser()
- result.add_argument('--no-internal', '--no_internal',
- help='Do not run Google internal tests.',
- default=False, action='store_true')
- result.add_argument('--archive-failures', '--archive_failures',
- help='Upload test results to cloud storage on failure.',
- default=False, action='store_true')
- result.add_argument('--archive-failures-file-name',
- '--archive_failures_file_name',
- help='Set file name for the archived failures file name',
- default=uuid.uuid4())
- result.add_argument('--only-internal', '--only_internal',
- help='Only run Google internal tests.',
- default=False, action='store_true')
- result.add_argument('--all-tests', '--all_tests',
- help='Run tests in all configurations.',
- default=False, action='store_true')
- result.add_argument('--slow-tests', '--slow_tests',
- help='Also run slow tests.',
- default=False, action='store_true')
- result.add_argument('-v', '--verbose',
- help='Print test stdout to, well, stdout.',
- default=False, action='store_true')
- result.add_argument('--dex-vm', '--dex_vm',
- help='The android version of the vm to use. "all" will run the tests on '
- 'all art vm versions (stopping after first failed execution)',
- default="default",
- choices=ALL_ART_VMS + ["all"])
- result.add_argument('--dex-vm-kind', '--dex_vm_kind',
- help='Whether to use host or target version of runtime',
- default="host",
- nargs=1,
- choices=["host", "target"])
- result.add_argument('--one-line-per-test', '--one_line_per_test',
- help='Print a line before a tests starts and after it ends to stdout.',
- default=False, action='store_true')
- result.add_argument('--tool',
- help='Tool to run ART tests with: "r8" (default) or "d8" or "r8cf"'
- ' (r8 w/CF-backend). Ignored if "--all_tests" enabled.',
- default=None, choices=["r8", "d8", "r8cf"])
- result.add_argument('--disable-assertions', '--disable_assertions', '-da',
- help='Disable Java assertions when running the compiler '
- '(default enabled)',
- default=False, action='store_true')
- result.add_argument('--with-code-coverage', '--with_code_coverage',
- help='Enable code coverage with Jacoco.',
- default=False, action='store_true')
- result.add_argument('--test-dir', '--test_dir',
- help='Use a custom directory for the test artifacts instead of a'
- ' temporary (which is automatically removed after the test).'
- ' Note that the directory will not be cleared before the test.')
- result.add_argument('--command-cache-dir', '--command_cache_dir',
- help='Cache command invocations to this directory, speeds up test runs',
- default=os.environ.get('R8_COMMAND_CACHE_DIR'))
- result.add_argument('--command-cache-stats', '--command_cache_stats',
- help='Collect and print statistics about the command cache.',
- default=False, action='store_true')
- result.add_argument('--java-home', '--java_home',
- help='Use a custom java version to run tests.')
- result.add_argument('--java-max-memory-size', '--java_max_memory_size',
- help='Set memory for running tests, default 4G',
- default=os.environ.get('R8_JAVA_MAX_MEMORY_SIZE', '4G'))
- result.add_argument('--test-namespace', '--test_namespace',
- help='Only run tests in this namespace. The namespace is relative to '
- 'com/android/tools/r8, e.g., desugar/desugaredlibrary',
- default=None)
- result.add_argument('--shard-count', '--shard_count',
- help='We are running this many shards.')
- result.add_argument('--shard-number', '--shard_number',
- help='We are running this shard.')
- result.add_argument('--generate-golden-files-to', '--generate_golden_files_to',
- help='Store dex files produced by tests in the specified directory.'
- ' It is aimed to be read on platforms with no host runtime available'
- ' for comparison.')
- result.add_argument('--use-golden-files-in', '--use_golden_files_in',
- help='Download golden files hierarchy for this commit in the specified'
- ' location and use them instead of executing on host runtime.')
- result.add_argument('--no-r8lib', '--no_r8lib',
- default=False, action='store_true',
- help='Run the tests on R8 full with relocated dependencies.')
- result.add_argument('--no-arttests', '--no_arttests',
- default=False, action='store_true',
- help='Do not run the art tests.')
- result.add_argument('--r8lib-no-deps', '--r8lib_no_deps',
- default=False, action='store_true',
- help='Run the tests on r8lib without relocated dependencies.')
- result.add_argument('--failed',
- default=False, action='store_true',
- help='Run the tests that failed last execution.')
- result.add_argument('--fail-fast', '--fail_fast',
- default=False, action='store_true',
- help='Stop on first failure. Passes --fail-fast to gradle test runner.')
- result.add_argument('--worktree',
- default=False, action='store_true',
- help='Tests are run in worktree and should not use gradle user home.')
- result.add_argument('--runtimes',
- default=None,
- help='Test parameter runtimes to use, separated by : (eg, none:jdk9).'
- ' Special values include: all (for all runtimes)'
- ' and empty (for no runtimes).')
- result.add_argument('--print-hanging-stacks', '--print_hanging_stacks',
- default=-1, type=int, help='Print hanging stacks after timeout in seconds')
- result.add_argument('--print-full-stacktraces', '--print_full_stacktraces',
- default=False, action='store_true',
- help='Print the full stacktraces without any filtering applied')
- result.add_argument(
- '--print-obfuscated-stacktraces', '--print_obfuscated_stacktraces',
- default=False, action='store_true',
- help='Print the obfuscated stacktraces')
- result.add_argument(
- '--debug-agent', '--debug_agent',
- help='Enable Java debug agent and suspend compilation (default disabled)',
- default=False,
- action='store_true')
- result.add_argument('--desugared-library-configuration',
- '--desugared_library-configuration',
- help='Use alternative desugared library configuration.')
- result.add_argument('--desugared-library', '--desugared_library',
- help='Build and use desugared library from GitHub.')
- result.add_argument('--print-times', '--print_times',
- help='Print the execution time of the slowest tests..',
- default=False, action='store_true')
- result.add_argument(
- '--testing-state-dir',
- help='Explicitly set the testing state directory '
- '(defaults to build/test-state/<git-branch>).')
- result.add_argument(
- '--rerun',
- help='Rerun tests (implicitly enables testing state).',
- choices=testing_state.CHOICES)
- result.add_argument(
- '--stacktrace',
- help='Pass --stacktrace to the gradle run',
- default=False, action='store_true')
- result.add_argument('--kotlin-compiler-dev',
- help='Specify to download a kotlin dev compiler and run '
- 'tests with that',
- default=False, action='store_true')
- result.add_argument('--kotlin-compiler-old',
- help='Specify to run tests on older kotlin compilers',
- default=False, action='store_true')
- return result.parse_known_args()
+ result = argparse.ArgumentParser()
+ result.add_argument('--no-internal',
+ '--no_internal',
+ help='Do not run Google internal tests.',
+ default=False,
+ action='store_true')
+ result.add_argument('--archive-failures',
+ '--archive_failures',
+ help='Upload test results to cloud storage on failure.',
+ default=False,
+ action='store_true')
+ result.add_argument(
+ '--archive-failures-file-name',
+ '--archive_failures_file_name',
+ help='Set file name for the archived failures file name',
+ default=uuid.uuid4())
+ result.add_argument('--only-internal',
+ '--only_internal',
+ help='Only run Google internal tests.',
+ default=False,
+ action='store_true')
+ result.add_argument('--all-tests',
+ '--all_tests',
+ help='Run tests in all configurations.',
+ default=False,
+ action='store_true')
+ result.add_argument('--slow-tests',
+ '--slow_tests',
+ help='Also run slow tests.',
+ default=False,
+ action='store_true')
+ result.add_argument('-v',
+ '--verbose',
+ help='Print test stdout to, well, stdout.',
+ default=False,
+ action='store_true')
+ result.add_argument(
+ '--dex-vm',
+ '--dex_vm',
+ help='The android version of the vm to use. "all" will run the tests on '
+ 'all art vm versions (stopping after first failed execution)',
+ default="default",
+ choices=ALL_ART_VMS + ["all"])
+ result.add_argument('--dex-vm-kind',
+ '--dex_vm_kind',
+ help='Whether to use host or target version of runtime',
+ default="host",
+ nargs=1,
+ choices=["host", "target"])
+ result.add_argument(
+ '--one-line-per-test',
+ '--one_line_per_test',
+ help='Print a line before a tests starts and after it ends to stdout.',
+ default=False,
+ action='store_true')
+ result.add_argument(
+ '--tool',
+ help='Tool to run ART tests with: "r8" (default) or "d8" or "r8cf"'
+ ' (r8 w/CF-backend). Ignored if "--all_tests" enabled.',
+ default=None,
+ choices=["r8", "d8", "r8cf"])
+ result.add_argument(
+ '--disable-assertions',
+ '--disable_assertions',
+ '-da',
+ help='Disable Java assertions when running the compiler '
+ '(default enabled)',
+ default=False,
+ action='store_true')
+ result.add_argument('--with-code-coverage',
+ '--with_code_coverage',
+ help='Enable code coverage with Jacoco.',
+ default=False,
+ action='store_true')
+ result.add_argument(
+ '--test-dir',
+ '--test_dir',
+ help='Use a custom directory for the test artifacts instead of a'
+ ' temporary (which is automatically removed after the test).'
+ ' Note that the directory will not be cleared before the test.')
+ result.add_argument(
+ '--command-cache-dir',
+ '--command_cache_dir',
+ help='Cache command invocations to this directory, speeds up test runs',
+ default=os.environ.get('R8_COMMAND_CACHE_DIR'))
+ result.add_argument(
+ '--command-cache-stats',
+ '--command_cache_stats',
+ help='Collect and print statistics about the command cache.',
+ default=False,
+ action='store_true')
+ result.add_argument('--java-home',
+ '--java_home',
+ help='Use a custom java version to run tests.')
+ result.add_argument('--java-max-memory-size',
+ '--java_max_memory_size',
+ help='Set memory for running tests, default 4G',
+ default=os.environ.get('R8_JAVA_MAX_MEMORY_SIZE', '4G'))
+ result.add_argument(
+ '--test-namespace',
+ '--test_namespace',
+ help='Only run tests in this namespace. The namespace is relative to '
+ 'com/android/tools/r8, e.g., desugar/desugaredlibrary',
+ default=None)
+ result.add_argument('--shard-count',
+ '--shard_count',
+ help='We are running this many shards.')
+ result.add_argument('--shard-number',
+ '--shard_number',
+ help='We are running this shard.')
+ result.add_argument(
+ '--generate-golden-files-to',
+ '--generate_golden_files_to',
+ help='Store dex files produced by tests in the specified directory.'
+ ' It is aimed to be read on platforms with no host runtime available'
+ ' for comparison.')
+ result.add_argument(
+ '--use-golden-files-in',
+ '--use_golden_files_in',
+ help='Download golden files hierarchy for this commit in the specified'
+ ' location and use them instead of executing on host runtime.')
+ result.add_argument(
+ '--no-r8lib',
+ '--no_r8lib',
+ default=False,
+ action='store_true',
+ help='Run the tests on R8 full with relocated dependencies.')
+ result.add_argument('--no-arttests',
+ '--no_arttests',
+ default=False,
+ action='store_true',
+ help='Do not run the art tests.')
+ result.add_argument(
+ '--r8lib-no-deps',
+ '--r8lib_no_deps',
+ default=False,
+ action='store_true',
+ help='Run the tests on r8lib without relocated dependencies.')
+ result.add_argument('--failed',
+ default=False,
+ action='store_true',
+ help='Run the tests that failed last execution.')
+ result.add_argument(
+ '--fail-fast',
+ '--fail_fast',
+ default=False,
+ action='store_true',
+ help='Stop on first failure. Passes --fail-fast to gradle test runner.')
+ result.add_argument(
+ '--worktree',
+ default=False,
+ action='store_true',
+ help='Tests are run in worktree and should not use gradle user home.')
+ result.add_argument(
+ '--runtimes',
+ default=None,
+ help='Test parameter runtimes to use, separated by : (eg, none:jdk9).'
+ ' Special values include: all (for all runtimes)'
+ ' and empty (for no runtimes).')
+ result.add_argument('--print-hanging-stacks',
+ '--print_hanging_stacks',
+ default=-1,
+ type=int,
+ help='Print hanging stacks after timeout in seconds')
+ result.add_argument(
+ '--print-full-stacktraces',
+ '--print_full_stacktraces',
+ default=False,
+ action='store_true',
+ help='Print the full stacktraces without any filtering applied')
+ result.add_argument('--print-obfuscated-stacktraces',
+ '--print_obfuscated_stacktraces',
+ default=False,
+ action='store_true',
+ help='Print the obfuscated stacktraces')
+ result.add_argument(
+ '--debug-agent',
+ '--debug_agent',
+ help=
+ 'Enable Java debug agent and suspend compilation (default disabled)',
+ default=False,
+ action='store_true')
+ result.add_argument('--desugared-library-configuration',
+ '--desugared_library-configuration',
+ help='Use alternative desugared library configuration.')
+ result.add_argument('--desugared-library',
+ '--desugared_library',
+ help='Build and use desugared library from GitHub.')
+ result.add_argument('--print-times',
+ '--print_times',
+ help='Print the execution time of the slowest tests..',
+ default=False,
+ action='store_true')
+ result.add_argument('--testing-state-dir',
+ help='Explicitly set the testing state directory '
+ '(defaults to build/test-state/<git-branch>).')
+ result.add_argument('--rerun',
+ help='Rerun tests (implicitly enables testing state).',
+ choices=testing_state.CHOICES)
+ result.add_argument('--stacktrace',
+ help='Pass --stacktrace to the gradle run',
+ default=False,
+ action='store_true')
+ result.add_argument(
+ '--kotlin-compiler-dev',
+ help='Specify to download a kotlin dev compiler and run '
+ 'tests with that',
+ default=False,
+ action='store_true')
+ result.add_argument('--kotlin-compiler-old',
+ help='Specify to run tests on older kotlin compilers',
+ default=False,
+ action='store_true')
+ return result.parse_known_args()
+
def has_failures(classes_file):
- with open(classes_file) as f:
- contents = f.read()
- # The report has a div tag with the percentage of tests that succeeded.
- assert '<div class="percent">' in contents
- return '<div class="percent">100%</div>' not in contents
+ with open(classes_file) as f:
+ contents = f.read()
+ # The report has a div tag with the percentage of tests that succeeded.
+ assert '<div class="percent">' in contents
+ return '<div class="percent">100%</div>' not in contents
+
def should_upload(filename, absolute_filename):
- # filename is relative to REPO_ROOT/build/reports/tests
- if filename.startswith('test/packages'):
- # We don't upload the package overview
- return False
- if filename.startswith('test/classes'):
- return has_failures(absolute_filename)
- # Always upload index, css and js
- return True
+ # filename is relative to REPO_ROOT/build/reports/tests
+ if filename.startswith('test/packages'):
+ # We don't upload the package overview
+ return False
+ if filename.startswith('test/classes'):
+ return has_failures(absolute_filename)
+ # Always upload index, css and js
+ return True
+
def archive_failures(options):
- upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
- file_name = options.archive_failures_file_name
- destination_dir = 'gs://%s/%s/' % (BUCKET, file_name)
- for (dir_path, dir_names, file_names) in os.walk(upload_dir):
- for f in file_names:
- absolute_file = os.path.join(dir_path, f)
- relative_file = absolute_file[len(upload_dir)+1:]
- if (should_upload(relative_file, absolute_file)):
- utils.upload_file_to_cloud_storage(absolute_file,
- destination_dir + relative_file)
- url = 'https://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET, file_name)
- print('Test results available at: %s' % url)
+ upload_dir = os.path.join(utils.REPO_ROOT, 'build', 'reports', 'tests')
+ file_name = options.archive_failures_file_name
+ destination_dir = 'gs://%s/%s/' % (BUCKET, file_name)
+ for (dir_path, dir_names, file_names) in os.walk(upload_dir):
+ for f in file_names:
+ absolute_file = os.path.join(dir_path, f)
+ relative_file = absolute_file[len(upload_dir) + 1:]
+ if (should_upload(relative_file, absolute_file)):
+ utils.upload_file_to_cloud_storage(
+ absolute_file, destination_dir + relative_file)
+ url = 'https://storage.googleapis.com/%s/%s/test/index.html' % (BUCKET,
+ file_name)
+ print('Test results available at: %s' % url)
+
def Main():
- (options, args) = ParseOptions()
- if utils.is_bot():
- gradle.RunGradle(['--no-daemon', 'clean'])
- print('Running with python ' + str(sys.version_info))
- # Always print stats on bots if command cache is enabled
- options.command_cache_stats = options.command_cache_dir is not None
+ (options, args) = ParseOptions()
+ if utils.is_bot():
+ gradle.RunGradle(['--no-daemon', 'clean'])
+ print('Running with python ' + str(sys.version_info))
+ # Always print stats on bots if command cache is enabled
+ options.command_cache_stats = options.command_cache_dir is not None
- desugar_jdk_json_dir = None
- if options.desugared_library_configuration:
- if options.desugared_library_configuration != 'jdk11':
- print("Only value supported for --desugared-library is 'jdk11'")
- exit(1)
- desugar_jdk_json_dir = 'src/library_desugar/jdk11'
+ desugar_jdk_json_dir = None
+ if options.desugared_library_configuration:
+ if options.desugared_library_configuration != 'jdk11':
+ print("Only value supported for --desugared-library is 'jdk11'")
+ exit(1)
+ desugar_jdk_json_dir = 'src/library_desugar/jdk11'
- desugar_jdk_libs = None
- if options.desugared_library:
- if options.desugared_library != 'HEAD':
- print("Only value supported for --desugared-library is 'HEAD'")
- exit(1)
- desugar_jdk_libs_dir = 'build/desugar_jdk_libs'
- shutil.rmtree(desugar_jdk_libs_dir, ignore_errors=True)
- os.makedirs(desugar_jdk_libs_dir)
- print('Building desugared library.')
- with utils.TempDir() as checkout_dir:
- archive_desugar_jdk_libs.CloneDesugaredLibrary('google', checkout_dir, 'HEAD')
- # Make sure bazel is extracted in third_party.
- utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
- utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
- utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
- (library_jar, maven_zip) = archive_desugar_jdk_libs.BuildDesugaredLibrary(checkout_dir, 'jdk11_legacy' if options.desugared_library_configuration == 'jdk11' else 'jdk8')
- desugar_jdk_libs = os.path.join(desugar_jdk_libs_dir, os.path.basename(library_jar))
- shutil.copyfile(library_jar, desugar_jdk_libs)
- print('Desugared library for test in ' + desugar_jdk_libs)
+ desugar_jdk_libs = None
+ if options.desugared_library:
+ if options.desugared_library != 'HEAD':
+ print("Only value supported for --desugared-library is 'HEAD'")
+ exit(1)
+ desugar_jdk_libs_dir = 'build/desugar_jdk_libs'
+ shutil.rmtree(desugar_jdk_libs_dir, ignore_errors=True)
+ os.makedirs(desugar_jdk_libs_dir)
+ print('Building desugared library.')
+ with utils.TempDir() as checkout_dir:
+ archive_desugar_jdk_libs.CloneDesugaredLibrary(
+ 'google', checkout_dir, 'HEAD')
+ # Make sure bazel is extracted in third_party.
+ utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE)
+ utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE)
+ utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE)
+ (library_jar,
+ maven_zip) = archive_desugar_jdk_libs.BuildDesugaredLibrary(
+ checkout_dir, 'jdk11_legacy' if
+ options.desugared_library_configuration == 'jdk11' else 'jdk8')
+ desugar_jdk_libs = os.path.join(desugar_jdk_libs_dir,
+ os.path.basename(library_jar))
+ shutil.copyfile(library_jar, desugar_jdk_libs)
+ print('Desugared library for test in ' + desugar_jdk_libs)
- gradle_args = []
+ gradle_args = []
- if options.stacktrace or utils.is_bot():
- gradle_args.append('--stacktrace')
+ if options.stacktrace or utils.is_bot():
+ gradle_args.append('--stacktrace')
- if utils.is_bot():
- # Bots don't like dangling processes.
- gradle_args.append('--no-daemon')
+ if utils.is_bot():
+ # Bots don't like dangling processes.
+ gradle_args.append('--no-daemon')
- # Set all necessary Gradle properties and options first.
- if options.shard_count:
- assert options.shard_number
- gradle_args.append('-Pshard_count=%s' % options.shard_count)
- gradle_args.append('-Pshard_number=%s' % options.shard_number)
- if options.verbose:
- gradle_args.append('-Pprint_test_stdout')
- if options.no_internal:
- gradle_args.append('-Pno_internal')
- if options.only_internal:
- gradle_args.append('-Ponly_internal')
- if options.all_tests:
- gradle_args.append('-Pall_tests')
- if options.slow_tests:
- gradle_args.append('-Pslow_tests=1')
- if options.tool:
- gradle_args.append('-Ptool=%s' % options.tool)
- if options.one_line_per_test:
- gradle_args.append('-Pone_line_per_test')
- if options.test_namespace:
- gradle_args.append('-Ptest_namespace=%s' % options.test_namespace)
- if options.disable_assertions:
- gradle_args.append('-Pdisable_assertions')
- if options.with_code_coverage:
- gradle_args.append('-Pwith_code_coverage')
- if options.print_full_stacktraces:
- gradle_args.append('-Pprint_full_stacktraces')
- if options.print_obfuscated_stacktraces:
- gradle_args.append('-Pprint_obfuscated_stacktraces')
- if options.kotlin_compiler_old:
- gradle_args.append('-Pkotlin_compiler_old')
- if options.kotlin_compiler_dev:
- gradle_args.append('-Pkotlin_compiler_dev')
- download_kotlin_dev.download_newest()
- if os.name == 'nt':
- gradle_args.append('-Pno_internal')
- if options.test_dir:
- gradle_args.append('-Ptest_dir=' + options.test_dir)
- if not os.path.exists(options.test_dir):
- os.makedirs(options.test_dir)
- if options.command_cache_dir:
- gradle_args.append('-Pcommand_cache_dir=' + options.command_cache_dir)
- if not os.path.exists(options.command_cache_dir):
- os.makedirs(options.command_cache_dir)
- if options.command_cache_stats:
- stats_dir = os.path.join(options.command_cache_dir, 'stats')
- gradle_args.append('-Pcommand_cache_stats_dir=' + stats_dir)
- if not os.path.exists(stats_dir):
- os.makedirs(stats_dir)
- # Clean out old stats files
- for (_, _, file_names) in os.walk(stats_dir):
- for f in file_names:
- os.remove(os.path.join(stats_dir, f))
- if options.java_home:
- gradle_args.append('-Dorg.gradle.java.home=' + options.java_home)
- if options.java_max_memory_size:
- gradle_args.append('-Ptest_xmx=' + options.java_max_memory_size)
- if options.generate_golden_files_to:
- gradle_args.append('-Pgenerate_golden_files_to=' + options.generate_golden_files_to)
- if not os.path.exists(options.generate_golden_files_to):
- os.makedirs(options.generate_golden_files_to)
- gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
- if options.use_golden_files_in:
- gradle_args.append('-Puse_golden_files_in=' + options.use_golden_files_in)
- if not os.path.exists(options.use_golden_files_in):
- os.makedirs(options.use_golden_files_in)
- gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
- if options.r8lib_no_deps and options.no_r8lib:
- print('Inconsistent arguments: both --no-r8lib and --r8lib-no-deps specified.')
- exit(1)
- if options.r8lib_no_deps:
- gradle_args.append('-Pr8lib_no_deps')
- elif not options.no_r8lib:
- gradle_args.append('-Pr8lib')
- if options.worktree:
- gradle_args.append('-g=' + os.path.join(utils.REPO_ROOT, ".gradle_user_home"))
- gradle_args.append('--no-daemon')
- if options.debug_agent:
- gradle_args.append('--no-daemon')
- if desugar_jdk_json_dir:
- gradle_args.append('-Pdesugar_jdk_json_dir=' + desugar_jdk_json_dir)
- if desugar_jdk_libs:
- gradle_args.append('-Pdesugar_jdk_libs=' + desugar_jdk_libs)
- if options.no_arttests:
- gradle_args.append('-Pno_arttests=true')
-
- if options.rerun:
- testing_state.set_up_test_state(gradle_args, options.rerun, options.testing_state_dir)
-
- # Enable completeness testing of ART profile rewriting.
- gradle_args.append('-Part_profile_rewriting_completeness_check=true')
-
- # Build an R8 with dependencies for bootstrapping tests before adding test sources.
- gradle_args.append(utils.GRADLE_TASK_R8)
- gradle_args.append(utils.GRADLE_TASK_CLEAN_TEST)
- gradle_args.append(utils.GRADLE_TASK_TEST)
- gradle_args.append('--stacktrace')
- gradle_args.append('-Pprint_full_stacktraces')
-
- if options.debug_agent:
- gradle_args.append('--debug-jvm')
- if options.fail_fast:
- gradle_args.append('--fail-fast')
- if options.failed:
- args = compute_failed_tests(args)
- if args is None:
- return 1
- if len(args) == 0:
- print("No failing tests")
- return 0
- # Test filtering. Must always follow the 'test' task.
- testFilterProperty = []
- for testFilter in args:
- gradle_args.append('--tests')
- gradle_args.append(testFilter)
- testFilterProperty.append(testFilter)
- assert not ("|" in testFilter), "| is used as separating character"
- if len(testFilterProperty) > 0:
- gradle_args.append("-Ptestfilter=" + "|".join(testFilterProperty))
- if options.with_code_coverage:
- # Create Jacoco report after tests.
- gradle_args.append('jacocoTestReport')
-
- if options.use_golden_files_in:
- sha1 = '%s' % utils.get_HEAD_sha1()
- with utils.ChangedWorkingDirectory(options.use_golden_files_in):
- utils.download_file_from_cloud_storage(
- 'gs://r8-test-results/golden-files/%s.tar.gz' % sha1,
- '%s.tar.gz' % sha1)
- utils.unpack_archive('%s.tar.gz' % sha1)
-
- print_stacks_timeout = options.print_hanging_stacks
- if (utils.is_bot() and not utils.IsWindows()) or print_stacks_timeout > -1:
- timestamp_file = os.path.join(utils.BUILD, 'last_test_time')
- if os.path.exists(timestamp_file):
- os.remove(timestamp_file)
- gradle_args.append('-Pupdate_test_timestamp=' + timestamp_file)
- print_stacks_timeout = (print_stacks_timeout
- if print_stacks_timeout != -1
- else TIMEOUT_HANDLER_PERIOD)
- if utils.is_python3():
- threading.Thread(
- target=timeout_handler,
- args=(timestamp_file, print_stacks_timeout),
- daemon=True).start()
- else:
- thread.start_new_thread(
- timeout_handler, (timestamp_file, print_stacks_timeout,))
- rotate_test_reports()
-
- # Now run tests on selected runtime(s).
- if options.runtimes:
- if options.dex_vm != 'default':
- print('Unexpected runtimes and dex_vm argument: ' + options.dex_vm)
- sys.exit(1)
- if options.runtimes == 'empty':
- # Set runtimes with no content will configure no runtimes.
- gradle_args.append('-Pruntimes=')
- elif options.runtimes == 'all':
- # An unset runtimes will configure all runtimes
- pass
- else:
- prefixes = [prefix.strip() for prefix in options.runtimes.split(':')]
- runtimes = []
- for prefix in prefixes:
- matches = [ rt for rt in VALID_RUNTIMES if rt.startswith(prefix) ]
- if len(matches) == 0:
- print("Invalid runtime prefix '%s'." % prefix)
- print("Must be just 'all', 'empty'," \
- " or a prefix of %s" % ', '.join(VALID_RUNTIMES))
- sys.exit(1)
- runtimes.extend(matches)
- gradle_args.append('-Pruntimes=%s' % ':'.join(runtimes))
-
- return_code = gradle.RunGradle(gradle_args, throw_on_failure=False)
- return archive_and_return(return_code, options)
-
- # Legacy testing populates the runtimes based on dex_vm.
- vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
-
- if options.print_times:
- gradle_args.append('-Pprint_times=true')
- for art_vm in vms_to_test:
- vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
- runtimes = ['dex-' + art_vm]
- # Append the "none" runtime and default JVM if running the "default" DEX VM.
- if art_vm == "default":
- runtimes.extend(['jdk11', 'none'])
- return_code = gradle.RunGradle(
- gradle_args + [
- '-Pdex_vm=%s' % art_vm + vm_suffix,
- '-Pruntimes=%s' % ':'.join(runtimes),
- ],
- throw_on_failure=False)
+ # Set all necessary Gradle properties and options first.
+ if options.shard_count:
+ assert options.shard_number
+ gradle_args.append('-Pshard_count=%s' % options.shard_count)
+ gradle_args.append('-Pshard_number=%s' % options.shard_number)
+ if options.verbose:
+ gradle_args.append('-Pprint_test_stdout')
+ if options.no_internal:
+ gradle_args.append('-Pno_internal')
+ if options.only_internal:
+ gradle_args.append('-Ponly_internal')
+ if options.all_tests:
+ gradle_args.append('-Pall_tests')
+ if options.slow_tests:
+ gradle_args.append('-Pslow_tests=1')
+ if options.tool:
+ gradle_args.append('-Ptool=%s' % options.tool)
+ if options.one_line_per_test:
+ gradle_args.append('-Pone_line_per_test')
+ if options.test_namespace:
+ gradle_args.append('-Ptest_namespace=%s' % options.test_namespace)
+ if options.disable_assertions:
+ gradle_args.append('-Pdisable_assertions')
+ if options.with_code_coverage:
+ gradle_args.append('-Pwith_code_coverage')
+ if options.print_full_stacktraces:
+ gradle_args.append('-Pprint_full_stacktraces')
+ if options.print_obfuscated_stacktraces:
+ gradle_args.append('-Pprint_obfuscated_stacktraces')
+ if options.kotlin_compiler_old:
+ gradle_args.append('-Pkotlin_compiler_old')
+ if options.kotlin_compiler_dev:
+ gradle_args.append('-Pkotlin_compiler_dev')
+ download_kotlin_dev.download_newest()
+ if os.name == 'nt':
+ gradle_args.append('-Pno_internal')
+ if options.test_dir:
+ gradle_args.append('-Ptest_dir=' + options.test_dir)
+ if not os.path.exists(options.test_dir):
+ os.makedirs(options.test_dir)
+ if options.command_cache_dir:
+ gradle_args.append('-Pcommand_cache_dir=' + options.command_cache_dir)
+ if not os.path.exists(options.command_cache_dir):
+ os.makedirs(options.command_cache_dir)
+ if options.command_cache_stats:
+ stats_dir = os.path.join(options.command_cache_dir, 'stats')
+ gradle_args.append('-Pcommand_cache_stats_dir=' + stats_dir)
+ if not os.path.exists(stats_dir):
+ os.makedirs(stats_dir)
+ # Clean out old stats files
+ for (_, _, file_names) in os.walk(stats_dir):
+ for f in file_names:
+ os.remove(os.path.join(stats_dir, f))
+ if options.java_home:
+ gradle_args.append('-Dorg.gradle.java.home=' + options.java_home)
+ if options.java_max_memory_size:
+ gradle_args.append('-Ptest_xmx=' + options.java_max_memory_size)
if options.generate_golden_files_to:
- sha1 = '%s' % utils.get_HEAD_sha1()
- with utils.ChangedWorkingDirectory(options.generate_golden_files_to):
- archive = utils.create_archive(sha1)
- utils.upload_file_to_cloud_storage(archive,
- 'gs://r8-test-results/golden-files/' + archive)
+ gradle_args.append('-Pgenerate_golden_files_to=' +
+ options.generate_golden_files_to)
+ if not os.path.exists(options.generate_golden_files_to):
+ os.makedirs(options.generate_golden_files_to)
+ gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
+ if options.use_golden_files_in:
+ gradle_args.append('-Puse_golden_files_in=' +
+ options.use_golden_files_in)
+ if not os.path.exists(options.use_golden_files_in):
+ os.makedirs(options.use_golden_files_in)
+ gradle_args.append('-PHEAD_sha1=' + utils.get_HEAD_sha1())
+ if options.r8lib_no_deps and options.no_r8lib:
+ print(
+ 'Inconsistent arguments: both --no-r8lib and --r8lib-no-deps specified.'
+ )
+ exit(1)
+ if options.r8lib_no_deps:
+ gradle_args.append('-Pr8lib_no_deps')
+ elif not options.no_r8lib:
+ gradle_args.append('-Pr8lib')
+ if options.worktree:
+ gradle_args.append('-g=' +
+ os.path.join(utils.REPO_ROOT, ".gradle_user_home"))
+ gradle_args.append('--no-daemon')
+ if options.debug_agent:
+ gradle_args.append('--no-daemon')
+ if desugar_jdk_json_dir:
+ gradle_args.append('-Pdesugar_jdk_json_dir=' + desugar_jdk_json_dir)
+ if desugar_jdk_libs:
+ gradle_args.append('-Pdesugar_jdk_libs=' + desugar_jdk_libs)
+ if options.no_arttests:
+ gradle_args.append('-Pno_arttests=true')
- return archive_and_return(return_code, options)
+ if options.rerun:
+ testing_state.set_up_test_state(gradle_args, options.rerun,
+ options.testing_state_dir)
- return 0
+ # Enable completeness testing of ART profile rewriting.
+ gradle_args.append('-Part_profile_rewriting_completeness_check=true')
+
+ # Build an R8 with dependencies for bootstrapping tests before adding test sources.
+ gradle_args.append(utils.GRADLE_TASK_R8)
+ gradle_args.append(utils.GRADLE_TASK_CLEAN_TEST)
+ gradle_args.append(utils.GRADLE_TASK_TEST)
+ gradle_args.append('--stacktrace')
+ gradle_args.append('-Pprint_full_stacktraces')
+
+ if options.debug_agent:
+ gradle_args.append('--debug-jvm')
+ if options.fail_fast:
+ gradle_args.append('--fail-fast')
+ if options.failed:
+ args = compute_failed_tests(args)
+ if args is None:
+ return 1
+ if len(args) == 0:
+ print("No failing tests")
+ return 0
+ # Test filtering. Must always follow the 'test' task.
+ testFilterProperty = []
+ for testFilter in args:
+ gradle_args.append('--tests')
+ gradle_args.append(testFilter)
+ testFilterProperty.append(testFilter)
+ assert not ("|" in testFilter), "| is used as separating character"
+ if len(testFilterProperty) > 0:
+ gradle_args.append("-Ptestfilter=" + "|".join(testFilterProperty))
+ if options.with_code_coverage:
+ # Create Jacoco report after tests.
+ gradle_args.append('jacocoTestReport')
+
+ if options.use_golden_files_in:
+ sha1 = '%s' % utils.get_HEAD_sha1()
+ with utils.ChangedWorkingDirectory(options.use_golden_files_in):
+ utils.download_file_from_cloud_storage(
+ 'gs://r8-test-results/golden-files/%s.tar.gz' % sha1,
+ '%s.tar.gz' % sha1)
+ utils.unpack_archive('%s.tar.gz' % sha1)
+
+ print_stacks_timeout = options.print_hanging_stacks
+ if (utils.is_bot() and not utils.IsWindows()) or print_stacks_timeout > -1:
+ timestamp_file = os.path.join(utils.BUILD, 'last_test_time')
+ if os.path.exists(timestamp_file):
+ os.remove(timestamp_file)
+ gradle_args.append('-Pupdate_test_timestamp=' + timestamp_file)
+ print_stacks_timeout = (print_stacks_timeout if print_stacks_timeout
+ != -1 else TIMEOUT_HANDLER_PERIOD)
+ if utils.is_python3():
+ threading.Thread(target=timeout_handler,
+ args=(timestamp_file, print_stacks_timeout),
+ daemon=True).start()
+ else:
+ thread.start_new_thread(timeout_handler, (
+ timestamp_file,
+ print_stacks_timeout,
+ ))
+ rotate_test_reports()
+
+ # Now run tests on selected runtime(s).
+ if options.runtimes:
+ if options.dex_vm != 'default':
+ print('Unexpected runtimes and dex_vm argument: ' + options.dex_vm)
+ sys.exit(1)
+ if options.runtimes == 'empty':
+ # Set runtimes with no content will configure no runtimes.
+ gradle_args.append('-Pruntimes=')
+ elif options.runtimes == 'all':
+ # An unset runtimes will configure all runtimes
+ pass
+ else:
+ prefixes = [
+ prefix.strip() for prefix in options.runtimes.split(':')
+ ]
+ runtimes = []
+ for prefix in prefixes:
+ matches = [rt for rt in VALID_RUNTIMES if rt.startswith(prefix)]
+ if len(matches) == 0:
+ print("Invalid runtime prefix '%s'." % prefix)
+ print("Must be just 'all', 'empty'," \
+ " or a prefix of %s" % ', '.join(VALID_RUNTIMES))
+ sys.exit(1)
+ runtimes.extend(matches)
+ gradle_args.append('-Pruntimes=%s' % ':'.join(runtimes))
+
+ return_code = gradle.RunGradle(gradle_args, throw_on_failure=False)
+ return archive_and_return(return_code, options)
+
+ # Legacy testing populates the runtimes based on dex_vm.
+ vms_to_test = [options.dex_vm] if options.dex_vm != "all" else ALL_ART_VMS
+
+ if options.print_times:
+ gradle_args.append('-Pprint_times=true')
+ for art_vm in vms_to_test:
+ vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
+ runtimes = ['dex-' + art_vm]
+ # Append the "none" runtime and default JVM if running the "default" DEX VM.
+ if art_vm == "default":
+ runtimes.extend(['jdk11', 'none'])
+ return_code = gradle.RunGradle(gradle_args + [
+ '-Pdex_vm=%s' % art_vm + vm_suffix,
+ '-Pruntimes=%s' % ':'.join(runtimes),
+ ],
+ throw_on_failure=False)
+ if options.generate_golden_files_to:
+ sha1 = '%s' % utils.get_HEAD_sha1()
+ with utils.ChangedWorkingDirectory(
+ options.generate_golden_files_to):
+ archive = utils.create_archive(sha1)
+ utils.upload_file_to_cloud_storage(
+ archive, 'gs://r8-test-results/golden-files/' + archive)
+
+ return archive_and_return(return_code, options)
+
+ return 0
+
def archive_and_return(return_code, options):
- if return_code != 0:
- if options.archive_failures:
- archive_failures(options)
- if options.command_cache_stats:
- stats_dir = os.path.join(options.command_cache_dir, 'stats')
- cache_hit = 0
- cache_miss = 0
- cache_put = 0
- for (_, _, file_names) in os.walk(stats_dir):
- for f in file_names:
- if f.endswith('CACHEHIT'):
- cache_hit += os.stat(os.path.join(stats_dir, f)).st_size
- if f.endswith('CACHEMISS'):
- cache_miss += os.stat(os.path.join(stats_dir, f)).st_size
- if f.endswith('CACHEPUT'):
- cache_put += os.stat(os.path.join(stats_dir, f)).st_size
- print('Command cache stats')
- print(' Cache hits: ' + str(cache_hit))
- print(' Cache miss: ' + str(cache_miss))
- print(' Cache puts: ' + str(cache_put))
- return return_code
+ if return_code != 0:
+ if options.archive_failures:
+ archive_failures(options)
+ if options.command_cache_stats:
+ stats_dir = os.path.join(options.command_cache_dir, 'stats')
+ cache_hit = 0
+ cache_miss = 0
+ cache_put = 0
+ for (_, _, file_names) in os.walk(stats_dir):
+ for f in file_names:
+ if f.endswith('CACHEHIT'):
+ cache_hit += os.stat(os.path.join(stats_dir, f)).st_size
+ if f.endswith('CACHEMISS'):
+ cache_miss += os.stat(os.path.join(stats_dir, f)).st_size
+ if f.endswith('CACHEPUT'):
+ cache_put += os.stat(os.path.join(stats_dir, f)).st_size
+ print('Command cache stats')
+ print(' Cache hits: ' + str(cache_hit))
+ print(' Cache miss: ' + str(cache_miss))
+ print(' Cache puts: ' + str(cache_put))
+ return return_code
+
def print_jstacks():
- processes = subprocess.check_output(['ps', 'aux']).decode('utf-8')
- for l in processes.splitlines():
- if 'art' in l or 'dalvik' in l:
- print('Running art of dalvik process: \n%s' % l)
- if 'java' in l and 'openjdk' in l:
- print('Running jstack on process: \n%s' % l)
- # Example line:
- # ricow 184313 2.6 0.0 36839068 31808 ? Sl 09:53 0:00 /us..
- columns = l.split()
- pid = columns[1]
- return_value = subprocess.call(['jstack', pid])
- if return_value:
- print('Could not jstack %s' % l)
+ processes = subprocess.check_output(['ps', 'aux']).decode('utf-8')
+ for l in processes.splitlines():
+ if 'art' in l or 'dalvik' in l:
+ print('Running art of dalvik process: \n%s' % l)
+ if 'java' in l and 'openjdk' in l:
+ print('Running jstack on process: \n%s' % l)
+ # Example line:
+ # ricow 184313 2.6 0.0 36839068 31808 ? Sl 09:53 0:00 /us..
+ columns = l.split()
+ pid = columns[1]
+ return_value = subprocess.call(['jstack', pid])
+ if return_value:
+ print('Could not jstack %s' % l)
+
def get_time_from_file(timestamp_file):
- if os.path.exists(timestamp_file):
- timestamp = os.stat(timestamp_file).st_mtime
- print('TIMEOUT HANDLER timestamp: %s' % (timestamp))
- sys.stdout.flush()
- return timestamp
- else:
- print('TIMEOUT HANDLER no timestamp file yet')
- sys.stdout.flush()
- return None
+ if os.path.exists(timestamp_file):
+ timestamp = os.stat(timestamp_file).st_mtime
+ print('TIMEOUT HANDLER timestamp: %s' % (timestamp))
+ sys.stdout.flush()
+ return timestamp
+ else:
+ print('TIMEOUT HANDLER no timestamp file yet')
+ sys.stdout.flush()
+ return None
+
def timeout_handler(timestamp_file, timeout_handler_period):
- last_timestamp = None
- while True:
- time.sleep(timeout_handler_period)
- new_timestamp = get_time_from_file(timestamp_file)
- if last_timestamp and new_timestamp == last_timestamp:
- print_jstacks()
- last_timestamp = new_timestamp
+ last_timestamp = None
+ while True:
+ time.sleep(timeout_handler_period)
+ new_timestamp = get_time_from_file(timestamp_file)
+ if last_timestamp and new_timestamp == last_timestamp:
+ print_jstacks()
+ last_timestamp = new_timestamp
+
def report_dir_path(index):
- if index == 0:
- return REPORTS_PATH
- return '%s%d' % (REPORTS_PATH, index)
+ if index == 0:
+ return REPORTS_PATH
+ return '%s%d' % (REPORTS_PATH, index)
+
def report_index_path(index):
- return os.path.join(report_dir_path(index), *REPORT_INDEX)
+ return os.path.join(report_dir_path(index), *REPORT_INDEX)
+
# Rotate test results so previous results are still accessible.
def rotate_test_reports():
- if not os.path.exists(report_dir_path(0)):
- return
- i = 1
- while i < NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
- i += 1
- if i == NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
- shutil.rmtree(report_dir_path(i))
- while i > 0:
- shutil.move(report_dir_path(i - 1), report_dir_path(i))
- i -= 1
+ if not os.path.exists(report_dir_path(0)):
+ return
+ i = 1
+ while i < NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
+ i += 1
+ if i == NUMBER_OF_TEST_REPORTS and os.path.exists(report_dir_path(i)):
+ shutil.rmtree(report_dir_path(i))
+ while i > 0:
+ shutil.move(report_dir_path(i - 1), report_dir_path(i))
+ i -= 1
+
def compute_failed_tests(args):
- if len(args) > 1:
- print("Running with --failed can take an optional path to a report index (or report number).")
- return None
- report = report_index_path(0)
- # If the default report does not exist, fall back to the previous report as it may be a failed
- # gradle run which has already moved the report to report1, but did not produce a new report.
- if not os.path.exists(report):
- report1 = report_index_path(1)
- if os.path.exists(report1):
- report = report1
- if len(args) == 1:
- try:
- # try to parse the arg as a report index.
- index = int(args[0])
- report = report_index_path(index)
- except ValueError:
- # if integer parsing failed assume it is a report file path.
- report = args[0]
- if not os.path.exists(report):
- print("Can't re-run failing, no report at:", report)
- return None
- print("Reading failed tests in", report)
- failing = set()
- inFailedSection = False
- for line in open(report):
- l = line.strip()
- if l == "<h2>Failed tests</h2>":
- inFailedSection = True
- elif l.startswith("<h2>"):
- inFailedSection = False
- prefix = '<a href="classes/'
- if inFailedSection and l.startswith(prefix):
- href = l[len(prefix):l.index('">')]
- # Ignore enties ending with .html which are test classes, not test methods.
- if not href.endswith('.html'):
- # Remove the .html and anchor separateor, also, a classMethod test is the static
- # setup failing so rerun the full class of tests.
- test = href.replace('.html','').replace('#', '.').replace('.classMethod', '')
- failing.add(test)
- return list(failing)
+ if len(args) > 1:
+ print(
+ "Running with --failed can take an optional path to a report index (or report number)."
+ )
+ return None
+ report = report_index_path(0)
+ # If the default report does not exist, fall back to the previous report as it may be a failed
+ # gradle run which has already moved the report to report1, but did not produce a new report.
+ if not os.path.exists(report):
+ report1 = report_index_path(1)
+ if os.path.exists(report1):
+ report = report1
+ if len(args) == 1:
+ try:
+ # try to parse the arg as a report index.
+ index = int(args[0])
+ report = report_index_path(index)
+ except ValueError:
+ # if integer parsing failed assume it is a report file path.
+ report = args[0]
+ if not os.path.exists(report):
+ print("Can't re-run failing, no report at:", report)
+ return None
+ print("Reading failed tests in", report)
+ failing = set()
+ inFailedSection = False
+ for line in open(report):
+ l = line.strip()
+ if l == "<h2>Failed tests</h2>":
+ inFailedSection = True
+ elif l.startswith("<h2>"):
+ inFailedSection = False
+ prefix = '<a href="classes/'
+ if inFailedSection and l.startswith(prefix):
+ href = l[len(prefix):l.index('">')]
+ # Ignore enties ending with .html which are test classes, not test methods.
+ if not href.endswith('.html'):
+ # Remove the .html and anchor separateor, also, a classMethod test is the static
+ # setup failing so rerun the full class of tests.
+ test = href.replace('.html', '').replace('#', '.').replace(
+ '.classMethod', '')
+ failing.add(test)
+ return list(failing)
+
+
if __name__ == '__main__':
- return_code = Main()
- if return_code != 0:
- notify.notify("Tests failed.")
- else:
- notify.notify("Tests passed.")
- sys.exit(return_code)
+ return_code = Main()
+ if return_code != 0:
+ notify.notify("Tests failed.")
+ else:
+ notify.notify("Tests passed.")
+ sys.exit(return_code)