Index: pjproject/trunk/pjsip-apps/src/test-pjsua/inc_cfg.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/inc_cfg.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/inc_cfg.py	(revision 2078)
@@ -1,9 +1,12 @@
 # $Id$
 import random
-from config_site import *
 
 DEFAULT_ECHO = True
 DEFAULT_TRACE = True
 DEFAULT_START_SIP_PORT = 50000
+
+# Shared vars
+ARGS = []		# arguments containing script module & config
+HAS_SND_DEV = 1		# specify 1 if system has sound device and prefer to use sound device in the tests
 
 # Individual pjsua instance configuration class
@@ -76,5 +79,4 @@
 	test_func = None
 	post_func = None
-	user_data = None
 	def __init__(	self, 
 			title, 		# Test title
@@ -82,6 +84,5 @@
 			func=None,
 			skip=False,
-			post_func=None,
-			user_data=None):
+			post_func=None):
 		self.title = title
 		self.inst_params = inst_params
@@ -89,5 +90,4 @@
 		self.test_func = func
 		self.post_func = post_func
-		self.user_data = user_data
 
 
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_call.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_call.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_call.py	(revision 2078)
@@ -7,5 +7,5 @@
 
 # Load configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 # Check media flow between ua1 and ua2
@@ -21,5 +21,5 @@
 
 # Test body function
-def test_func(t, user_data):
+def test_func(t):
 	callee = t.process[0]
 	caller = t.process[1]
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_media_playrec.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_media_playrec.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_media_playrec.py	(revision 2078)
@@ -15,7 +15,8 @@
 import subprocess
 import inc_const as const
+from inc_cfg import *
 
 # Load configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 # WAV similarity calculator
@@ -25,11 +26,13 @@
 COMPARE_THRESHOLD = 2
 
-# UserData
-class mod_media_playrec_user_data:
-	input_filename = ""
-	output_filename = ""
+# COMPARE params
+input_filename	= ""			# Input filename
+output_filename = ""			# Output filename
 
 # Test body function
 def test_func(t, ud):
+	global input_filename
+	global output_filename
+
 	endpt = t.process[0]
 	
@@ -68,4 +71,7 @@
 # Post body function
 def post_func(t, ud):
+	global input_filename
+	global output_filename
+
 	endpt = t.process[0]
 
@@ -94,3 +100,2 @@
 test.test_func = test_func
 test.post_func = post_func
-test.user_data = mod_media_playrec_user_data()
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pesq.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pesq.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pesq.py	(revision 2078)
@@ -22,22 +22,21 @@
 
 # Load configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 # PESQ configs
-# PESQ_THRESHOLD specifies the minimum acceptable PESQ MOS value, so test can be declared successful
-PESQ = "tools/pesq.exe"
-PESQ_DEFAULT_THRESHOLD = 3.4
+PESQ = "tools/pesq.exe"			# PESQ executable path
+PESQ_DEFAULT_THRESHOLD = 3.4		# Default minimum acceptable PESQ MOS value
 
-# UserData
-class mod_pesq_user_data:
-	# Sample rate option for PESQ
-	pesq_sample_rate_opt = ""
-	# Input/Reference filename
-	input_filename = ""
-	# Output/Degraded filename
-	output_filename = ""
+# PESQ params
+pesq_sample_rate_opt = ""		# Sample rate option for PESQ
+input_filename	= ""			# Input/Reference filename
+output_filename = ""			# Output/Degraded filename
+
 
 # Test body function
-def test_func(t, user_data):
+def test_func(t):
+	global pesq_sample_rate_opt
+	global input_filename
+	global output_filename
 
 	ua1 = t.process[0]
@@ -45,11 +44,11 @@
 
 	# Get input file name
-	user_data.input_filename = re.compile(const.MEDIA_PLAY_FILE).search(ua1.inst_param.arg).group(1)
+	input_filename = re.compile(const.MEDIA_PLAY_FILE).search(ua1.inst_param.arg).group(1)
 
 	# Get output file name
