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