source: rtems-tools/tester/rt/test.py @ 4001a74

4.11
Last change on this file since 4001a74 was 4001a74, checked in by Chris Johns <chrisj@…>, on Mar 2, 2016 at 9:54:06 AM

Update rtems-tool to support Python 2 and 3.

Add solaris and netbsd.

Close #2619.

  • Property mode set to 100644
File size: 12.7 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013-2016 Chris Johns (chrisj@rtems.org)
4# All rights reserved.
5#
6# This file is part of the RTEMS Tools package in 'rtems-tools'.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions are met:
10#
11# 1. Redistributions of source code must retain the above copyright notice,
12# this list of conditions and the following disclaimer.
13#
14# 2. Redistributions in binary form must reproduce the above copyright notice,
15# this list of conditions and the following disclaimer in the documentation
16# and/or other materials provided with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
29#
30
31from __future__ import print_function
32
33import copy
34import datetime
35import fnmatch
36import os
37import sys
38import threading
39import time
40
41from rtemstoolkit import error
42from rtemstoolkit import log
43from rtemstoolkit import path
44from rtemstoolkit import stacktraces
45from rtemstoolkit import version
46
47from . import bsps
48from . import config
49from . import console
50from . import options
51from . import report
52
53class test(object):
54    def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
55        self.index = index
56        self.total = total
57        self.report = report
58        self.bsp = bsp
59        self.bsp_config = bsp_config
60        self.opts = copy.copy(opts)
61        self.opts.defaults['test_index'] = str(index)
62        self.opts.defaults['test_total'] = str(total)
63        self.opts.defaults['bsp'] = bsp
64        self.opts.defaults['bsp_arch'] = '%%{%s_arch}' % (bsp)
65        self.opts.defaults['bsp_opts'] = '%%{%s_opts}' % (bsp)
66        if not path.isfile(executable):
67            raise error.general('cannot find executable: %s' % (executable))
68        self.opts.defaults['test_executable'] = executable
69        if rtems_tools:
70            rtems_tools_bin = path.join(self.opts.defaults.expand(rtems_tools), 'bin')
71            if not path.isdir(rtems_tools_bin):
72                raise error.general('cannot find RTEMS tools path: %s' % (rtems_tools_bin))
73            self.opts.defaults['rtems_tools'] = rtems_tools_bin
74        self.config = config.file(self.report, self.bsp_config, self.opts)
75
76    def run(self):
77        if self.config:
78            self.config.run()
79
80    def kill(self):
81        if self.config:
82            self.config.kill()
83
84class test_run(object):
85    def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
86        self.test = None
87        self.result = None
88        self.start_time = None
89        self.end_time = None
90        self.index = copy.copy(index)
91        self.total = total
92        self.report = report
93        self.executable = copy.copy(executable)
94        self.rtems_tools = rtems_tools
95        self.bsp = bsp
96        self.bsp_config = bsp_config
97        self.opts = opts
98
99    def runner(self):
100        self.start_time = datetime.datetime.now()
101        try:
102            self.test = test(self.index, self.total, self.report,
103                             self.executable, self.rtems_tools,
104                             self.bsp, self.bsp_config,
105                             self.opts)
106            self.test.run()
107        except KeyboardInterrupt:
108            pass
109        except:
110            self.result = sys.exc_info()
111        self.end_time = datetime.datetime.now()
112
113    def run(self):
114        self.thread = threading.Thread(target = self.runner,
115                                       name = 'test[%s]' % path.basename(self.executable))
116        self.thread.start()
117
118    def is_alive(self):
119        return self.thread and self.thread.is_alive()
120
121    def reraise(self):
122        if self.result is not None:
123            raise self.result[0](self.result[1]).with_traceback(self.result[2])
124
125    def kill(self):
126        if self.test:
127            self.test.kill()
128
129def find_executables(paths, glob):
130    executables = []
131    for p in paths:
132        if path.isfile(p):
133            executables += [p]
134        elif path.isdir(p):
135            for root, dirs, files in os.walk(p, followlinks = True):
136                for f in files:
137                    if fnmatch.fnmatch(f.lower(), glob):
138                        executables += [path.join(root, f)]
139    return sorted(executables)
140
141def report_finished(reports, report_mode, reporting, finished, job_trace):
142    processing = True
143    while processing:
144        processing = False
145        reported = []
146        for tst in finished:
147            if tst not in reported and \
148                    (reporting < 0 or tst.index == reporting):
149                if job_trace:
150                    log.notice('}} %*d: %s: %s (%d)' % (len(str(tst.total)), tst.index,
151                                                        path.basename(tst.executable),
152                                                        'reporting',
153                                                        reporting))
154                processing = True
155                reports.log(tst.executable, report_mode)
156                reported += [tst]
157                reporting += 1
158        finished[:] = [t for t in finished if t not in reported]
159        if len(reported):
160            del reported[:]
161            if job_trace:
162                print('}} threading:', threading.active_count())
163                for t in threading.enumerate():
164                    print('}} ', t.name)
165    return reporting
166
167def _job_trace(tst, msg, total, exe, active, reporting):
168    s = ''
169    for a in active:
170        s += ' %d:%s' % (a.index, path.basename(a.executable))
171    log.notice('}} %*d: %s: %s (%d %d %d%s)' % (len(str(tst.total)), tst.index,
172                                                path.basename(tst.executable),
173                                                msg,
174                                                reporting, total, exe, s))
175
176def list_bsps(opts):
177    path_ = opts.defaults.expand('%%{_configdir}/bsps/*.mc')
178    bsps = path.collect_files(path_)
179    log.notice(' BSP List:')
180    for bsp in bsps:
181        log.notice(%s' % (path.basename(bsp[:-3])))
182    raise error.exit()
183
184def killall(tests):
185    for test in tests:
186        test.kill()
187
188def run(command_path = None):
189    import sys
190    tests = []
191    stdtty = console.save()
192    opts = None
193    default_exefilter = '*.exe'
194    try:
195        optargs = { '--rtems-tools': 'The path to the RTEMS tools',
196                    '--rtems-bsp':   'The RTEMS BSP to run the test on',
197                    '--report-mode': 'Reporting modes, failures (default),all,none',
198                    '--list-bsps':   'List the supported BSPs',
199                    '--debug-trace': 'Debug trace based on specific flags',
200                    '--filter':      'Glob that executables must match to run (default: ' +
201                              default_exefilter + ')',
202                    '--stacktrace':  'Dump a stack trace on a user termination (^C)' }
203        opts = options.load(sys.argv,
204                            optargs = optargs,
205                            command_path = command_path)
206        log.notice('RTEMS Testing - Tester, %s' % (version.str()))
207        if opts.find_arg('--list-bsps'):
208            bsps.list(opts)
209        exe_filter = opts.find_arg('--filter')
210        if exe_filter:
211            exe_filter = exe_filter[1]
212        else:
213            exe_filter = default_exefilter
214        opts.log_info()
215        debug_trace = opts.find_arg('--debug-trace')
216        if debug_trace:
217            debug_trace = debug_trace[1]
218        else:
219            debug_trace = ''
220        opts.defaults['debug_trace'] = debug_trace
221        job_trace = 'jobs' in debug_trace.split(',')
222        rtems_tools = opts.find_arg('--rtems-tools')
223        if rtems_tools:
224            if len(rtems_tools) != 2:
225                raise error.general('invalid RTEMS tools option')
226            rtems_tools = rtems_tools[1]
227        else:
228            rtems_tools = '%{_prefix}'
229        bsp = opts.find_arg('--rtems-bsp')
230        if bsp is None or len(bsp) != 2:
231            raise error.general('RTEMS BSP not provided or invalid option')
232        opts.defaults.load('%%{_configdir}/bsps/%s.mc' % (bsp[1]))
233        bsp = opts.defaults.get('%{bsp}')
234        if not bsp:
235            raise error.general('BSP definition (%{bsp}) not found in the global map')
236        bsp = bsp[2]
237        if not opts.defaults.set_read_map(bsp):
238            raise error.general('no BSP map found')
239        bsp_script = opts.defaults.get(bsp)
240        if not bsp_script:
241            raise error.general('BSP script not found: %s' % (bsp))
242        bsp_config = opts.defaults.expand(opts.defaults[bsp])
243        report_mode = opts.find_arg('--report-mode')
244        if report_mode:
245            if report_mode[1] != 'failures' and \
246                    report_mode[1] != 'all' and \
247                    report_mode[1] != 'none':
248                raise error.general('invalid report mode')
249            report_mode = report_mode[1]
250        else:
251            report_mode = 'failures'
252        executables = find_executables(opts.params(), exe_filter)
253        if len(executables) == 0:
254            raise error.general('no executables supplied')
255        start_time = datetime.datetime.now()
256        total = len(executables)
257        reports = report.report(total)
258        invalid_tests = opts.defaults['invalid_tests']
259        if invalid_tests:
260            reports.set_invalid_tests([l.strip() for l in invalid_tests.splitlines()])
261        reporting = 1
262        jobs = int(opts.jobs(opts.defaults['_ncpus']))
263        exe = 0
264        finished = []
265        if jobs > len(executables):
266            jobs = len(executables)
267        while exe < total or len(tests) > 0:
268            if exe < total and len(tests) < jobs:
269                tst = test_run(exe + 1, total, reports,
270                               executables[exe],
271                               rtems_tools, bsp, bsp_config,
272                               opts)
273                exe += 1
274                tests += [tst]
275                if job_trace:
276                    _job_trace(tst, 'create',
277                               total, exe, tests, reporting)
278                tst.run()
279            else:
280                dead = [t for t in tests if not t.is_alive()]
281                tests[:] = [t for t in tests if t not in dead]
282                for tst in dead:
283                    if job_trace:
284                        _job_trace(tst, 'dead',
285                                   total, exe, tests, reporting)
286                    finished += [tst]
287                    tst.reraise()
288                del dead
289                if len(tests) >= jobs or exe >= total:
290                    time.sleep(0.250)
291                if len(finished):
292                    reporting = report_finished(reports,
293                                                report_mode,
294                                                reporting,
295                                                finished,
296                                                job_trace)
297        finished_time = datetime.datetime.now()
298        reporting = report_finished(reports, report_mode,
299                                    reporting, finished, job_trace)
300        if reporting < total:
301            log.warning('finished jobs does match: %d' % (reporting))
302            report_finished(reports, report_mode, -1, finished, job_trace)
303        reports.summary()
304        end_time = datetime.datetime.now()
305        log.notice('Average test time: %s' % (str((end_time - start_time) / total)))
306        log.notice('Testing time     : %s' % (str(end_time - start_time)))
307    except error.general as gerr:
308        print(gerr)
309        sys.exit(1)
310    except error.internal as ierr:
311        print(ierr)
312        sys.exit(1)
313    except error.exit as eerr:
314        sys.exit(2)
315    except KeyboardInterrupt:
316        if opts is not None and opts.find_arg('--stacktrace'):
317            print('}} dumping:', threading.active_count())
318            for t in threading.enumerate():
319                print('}} ', t.name)
320            print(stacktraces.trace())
321        log.notice('abort: user terminated')
322        killall(tests)
323        sys.exit(1)
324    finally:
325        console.restore(stdtty)
326    sys.exit(0)
327
328if __name__ == "__main__":
329    run()
Note: See TracBrowser for help on using the repository browser.