blob: 1d0727b5627320a6d92289bde92c7dc9a74c72c6 [file] [log] [blame]
Alexandre Ramesb78f1392016-07-01 14:22:22 +01001# Copyright 2015, VIXL authors
armvixldb644342015-07-21 11:37:10 +01002# 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
Anton Kirilov88a33762016-08-26 15:46:33 +010034from known_test_failures import FilterKnownTestFailures
armvixldb644342015-07-21 11:37:10 +010035import 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
armvixl684cd2a2015-10-23 13:38:33 +010069test_runner_under_valgrind = False
armvixldb644342015-07-21 11:37:10 +010070n_tests = None
71start_time = None
72progress_prefix = None
73
74
75def RunTest(test):
76 command = [test_runner, test] + test_runner_runtime_options
armvixl684cd2a2015-10-23 13:38:33 +010077 if test_runner_under_valgrind:
78 command = ['valgrind'] + command
armvixldb644342015-07-21 11:37:10 +010079
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,
armvixl684cd2a2015-10-23 13:38:33 +0100116 under_valgrind = False,
armvixldb644342015-07-21 11:37:10 +0100117 jobs = 1, prefix = ''):
118 global test_runner
119 global test_runner_runtime_options
armvixl684cd2a2015-10-23 13:38:33 +0100120 global test_runner_under_valgrind
armvixldb644342015-07-21 11:37:10 +0100121 global n_tests
122 global start_time
123 global progress_prefix
124
125 tests = GetTests(test_runner_command, filters)
Anton Kirilov88a33762016-08-26 15:46:33 +0100126 tests = FilterKnownTestFailures(tests, under_valgrind=under_valgrind)
armvixldb644342015-07-21 11:37:10 +0100127
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
armvixl684cd2a2015-10-23 13:38:33 +0100138 test_runner_under_valgrind = under_valgrind
armvixldb644342015-07-21 11:37:10 +0100139 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