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

5
Last change on this file since 3a867a4 was 3a867a4, checked in by Chris Johns <chrisj@…>, on 09/21/17 at 08:26:20

Add TFTP as a back end option for testing. Add telnet as a console option.

TFTP runs a local TFTP server on port 69 or another specified port and
serves each test for any requested file.

Telnet is now a console option.

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