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)