-	user_data.output_filename = re.compile(const.MEDIA_REC_FILE).search(ua2.inst_param.arg).group(1)
+	output_filename = re.compile(const.MEDIA_REC_FILE).search(ua2.inst_param.arg).group(1)
 
 	# Get WAV input length, in seconds
-	fin = wave.open(user_data.input_filename, "r")
+	fin = wave.open(input_filename, "r")
 	if fin == None:
 		raise TestError("Failed opening input WAV file")
@@ -60,5 +59,5 @@
 
 	# Get clock rate of the output
-	mo_clock_rate = re.compile("\.(\d+)\.wav").search(user_data.output_filename)
+	mo_clock_rate = re.compile("\.(\d+)\.wav").search(output_filename)
 	if (mo_clock_rate==None):
 		raise TestError("Cannot compare input & output, incorrect output filename format")
@@ -73,10 +72,8 @@
 	# (PESQ evaluates only files whose same clock rate & channel count)
 	if channel_count == 2:
-	    if re.search("\.\d+\.\d+\.wav", user_data.input_filename) != None:
-		    user_data.input_filename = re.sub("\.\d+\.\d+\.wav", 
-						      "." + str(channel_count) + "."+clock_rate+".wav", user_data.input_filename)
+	    if re.search("\.\d+\.\d+\.wav", input_filename) != None:
+		    input_filename = re.sub("\.\d+\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
 	    else:
-		    user_data.input_filename = re.sub("\.\d+\.wav", 
-						      "." + str(channel_count) + "."+clock_rate+".wav", user_data.input_filename)
+		    input_filename = re.sub("\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
 
 	if (clock_rate != "8") & (clock_rate != "16"):
@@ -84,5 +81,5 @@
 
 	# Get conference clock rate of UA2 for PESQ sample rate option
-	user_data.pesq_sample_rate_opt = "+" + clock_rate + "000"
+	pesq_sample_rate_opt = "+" + clock_rate + "000"
 
 	# UA1 making call
@@ -114,9 +111,13 @@
 
 # Post body function
-def post_func(t, user_data):
+def post_func(t):
+	global pesq_sample_rate_opt
+	global input_filename
+	global output_filename
+
 	endpt = t.process[0]
 
 	# Execute PESQ
-	fullcmd = PESQ + " " + user_data.pesq_sample_rate_opt + " " + user_data.input_filename + " " + user_data.output_filename
+	fullcmd = PESQ + " " + pesq_sample_rate_opt + " " + input_filename + " " + output_filename
 	endpt.trace("Popen " + fullcmd)
 	pesq_proc = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, universal_newlines=True)
@@ -147,4 +148,3 @@
 test.test_func = test_func
 test.post_func = post_func
-test.user_data = mod_pesq_user_data()
 
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pres.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pres.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_pres.py	(revision 2078)
@@ -4,11 +4,12 @@
 import sys
 import inc_const as const
+from inc_cfg import *
 
 # Load configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 
 # Test body function
-def test_func(t, user_data):
+def test_func(t):
 	u1 = t.process[0]
 	uri1 = cfg_file.test_param.inst_params[0].uri
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_run.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_run.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_run.py	(revision 2078)
@@ -1,9 +1,10 @@
-# $Id:$
+# $Id$
 import imp
 import sys
 
+from inc_cfg import *
 
 # Read configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 # Here where it all comes together
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/mod_sendto.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/mod_sendto.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/mod_sendto.py	(revision 2078)
@@ -8,8 +8,8 @@
 
 # Read configuration
-cfg_file = imp.load_source("cfg_file", sys.argv[2])
+cfg_file = imp.load_source("cfg_file", ARGS[1])
 
 # Test body function
-def test_func(t, userdata):
+def test_func(t):
 	pjsua = t.process[0]
 	# Create dialog
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/run.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/run.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/run.py	(revision 2078)
@@ -7,37 +7,85 @@
 import random
 import time
+import getopt
 
 import inc_const as const
-from inc_cfg import *
+import inc_cfg as inc
+
+# Vars
+G_EXE = ""		# pjsua executable path
+G_INUNIX = False	# flags that test is running in Unix
+
+
+# Usage string
+usage = \
+"""
+run.py - Automated test driver
+
+Usage:
+	run.py [options] MODULE CONFIG
+Options:
+	--exe, -e		pjsua executable path
+	--null-audio, -n	use null audio
+Sample:
+	run.py -n mod_run.py scripts-run/100_simple.py
+"""
+
+# Parse arguments
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "hne:", ["help", "null-audio", "exe="])
+except getopt.GetoptError, err:
+    print str(err)
+    print usage
+    sys.exit(2)
+for o, a in opts:
+    if o in ("-h", "--help"):
+	print usage
+	sys.exit()
+    elif o in ("-n", "--null-audio"):
+        inc.HAS_SND_DEV = 0
+    elif o in ("-e", "--exe"):
+        G_EXE = a
+    else:
+        print "Unknown options"
+	sys.exit(2)
+
+if len(args) != 2:
+	print "Invalid arguments"
+	print usage
+	sys.exit(2)
+
+# Set global ARGS to be used by modules
+inc.ARGS = args
 
 # Get the pjsua executable name
