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 03/02/16 at 09:54:06

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
RevLine 
[50fdf12]1#
2# RTEMS Tools Project (http://www.rtems.org/)
[4001a74]3# Copyright 2013-2016 Chris Johns (chrisj@rtems.org)
[50fdf12]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
[4001a74]31from __future__ import print_function
32
[50fdf12]33import copy
34import datetime
[4001a74]35import fnmatch
[50fdf12]36import os
37import sys
38import threading
39import time
40
41from rtemstoolkit import error
42from rtemstoolkit import log
43from rtemstoolkit import path
[c04a849]44from rtemstoolkit import stacktraces
[7279748]45from rtemstoolkit import version
[50fdf12]46
[4001a74]47from . import bsps
48from . import config
49from . import console
50from . import options
51from . import report
[50fdf12]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:
[9600c39]70            rtems_tools_bin = path.join(self.opts.defaults.expand(rtems_tools), 'bin')
[50fdf12]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
[c04a849]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()
[50fdf12]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)
[c04a849]106            self.test.run()
[50fdf12]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:
[4001a74]123            raise self.result[0](self.result[1]).with_traceback(self.result[2])
[50fdf12]124
[c04a849]125    def kill(self):
126        if self.test:
127            self.test.kill()
128
[803b63d]129def find_executables(paths, glob):
[50fdf12]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:
[803b63d]137                    if fnmatch.fnmatch(f.lower(), glob):
[50fdf12]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:
[4001a74]162                print('}} threading:', threading.active_count())
[50fdf12]163                for t in threading.enumerate():
[4001a74]164                    print('}} ', t.name)
[50fdf12]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
[c04a849]184def killall(tests):
185    for test in tests:
186        test.kill()
187
[50fdf12]188def run(command_path = None):
189    import sys
[c04a849]190    tests = []
[50fdf12]191    stdtty = console.save()
192    opts = None
[803b63d]193    default_exefilter = '*.exe'
[50fdf12]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',
[803b63d]200                    '--filter':      'Glob that executables must match to run (default: ' +
201                              default_exefilter + ')',
[50fdf12]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)
[7279748]206        log.notice('RTEMS Testing - Tester, %s' % (version.str()))
[50fdf12]207        if opts.find_arg('--list-bsps'):
[c04a849]208            bsps.list(opts)
[803b63d]209        exe_filter = opts.find_arg('--filter')
210        if exe_filter:
211            exe_filter = exe_filter[1]
212        else:
213            exe_filter = default_exefilter
[50fdf12]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:
[9600c39]224            if len(rtems_tools) != 2:
225                raise error.general('invalid RTEMS tools option')
[50fdf12]226            rtems_tools = rtems_tools[1]
[9600c39]227        else:
228            rtems_tools = '%{_prefix}'
[50fdf12]229        bsp = opts.find_arg('--rtems-bsp')
[9600c39]230        if bsp is None or len(bsp) != 2:
231            raise error.general('RTEMS BSP not provided or invalid option')
[50fdf12]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'
[803b63d]252        executables = find_executables(opts.params(), exe_filter)
[50fdf12]253        if len(executables) == 0:
[e4014f0]254            raise error.general('no executables supplied')
[50fdf12]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()
[c04a849]305        log.notice('Average test time: %s' % (str((end_time - start_time) / total)))
306        log.notice('Testing time     : %s' % (str(end_time - start_time)))
[4001a74]307    except error.general as gerr:
308        print(gerr)
[50fdf12]309        sys.exit(1)
[4001a74]310    except error.internal as ierr:
311        print(ierr)
[50fdf12]312        sys.exit(1)
[4001a74]313    except error.exit as eerr:
[50fdf12]314        sys.exit(2)
315    except KeyboardInterrupt:
[b7d48ef]316        if opts is not None and opts.find_arg('--stacktrace'):
[4001a74]317            print('}} dumping:', threading.active_count())
[50fdf12]318            for t in threading.enumerate():
[4001a74]319                print('}} ', t.name)
320            print(stacktraces.trace())
[50fdf12]321        log.notice('abort: user terminated')
[c04a849]322        killall(tests)
[50fdf12]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.