1# Copyright 2015, VIXL authors 2# All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are met: 6# 7# * Redistributions of source code must retain the above copyright notice, 8# this list of conditions and the following disclaimer. 9# * Redistributions in binary form must reproduce the above copyright notice, 10# this list of conditions and the following disclaimer in the documentation 11# and/or other materials provided with the distribution. 12# * Neither the name of ARM Limited nor the names of its contributors may be 13# used to endorse or promote products derived from this software without 14# specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27import multiprocessing 28import re 29import signal 30import subprocess 31import sys 32import time 33 34from known_test_failures import FilterKnownTestFailures 35import printer 36import util 37 38# Catch SIGINT to gracefully exit when ctrl+C is pressed. 39def SigIntHandler(signal, frame): 40 sys.exit(1) 41 42signal.signal(signal.SIGINT, SigIntHandler) 43 44 45# Scan matching tests and return a test manifest. 46def GetTests(runner, filters = []): 47 rc, output = util.getstatusoutput(runner + ' --list') 48 if rc != 0: util.abort('Failed to list all tests') 49 50 tests = output.split() 51 for f in filters: 52 print f 53 tests = filter(re.compile(f).search, tests) 54 55 return tests 56 57 58# Shared state for multiprocessing. Ideally the context should be passed with 59# arguments, but constraints from the multiprocessing module prevent us from 60# doing so: the shared variables (multiprocessing.Value) must be global, or no 61# work is started. So we abstract some additional state into global variables to 62# simplify the implementation. 63# Read-write variables for the workers. 64n_tests_passed = multiprocessing.Value('i', 0) 65n_tests_failed = multiprocessing.Value('i', 0) 66# Read-only for workers. 67test_runner = None 68test_runner_runtime_options = None 69test_runner_under_valgrind = False 70n_tests = None 71start_time = None 72progress_prefix = None 73 74 75def RunTest(test): 76 command = [test_runner, test] + test_runner_runtime_options 77 if test_runner_under_valgrind: 78 command = ['valgrind'] + command 79 80 p = subprocess.Popen(command, 81 stdout=subprocess.PIPE, 82 stderr=subprocess.STDOUT) 83 p_out, p_err = p.communicate() 84 rc = p.poll() 85 86 if rc == 0: 87 with n_tests_passed.get_lock(): n_tests_passed.value += 1 88 else: 89 with n_tests_failed.get_lock(): n_tests_failed.value += 1 90 91 printer.__print_lock__.acquire() 92 93 printer.UpdateProgress(start_time, 94 n_tests_passed.value, 95 n_tests_failed.value, 96 n_tests, 97 test, 98 prevent_next_overwrite = (rc != 0), 99 has_lock = True, 100 prefix = progress_prefix) 101 102 if rc != 0: 103 printer.Print('FAILED: ' + test, has_lock = True) 104 printer.Print(printer.COLOUR_RED + ' '.join(command) + printer.NO_COLOUR, 105 has_lock = True) 106 printer.Print(p_out, has_lock = True) 107 108 printer.__print_lock__.release() 109 110 111# Run the specified tests. 112# This function won't run in parallel due to constraints from the 113# multiprocessing module. 114__run_tests_lock__ = multiprocessing.Lock() 115def RunTests(test_runner_command, filters, runtime_options, 116 under_valgrind = False, 117 jobs = 1, prefix = ''): 118 global test_runner 119 global test_runner_runtime_options 120 global test_runner_under_valgrind 121 global n_tests 122 global start_time 123 global progress_prefix 124 125 tests = GetTests(test_runner_command, filters) 126 tests = FilterKnownTestFailures(tests, under_valgrind=under_valgrind) 127 128 if n_tests == 0: 129 printer.Print('No tests to run.') 130 return 0 131 132 with __run_tests_lock__: 133 134 # Initialisation. 135 start_time = time.time() 136 test_runner = test_runner_command 137 test_runner_runtime_options = runtime_options 138 test_runner_under_valgrind = under_valgrind 139 n_tests = len(tests) 140 n_tests_passed.value = 0 141 n_tests_failed.value = 0 142 progress_prefix = prefix 143 144 pool = multiprocessing.Pool(jobs) 145 # The '.get(9999999)' is a workaround to allow killing the test script with 146 # ctrl+C from the shell. This bug is documented at 147 # http://bugs.python.org/issue8296. 148 work = pool.map_async(RunTest, tests).get(9999999) 149 pool.close() 150 pool.join() 151 152 printer.UpdateProgress(start_time, 153 n_tests_passed.value, 154 n_tests_failed.value, 155 n_tests, 156 '== Done ==', 157 prevent_next_overwrite = True, 158 prefix = progress_prefix) 159 160 # `0` indicates success 161 return n_tests_failed.value 162