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