source: rtems-tools/tester/rt/test.py @ 662e1f7

5
Last change on this file since 662e1f7 was 662e1f7, checked in by Chris Johns <chrisj@…>, on 10/10/17 at 02:30:31

tester: Add a tester off to turn the target off when finished.

Turning the target off stops it sitting asking for a download if you have
more than one target in a test set up. Any target can jump in and
nick the download.

  • Property mode set to 100644
File size: 12.6 KB
Line 
1#
2# RTEMS Tools Project (http://www.rtems.org/)
3# Copyright 2013-2014 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 reraise
45from rtemstoolkit import stacktraces
46from rtemstoolkit import version
47
48from . import bsps
49from . import config
50from . import console
51from . import options
52from . import report
53
54class test(object):
55    def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
56        self.index = index
57        self.total = total
58        self.report = report
59        self.bsp = bsp
60        self.bsp_config = bsp_config
61        self.opts = copy.copy(opts)
62        self.opts.defaults['test_index'] = str(index)
63        self.opts.defaults['test_total'] = str(total)
64        self.opts.defaults['bsp'] = bsp
65        self.opts.defaults['bsp_arch'] = '%%{%s_arch}' % (bsp)
66        self.opts.defaults['bsp_opts'] = '%%{%s_opts}' % (bsp)
67        if not path.isfile(executable):
68            raise error.general('cannot find executable: %s' % (executable))
69        self.opts.defaults['test_executable'] = executable
70        if rtems_tools:
71            rtems_tools_bin = path.join(self.opts.defaults.expand(rtems_tools), 'bin')
72            if not path.isdir(rtems_tools_bin):
73                raise error.general('cannot find RTEMS tools path: %s' % (rtems_tools_bin))
74            self.opts.defaults['rtems_tools'] = rtems_tools_bin
75        self.config = config.file(index, total, self.report, self.bsp_config, self.opts)
76
77    def run(self):
78        if self.config:
79            self.config.run()
80
81    def kill(self):
82        if self.config:
83            self.config.kill()
84
85class test_run(object):
86    def __init__(self, index, total, report, executable, rtems_tools, bsp, bsp_config, opts):
87        self.test = None
88        self.result = None
89        self.start_time = None
90        self.end_time = None
91        self.index = copy.copy(index)
92        self.total = total
93        self.report = report
94        self.executable = copy.copy(executable)
95        self.rtems_tools = rtems_tools
96        self.bsp = bsp
97        self.bsp_config = bsp_config
98        self.opts = opts
99
100    def runner(self):
101        self.start_time = datetime.datetime.now()
102        try:
103            self.test = test(self.index, self.total, self.report,
104                             self.executable, self.rtems_tools,
105                             self.bsp, self.bsp_config,
106                             self.opts)
107            self.test.run()
108        except KeyboardInterrupt:
109            pass
110        except:
111            self.result = sys.exc_info()
112        self.end_time = datetime.datetime.now()
113
114    def run(self):
115        self.thread = threading.Thread(target = self.runner,
116                                       name = 'test[%s]' % path.basename(self.executable))
117        self.thread.start()
118
119    def is_alive(self):
120        return self.thread and self.thread.is_alive()
121
122    def reraise(self):
123        if self.result is not None:
124            reraise.reraise(*self.result)
125
126    def kill(self):
127        if self.test:
128            self.test.kill()
129
130def find_executables(paths, glob):
131    executables = []
132    for p in paths:
133        if path.isfile(p):
134            executables += [p]
135        elif path.isdir(p):
136            for root, dirs, files in os.walk(p, followlinks = True):
137                for f in files:
138                    if fnmatch.fnmatch(f.lower(), glob):
139                        executables += [path.join(root, f)]
140    return sorted(executables)
141
142def report_finished(reports, report_mode, reporting, finished, job_trace):
143    processing = True
144    while processing:
145        processing = False
146        reported = []
147        for tst in finished:
148            if tst not in reported and \
149                    (reporting < 0 or tst.index == reporting):
150                if job_trace:
151                    log.notice('}} %*d: %s: %s (%d)' % (len(str(tst.total)), tst.index,
152                                                        path.basename(tst.executable),
153                                                        'reporting',
154                                                        reporting))
155                processing = True
156                reports.log(tst.executable, report_mode)
157                reported += [tst]
158                reporting += 1
159        finished[:] = [t for t in finished if t not in reported]
160        if len(reported):
161            del reported[:]
162            if job_trace:
163                print('}} threading:', threading.active_count())
164                for t in threading.enumerate():
165                    print('}} ', t.name)
166    return reporting
167
168def _job_trace(tst, msg, total, exe, active, reporting):
169    s = ''
170    for a in active:
171        s += ' %d:%s' % (a.index, path.basename(a.executable))
172    log.notice('}} %*d: %s: %s (%d %d %d%s)' % (len(str(tst.total)), tst.index,
173                                                path.basename(tst.executable),
174                                                msg,
175                                                reporting, total, exe, s))
176
177def list_bsps(opts):
178    path_ = opts.defaults.expand('%%{_configdir}/bsps/*.mc')
179    bsps = path.collect_files(path_)
180    log.notice(' BSP List:')
181    for bsp in bsps:
182        log.notice('  %s' % (path.basename(bsp[:-3])))
183    raise error.exit()
184
185def killall(tests):
186    for test in tests:
187        test.kill()
188
189def run(command_path = None):
190    import sys
191    tests = []
192    stdtty = console.save()
193    opts = None
194    default_exefilter = '*.exe'
195    try:
196        optargs = { '--rtems-tools': 'The path to the RTEMS tools',
197                    '--rtems-bsp':   'The RTEMS BSP to run the test on',
198                    '--report-mode': 'Reporting modes, failures (default),all,none',
199                    '--list-bsps':   'List the supported BSPs',
200                    '--debug-trace': 'Debug trace based on specific flags',
201                    '--filter':      'Glob that executables must match to run (default: ' +
202                              default_exefilter + ')',
203                    '--stacktrace':  'Dump a stack trace on a user termination (^C)' }
204        opts = options.load(sys.argv,
205                            optargs = optargs,
206                            command_path = command_path)
207        log.notice('RTEMS Testing - Tester, %s' % (version.str()))
208        if opts.find_arg('--list-bsps'):
209            bsps.list(opts)
210        exe_filter = opts.find_arg('--filter')
211        if exe_filter:
212            exe_filter = exe_filter[1]
213        else:
214            exe_filter = default_exefilter
215        opts.log_info()
216        debug_trace = opts.find_arg('--debug-trace')
217        if debug_trace:
218            if len(debug_trace) != 1:
219                debug_trace = debug_trace[1]
220            else:
221                raise error.general('no debug flags, can be: console,gdb,output')
222        else:
223            debug_trace = ''
224        opts.defaults['debug_trace'] = debug_trace
225        job_trace = 'jobs' in debug_trace.split(',')
226        rtems_tools = opts.find_arg('--rtems-tools')
227        if rtems_tools:
228            if len(rtems_tools) != 2:
229                raise error.general('invalid RTEMS tools option')
230            rtems_tools = rtems_tools[1]
231        else:
232            rtems_tools = '%{_prefix}'
233        bsp = opts.find_arg('--rtems-bsp')
234        if bsp is None or len(bsp) != 2:
235            raise error.general('RTEMS BSP not provided or invalid option')
236        opts.defaults.load('%%{_configdir}/bsps/%s.mc' % (bsp[1]))
237        bsp = opts.defaults.get('%{bsp}')
238        if not bsp:
239            raise error.general('BSP definition (%{bsp}) not found in the global map')
240        bsp = bsp[2]
241        if not opts.defaults.set_read_map(bsp):
242            raise error.general('no BSP map found')
243        bsp_script = opts.defaults.get(bsp)
244        if not bsp_script:
245            raise error.general('BSP script not found: %s' % (bsp))
246        bsp_config = opts.defaults.expand(opts.defaults[bsp])
247        report_mode = opts.find_arg('--report-mode')
248        if report_mode:
249            if report_mode[1] != 'failures' and \
250                    report_mode[1] != 'all' and \
251                    report_mode[1] != 'none':
252                raise error.general('invalid report mode')
253            report_mode = report_mode[1]
254        else:
255            report_mode = 'failures'
256        executables = find_executables(opts.params(), exe_filter)
257        if len(executables) == 0:
258            raise error.general('no executables supplied')
259        start_time = datetime.datetime.now()
260        total = len(executables)
261        reports = report.report(total)
262        reporting = 1
263        jobs = int(opts.jobs(opts.defaults['_ncpus']))
264        exe = 0
265        finished = []
266        if jobs > len(executables):
267            jobs = len(executables)
268        while exe < total or len(tests) > 0:
269            if exe < total and len(tests) < jobs:
270                tst = test_run(exe + 1, total, reports,
271                               executables[exe],
272                               rtems_tools, bsp, bsp_config,
273                               opts)
274                exe += 1
275                tests += [tst]
276                if job_trace:
277                    _job_trace(tst, 'create',
278                               total, exe, tests, reporting)
279                tst.run()
280            else:
281                dead = [t for t in tests if not t.is_alive()]
282                tests[:] = [t for t in tests if t not in dead]
283                for tst in dead:
284                    if job_trace:
285                        _job_trace(tst, 'dead',
286                                   total, exe, tests, reporting)
287                    finished += [tst]
288                    tst.reraise()
289                del dead
290                if len(tests) >= jobs or exe >= total:
291                    time.sleep(0.250)
292                if len(finished):
293                    reporting = report_finished(reports,
294                                                report_mode,
295                                                reporting,
296                                                finished,
297                                                job_trace)
298        finished_time = datetime.datetime.now()
299        reporting = report_finished(reports, report_mode,
300                                    reporting, finished, job_trace)
301        if reporting < total:
302            log.warning('finished jobs does match: %d' % (reporting))
303            report_finished(reports, report_mode, -1, finished, job_trace)
304        reports.summary()
305        end_time = datetime.datetime.now()
306        log.notice('Average test time: %s' % (str((end_time - start_time) / total)))
307        log.notice('Testing time     : %s' % (str(end_time - start_time)))
308    except error.general as gerr:
309        print(gerr)
310        sys.exit(1)
311    except error.internal as ierr:
312        print(ierr)
313        sys.exit(1)
314    except error.exit:
315        sys.exit(2)
316    except KeyboardInterrupt:
317        if opts is not None and opts.find_arg('--stacktrace'):
318            print('}} dumping:', threading.active_count())
319            for t in threading.enumerate():
320                print('}} ', t.name)
321            print(stacktraces.trace())
322        log.notice('abort: user terminated')
323        killall(tests)
324        sys.exit(1)
325    finally:
326        console.restore(stdtty)
327    sys.exit(0)
328
329if __name__ == "__main__":
330    run()
Note: See TracBrowser for help on using the repository browser.