Home | History | Annotate | Download | only in testrunner
      1 #!/usr/bin/env python3
      2 #
      3 # Copyright 2017, The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """ART Run-Test TestRunner
     18 
     19 The testrunner runs the ART run-tests by simply invoking the script.
     20 It fetches the list of eligible tests from art/test directory, and list of
     21 disabled tests from art/test/knownfailures.json. It runs the tests by
     22 invoking art/test/run-test script and checks the exit value to decide if the
     23 test passed or failed.
     24 
     25 Before invoking the script, first build all the tests dependencies.
     26 There are two major build targets for building target and host tests
     27 dependencies:
     28 1) test-art-host-run-test
     29 2) test-art-target-run-test
     30 
     31 There are various options to invoke the script which are:
     32 -t: Either the test name as in art/test or the test name including the variant
     33     information. Eg, "-t 001-HelloWorld",
     34     "-t test-art-host-run-test-debug-prebuild-optimizing-relocate-ntrace-cms-checkjni-picimage-ndebuggable-001-HelloWorld32"
     35 -j: Number of thread workers to be used. Eg - "-j64"
     36 --dry-run: Instead of running the test name, just print its name.
     37 --verbose
     38 -b / --build-dependencies: to build the dependencies before running the test
     39 
     40 To specify any specific variants for the test, use --<<variant-name>>.
     41 For eg, for compiler type as optimizing, use --optimizing.
     42 
     43 
     44 In the end, the script will print the failed and skipped tests if any.
     45 
     46 """
     47 import argparse
     48 import collections
     49 import contextlib
     50 import fnmatch
     51 import itertools
     52 import json
     53 import multiprocessing
     54 import os
     55 import re
     56 import shlex
     57 import shutil
     58 import subprocess
     59 import sys
     60 import tempfile
     61 import threading
     62 import time
     63 
     64 import env
     65 from target_config import target_config
     66 from device_config import device_config
     67 
     68 # timeout for individual tests.
     69 # TODO: make it adjustable per tests and for buildbots
     70 timeout = 3000 # 50 minutes
     71 
     72 # DISABLED_TEST_CONTAINER holds information about the disabled tests. It is a map
     73 # that has key as the test name (like 001-HelloWorld), and value as set of
     74 # variants that the test is disabled for.
     75 DISABLED_TEST_CONTAINER = {}
     76 
     77 # The Dict contains the list of all possible variants for a given type. For example,
     78 # for key TARGET, the value would be target and host. The list is used to parse
     79 # the test name given as the argument to run.
     80 VARIANT_TYPE_DICT = {}
     81 
     82 # The set contains all the variants of each time.
     83 TOTAL_VARIANTS_SET = set()
     84 
     85 # The colors are used in the output. When a test passes, COLOR_PASS is used,
     86 # and so on.
     87 COLOR_ERROR = '\033[91m'
     88 COLOR_PASS = '\033[92m'
     89 COLOR_SKIP = '\033[93m'
     90 COLOR_NORMAL = '\033[0m'
     91 
     92 # The mutex object is used by the threads for exclusive access of test_count
     93 # to make any changes in its value.
     94 test_count_mutex = threading.Lock()
     95 
     96 # The set contains the list of all the possible run tests that are in art/test
     97 # directory.
     98 RUN_TEST_SET = set()
     99 
    100 # The semaphore object is used by the testrunner to limit the number of
    101 # threads to the user requested concurrency value.
    102 semaphore = threading.Semaphore(1)
    103 
    104 # The mutex object is used to provide exclusive access to a thread to print
    105 # its output.
    106 print_mutex = threading.Lock()
    107 failed_tests = []
    108 skipped_tests = []
    109 
    110 # Flags
    111 n_thread = -1
    112 test_count = 0
    113 total_test_count = 0
    114 verbose = False
    115 dry_run = False
    116 ignore_skips = False
    117 build = False
    118 gdb = False
    119 gdb_arg = ''
    120 runtime_option = ''
    121 with_agent = []
    122 zipapex_loc = None
    123 run_test_option = []
    124 stop_testrunner = False
    125 dex2oat_jobs = -1   # -1 corresponds to default threads for dex2oat
    126 run_all_configs = False
    127 
    128 # Dict containing extra arguments
    129 extra_arguments = { "host" : [], "target" : [] }
    130 
    131 # Dict to store user requested test variants.
    132 # key: variant_type.
    133 # value: set of variants user wants to run of type <key>.
    134 _user_input_variants = collections.defaultdict(set)
    135 
    136 def gather_test_info():
    137   """The method gathers test information about the test to be run which includes
    138   generating the list of total tests from the art/test directory and the list
    139   of disabled test. It also maps various variants to types.
    140   """
    141   global TOTAL_VARIANTS_SET
    142   global DISABLED_TEST_CONTAINER
    143   # TODO: Avoid duplication of the variant names in different lists.
    144   VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'}
    145   VARIANT_TYPE_DICT['target'] = {'target', 'host', 'jvm'}
    146   VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'}
    147   VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image'}
    148   VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
    149   VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'}
    150   VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'prebuild'}
    151   VARIANT_TYPE_DICT['cdex_level'] = {'cdex-none', 'cdex-fast'}
    152   VARIANT_TYPE_DICT['relocate'] = {'relocate', 'no-relocate'}
    153   VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
    154   VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
    155   VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress',
    156                                 'field-stress', 'step-stress'}
    157   VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'jit-on-first-use',
    158                                    'optimizing', 'regalloc_gc', 'speed-profile', 'baseline'}
    159 
    160   for v_type in VARIANT_TYPE_DICT:
    161     TOTAL_VARIANTS_SET = TOTAL_VARIANTS_SET.union(VARIANT_TYPE_DICT.get(v_type))
    162 
    163   test_dir = env.ANDROID_BUILD_TOP + '/art/test'
    164   for f in os.listdir(test_dir):
    165     if fnmatch.fnmatch(f, '[0-9]*'):
    166       RUN_TEST_SET.add(f)
    167   DISABLED_TEST_CONTAINER = get_disabled_test_info()
    168 
    169 
    170 def setup_test_env():
    171   """The method sets default value for the various variants of the tests if they
    172   are already not set.
    173   """
    174   if env.ART_TEST_BISECTION:
    175     env.ART_TEST_RUN_TEST_NO_PREBUILD = True
    176     env.ART_TEST_RUN_TEST_PREBUILD = False
    177     # Bisection search writes to standard output.
    178     env.ART_TEST_QUIET = False
    179 
    180   global _user_input_variants
    181   global run_all_configs
    182   # These are the default variant-options we will use if nothing in the group is specified.
    183   default_variants = {
    184       'target': {'host', 'target'},
    185       'prebuild': {'prebuild'},
    186       'cdex_level': {'cdex-fast'},
    187       'jvmti': { 'no-jvmti'},
    188       'compiler': {'optimizing',
    189                    'jit',
    190                    'interpreter',
    191                    'interp-ac',
    192                    'speed-profile'},
    193       'relocate': {'no-relocate'},
    194       'trace': {'ntrace'},
    195       'gc': {'cms'},
    196       'jni': {'checkjni'},
    197       'image': {'picimage'},
    198       'debuggable': {'ndebuggable'},
    199       'run': {'debug'},
    200       # address_sizes_target depends on the target so it is dealt with below.
    201   }
    202   # We want to pull these early since the full VARIANT_TYPE_DICT has a few additional ones we don't
    203   # want to pick up if we pass --all.
    204   default_variants_keys = default_variants.keys()
    205   if run_all_configs:
    206     default_variants = VARIANT_TYPE_DICT
    207 
    208   for key in default_variants_keys:
    209     if not _user_input_variants[key]:
    210       _user_input_variants[key] = default_variants[key]
    211 
    212   _user_input_variants['address_sizes_target'] = collections.defaultdict(set)
    213   if not _user_input_variants['address_sizes']:
    214     _user_input_variants['address_sizes_target']['target'].add(
    215         env.ART_PHONY_TEST_TARGET_SUFFIX)
    216     _user_input_variants['address_sizes_target']['host'].add(
    217         env.ART_PHONY_TEST_HOST_SUFFIX)
    218     if env.ART_TEST_RUN_TEST_2ND_ARCH:
    219       _user_input_variants['address_sizes_target']['host'].add(
    220           env.ART_2ND_PHONY_TEST_HOST_SUFFIX)
    221       _user_input_variants['address_sizes_target']['target'].add(
    222           env.ART_2ND_PHONY_TEST_TARGET_SUFFIX)
    223   else:
    224     _user_input_variants['address_sizes_target']['host'] = _user_input_variants['address_sizes']
    225     _user_input_variants['address_sizes_target']['target'] = _user_input_variants['address_sizes']
    226 
    227   global n_thread
    228   if n_thread is -1:
    229     if 'target' in _user_input_variants['target']:
    230       n_thread = get_default_threads('target')
    231     else:
    232       n_thread = get_default_threads('host')
    233     print_text("Concurrency: " + str(n_thread) + "\n")
    234 
    235   global extra_arguments
    236   for target in _user_input_variants['target']:
    237     extra_arguments[target] = find_extra_device_arguments(target)
    238 
    239   global semaphore
    240   semaphore = threading.Semaphore(n_thread)
    241 
    242   if not sys.stdout.isatty():
    243     global COLOR_ERROR
    244     global COLOR_PASS
    245     global COLOR_SKIP
    246     global COLOR_NORMAL
    247     COLOR_ERROR = ''
    248     COLOR_PASS = ''
    249     COLOR_SKIP = ''
    250     COLOR_NORMAL = ''
    251 
    252 def find_extra_device_arguments(target):
    253   """
    254   Gets any extra arguments from the device_config.
    255   """
    256   device_name = target
    257   if target == 'target':
    258     device_name = get_device_name()
    259   return device_config.get(device_name, { 'run-test-args' : [] })['run-test-args']
    260 
    261 def get_device_name():
    262   """
    263   Gets the value of ro.product.name from remote device.
    264   """
    265   proc = subprocess.Popen(['adb', 'shell', 'getprop', 'ro.product.name'],
    266                           stderr=subprocess.STDOUT,
    267                           stdout = subprocess.PIPE,
    268                           universal_newlines=True)
    269   # only wait 2 seconds.
    270   output = proc.communicate(timeout = 2)[0]
    271   success = not proc.wait()
    272   if success:
    273     return output.strip()
    274   else:
    275     print_text("Unable to determine device type!\n")
    276     print_text("Continuing anyway.\n")
    277     return "UNKNOWN_TARGET"
    278 
    279 def run_tests(tests):
    280   """Creates thread workers to run the tests.
    281 
    282   The method generates command and thread worker to run the tests. Depending on
    283   the user input for the number of threads to be used, the method uses a
    284   semaphore object to keep a count in control for the thread workers. When a new
    285   worker is created, it acquires the semaphore object, and when the number of
    286   workers reaches the maximum allowed concurrency, the method wait for an
    287   existing thread worker to release the semaphore object. Worker releases the
    288   semaphore object when they finish printing the output.
    289 
    290   Args:
    291     tests: The set of tests to be run.
    292   """
    293   options_all = ''
    294 
    295   # jvm does not run with all these combinations,
    296   # or at least it doesn't make sense for most of them.
    297   # TODO: support some jvm variants like jvmti ?
    298   target_input_variants = _user_input_variants['target']
    299   uncombinated_target_input_variants = []
    300   if 'jvm' in target_input_variants:
    301     _user_input_variants['target'].remove('jvm')
    302     uncombinated_target_input_variants.append('jvm')
    303 
    304   global total_test_count
    305   total_test_count = len(tests)
    306   if target_input_variants:
    307     for variant_type in VARIANT_TYPE_DICT:
    308       if not (variant_type == 'target' or 'address_sizes' in variant_type):
    309         total_test_count *= len(_user_input_variants[variant_type])
    310   target_address_combinations = 0
    311   for target in target_input_variants:
    312     for address_size in _user_input_variants['address_sizes_target'][target]:
    313       target_address_combinations += 1
    314   target_address_combinations += len(uncombinated_target_input_variants)
    315   total_test_count *= target_address_combinations
    316 
    317   if env.ART_TEST_WITH_STRACE:
    318     options_all += ' --strace'
    319 
    320   if env.ART_TEST_RUN_TEST_ALWAYS_CLEAN:
    321     options_all += ' --always-clean'
    322 
    323   if env.ART_TEST_BISECTION:
    324     options_all += ' --bisection-search'
    325 
    326   if gdb:
    327     options_all += ' --gdb'
    328     if gdb_arg:
    329       options_all += ' --gdb-arg ' + gdb_arg
    330 
    331   options_all += ' ' + ' '.join(run_test_option)
    332 
    333   if runtime_option:
    334     for opt in runtime_option:
    335       options_all += ' --runtime-option ' + opt
    336   if with_agent:
    337     for opt in with_agent:
    338       options_all += ' --with-agent ' + opt
    339 
    340   if dex2oat_jobs != -1:
    341     options_all += ' --dex2oat-jobs ' + str(dex2oat_jobs)
    342 
    343   def iter_config(tests, input_variants, user_input_variants):
    344     config = itertools.product(tests, input_variants, user_input_variants['run'],
    345                                  user_input_variants['prebuild'], user_input_variants['compiler'],
    346                                  user_input_variants['relocate'], user_input_variants['trace'],
    347                                  user_input_variants['gc'], user_input_variants['jni'],
    348                                  user_input_variants['image'],
    349                                  user_input_variants['debuggable'], user_input_variants['jvmti'],
    350                                  user_input_variants['cdex_level'])
    351     return config
    352 
    353   # [--host, --target] combines with all the other user input variants.
    354   config = iter_config(tests, target_input_variants, _user_input_variants)
    355   # [--jvm] currently combines with nothing else. most of the extra flags we'd insert
    356   # would be unrecognizable by the 'java' binary, so avoid inserting any extra flags for now.
    357   uncombinated_config = iter_config(tests, uncombinated_target_input_variants, { 'run': [''],
    358       'prebuild': [''], 'compiler': [''],
    359       'relocate': [''], 'trace': [''],
    360       'gc': [''], 'jni': [''],
    361       'image': [''],
    362       'debuggable': [''], 'jvmti': [''],
    363       'cdex_level': ['']})
    364 
    365   def start_combination(config_tuple, global_options, address_size):
    366       test, target, run, prebuild, compiler, relocate, trace, gc, \
    367       jni, image, debuggable, jvmti, cdex_level = config_tuple
    368 
    369       if stop_testrunner:
    370         # When ART_TEST_KEEP_GOING is set to false, then as soon as a test
    371         # fails, stop_testrunner is set to True. When this happens, the method
    372         # stops creating any any thread and wait for all the exising threads
    373         # to end.
    374         while threading.active_count() > 2:
    375           time.sleep(0.1)
    376           return
    377       # NB The order of components here should match the order of
    378       # components in the regex parser in parse_test_name.
    379       test_name = 'test-art-'
    380       test_name += target + '-run-test-'
    381       test_name += run + '-'
    382       test_name += prebuild + '-'
    383       test_name += compiler + '-'
    384       test_name += relocate + '-'
    385       test_name += trace + '-'
    386       test_name += gc + '-'
    387       test_name += jni + '-'
    388       test_name += image + '-'
    389       test_name += debuggable + '-'
    390       test_name += jvmti + '-'
    391       test_name += cdex_level + '-'
    392       test_name += test
    393       test_name += address_size
    394 
    395       variant_set = {target, run, prebuild, compiler, relocate, trace, gc, jni,
    396                      image, debuggable, jvmti, cdex_level, address_size}
    397 
    398       options_test = global_options
    399 
    400       if target == 'host':
    401         options_test += ' --host'
    402       elif target == 'jvm':
    403         options_test += ' --jvm'
    404 
    405       # Honor ART_TEST_CHROOT, ART_TEST_ANDROID_ROOT, ART_TEST_ANDROID_RUNTIME_ROOT,
    406       # and ART_TEST_ANDROID_TZDATA_ROOT but only for target tests.
    407       if target == 'target':
    408         if env.ART_TEST_CHROOT:
    409           options_test += ' --chroot ' + env.ART_TEST_CHROOT
    410         if env.ART_TEST_ANDROID_ROOT:
    411           options_test += ' --android-root ' + env.ART_TEST_ANDROID_ROOT
    412         if env.ART_TEST_ANDROID_RUNTIME_ROOT:
    413           options_test += ' --android-runtime-root ' + env.ART_TEST_ANDROID_RUNTIME_ROOT
    414         if env.ART_TEST_ANDROID_TZDATA_ROOT:
    415           options_test += ' --android-tzdata-root ' + env.ART_TEST_ANDROID_TZDATA_ROOT
    416 
    417       if run == 'ndebug':
    418         options_test += ' -O'
    419 
    420       if prebuild == 'prebuild':
    421         options_test += ' --prebuild'
    422       elif prebuild == 'no-prebuild':
    423         options_test += ' --no-prebuild'
    424 
    425       if cdex_level:
    426         # Add option and remove the cdex- prefix.
    427         options_test += ' --compact-dex-level ' + cdex_level.replace('cdex-','')
    428 
    429       if compiler == 'optimizing':
    430         options_test += ' --optimizing'
    431       elif compiler == 'regalloc_gc':
    432         options_test += ' --optimizing -Xcompiler-option --register-allocation-strategy=graph-color'
    433       elif compiler == 'interpreter':
    434         options_test += ' --interpreter'
    435       elif compiler == 'interp-ac':
    436         options_test += ' --interpreter --verify-soft-fail'
    437       elif compiler == 'jit':
    438         options_test += ' --jit'
    439       elif compiler == 'jit-on-first-use':
    440         options_test += ' --jit --runtime-option -Xjitthreshold:0'
    441       elif compiler == 'speed-profile':
    442         options_test += ' --random-profile'
    443       elif compiler == 'baseline':
    444         options_test += ' --baseline'
    445 
    446       if relocate == 'relocate':
    447         options_test += ' --relocate'
    448       elif relocate == 'no-relocate':
    449         options_test += ' --no-relocate'
    450 
    451       if trace == 'trace':
    452         options_test += ' --trace'
    453       elif trace == 'stream':
    454         options_test += ' --trace --stream'
    455 
    456       if gc == 'gcverify':
    457         options_test += ' --gcverify'
    458       elif gc == 'gcstress':
    459         options_test += ' --gcstress'
    460 
    461       if jni == 'forcecopy':
    462         options_test += ' --runtime-option -Xjniopts:forcecopy'
    463       elif jni == 'checkjni':
    464         options_test += ' --runtime-option -Xcheck:jni'
    465 
    466       if image == 'no-image':
    467         options_test += ' --no-image'
    468 
    469       if debuggable == 'debuggable':
    470         options_test += ' --debuggable'
    471 
    472       if jvmti == 'jvmti-stress':
    473         options_test += ' --jvmti-trace-stress --jvmti-redefine-stress --jvmti-field-stress'
    474       elif jvmti == 'field-stress':
    475         options_test += ' --jvmti-field-stress'
    476       elif jvmti == 'trace-stress':
    477         options_test += ' --jvmti-trace-stress'
    478       elif jvmti == 'redefine-stress':
    479         options_test += ' --jvmti-redefine-stress'
    480       elif jvmti == 'step-stress':
    481         options_test += ' --jvmti-step-stress'
    482 
    483       if address_size == '64':
    484         options_test += ' --64'
    485 
    486         if env.DEX2OAT_HOST_INSTRUCTION_SET_FEATURES:
    487           options_test += ' --instruction-set-features' + env.DEX2OAT_HOST_INSTRUCTION_SET_FEATURES
    488 
    489       elif address_size == '32':
    490         if env.HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES:
    491           options_test += ' --instruction-set-features ' + \
    492                           env.HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES
    493 
    494       # TODO(http://36039166): This is a temporary solution to
    495       # fix build breakages.
    496       options_test = (' --output-path %s') % (
    497           tempfile.mkdtemp(dir=env.ART_HOST_TEST_DIR)) + options_test
    498 
    499       run_test_sh = env.ANDROID_BUILD_TOP + '/art/test/run-test'
    500       command = ' '.join((run_test_sh, options_test, ' '.join(extra_arguments[target]), test))
    501 
    502       semaphore.acquire()
    503       worker = threading.Thread(target=run_test, args=(command, test, variant_set, test_name))
    504       worker.daemon = True
    505       worker.start()
    506 
    507   #  Use a context-manager to handle cleaning up the extracted zipapex if needed.
    508   with handle_zipapex(zipapex_loc) as zipapex_opt:
    509     options_all += zipapex_opt
    510     for config_tuple in config:
    511       target = config_tuple[1]
    512       for address_size in _user_input_variants['address_sizes_target'][target]:
    513         start_combination(config_tuple, options_all, address_size)
    514 
    515     for config_tuple in uncombinated_config:
    516         start_combination(config_tuple, options_all, "")  # no address size
    517 
    518     while threading.active_count() > 2:
    519       time.sleep(0.1)
    520 
    521 @contextlib.contextmanager
    522 def handle_zipapex(ziploc):
    523   """Extracts the zipapex (if present) and handles cleanup.
    524 
    525   If we are running out of a zipapex we want to unzip it once and have all the tests use the same
    526   extracted contents. This extracts the files and handles cleanup if needed. It returns the
    527   required extra arguments to pass to the run-test.
    528   """
    529   if ziploc is not None:
    530     with tempfile.TemporaryDirectory() as tmpdir:
    531       subprocess.check_call(["unzip", "-qq", ziploc, "apex_payload.zip", "-d", tmpdir])
    532       subprocess.check_call(
    533         ["unzip", "-qq", os.path.join(tmpdir, "apex_payload.zip"), "-d", tmpdir])
    534       yield " --runtime-extracted-zipapex " + tmpdir
    535   else:
    536     yield ""
    537 
    538 def run_test(command, test, test_variant, test_name):
    539   """Runs the test.
    540 
    541   It invokes art/test/run-test script to run the test. The output of the script
    542   is checked, and if it ends with "Succeeded!", it assumes that the tests
    543   passed, otherwise, put it in the list of failed test. Before actually running
    544   the test, it also checks if the test is placed in the list of disabled tests,
    545   and if yes, it skips running it, and adds the test in the list of skipped
    546   tests. The method uses print_text method to actually print the output. After
    547   successfully running and capturing the output for the test, it releases the
    548   semaphore object.
    549 
    550   Args:
    551     command: The command to be used to invoke the script
    552     test: The name of the test without the variant information.
    553     test_variant: The set of variant for the test.
    554     test_name: The name of the test along with the variants.
    555   """
    556   global stop_testrunner
    557   try:
    558     if is_test_disabled(test, test_variant):
    559       test_skipped = True
    560     else:
    561       test_skipped = False
    562       if gdb:
    563         proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, universal_newlines=True)
    564       else:
    565         proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout = subprocess.PIPE,
    566                                 universal_newlines=True)
    567       script_output = proc.communicate(timeout=timeout)[0]
    568       test_passed = not proc.wait()
    569 
    570     if not test_skipped:
    571       if test_passed:
    572         print_test_info(test_name, 'PASS')
    573       else:
    574         failed_tests.append((test_name, str(command) + "\n" + script_output))
    575         if not env.ART_TEST_KEEP_GOING:
    576           stop_testrunner = True
    577         print_test_info(test_name, 'FAIL', ('%s\n%s') % (
    578           command, script_output))
    579     elif not dry_run:
    580       print_test_info(test_name, 'SKIP')
    581       skipped_tests.append(test_name)
    582     else:
    583       print_test_info(test_name, '')
    584   except subprocess.TimeoutExpired as e:
    585     failed_tests.append((test_name, 'Timed out in %d seconds' % timeout))
    586     print_test_info(test_name, 'TIMEOUT', 'Timed out in %d seconds\n%s' % (
    587         timeout, command))
    588   except Exception as e:
    589     failed_tests.append((test_name, str(e)))
    590     print_test_info(test_name, 'FAIL',
    591     ('%s\n%s\n\n') % (command, str(e)))
    592   finally:
    593     semaphore.release()
    594 
    595 
    596 def print_test_info(test_name, result, failed_test_info=""):
    597   """Print the continous test information
    598 
    599   If verbose is set to True, it continuously prints test status information
    600   on a new line.
    601   If verbose is set to False, it keeps on erasing test
    602   information by overriding it with the latest test information. Also,
    603   in this case it stictly makes sure that the information length doesn't
    604   exceed the console width. It does so by shortening the test_name.
    605 
    606   When a test fails, it prints the output of the run-test script and
    607   command used to invoke the script. It doesn't override the failing
    608   test information in either of the cases.
    609   """
    610 
    611   global test_count
    612   info = ''
    613   if not verbose:
    614     # Without --verbose, the testrunner erases passing test info. It
    615     # does that by overriding the printed text with white spaces all across
    616     # the console width.
    617     console_width = int(os.popen('stty size', 'r').read().split()[1])
    618     info = '\r' + ' ' * console_width + '\r'
    619   try:
    620     print_mutex.acquire()
    621     test_count += 1
    622     percent = (test_count * 100) / total_test_count
    623     progress_info = ('[ %d%% %d/%d ]') % (
    624       percent,
    625       test_count,
    626       total_test_count)
    627 
    628     if result == 'FAIL' or result == 'TIMEOUT':
    629       if not verbose:
    630         info += ('%s %s %s\n') % (
    631           progress_info,
    632           test_name,
    633           COLOR_ERROR + result + COLOR_NORMAL)
    634       else:
    635         info += ('%s %s %s\n%s\n') % (
    636           progress_info,
    637           test_name,
    638           COLOR_ERROR + result + COLOR_NORMAL,
    639           failed_test_info)
    640     else:
    641       result_text = ''
    642       if result == 'PASS':
    643         result_text += COLOR_PASS + 'PASS' + COLOR_NORMAL
    644       elif result == 'SKIP':
    645         result_text += COLOR_SKIP + 'SKIP' + COLOR_NORMAL
    646 
    647       if verbose:
    648         info += ('%s %s %s\n') % (
    649           progress_info,
    650           test_name,
    651           result_text)
    652       else:
    653         total_output_length = 2 # Two spaces
    654         total_output_length += len(progress_info)
    655         total_output_length += len(result)
    656         allowed_test_length = console_width - total_output_length
    657         test_name_len = len(test_name)
    658         if allowed_test_length < test_name_len:
    659           test_name = ('...%s') % (
    660             test_name[-(allowed_test_length - 3):])
    661         info += ('%s %s %s') % (
    662           progress_info,
    663           test_name,
    664           result_text)
    665     print_text(info)
    666   except Exception as e:
    667     print_text(('%s\n%s\n') % (test_name, str(e)))
    668     failed_tests.append(test_name)
    669   finally:
    670     print_mutex.release()
    671 
    672 def verify_knownfailure_entry(entry):
    673   supported_field = {
    674       'tests' : (list, str),
    675       'test_patterns' : (list,),
    676       'description' : (list, str),
    677       'bug' : (str,),
    678       'variant' : (str,),
    679       'env_vars' : (dict,),
    680   }
    681   for field in entry:
    682     field_type = type(entry[field])
    683     if field_type not in supported_field[field]:
    684       raise ValueError('%s is not supported type for %s\n%s' % (
    685           str(field_type),
    686           field,
    687           str(entry)))
    688 
    689 def get_disabled_test_info():
    690   """Generate set of known failures.
    691 
    692   It parses the art/test/knownfailures.json file to generate the list of
    693   disabled tests.
    694 
    695   Returns:
    696     The method returns a dict of tests mapped to the variants list
    697     for which the test should not be run.
    698   """
    699   known_failures_file = env.ANDROID_BUILD_TOP + '/art/test/knownfailures.json'
    700   with open(known_failures_file) as known_failures_json:
    701     known_failures_info = json.loads(known_failures_json.read())
    702 
    703   disabled_test_info = {}
    704   for failure in known_failures_info:
    705     verify_knownfailure_entry(failure)
    706     tests = failure.get('tests', [])
    707     if isinstance(tests, str):
    708       tests = [tests]
    709     patterns = failure.get("test_patterns", [])
    710     if (not isinstance(patterns, list)):
    711       raise ValueError("test_patters is not a list in %s" % failure)
    712 
    713     tests += [f for f in RUN_TEST_SET if any(re.match(pat, f) is not None for pat in patterns)]
    714     variants = parse_variants(failure.get('variant'))
    715     env_vars = failure.get('env_vars')
    716 
    717     if check_env_vars(env_vars):
    718       for test in tests:
    719         if test not in RUN_TEST_SET:
    720           raise ValueError('%s is not a valid run-test' % (
    721               test))
    722         if test in disabled_test_info:
    723           disabled_test_info[test] = disabled_test_info[test].union(variants)
    724         else:
    725           disabled_test_info[test] = variants
    726   return disabled_test_info
    727 
    728 
    729 def check_env_vars(env_vars):
    730   """Checks if the env variables are set as required to run the test.
    731 
    732   Returns:
    733     True if all the env variables are set as required, otherwise False.
    734   """
    735 
    736   if not env_vars:
    737     return True
    738   for key in env_vars:
    739     if env.get_env(key) != env_vars.get(key):
    740       return False
    741   return True
    742 
    743 
    744 def is_test_disabled(test, variant_set):
    745   """Checks if the test along with the variant_set is disabled.
    746 
    747   Args:
    748     test: The name of the test as in art/test directory.
    749     variant_set: Variants to be used for the test.
    750   Returns:
    751     True, if the test is disabled.
    752   """
    753   if dry_run:
    754     return True
    755   if test in env.EXTRA_DISABLED_TESTS:
    756     return True
    757   if ignore_skips:
    758     return False
    759   variants_list = DISABLED_TEST_CONTAINER.get(test, {})
    760   for variants in variants_list:
    761     variants_present = True
    762     for variant in variants:
    763       if variant not in variant_set:
    764         variants_present = False
    765         break
    766     if variants_present:
    767       return True
    768   return False
    769 
    770 
    771 def parse_variants(variants):
    772   """Parse variants fetched from art/test/knownfailures.json.
    773   """
    774   if not variants:
    775     variants = ''
    776     for variant in TOTAL_VARIANTS_SET:
    777       variants += variant
    778       variants += '|'
    779     variants = variants[:-1]
    780   variant_list = set()
    781   or_variants = variants.split('|')
    782   for or_variant in or_variants:
    783     and_variants = or_variant.split('&')
    784     variant = set()
    785     for and_variant in and_variants:
    786       and_variant = and_variant.strip()
    787       if and_variant not in TOTAL_VARIANTS_SET:
    788         raise ValueError('%s is not a valid variant' % (
    789             and_variant))
    790       variant.add(and_variant)
    791     variant_list.add(frozenset(variant))
    792   return variant_list
    793 
    794 def print_text(output):
    795   sys.stdout.write(output)
    796   sys.stdout.flush()
    797 
    798 def print_analysis():
    799   if not verbose:
    800     # Without --verbose, the testrunner erases passing test info. It
    801     # does that by overriding the printed text with white spaces all across
    802     # the console width.
    803     console_width = int(os.popen('stty size', 'r').read().split()[1])
    804     eraser_text = '\r' + ' ' * console_width + '\r'
    805     print_text(eraser_text)
    806 
    807   # Prints information about the total tests run.
    808   # E.g., "2/38 (5%) tests passed".
    809   passed_test_count = total_test_count - len(skipped_tests) - len(failed_tests)
    810   passed_test_information = ('%d/%d (%d%%) %s passed.\n') % (
    811       passed_test_count,
    812       total_test_count,
    813       (passed_test_count*100)/total_test_count,
    814       'tests' if passed_test_count > 1 else 'test')
    815   print_text(passed_test_information)
    816 
    817   # Prints the list of skipped tests, if any.
    818   if skipped_tests:
    819     print_text(COLOR_SKIP + 'SKIPPED TESTS: ' + COLOR_NORMAL + '\n')
    820     for test in skipped_tests:
    821       print_text(test + '\n')
    822     print_text('\n')
    823 
    824   # Prints the list of failed tests, if any.
    825   if failed_tests:
    826     print_text(COLOR_ERROR + 'FAILED: ' + COLOR_NORMAL + '\n')
    827     for test_info in failed_tests:
    828       print_text(('%s\n%s\n' % (test_info[0], test_info[1])))
    829     print_text(COLOR_ERROR + '----------' + COLOR_NORMAL + '\n')
    830     for failed_test in sorted([test_info[0] for test_info in failed_tests]):
    831       print_text(('%s\n' % (failed_test)))
    832 
    833 
    834 def parse_test_name(test_name):
    835   """Parses the testname provided by the user.
    836   It supports two types of test_name:
    837   1) Like 001-HelloWorld. In this case, it will just verify if the test actually
    838   exists and if it does, it returns the testname.
    839   2) Like test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-cms-checkjni-picimage-ndebuggable-001-HelloWorld32
    840   In this case, it will parse all the variants and check if they are placed
    841   correctly. If yes, it will set the various VARIANT_TYPES to use the
    842   variants required to run the test. Again, it returns the test_name
    843   without the variant information like 001-HelloWorld.
    844   """
    845   test_set = set()
    846   for test in RUN_TEST_SET:
    847     if test.startswith(test_name):
    848       test_set.add(test)
    849   if test_set:
    850     return test_set
    851 
    852   regex = '^test-art-'
    853   regex += '(' + '|'.join(VARIANT_TYPE_DICT['target']) + ')-'
    854   regex += 'run-test-'
    855   regex += '(' + '|'.join(VARIANT_TYPE_DICT['run']) + ')-'
    856   regex += '(' + '|'.join(VARIANT_TYPE_DICT['prebuild']) + ')-'
    857   regex += '(' + '|'.join(VARIANT_TYPE_DICT['compiler']) + ')-'
    858   regex += '(' + '|'.join(VARIANT_TYPE_DICT['relocate']) + ')-'
    859   regex += '(' + '|'.join(VARIANT_TYPE_DICT['trace']) + ')-'
    860   regex += '(' + '|'.join(VARIANT_TYPE_DICT['gc']) + ')-'
    861   regex += '(' + '|'.join(VARIANT_TYPE_DICT['jni']) + ')-'
    862   regex += '(' + '|'.join(VARIANT_TYPE_DICT['image']) + ')-'
    863   regex += '(' + '|'.join(VARIANT_TYPE_DICT['debuggable']) + ')-'
    864   regex += '(' + '|'.join(VARIANT_TYPE_DICT['jvmti']) + ')-'
    865   regex += '(' + '|'.join(VARIANT_TYPE_DICT['cdex_level']) + ')-'
    866   regex += '(' + '|'.join(RUN_TEST_SET) + ')'
    867   regex += '(' + '|'.join(VARIANT_TYPE_DICT['address_sizes']) + ')$'
    868   match = re.match(regex, test_name)
    869   if match:
    870     _user_input_variants['target'].add(match.group(1))
    871     _user_input_variants['run'].add(match.group(2))
    872     _user_input_variants['prebuild'].add(match.group(3))
    873     _user_input_variants['compiler'].add(match.group(4))
    874     _user_input_variants['relocate'].add(match.group(5))
    875     _user_input_variants['trace'].add(match.group(6))
    876     _user_input_variants['gc'].add(match.group(7))
    877     _user_input_variants['jni'].add(match.group(8))
    878     _user_input_variants['image'].add(match.group(9))
    879     _user_input_variants['debuggable'].add(match.group(10))
    880     _user_input_variants['jvmti'].add(match.group(11))
    881     _user_input_variants['cdex_level'].add(match.group(12))
    882     _user_input_variants['address_sizes'].add(match.group(14))
    883     return {match.group(13)}
    884   raise ValueError(test_name + " is not a valid test")
    885 
    886 
    887 def setup_env_for_build_target(build_target, parser, options):
    888   """Setup environment for the build target
    889 
    890   The method setup environment for the master-art-host targets.
    891   """
    892   os.environ.update(build_target['env'])
    893   os.environ['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true'
    894   print_text('%s\n' % (str(os.environ)))
    895 
    896   target_options = vars(parser.parse_args(build_target['flags']))
    897   target_options['host'] = True
    898   target_options['verbose'] = True
    899   target_options['build'] = True
    900   target_options['n_thread'] = options['n_thread']
    901   target_options['dry_run'] = options['dry_run']
    902 
    903   return target_options
    904 
    905 def get_default_threads(target):
    906   if target is 'target':
    907     adb_command = 'adb shell cat /sys/devices/system/cpu/present'
    908     cpu_info_proc = subprocess.Popen(adb_command.split(), stdout=subprocess.PIPE)
    909     cpu_info = cpu_info_proc.stdout.read()
    910     if type(cpu_info) is bytes:
    911       cpu_info = cpu_info.decode('utf-8')
    912     cpu_info_regex = '\d*-(\d*)'
    913     match = re.match(cpu_info_regex, cpu_info)
    914     if match:
    915       return int(match.group(1))
    916     else:
    917       raise ValueError('Unable to predict the concurrency for the target. '
    918                        'Is device connected?')
    919   else:
    920     return multiprocessing.cpu_count()
    921 
    922 def parse_option():
    923   global verbose
    924   global dry_run
    925   global ignore_skips
    926   global n_thread
    927   global build
    928   global gdb
    929   global gdb_arg
    930   global runtime_option
    931   global run_test_option
    932   global timeout
    933   global dex2oat_jobs
    934   global run_all_configs
    935   global with_agent
    936   global zipapex_loc
    937 
    938   parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
    939   parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
    940   global_group = parser.add_argument_group('Global options',
    941                                            'Options that affect all tests being run')
    942   global_group.add_argument('-j', type=int, dest='n_thread')
    943   global_group.add_argument('--timeout', default=timeout, type=int, dest='timeout')
    944   global_group.add_argument('--verbose', '-v', action='store_true', dest='verbose')
    945   global_group.add_argument('--dry-run', action='store_true', dest='dry_run')
    946   global_group.add_argument("--skip", action='append', dest="skips", default=[],
    947                             help="Skip the given test in all circumstances.")
    948   global_group.add_argument("--no-skips", dest="ignore_skips", action='store_true', default=False,
    949                             help="""Don't skip any run-test configurations listed in
    950                             knownfailures.json.""")
    951   global_group.add_argument('--no-build-dependencies',
    952                             action='store_false', dest='build',
    953                             help="""Don't build dependencies under any circumstances. This is the
    954                             behavior if ART_TEST_RUN_TEST_ALWAYS_BUILD is not set to 'true'.""")
    955   global_group.add_argument('-b', '--build-dependencies',
    956                             action='store_true', dest='build',
    957                             help="""Build dependencies under all circumstances. By default we will
    958                             not build dependencies unless ART_TEST_RUN_TEST_BUILD=true.""")
    959   global_group.add_argument('--build-target', dest='build_target', help='master-art-host targets')
    960   global_group.set_defaults(build = env.ART_TEST_RUN_TEST_BUILD)
    961   global_group.add_argument('--gdb', action='store_true', dest='gdb')
    962   global_group.add_argument('--gdb-arg', dest='gdb_arg')
    963   global_group.add_argument('--run-test-option', action='append', dest='run_test_option',
    964                             default=[],
    965                             help="""Pass an option, unaltered, to the run-test script.
    966                             This should be enclosed in single-quotes to allow for spaces. The option
    967                             will be split using shlex.split() prior to invoking run-test.
    968                             Example \"--run-test-option='--with-agent libtifast.so=MethodExit'\"""")
    969   global_group.add_argument('--with-agent', action='append', dest='with_agent',
    970                             help="""Pass an agent to be attached to the runtime""")
    971   global_group.add_argument('--runtime-option', action='append', dest='runtime_option',
    972                             help="""Pass an option to the runtime. Runtime options
    973                             starting with a '-' must be separated by a '=', for
    974                             example '--runtime-option=-Xjitthreshold:0'.""")
    975   global_group.add_argument('--dex2oat-jobs', type=int, dest='dex2oat_jobs',
    976                             help='Number of dex2oat jobs')
    977   global_group.add_argument('--runtime-zipapex', dest='runtime_zipapex', default=None,
    978                             help='Location for runtime zipapex.')
    979   global_group.add_argument('-a', '--all', action='store_true', dest='run_all',
    980                             help="Run all the possible configurations for the input test set")
    981   for variant_type, variant_set in VARIANT_TYPE_DICT.items():
    982     var_group = parser.add_argument_group(
    983         '{}-type Options'.format(variant_type),
    984         "Options that control the '{}' variants.".format(variant_type))
    985     for variant in variant_set:
    986       flag = '--' + variant
    987       var_group.add_argument(flag, action='store_true', dest=variant)
    988 
    989   options = vars(parser.parse_args())
    990   if options['build_target']:
    991     options = setup_env_for_build_target(target_config[options['build_target']],
    992                                          parser, options)
    993 
    994   tests = None
    995   env.EXTRA_DISABLED_TESTS.update(set(options['skips']))
    996   if options['tests']:
    997     tests = set()
    998     for test_name in options['tests']:
    999       tests |= parse_test_name(test_name)
   1000 
   1001   for variant_type in VARIANT_TYPE_DICT:
   1002     for variant in VARIANT_TYPE_DICT[variant_type]:
   1003       if options.get(variant):
   1004         _user_input_variants[variant_type].add(variant)
   1005 
   1006   if options['verbose']:
   1007     verbose = True
   1008   if options['n_thread']:
   1009     n_thread = max(1, options['n_thread'])
   1010   ignore_skips = options['ignore_skips']
   1011   if options['dry_run']:
   1012     dry_run = True
   1013     verbose = True
   1014   build = options['build']
   1015   if options['gdb']:
   1016     n_thread = 1
   1017     gdb = True
   1018     if options['gdb_arg']:
   1019       gdb_arg = options['gdb_arg']
   1020   runtime_option = options['runtime_option'];
   1021   with_agent = options['with_agent'];
   1022   run_test_option = sum(map(shlex.split, options['run_test_option']), [])
   1023   zipapex_loc = options['runtime_zipapex']
   1024 
   1025   timeout = options['timeout']
   1026   if options['dex2oat_jobs']:
   1027     dex2oat_jobs = options['dex2oat_jobs']
   1028   if options['run_all']:
   1029     run_all_configs = True
   1030 
   1031   return tests
   1032 
   1033 def main():
   1034   gather_test_info()
   1035   user_requested_tests = parse_option()
   1036   setup_test_env()
   1037   if build:
   1038     build_targets = ''
   1039     if 'host' in _user_input_variants['target']:
   1040       build_targets += 'test-art-host-run-test-dependencies '
   1041     if 'target' in _user_input_variants['target']:
   1042       build_targets += 'test-art-target-run-test-dependencies '
   1043     if 'jvm' in _user_input_variants['target']:
   1044       build_targets += 'test-art-host-run-test-dependencies '
   1045     build_command = env.ANDROID_BUILD_TOP + '/build/soong/soong_ui.bash --make-mode'
   1046     build_command += ' DX='
   1047     build_command += ' ' + build_targets
   1048     if subprocess.call(build_command.split()):
   1049       # Debugging for b/62653020
   1050       if env.DIST_DIR:
   1051         shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja')
   1052       sys.exit(1)
   1053   if user_requested_tests:
   1054     test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,))
   1055   else:
   1056     test_runner_thread = threading.Thread(target=run_tests, args=(RUN_TEST_SET,))
   1057   test_runner_thread.daemon = True
   1058   try:
   1059     test_runner_thread.start()
   1060     # This loops waits for all the threads to finish, unless
   1061     # stop_testrunner is set to True. When ART_TEST_KEEP_GOING
   1062     # is set to false, stop_testrunner is set to True as soon as
   1063     # a test fails to signal the parent thread  to stop
   1064     # the execution of the testrunner.
   1065     while threading.active_count() > 1 and not stop_testrunner:
   1066       time.sleep(0.1)
   1067     print_analysis()
   1068   except Exception as e:
   1069     print_analysis()
   1070     print_text(str(e))
   1071     sys.exit(1)
   1072   if failed_tests:
   1073     sys.exit(1)
   1074   sys.exit(0)
   1075 
   1076 if __name__ == '__main__':
   1077   main()
   1078