Home | History | Annotate | Download | only in tools
      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 
     27 import multiprocessing
     28 import re
     29 import signal
     30 import subprocess
     31 import sys
     32 import time
     33 
     34 from known_test_failures import FilterKnownTestFailures
     35 import printer
     36 import util
     37 
     38 # Catch SIGINT to gracefully exit when ctrl+C is pressed.
     39 def SigIntHandler(signal, frame):
     40   sys.exit(1)
     41 
     42 signal.signal(signal.SIGINT, SigIntHandler)
     43 
     44 
     45 # Scan matching tests and return a test manifest.
     46 def 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.
     64 n_tests_passed = multiprocessing.Value('i', 0)
     65 n_tests_failed = multiprocessing.Value('i', 0)
     66 # Read-only for workers.
     67 test_runner = None
     68 test_runner_runtime_options = None
     69 test_runner_under_valgrind = False
     70 n_tests = None
     71 start_time = None
     72 progress_prefix = None
     73 
     74 
     75 def 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()
    115 def 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