source: rtems-tools/tester/rt/config.py @ fce29b0

5
Last change on this file since fce29b0 was fce29b0, checked in by Chris Johns <chrisj@…>, on 10/11/17 at 19:13:13

tester: Change the capture console prompt to avoid email reply processing in clients.

  • Property mode set to 100644
File size: 12.6 KB
RevLine 
[50fdf12]1#
2# RTEMS Tools Project (http://www.rtems.org/)
[3a867a4]3# Copyright 2013-2017 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
31#
32# RTEMS Testing Config
33#
34
[b0fa2ae]35from __future__ import print_function
36
[50fdf12]37import datetime
38import os
[0737b46]39import re
[50fdf12]40import threading
41
42from rtemstoolkit import config
43from rtemstoolkit import error
44from rtemstoolkit import execute
45from rtemstoolkit import log
46from rtemstoolkit import path
47
[b0fa2ae]48from . import console
49from . import gdb
[3a867a4]50from . import tftp
[50fdf12]51
52timeout = 15
53
54class file(config.file):
55    """RTEMS Testing configuration."""
56
57    _directives = ['%execute',
58                   '%gdb',
[3a867a4]59                   '%tftp',
[50fdf12]60                   '%console']
61
[662e1f7]62    def __init__(self, index, total, report, name, opts, _directives = _directives):
[50fdf12]63        super(file, self).__init__(name, opts, directives = _directives)
64        self.lock = threading.Lock()
65        self.realtime_trace = self.debug_trace('output')
66        self.process = None
67        self.console = None
68        self.output = None
[3a867a4]69        self.index = index
[662e1f7]70        self.total = total
[50fdf12]71        self.report = report
[c04a849]72        self.name = name
[60937e1]73        self.timedout = False
[3a867a4]74        self.test_started = False
[15a3e06]75        self.kill_good = False
[3a867a4]76        self.kill_on_end = False
77        self.test_label = None
[0737b46]78        self.target_reset_regx = None
[9580804]79        self.target_start_regx = None
[50fdf12]80
81    def __del__(self):
82        if self.console:
83            del self.console
84        super(file, self).__del__()
85
86    def _lock(self):
87        self.lock.acquire()
88
89    def _unlock(self):
90        self.lock.release()
91
92    def _timeout(self):
[60937e1]93        self._lock()
94        self.timedout = True
95        self._unlock()
[50fdf12]96        self.capture('*** TIMEOUT TIMEOUT')
97
[15a3e06]98    def _ok_kill(self):
[3c7cd05]99        self._lock()
[15a3e06]100        self.kill_good = True
[3c7cd05]101        self._unlock()
[15a3e06]102        try:
103            self.process.kill()
104        except:
105            pass
106
[9580804]107    def _target_regex(self, label):
108        regex = None
109        if self.defined(label):
110            r = self.expand('%%{%s}' % (label))
111            try:
112                regex = re.compile(r, re.MULTILINE)
113            except:
114                msg = 'invalid target regex: %s: %s' % (label, r)
115                raise error.general(msg)
116        return regex
117
[662e1f7]118    def _target_command(self, command):
119        if self.defined('target_%s_command' % (command)):
120            cmd = self.expand('%%{target_%s_command}' % (command)).strip()
121            if len(cmd) > 0:
[3a867a4]122                rs_proc = execute.capture_execution()
[662e1f7]123                ec, proc, output = rs_proc.open(cmd, shell = True)
124                self._capture_console('target %s: %s: %s' % (command, cmd, output))
[3a867a4]125
126    def _output_length(self):
127        self._lock()
128        if self.test_started:
129            l = len(self.output)
130            self._unlock()
131            return l
132        self._unlock()
133        return 0
134
135    def _capture_console(self, text):
[fce29b0]136        text = [('=>', l) for l in text.replace(chr(13), '').splitlines()]
[3a867a4]137        if self.output is not None:
138            self._realtime_trace(text)
139            self.output += text
140
[50fdf12]141    def _dir_console(self, data):
142        if self.console is not None:
143            raise error.general(self._name_line_msg('console already configured'))
144        if len(data) == 0:
145            raise error.general(self._name_line_msg('no console configuration provided'))
146        console_trace = trace = self.debug_trace('console')
147        if data[0] == 'stdio':
148            self.console = console.stdio(trace = console_trace)
149        elif data[0] == 'tty':
150            if len(data) < 2 or len(data) >3:
151                raise error.general(self._name_line_msg('no tty configuration provided'))
152            if len(data) == 3:
153                settings = data[2]
154            else:
155                settings = None
156            self.console = console.tty(data[1],
157                                       output = self.capture,
158                                       setup = settings,
159                                       trace = console_trace)
160        else:
161            raise error.general(self._name_line_msg('invalid console type'))
162
163    def _dir_execute(self, data, total, index, exe, bsp_arch, bsp):
164        self.process = execute.execute(output = self.capture)
165        if not self.in_error:
166            if self.console:
167                self.console.open()
168            self.capture_console('run: %s' % (' '.join(data)))
169            ec, proc = self.process.open(data,
170                                         timeout = (int(self.expand('%{timeout}')),
171                                                    self._timeout))
[60937e1]172            self._lock()
[15a3e06]173            if not self.kill_good and ec > 0:
[a116962]174                self._error('execute failed: %s: exit-code:%d' % (' '.join(data), ec))
[60937e1]175            elif self.timedout:
176                self.process.kill()
177            self._unlock()
[50fdf12]178            if self.console:
179                self.console.close()
180
181    def _dir_gdb(self, data, total, index, exe, bsp_arch, bsp):
182        if len(data) < 3 or len(data) > 4:
183            raise error.general('invalid %gdb arguments')
184        self.process = gdb.gdb(bsp_arch, bsp,
185                               trace = self.debug_trace('gdb'),
186                               mi_trace = self.debug_trace('gdb-mi'))
187        script = self.expand('%%{%s}' % data[2])
188        if script:
189            script = [l.strip() for l in script.splitlines()]
190        if not self.in_error:
191            if self.console:
192                self.console.open()
193            self.process.open(data[0], data[1],
194                              script = script,
195                              output = self.capture,
196                              gdb_console = self.capture_console,
197                              timeout = int(self.expand('%{timeout}')))
198            if self.console:
199                self.console.close()
200
[3a867a4]201    def _dir_tftp(self, data, total, index, exe, bsp_arch, bsp):
202        if len(data) != 2:
203            raise error.general('invalid %tftp arguments')
204        try:
205            port = int(data[1])
206        except:
207            raise error.general('invalid %tftp port')
208        self.kill_on_end = True
209        self.process = tftp.tftp(bsp_arch, bsp,
210                                 trace = self.debug_trace('gdb'))
211        if not self.in_error:
212            if self.console:
213                self.console.open()
214            self.process.open(executable = data[0],
215                              port = port,
216                              output_length = self._output_length,
217                              console = self.capture_console,
218                              timeout = (int(self.expand('%{timeout}')),
219                                         self._timeout))
220            if self.console:
221                self.console.close()
222
[50fdf12]223    def _directive_filter(self, results, directive, info, data):
224        if results[0] == 'directive':
225            _directive = results[1]
226            _data = results[2]
227            ds = []
228            if len(_data):
229                ds = [_data[0]]
230                if len(_data) > 1:
231                    ds += _data[1].split()
232            ds = self.expand(ds)
233
234            if _directive == '%console':
235                self._dir_console(ds)
236            else:
237                self._lock()
238                try:
[3a867a4]239                    self.output = []
[50fdf12]240                    total = int(self.expand('%{test_total}'))
241                    index = int(self.expand('%{test_index}'))
242                    exe = self.expand('%{test_executable}')
243                    bsp_arch = self.expand('%{bsp_arch}')
244                    bsp = self.expand('%{bsp}')
245                    self.report.start(index, total, exe, exe, bsp_arch, bsp)
[3a867a4]246                    if self.index == 1:
[662e1f7]247                        self._target_command('reset')
[50fdf12]248                finally:
249                    self._unlock()
250                if _directive == '%execute':
251                    self._dir_execute(ds, total, index, exe, bsp_arch, bsp)
252                elif _directive == '%gdb':
253                    self._dir_gdb(ds, total, index, exe, bsp_arch, bsp)
[3a867a4]254                elif _directive == '%tftp':
255                    self._dir_tftp(ds, total, index, exe, bsp_arch, bsp)
[50fdf12]256                else:
257                    raise error.general(self._name_line_msg('invalid directive'))
258                self._lock()
[662e1f7]259                if self.index == self.total:
260                    self._target_command('off')
[50fdf12]261                try:
[3a867a4]262                    status = self.report.end(exe, self.output)
[662e1f7]263                    self._capture_console('test result: %s' % (status))
[3a867a4]264                    if status == 'timeout':
[662e1f7]265                        if self.index == self.total:
266                            self._target_command('off')
267                        else:
268                            self._target_command('reset')
[50fdf12]269                    self.process = None
270                    self.output = None
271                finally:
272                    self._unlock()
273        return None, None, None
274
275    def _realtime_trace(self, text):
276        if self.realtime_trace:
277            for l in text:
[04a5204]278                print(' '.join(l))
[50fdf12]279
[c04a849]280    def run(self):
[9580804]281        self.target_start_regx = self._target_regex('target_start_regex')
282        self.target_reset_regx = self._target_regex('target_reset_regex')
[c04a849]283        self.load(self.name)
284
[50fdf12]285    def capture(self, text):
[3a867a4]286        if not self.test_started:
[9580804]287            s = text.find('*** BEGIN OF TEST ')
288            if s >= 0:
289                self.test_started = True
290                e = text[s + 3:].find('***')
291                if e >= 0:
292                    self.test_label = text[s + len('*** BEGIN OF TEST '):s + e + 3 - 1]
293                self.capture_console('test start: %s' % (self.test_label))
[3a867a4]294        ok_to_kill = '*** TEST STATE: USER_INPUT' in text or \
295                     '*** TEST STATE: BENCHMARK' in text
296        if ok_to_kill:
297            reset_target = True
[0737b46]298        else:
299            reset_target = False
[9580804]300        if self.test_started and self.target_start_regx is not None:
301            if self.target_start_regx.match(text):
302                self.capture_console('target start detected')
[0737b46]303                ok_to_kill = True
[9580804]304        if not reset_target and self.target_reset_regx is not None:
305            if self.target_reset_regx.match(text):
306                self.capture_console('target reset condition detected')
307                reset_target = True
[3a867a4]308        if self.kill_on_end:
[9580804]309            if not ok_to_kill and '*** END OF TEST ' in text:
310                self.capture_console('test end: %s' % (self.test_label))
311                if self.test_label is not None:
312                    ok_to_kill = '*** END OF TEST %s ***' % (self.test_label) in text
[50fdf12]313        text = [(']', l) for l in text.replace(chr(13), '').splitlines()]
314        self._lock()
315        if self.output is not None:
316            self._realtime_trace(text)
317            self.output += text
[3a867a4]318        if reset_target:
[662e1f7]319            if self.index == self.total:
320                self._target_command('off')
321            else:
322                self._target_command('reset')
[3c7cd05]323        self._unlock()
[15a3e06]324        if ok_to_kill:
325            self._ok_kill()
[50fdf12]326
327    def capture_console(self, text):
328        self._lock()
[3a867a4]329        self._capture_console(text)
[50fdf12]330        self._unlock()
331
332    def debug_trace(self, flag):
333        dt = self.macros['debug_trace']
334        if dt:
335            if flag in dt.split(','):
336                return True
337        return False
[c04a849]338
339    def kill(self):
340        if self.process:
[15a3e06]341            try:
342                self.process.kill()
343            except:
344                pass
Note: See TracBrowser for help on using the repository browser.