blob: 7eee316a3bc543c680fe14b4d161e83fda89e607 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001# $Id$
2import sys
3import imp
4import re
5import os
6import subprocess
7import random
8import time
9import getopt
10
11import inc_const as const
12import inc_cfg as inc
13
14# Vars
15G_EXE = "" # pjsua executable path
16G_INUNIX = False # flags that test is running in Unix
17
18
19# Usage string
20usage = \
21"""
22run.py - Automated test driver
23
24Usage:
25 run.py [options] MODULE CONFIG
26Options:
27 --exe, -e pjsua executable path
28 --null-audio, -n use null audio
29Sample:
30 run.py -n mod_run.py scripts-run/100_simple.py
31"""
32
33# Parse arguments
34try:
35 opts, args = getopt.getopt(sys.argv[1:], "hne:", ["help", "null-audio", "exe="])
36except getopt.GetoptError, err:
37 print str(err)
38 print usage
39 sys.exit(2)
40for o, a in opts:
41 if o in ("-h", "--help"):
42 print usage
43 sys.exit()
44 elif o in ("-n", "--null-audio"):
45 inc.HAS_SND_DEV = 0
46 elif o in ("-e", "--exe"):
47 G_EXE = a
48 else:
49 print "Unknown options"
50 sys.exit(2)
51
52if len(args) != 2:
53 print "Invalid arguments"
54 print usage
55 sys.exit(2)
56
57# Set global ARGS to be used by modules
58inc.ARGS = args
59
60# Get the pjsua executable name
61if G_EXE == "":
62 if sys.platform.find("win32")!=-1:
63 EXE_DIR = "../../pjsip-apps/bin/"
64 EXECUTABLES = [ "pjsua_vc6d.exe",
65 "pjsua_vc6.exe",
66 "pjsua-i386-Win32-vc8-Debug.exe",
67 "pjsua-i386-Win32-vc8-Debug-Dynamic.exe",
68 "pjsua-i386-Win32-vc8-Debug-Static.exe",
69 "pjsua-i386-Win32-vc8-Release.exe",
70 "pjsua-i386-Win32-vc8-Release-Dynamic.exe",
71 "pjsua-i386-Win32-vc8-Release-Static.exe"
72 ]
73 e_ts = 0
74 for e in EXECUTABLES:
75 e = EXE_DIR + e
76 if os.access(e, os.F_OK):
77 st = os.stat(e)
78 if e_ts==0 or e_ts<st.st_mtime:
79 G_EXE = e
80 e_ts = st.st_mtime
81
82 if G_EXE=="":
83 print "Unable to find valid pjsua. Please build pjsip first"
84 sys.exit(1)
85
86 G_INUNIX = False
87 else:
88 f = open("../../build.mak", "r")
89 while True:
90 line = f.readline()
91 if not line:
92 break
93 if line.find("TARGET_NAME")!=-1:
94 print line
95 G_EXE="../../pjsip-apps/bin/pjsua-" + line.split(":= ")[1]
96 break
97 if G_EXE=="":
98 print "Unable to find ../../../build.mak. Please build pjsip first"
99 sys.exit(1)
100 G_INUNIX = True
101else:
102 if sys.platform.lower().find("win32")!=-1 or sys.platform.lower().find("microsoft")!=-1:
103 G_INUNIX = False
104 else:
105 G_INUNIX = True
106
107
108G_EXE = G_EXE.rstrip("\n\r \t")
109
110###################################
111# Poor man's 'expect'-like class
112class Expect:
113 proc = None
114 echo = False
115 trace_enabled = False
116 name = ""
117 inst_param = None
118 rh = re.compile(const.DESTROYED)
119 ra = re.compile(const.ASSERT, re.I)
120 rr = re.compile(const.STDOUT_REFRESH)
121 t0 = time.time()
122 def __init__(self, inst_param):
123 self.inst_param = inst_param
124 self.name = inst_param.name
125 self.echo = inst_param.echo_enabled
126 self.trace_enabled = inst_param.trace_enabled
127 fullcmd = G_EXE + " " + inst_param.arg + " --stdout-refresh=5 --stdout-refresh-text=" + const.STDOUT_REFRESH
128 if not inst_param.enable_buffer:
129 fullcmd = fullcmd + " --stdout-no-buf"
130 self.trace("Popen " + fullcmd)
131 self.proc = subprocess.Popen(fullcmd, shell=G_INUNIX, bufsize=0, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=False)
132 def send(self, cmd):
133 self.trace("send " + cmd)
134 self.proc.stdin.writelines(cmd + "\n")
135 self.proc.stdin.flush()
136 def expect(self, pattern, raise_on_error=True, title=""):
137 self.trace("expect " + pattern)
138 r = re.compile(pattern, re.I)
139 refresh_cnt = 0
140 while True:
141 line = self.proc.stdout.readline()
142 if line == "":
143 raise inc.TestError(self.name + ": Premature EOF")
144 # Print the line if echo is ON
145 if self.echo:
146 print self.name + ": " + line.rstrip()
147 # Trap assertion error
148 if self.ra.search(line) != None:
149 if raise_on_error:
150 raise inc.TestError(self.name + ": " + line)
151 else:
152 return None
153 # Count stdout refresh text.
154 if self.rr.search(line) != None:
155 refresh_cnt = refresh_cnt+1
156 if refresh_cnt >= 6:
157 self.trace("Timed-out!")
158 if raise_on_error:
159 raise inc.TestError(self.name + " " + title + ": Timeout expecting pattern: \"" + pattern + "\"")
160 else:
161 return None # timeout
162 # Search for expected text
163 if r.search(line) != None:
164 return line
165
166 def sync_stdout(self):
167 self.trace("sync_stdout")
168 cmd = "echo 1" + str(random.randint(1000,9999))
169 self.send(cmd)
170 self.expect(cmd)
171
172 def wait(self):
173 self.trace("wait")
174 self.proc.communicate()
175
176 def trace(self, s):
177 if self.trace_enabled:
178 now = time.time()
179 fmt = self.name + ": " + "================== " + s + " ==================" + " [at t=%(time)03d]"
180 print fmt % {'time':int(now - self.t0)}
181
182#########################
183# Error handling
184def handle_error(errmsg, t, close_processes = True):
185 print "====== Caught error: " + errmsg + " ======"
186 if (close_processes):
187 time.sleep(1)
188 for p in t.process:
189 # Protect against 'Broken pipe' exception
190 try:
191 p.send("q")
192 p.send("q")
193 except:
194 pass
195 is_err = False
196 try:
197 ret = p.expect(const.DESTROYED, False)
198 if not ret:
199 is_err = True
200 except:
201 is_err = True
202 if is_err:
203 if sys.hexversion >= 0x02060000:
204 p.proc.terminate()
205 else:
206 p.wait()
207 else:
208 p.wait()
209 print "Test completed with error: " + errmsg
210 sys.exit(1)
211
212
213#########################
214# MAIN
215
216# Import the test script
217script = imp.load_source("script", inc.ARGS[0])
218
219# Init random seed
220random.seed()
221
222# Validate
223if script.test == None:
224 print "Error: no test defined"
225 sys.exit(1)
226
227if script.test.skip:
228 print "Test " + script.test.title + " is skipped"
229 sys.exit(0)
230
231if len(script.test.inst_params) == 0:
232 print "Error: test doesn't contain pjsua run descriptions"
233 sys.exit(1)
234
235# Instantiate pjsuas
236print "====== Running " + script.test.title + " ======"
237print "Using " + G_EXE + " as pjsua executable"
238
239for inst_param in script.test.inst_params:
240 try:
241 # Create pjsua's Expect instance from the param
242 p = Expect(inst_param)
243 # Wait until registration completes
244 if inst_param.have_reg:
245 p.expect(inst_param.uri+".*registration success")
246 # Synchronize stdout
247 p.send("")
248 p.expect(const.PROMPT)
249 p.send("echo 1")
250 p.send("echo 1")
251 p.expect("echo 1")
252 # add running instance
253 script.test.process.append(p)
254
255 except inc.TestError, e:
256 handle_error(e.desc, script.test)
257
258# Run the test function
259if script.test.test_func != None:
260 try:
261 script.test.test_func(script.test)
262 except inc.TestError, e:
263 handle_error(e.desc, script.test)
264
265# Shutdown all instances
266time.sleep(2)
267for p in script.test.process:
268 # Unregister if we have_reg to make sure that next tests
269 # won't wail
270 if p.inst_param.have_reg:
271 p.send("ru")
272 p.expect(p.inst_param.uri+".*unregistration success")
273 p.send("q")
274 p.send("q")
275 time.sleep(0.5)
276 p.expect(const.DESTROYED, False)
277 p.wait()
278
279# Run the post test function
280if script.test.post_func != None:
281 try:
282 script.test.post_func(script.test)
283 except inc.TestError, e:
284 handle_error(e.desc, script.test, False)
285
286# Done
287print "Test " + script.test.title + " completed successfully"
288sys.exit(0)
289