-if sys.platform.find("win32")!=-1:
-    e = "../../bin/pjsua_vc6d.exe"
-    st1 = os.stat(e)
-    if st1 != None:
-	G_EXE = e
-    e = "../../bin/pjsua_vc6d.exe"
-    st2 = os.stat(e)
-    if st2 != None and st2.st_mtime > st1.st_mtime:
-	G_EXE = e
-	st1 = st2
-    if G_EXE=="":
-	print "Unable to find valid pjsua. Please build pjsip first"
-	sys.exit(1)
-    G_INUNIX = False
-else:
-    f = open("../../../build.mak", "r")
-    while True:
-	line = f.readline()
-	if not line:
-	    break
-	if line.find("TARGET_NAME")!=-1:
-	    print line
-	    G_EXE="../../bin/pjsua-" + line.split(":= ")[1]
-	    break
-    if G_EXE=="":
-	print "Unable to find ../../../build.mak. Please build pjsip first"
-	sys.exit(1)
-    G_INUNIX = True
+if G_EXE == "":
+	if sys.platform.find("win32")!=-1:
+	    e = "../../bin/pjsua_vc6d.exe"
+	    st1 = os.stat(e)
+	    if st1 != None:
+		G_EXE = e
+	    e = "../../bin/pjsua_vc6d.exe"
+	    st2 = os.stat(e)
+	    if st2 != None and st2.st_mtime > st1.st_mtime:
+		G_EXE = e
+		st1 = st2
+	    if G_EXE=="":
+		print "Unable to find valid pjsua. Please build pjsip first"
+		sys.exit(1)
+	    G_INUNIX = False
+	else:
+	    f = open("../../../build.mak", "r")
+	    while True:
+		line = f.readline()
+		if not line:
+		    break
+		if line.find("TARGET_NAME")!=-1:
+		    print line
+		    G_EXE="../../bin/pjsua-" + line.split(":= ")[1]
+		    break
+	    if G_EXE=="":
+		print "Unable to find ../../../build.mak. Please build pjsip first"
+		sys.exit(1)
+	    G_INUNIX = True
 
 
@@ -75,5 +123,5 @@
 			line = self.proc.stdout.readline()
 		  	if line == "":
-				raise TestError(self.name + ": Premature EOF")
+				raise inc.TestError(self.name + ": Premature EOF")
 			# Print the line if echo is ON
 			if self.echo:
@@ -82,5 +130,5 @@
 			if self.ra.search(line) != None:
 				if raise_on_error:
-					raise TestError(self.name + ": " + line)
+					raise inc.TestError(self.name + ": " + line)
 				else:
 					return None
@@ -91,5 +139,5 @@
 					self.trace("Timed-out!")
 					if raise_on_error:
-						raise TestError(self.name + " " + title + ": Timeout expecting pattern: \"" + pattern + "\"")
+						raise inc.TestError(self.name + " " + title + ": Timeout expecting pattern: \"" + pattern + "\"")
 					else:
 						return None		# timeout
@@ -131,13 +179,6 @@
 # MAIN	
 
