1
2
3 '''
4 PersistentApplication -- Implements a persistent application for performing
5 multiple tests and offers an interface to perform the necessary tasks around
6 testing such an application.
7
8 @author: Christian Holler (:decoder)
9
10 @license:
11
12 This Source Code Form is subject to the terms of the Mozilla Public
13 License, v. 2.0. If a copy of the MPL was not distributed with this
14 file, You can obtain one at http://mozilla.org/MPL/2.0/.
15
16 @contact: choller@mozilla.com
17 '''
18
19
20 from __future__ import print_function
21
22 from abc import ABCMeta
23 import subprocess
24 import os
25 import Queue
26 import time
27 import signal
28
29 from FTB.Running.StreamCollector import StreamCollector
30
31
34
36 '''
37 Abstract base class that defines the interface
38 '''
39 __metaclass__ = ABCMeta
40
41 - def __init__(self, binary, args=None, env=None, cwd=None):
42 self.binary = binary
43 self.cwd = cwd
44
45
46 self.env = dict(os.environ)
47 if env:
48 for envkey in env:
49 self.env[envkey] = env[envkey]
50
51 self.args = args
52 if self.args is None:
53 self.args = []
54
55 assert isinstance(self.env, dict)
56 assert isinstance(self.args, list)
57
58
59 self.process = None
60 self.stdout = None
61 self.stderr = None
62 self.testLog = None
63
66
69
72
75
77 - def __init__(self, binary, args=None, env=None, cwd=None):
82
84 assert self.process == None or self.process.poll() != None
85
86
87 self.testLog = []
88
89 popenArgs = [ self.binary ]
90 popenArgs.extend(self.args)
91
92 self.process = subprocess.Popen(
93 popenArgs,
94 stdin=subprocess.PIPE,
95 stdout=subprocess.PIPE,
96 stderr=subprocess.PIPE,
97 cwd=self.cwd,
98 env=self.env,
99 universal_newlines=True
100 )
101
102
103
104 self.responseQueue = Queue.Queue()
105
106 self.outCollector = StreamCollector(self.process.stdout, self.responseQueue, logResponses=False, maxBacklog=256)
107 self.errCollector = StreamCollector(self.process.stderr, self.responseQueue, logResponses=False, maxBacklog=256)
108
109
110 self.outCollector.addResponsePrefix("SPFP: ")
111 self.errCollector.addResponsePrefix("SPFP: ")
112
113 self.outCollector.start()
114 self.errCollector.start()
115
116 try:
117 self.process.stdin.write('selftest\n')
118 except IOError:
119 raise RuntimeError("SPFP Error: Selftest failed, application did not start properly.")
120
121 try:
122 response = self.responseQueue.get(block=True, timeout=self.processingTimeout)
123 except Queue.Empty:
124 raise RuntimeError("SPFP Error: Selftest failed, no response.")
125
126 if response != "PASSED":
127 raise RuntimeError("SPFP Error: Selftest failed, unsupported application response: %s" % response)
128
130 self._terminateProcess()
131
132
133 self.outCollector.join()
134 self.errCollector.join()
135
136
137 self.stdout = self.outCollector.output
138 self.stderr = self.errCollector.output
139
141 if self.process == None or self.process.poll() != None:
142 self.start()
143
144 self.testLog.append(test)
145 self.process.stdin.write('%s\n' % test)
146
147 try:
148 response = self.responseQueue.get(block=True, timeout=self.processingTimeout)
149 except Queue.Empty:
150 if self.process.poll() == None:
151
152 self.stop()
153 return ApplicationStatus.TIMEDOUT
154 else:
155
156
157 self.stop()
158
159 if self.process.returncode < 0:
160 crashSignals = [
161
162 signal.SIGILL,
163 signal.SIGABRT,
164 signal.SIGFPE,
165 signal.SIGSEGV,
166
167 signal.SIGBUS,
168 signal.SIGSYS,
169 signal.SIGTRAP,
170 ]
171
172 for crashSignal in crashSignals:
173 if self.process.returncode == -crashSignal:
174 return ApplicationStatus.CRASHED
175
176
177
178
179
180
181 raise RuntimeError("SPFP Error: Application terminated with signal: %s" % self.process.returncode)
182 else:
183
184
185 raise RuntimeError("SPFP Error: Application exited without message. Exitcode: %s" % self.process.returncode)
186
187 if response == 'OK':
188 return ApplicationStatus.OK
189 elif response == 'ERROR':
190 return ApplicationStatus.ERROR
191
192 raise RuntimeError("SPFP Error: Unsupported application response: %s" % response)
193
195 if self.process:
196 if self.process.poll() == None:
197
198 self.process.terminate()
199
200
201
202 (maxSleepTime, pollInterval) = (3, 0.2)
203 while self.process.poll() == None and maxSleepTime > 0:
204 maxSleepTime -= pollInterval
205 time.sleep(pollInterval)
206
207
208 if self.process.poll() == None:
209 self.process.kill()
210 self.process.wait()
211