-if len(sys.argv)!=3:
-	print "Usage: run.py MODULE CONFIG"
-	print "Sample:"
-	print "  run.py mod_run.py scripts-run/100_simple.py"
-	sys.exit(1)
-
-
 # Import the test script
-script = imp.load_source("script", sys.argv[1])  
+script = imp.load_source("script", inc.ARGS[0])  
 
 # Init random seed
@@ -177,5 +218,5 @@
 		script.test.process.append(p)
 
-	except TestError, e:
+	except inc.TestError, e:
 		handle_error(e.desc, script.test)
 
@@ -183,6 +224,6 @@
 if script.test.test_func != None:
 	try:
-		script.test.test_func(script.test, script.test.user_data)
-	except TestError, e:
+		script.test.test_func(script.test)
+	except inc.TestError, e:
 		handle_error(e.desc, script.test)
 
@@ -204,6 +245,6 @@
 if script.test.post_func != None:
 	try:
-		script.test.post_func(script.test, script.test.user_data)
-	except TestError, e:
+		script.test.post_func(script.test)
+	except inc.TestError, e:
 		handle_error(e.desc, script.test, False)
 
Index: pjproject/trunk/pjsip-apps/src/test-pjsua/runall.py
===================================================================
--- pjproject/trunk/pjsip-apps/src/test-pjsua/runall.py	(revision 2077)
+++ pjproject/trunk/pjsip-apps/src/test-pjsua/runall.py	(revision 2078)
@@ -3,4 +3,7 @@
 import sys
 import time
+import re
+import shutil
+
 
 # Usage:
@@ -54,13 +57,38 @@
 resume_script=""
 if len(sys.argv) > 1:
-    if sys.argv[1][0]=='-' or sys.argv[1][0]=='/':
+    if sys.argv[1]=='-r' or sys.argv[1]=='--resume':
+	resume_script=sys.argv[2]
+    if sys.argv[1]=='/h' or sys.argv[1]=='-h' or sys.argv[1]=='--help' or sys.argv[1]=='/help':
         print "Usage:"
-	print "  runall.py [RESUME]"
-	print "where"
-	print "  RESUME is string/substring to specify where to resume tests."
-	print "  If this argument is omited, tests will start from the beginning."
+	print "  runall.py [OPTIONS] [run.py OPTIONS]"
+	print "Options:"
+	print "  --resume,-r RESUME"
+	print "  where"
+	print "      RESUME is string/substring to specify where to resume tests."
+	print "      If this argument is omited, tests will start from the beginning."
 	sys.exit(0)
-    resume_script=sys.argv[1]
 
+
+# Generate arguments for run.py
+argv = sys.argv
+argv_to_skip = 1
+if resume_script != "":
+    argv_to_skip += 2
+argv_st = ""
+for a in argv:
+    if argv_to_skip > 0:
+        argv_to_skip -= 1
+    else:
+        argv_st += a + " "
+
+
+# Init vars
+failed_cnt = 0
+
+# Create "logs" directory
+try:
+    os.mkdir("logs")
+except:
+    print
 
 # Now run the tests
@@ -70,5 +98,5 @@
 	    continue
 	resume_script=""
-	cmdline = "python run.py " + t
+	cmdline = "python run.py " + argv_st + t
 	t0 = time.time()
 	print "Running " + cmdline + "...",
@@ -78,9 +106,16 @@
 		dur = int(t1 - t0)
 		print " failed!! [" + str(dur) + "s]"
-		print "Please see 'output.log' for the test log."
-		sys.exit(1)
+		logname = re.search(".*\s+(.*)", t).group(1)
+		logname = re.sub("[\\\/]", "_", logname)
+		logname = "logs/" + logname
+		shutil.move("output.log", logname)
+		print "Please see '" + logname + "' for the test log."
 	else:
 		dur = int(t1 - t0)
 		print " ok [" + str(dur) + "s]"
 
-print "All tests completed successfully"
+if failed_cnt == 0:
+	print "All tests completed successfully"
+else:
+	print "Tests completed, with " +  str(failed_cnt) + " test(s) failed"
+
