updated to use threads and fixed a few bugs
authorJoel Maher <joel.maher@gmail.com>
Mon, 12 Oct 2009 21:40:23 -0400
changeset 10 a24ca1062300e71e2d0633ab9eedb3f88d91053f
parent 9 b709ca8c2873c09915f06cea6cf7bc5cabd7b0c3
child 11 3850480016d08192460891efdfe532e73885a227
push id11
push userjmaher@mozilla.com
push dateTue, 13 Oct 2009 01:40:34 +0000
updated to use threads and fixed a few bugs
ce.py
maemkit.py
mochidriver.py
--- a/ce.py
+++ b/ce.py
@@ -1,14 +1,49 @@
 import socket
 import time, datetime
 import os
 import re
+from threading import Thread
 
 
+class myProc(Thread):
+  def __init__(self, hostip, hostport, cmd, new_line = True, sleeptime = 0):
+    self.cmdline = cmd
+    self.newline = new_line
+    self.sleep = sleeptime
+    self.host = hostip
+    self.port = hostport
+    Thread.__init__(self)
+
+  def run(self):
+    promptre =re.compile('.*\$\>.$')
+    data = ""
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.connect((self.host, self.port))
+    s.recv(1024)
+    for cmd in self.cmdline:
+      if (cmd == 'quit'): break
+      if self.newline: cmd += '\r\n'
+      s.send(cmd)
+      time.sleep(int(self.sleep))
+
+      found = False
+      while (found == False):
+        temp = s.recv(1024)
+        lines = temp.split('\n')
+        for line in lines:
+          if (promptre.match(line)):
+            found = True
+          data += temp
+
+    s.send('quit\r\n')
+    s.close()
+    return data
+
 class TestAgentCE:
   host = '192.168.1.110'
   port = 27020
   debug = 2
 
 
   def sendCMD(self, cmdline, newline = True, sleep = 0):
     promptre = re.compile('.*\$\>.$')
@@ -123,17 +158,17 @@ class TestAgentCE:
     self.sendCMD(['rm ' + filename, 'quit'])
   
   
   #does a recursive delete of directory on the device: rm -Rf remoteDir
   def removeDir(self, remoteDir):
     filelist = self.listFiles(remoteDir)
   
     #TODO: logic is way too simple and basic, make more robust
-    isFile = re.compile('^([a-zA-Z0-9_\- ]+)\.([a-zA-Z0-9]+)$')
+    isFile = re.compile('^([a-zA-Z0-9_\-\. ]+)\.([a-zA-Z0-9]+)$')
     for f in filelist:
       if (isFile.match(f)):
         self.removeFile(remoteDir + "\\" + f)
       else:
         self.removeDir(remoteDir + "\\" + f)
 
     if (self.debug >= 3): print "removing: " + remoteDir
     self.sendCMD(['rmdr ' + remoteDir, 'rm ' + remoteDir, 'quit'], sleep = 1)
@@ -142,24 +177,34 @@ class TestAgentCE:
   def getProcessList(self):
     data = self.sendCMD(['ps', 'quit'], sleep = 3)
     retVal = self.stripPrompt(data)
     lines = retVal.split('\n')
     files = []
     for line in lines:
       if (line.strip() != ''):
         pidproc = line.strip().split(' ')
-        files += [[pidproc[0], pidproc[1]]]
+        if (len(pidproc) == 2):
+          files += [[pidproc[0], pidproc[1]]]
       
     return files
 
 
+  def getMemInfo(self):
+    data = self.sendCMD(['mems', 'quit'])
+    retVal = self.stripPrompt(data)
+    #TODO: this is hardcoded for now
+    fhandle = open("memlog.txt", 'a')
+    fhandle.write("\n")
+    fhandle.write(retVal)
+    fhandle.close()
+
   def fireProcess(self, appname):
-    self.sendCMD(['fire ' + appname, 'quit'])
-  
+    self.process = myProc(self.host, self.port, ['exec ' + appname, 'quit'])
+    self.process.start()  
 
   #iterates process list and returns pid if exists, otherwise ''
   def processExist(self, appname):
     pid = ''
   
     pieces = appname.split('\\')
     app = pieces[-1]
     procre = re.compile('.*' + app + '.*')
@@ -201,16 +246,16 @@ class TestAgentCE:
   def getDirectory(self, remoteDir, localDir):
     if (self.debug >= 2): print "getting files in '" + remoteDir + "'"
     filelist = self.listFiles(remoteDir)
     if (self.debug >= 3): print filelist
     if not os.path.exists(localDir):
       os.makedirs(localDir)
   
     #TODO: is this a comprehensive file regex?
-    isFile = re.compile('^([a-zA-Z0-9_\- ]+)\.([a-zA-Z0-9]+)$')
+    isFile = re.compile('^([a-zA-Z0-9_\-\. ]+)\.([a-zA-Z0-9]+)$')
     for f in filelist:
       if (isFile.match(f)):
         self.getFile(remoteDir + "\\" + f, os.path.join(localDir, f))
       else:
         self.getDirectory(remoteDir + "\\" + f, os.path.join(localDir, f))
 
   
--- a/maemkit.py
+++ b/maemkit.py
@@ -1,314 +1,317 @@
-import os, sys, re, copy, shutil
-import ConfigParser, optparse
-import datetime, time, subprocess, commands
-import ce
-
-class MaemKit(object):
-  config_options = {}
-  default_options = {}
-  testtype = "mochitest"
-  testdriver = ""
-  config_file = "maemkit.cfg"
-  dirtype = "/"
-  debug = 0
-  singletest = True
-  testagent = ce.TestAgentCE()
-
-  testtypes = ["mochitest","chrome","reftest","crashtest","xpcshell"]
-
-  def __init__(self):
-    self.defaultOptions()
-    self.getConfig()
-    self.getCli()
-    ostype = self.config_options[self.testtype]["ostype"]
-    if (ostype == "linux"): self.dirtype = "/"
-    if (ostype == "macos"): self.dirtype = "/"
-    if (ostype == "windows"): self.dirtype = "\\\\"
-    if (ostype == "wince"): self.dirtype = "\\"
-
-  def defaultOptions(self):
-    self.default_options["debug"] = 0
-    self.default_options["ostype"] = "linux"
-    self.default_options["close-when-done"] = False
-    self.default_options["utility-path"] = "."
-    self.default_options["xre-path"] = "."
-    self.default_options["certificate-path"] = "certs"
-    self.default_options["log-file"] = ""
-    self.default_options["autorun"] = False
-    self.default_options["console-level"] = ""
-    self.default_options["file-level"] = "INFO"
-    self.default_options["chrome"] = False
-    self.default_options["browser-chrome"] = False
-    self.default_options["a11y"] = False
-    self.default_options["setenv"] = []
-    self.default_options["browser-arg"] = []
-    self.default_options["leak-threshold"] = []
-    self.default_options["fatal-assertions"] = False
-    self.default_options["test-path"] = ""
-
-    self.default_options["split-directories"] = ""
-    self.default_options["split-percentage"] = 20   
-
-    self.default_options["appname"] = "fennec"
-    self.default_options["total-clients"] = 1
-    self.default_options["client-number"] = 1
-    self.default_options["testroot"] = "."
-    self.default_options["logdir"] = "logs"
-
-    for type in self.testtypes:
-      self.config_options[type] = {}
-      self.config_options[type].update(self.default_options)
-
-  def mkLogDir(self, logdir):
-    logdir = os.path.normpath(os.path.abspath(logdir))
-    if (os.path.exists(logdir)):
-      self.rmdir(logdir + ".bak")
-      self.move(logdir, logdir + ".bak")
-    self.mkdir(logdir)
-
-
-  def getConfigFile(self, input_section):
-    config = ConfigParser.ConfigParser()
-    config.read(self.config_file)
-    temp_options = {}
-    if (config.has_section(input_section)):
-      for item in config.items(input_section):
-        temp_options[item[0]] = item[1]
-
-    return temp_options
-
-  def getConfig(self):
-    general = self.getConfigFile("general")
-    for section in self.testtypes:
-      self.config_options[section].update(general)
-      self.config_options[section].update(self.getConfigFile(section))
-
-  def getCli(self):
-    parser = optparse.OptionParser()
-
-    parser.add_option("--testtype", "-t",
-                       default="mochitest", action="store", type="string", dest="testtype",
-                       help="Which test to run: mochitest, chrome, reftest, crashtest")
-
-    parser.add_option("--client-number",
-                       default="-1", action="store", type="int", dest="clientnumber",
-                       help="Which client you are in a parallel run. Range is 0:total-clients")
-
-    parser.add_option("--total-clients",
-                       default="-1", action="store", type="int", dest="totalclients",
-                       help="Number of clients in your testpass in parallel mode.  Used with client-number")
-
-    #TODO: add other cli options;  need to figure out which ones we really care about
-    (cli_options, args) = parser.parse_args()
-    self.testtype = cli_options.testtype
-    if (cli_options.clientnumber != -1): self.config_options[self.testtype]["client-number"] = cli_options.clientnumber
-    if (cli_options.totalclients != -1): self.config_options[self.testtype]["total-clients"] = cli_options.totalclients
-
-  def shellCommand(self, cmd):
-    return commands.getoutput(cmd)
-
-  def addCommand(self, newcmd, retry=False, timeout=60):
-    testroot = os.path.normpath(self.options["testroot"])
-    xrepath = os.path.normpath(self.options["xre-path"])
-    topsrc = testroot
-    myenv = copy.copy(os.environ)
-    myenv["NATIVE_TOPSRCDIR"] = testroot
-    myenv["TOPSRCDIR"] = testroot
-    myenv["MOZILLA_FIVE_HOME"] = xrepath
-    myenv["LD_LIBRARY_PATH"] = xrepath
-
-    parts = []
-    for cmd in newcmd.split(" "):
-      if (cmd.strip() != ""):
-        parts.append(cmd.strip())
-    if (self.debug >= 0): print " ".join(parts)
-    sub_proc = subprocess.Popen(parts, bufsize=-1, env=myenv, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
-    starttime = datetime.datetime.now()
-
-    retVal = ""
-    std_out = ""
-    std_err = ""
-
-    while (datetime.datetime.now() - starttime < datetime.timedelta(minutes=timeout)):
-      if (retry == True):
-        #TODO: consider putting this in a thread so it isn't blocking
-        std_out += sub_proc.stdout.read();
-        std_err += sub_proc.stderr.read();
-      if (sub_proc.poll() is not None):
-        std_out += sub_proc.stdout.read();
-        std_err += sub_proc.stderr.read();
-        break
-      time.sleep(1)
-    retVal = std_out + "\n" + std_err
-
-    if (datetime.datetime.now() - starttime >= datetime.timedelta(minutes=timeout)):
-      retVal = "*** TIMEOUT ***"
-    return retVal
-
-  def addCommandWinCE(self, newcmd, retry=False, timeout=60):
-      parts = []
-      #TODO: hardcoded for now
-      appname = "fennec.exe"
-      for cmd in newcmd.split(" "):
-          if (cmd.strip() != ""):
-              parts.append(cmd.strip())
-      if (self.debug >= 0): print " ".join(parts)
-      self.testagent.fireProcess(" ".join(parts))
-      starttime = datetime.datetime.now()
-
+import os, sys, re, copy, shutil
+import ConfigParser, optparse
+import datetime, time, subprocess, commands
+import ce
+
+class MaemKit(object):
+  config_options = {}
+  default_options = {}
+  testtype = "mochitest"
+  testdriver = ""
+  config_file = "maemkit.cfg"
+  dirtype = "/"
+  debug = 0
+  singletest = True
+  testagent = ce.TestAgentCE()
+
+  testtypes = ["mochitest","chrome","reftest","crashtest","xpcshell"]
+
+  def __init__(self):
+    self.defaultOptions()
+    self.getConfig()
+    self.getCli()
+    ostype = self.config_options[self.testtype]["ostype"]
+    if (ostype == "linux"): self.dirtype = "/"
+    if (ostype == "macos"): self.dirtype = "/"
+    if (ostype == "windows"): self.dirtype = "\\\\"
+    if (ostype == "wince"): self.dirtype = "\\"
+
+  def defaultOptions(self):
+    self.default_options["debug"] = 0
+    self.default_options["ostype"] = "linux"
+    self.default_options["close-when-done"] = False
+    self.default_options["utility-path"] = "."
+    self.default_options["xre-path"] = "."
+    self.default_options["certificate-path"] = "certs"
+    self.default_options["log-file"] = ""
+    self.default_options["autorun"] = False
+    self.default_options["console-level"] = ""
+    self.default_options["file-level"] = "INFO"
+    self.default_options["chrome"] = False
+    self.default_options["browser-chrome"] = False
+    self.default_options["a11y"] = False
+    self.default_options["setenv"] = []
+    self.default_options["browser-arg"] = []
+    self.default_options["leak-threshold"] = []
+    self.default_options["fatal-assertions"] = False
+    self.default_options["test-path"] = ""
+
+    self.default_options["split-directories"] = ""
+    self.default_options["split-percentage"] = 20   
+
+    self.default_options["appname"] = "fennec"
+    self.default_options["total-clients"] = 1
+    self.default_options["client-number"] = 1
+    self.default_options["testroot"] = "."
+    self.default_options["logdir"] = "logs"
+
+    for type in self.testtypes:
+      self.config_options[type] = {}
+      self.config_options[type].update(self.default_options)
+
+  def mkLogDir(self, logdir):
+    logdir = os.path.normpath(os.path.abspath(logdir))
+    if (os.path.exists(logdir)):
+      self.rmdir(logdir + ".bak")
+      self.move(logdir, logdir + ".bak")
+    self.mkdir(logdir)
+
+
+  def getConfigFile(self, input_section):
+    config = ConfigParser.ConfigParser()
+    config.read(self.config_file)
+    temp_options = {}
+    if (config.has_section(input_section)):
+      for item in config.items(input_section):
+        temp_options[item[0]] = item[1]
+
+    return temp_options
+
+  def getConfig(self):
+    general = self.getConfigFile("general")
+    for section in self.testtypes:
+      self.config_options[section].update(general)
+      self.config_options[section].update(self.getConfigFile(section))
+
+  def getCli(self):
+    parser = optparse.OptionParser()
+
+    parser.add_option("--testtype", "-t",
+                       default="mochitest", action="store", type="string", dest="testtype",
+                       help="Which test to run: mochitest, chrome, reftest, crashtest")
+
+    parser.add_option("--client-number",
+                       default="-1", action="store", type="int", dest="clientnumber",
+                       help="Which client you are in a parallel run. Range is 0:total-clients")
+
+    parser.add_option("--total-clients",
+                       default="-1", action="store", type="int", dest="totalclients",
+                       help="Number of clients in your testpass in parallel mode.  Used with client-number")
+
+    #TODO: add other cli options;  need to figure out which ones we really care about
+    (cli_options, args) = parser.parse_args()
+    self.testtype = cli_options.testtype
+    if (cli_options.clientnumber != -1): self.config_options[self.testtype]["client-number"] = cli_options.clientnumber
+    if (cli_options.totalclients != -1): self.config_options[self.testtype]["total-clients"] = cli_options.totalclients
+
+  def shellCommand(self, cmd):
+    return commands.getoutput(cmd)
+
+  def addCommand(self, newcmd, retry=False, timeout=60):
+    testroot = os.path.normpath(self.options["testroot"])
+    xrepath = os.path.normpath(self.options["xre-path"])
+    topsrc = testroot
+    myenv = copy.copy(os.environ)
+    myenv["NATIVE_TOPSRCDIR"] = testroot
+    myenv["TOPSRCDIR"] = testroot
+    myenv["MOZILLA_FIVE_HOME"] = xrepath
+    myenv["LD_LIBRARY_PATH"] = xrepath
+
+    parts = []
+    for cmd in newcmd.split(" "):
+      if (cmd.strip() != ""):
+        parts.append(cmd.strip())
+    if (self.debug >= 0): print " ".join(parts)
+    sub_proc = subprocess.Popen(parts, bufsize=-1, env=myenv, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
+    starttime = datetime.datetime.now()
+
+    retVal = ""
+    std_out = ""
+    std_err = ""
+
+    while (datetime.datetime.now() - starttime < datetime.timedelta(minutes=timeout)):
+      if (retry == True):
+        #TODO: consider putting this in a thread so it isn't blocking
+        std_out += sub_proc.stdout.read();
+        std_err += sub_proc.stderr.read();
+      if (sub_proc.poll() is not None):
+        std_out += sub_proc.stdout.read();
+        std_err += sub_proc.stderr.read();
+        break
+      time.sleep(1)
+    retVal = std_out + "\n" + std_err
+
+    if (datetime.datetime.now() - starttime >= datetime.timedelta(minutes=timeout)):
+      retVal = "*** TIMEOUT ***"
+    return retVal
+
+  def addCommandWinCE(self, newcmd, retry=False, timeout=60):
+      parts = []
+      retVal = ""
+      #TODO: hardcoded for now
+      appname = "fennec.exe"
+      for cmd in newcmd.split(" "):
+          if (cmd.strip() != ""):
+              parts.append(cmd.strip())
+      if (self.debug >= 0): print " ".join(parts)
+      self.testagent.fireProcess(" ".join(parts))
+      starttime = datetime.datetime.now()
+
+      #allow for launching of browser
+      time.sleep(20)
       while (datetime.datetime.now() - starttime < datetime.timedelta(minutes=timeout)):
-        if (not self.testagent.processExist(appname)):
+        if (not self.testagent.process.isAlive()):
           break
       time.sleep(5)
     
       if (datetime.datetime.now() - starttime >= datetime.timedelta(minutes=timeout)):
         retVal = "*** TIMEOUT ***"
         self.testagent.killProcess(appname)
-
-      return retVal
-
-
-  #parse log files, tally results
-  #TODO: test on all types of logs
-  def stitchLogs(self):
-    p = 0
-    f = 0
-    t = 0
-    myre = re.compile('.*TEST\-([A-Z]+).*')
-
-    masterLog = open(os.path.normpath(self.options["log-file"]), "a")
-
-    for root, dirs, files in os.walk(os.path.normpath(self.options["logdir"])):
-      for logFile in files:
-
-        #parse each line, looking for TEST-PASS (pass), TEST-UNEXPECTED-FAIL (fail), TEST-KNOWN-FAIL (todo)
-        fLog = open(os.path.normpath(os.path.join(self.options["logdir"], logFile)), "r")
-        for line in fLog:
-          res = None
-          res = myre.match(line)
-          if (res):
-            if (res.group(1) == "PASS"): p = p + 1
-            if (res.group(1) == "UNEXPECTED"): f = f + 1
-            if (res.group(1) == "KNOWN"): t = t + 1
-            masterLog.write(line)
-
-    summarystr = "\n\n-------------------------\n"
-    summarystr += "INFO Passed: " + str(p) + "\n"
-    summarystr += "INFO Failed: " + str(f) + "\n"
-    summarystr += "INFO Todo: " + str(t) + "\n"
-    masterLog.write(summarystr)
-    masterLog.close()
-    print summarystr
-
-
-  def cleanup(self):
-    #TODO: figure out a method for killing process on wince (custom program)
-    #TODO: what if we are using firefox instead of fennec?
-    terminate = "killall"
-    if (self.dirtype == '\\\\'): terminate = "tskill"
-
-    try:
-      if (self.options["ostype"] == "wince"):
-        self.testagent.killProcess("fennec.exe")
-      else:
-        self.addCommand(terminate + " fennec")
-        self.addCommand(terminate + " ssltunnel")
-        self.addCommand(terminate + " xpcshell")
-    except:
-      if (self.debug >= 1): print "error in cleanup() command"
-      pass
-
-  def getLogFileName(self, aDir):
-    # returns the name of a log file from a directory
-    logPrefix = "log_"
-    logExtension = ".txt"
-    p = re.compile("[\/\\\\:]+")
-    logMiddle = p.sub('_', aDir)
-    logFileName = logPrefix + logMiddle + logExtension
-    return logFileName
-
-
-  #split a list of tests up to be run as parallel.
-  #requires: total-clients and client-number in order to
-  #calculate number of list items to return
-  def splitListParallel(self, list, options):
-    dirlist = []
-    for dir in list: dirlist.append(dir)
-
-    if (int(options["total-clients"]) > 1):
-      client_workload = []
-      dirs_per_client = abs(len(list) / int(options["total-clients"])) + 1
-
-      iter = -1
-      for dir in dirlist:
-        iter += 1
-        if (iter < (dirs_per_client * (int(options["client-number"]) - 1))): continue
-        if (iter > (dirs_per_client * int(options["client-number"]))): break
-        client_workload.append(dir)
-      return client_workload
-
-    # return self if we have just 1 client
-    return dirlist
-
-  def copytree(self, src, dst):
-    dest = os.path.normpath(dst)
-    source = os.path.normpath(src)
-
-    try:
-      if (os.path.isdir(source)):
-        shutil.copytree(source, dest)
-      else:
-        fname = os.path.basename(source)
-        self.mkdir(dest)
-        self.copyfile(source, os.path.join(dest, fname))
-    except:
-      if (self.debug > 1): print "exception in copytree: " + os.path.normpath(src) + " " + os.path.normpath(dst)
-      pass
-
-  def copyfile(self, src, dst):
-    dest = os.path.normpath(dst)
-    file = os.path.basename(os.path.normpath(src))
-    root = os.path.dirname(os.path.normpath(src))
-    if (file == "*"): file = ".*"
-    p = re.compile(file)
-
-    try:
-      files = [file for file in os.listdir(root) if p.match(file)]
-      for file in files:
-        if (os.path.isdir(os.path.join(root,file)) == False):
-          shutil.copy(os.path.join(root, file), dest)
-    except:
-      if (self.debug > 1): print "exception in copy: " + os.path.normpath(file) + " " + os.path.normpath(dst)
-      pass
-
-  def move(self, src, dst):
-    try:
-      dest = os.path.normpath(dst)
-      filename = os.path.basename(os.path.normpath(src))
-      root = os.path.dirname(os.path.normpath(src))
-      if (filename == "*"):
-        filename = ".*"
-      p = re.compile(filename)
-
-      filelist = os.listdir(root)
-      for file in filelist:
-        if p.match(file.strip()):
-          if os.path.isfile(os.path.join(root, file.strip())):
-            shutil.move(os.path.join(root, file.strip()), dest)
-    except:
-      if (self.debug > 1): print "exception in move: " + os.path.join(root, file) + " " + dest
-      pass
-
-  def mkdir(self, src):
-    try:
-      os.makedirs(os.path.normpath(src))
-    except:
-      if (self.debug > 1): print "exception in mkdir: " + os.path.normpath(src)
-      pass
-
-  def rmdir(self, src):
-    try:
-      shutil.rmtree(os.path.normpath(src))
-    except:
-      if (self.debug > 1): print "exception in rmdir: " + os.path.normpath(src)
-      pass
+
+      return retVal
+
+
+  #parse log files, tally results
+  #TODO: test on all types of logs
+  def stitchLogs(self):
+    p = 0
+    f = 0
+    t = 0
+    myre = re.compile('.*TEST\-([A-Z]+).*')
+
+    masterLog = open(os.path.normpath(self.options["log-file"]), "a")
+
+    for root, dirs, files in os.walk(os.path.normpath(self.options["logdir"])):
+      for logFile in files:
+
+        #parse each line, looking for TEST-PASS (pass), TEST-UNEXPECTED-FAIL (fail), TEST-KNOWN-FAIL (todo)
+        fLog = open(os.path.normpath(os.path.join(self.options["logdir"], logFile)), "r")
+        for line in fLog:
+          res = None
+          res = myre.match(line)
+          if (res):
+            if (res.group(1) == "PASS"): p = p + 1
+            if (res.group(1) == "UNEXPECTED"): f = f + 1
+            if (res.group(1) == "KNOWN"): t = t + 1
+            masterLog.write(line)
+
+    summarystr = "\n\n-------------------------\n"
+    summarystr += "INFO Passed: " + str(p) + "\n"
+    summarystr += "INFO Failed: " + str(f) + "\n"
+    summarystr += "INFO Todo: " + str(t) + "\n"
+    masterLog.write(summarystr)
+    masterLog.close()
+    print summarystr
+
+
+  def cleanup(self):
+    #TODO: figure out a method for killing process on wince (custom program)
+    #TODO: what if we are using firefox instead of fennec?
+    terminate = "killall"
+    if (self.dirtype == '\\\\'): terminate = "tskill"
+
+    try:
+      if (self.options["ostype"] == "wince"):
+        self.testagent.killProcess("fennec.exe")
+      else:
+        self.addCommand(terminate + " fennec")
+        self.addCommand(terminate + " ssltunnel")
+        self.addCommand(terminate + " xpcshell")
+    except:
+      if (self.debug >= 1): print "error in cleanup() command"
+      pass
+
+  def getLogFileName(self, aDir):
+    # returns the name of a log file from a directory
+    logPrefix = "log_"
+    logExtension = ".txt"
+    p = re.compile("[\/\\\\:]+")
+    logMiddle = p.sub('_', aDir)
+    logFileName = logPrefix + logMiddle + logExtension
+    return logFileName
+
+
+  #split a list of tests up to be run as parallel.
+  #requires: total-clients and client-number in order to
+  #calculate number of list items to return
+  def splitListParallel(self, list, options):
+    dirlist = []
+    for dir in list: dirlist.append(dir)
+
+    if (int(options["total-clients"]) > 1):
+      client_workload = []
+      dirs_per_client = abs(len(list) / int(options["total-clients"])) + 1
+
+      iter = -1
+      for dir in dirlist:
+        iter += 1
+        if (iter < (dirs_per_client * (int(options["client-number"]) - 1))): continue
+        if (iter > (dirs_per_client * int(options["client-number"]))): break
+        client_workload.append(dir)
+      return client_workload
+
+    # return self if we have just 1 client
+    return dirlist
+
+  def copytree(self, src, dst):
+    dest = os.path.normpath(dst)
+    source = os.path.normpath(src)
+
+    try:
+      if (os.path.isdir(source)):
+        shutil.copytree(source, dest)
+      else:
+        fname = os.path.basename(source)
+        self.mkdir(dest)
+        self.copyfile(source, os.path.join(dest, fname))
+    except:
+      if (self.debug > 1): print "exception in copytree: " + os.path.normpath(src) + " " + os.path.normpath(dst)
+      pass
+
+  def copyfile(self, src, dst):
+    dest = os.path.normpath(dst)
+    file = os.path.basename(os.path.normpath(src))
+    root = os.path.dirname(os.path.normpath(src))
+    if (file == "*"): file = ".*"
+    p = re.compile(file)
+
+    try:
+      files = [file for file in os.listdir(root) if p.match(file)]
+      for file in files:
+        if (os.path.isdir(os.path.join(root,file)) == False):
+          shutil.copy(os.path.join(root, file), dest)
+    except:
+      if (self.debug > 1): print "exception in copy: " + os.path.normpath(file) + " " + os.path.normpath(dst)
+      pass
+
+  def move(self, src, dst):
+    try:
+      dest = os.path.normpath(dst)
+      filename = os.path.basename(os.path.normpath(src))
+      root = os.path.dirname(os.path.normpath(src))
+      if (filename == "*"):
+        filename = ".*"
+      p = re.compile(filename)
+
+      filelist = os.listdir(root)
+      for file in filelist:
+        if p.match(file.strip()):
+          if os.path.isfile(os.path.join(root, file.strip())):
+            shutil.move(os.path.join(root, file.strip()), dest)
+    except:
+      if (self.debug > 1): print "exception in move: " + os.path.join(root, file) + " " + dest
+      pass
+
+  def mkdir(self, src):
+    try:
+      os.makedirs(os.path.normpath(src))
+    except:
+      if (self.debug > 1): print "exception in mkdir: " + os.path.normpath(src)
+      pass
+
+  def rmdir(self, src):
+    try:
+      shutil.rmtree(os.path.normpath(src))
+    except:
+      if (self.debug > 1): print "exception in rmdir: " + os.path.normpath(src)
+      pass
--- a/mochidriver.py
+++ b/mochidriver.py
@@ -1,10 +1,11 @@
 import os,re,maemkit
 import ce
+import time
 
 class MochiKit(maemkit.MaemKit):
   options = {}
   mochitestCommand = []
   directories = []
   testagent = ce.TestAgentCE()
 
   def __init__(self, mkit):
@@ -250,37 +251,38 @@ class MochiKit(maemkit.MaemKit):
       #TODO: figure out a method for utilizing all the other config/cli options available
       self.mochitestCommand = ["python " + os.path.normpath(os.path.join(self.options["testroot"], "runtests.py")) + " --autorun --close-when-done"]
       for option in ["utility-path","appname","xre-path","certificate-path"]:
           self.mochitestCommand.append("--" + option + "=" + os.path.normpath(self.options[option]))
       mCommand = " ".join(self.mochitestCommand)
 
       singletest = [self.options["appname"], '--environ:NO_EM_RESTART=1', '-no-remote', '-profile', os.path.join(self.options["remote-logdir"], self.options["profiledir"]) ]
       if (self.singletest == True):
-          self.setupRemoteProfile()
+#HACK: comment out for now to speed up on WinCE
+#          self.setupRemoteProfile()
           self.setupLocalWebserver()
 
       for directory in self.directories:
           if (self.singletest == True):
               cmd = "http://" + self.options["remote-webserver"] + "/tests/"
               cmd += directory.replace("\\", "/") + "?logFile="
               remoteLog = os.path.join(self.options["remote-logdir"], self.options["log-file"]).replace("\\", "%5c")
               cmd += remoteLog
 
-              self.testagent.removeFile(remoteLog)
-              if (self.addCommandWinCE(" ".join(singletest) + " " + cmd + " > \\tests\\fennec.txt", timeout=2) == "*** TIMEOUT ***"):
+              self.testagent.removeFile(remoteLog.replace("%5c", "\\"))
+              if (self.addCommandWinCE(" ".join(singletest) + " " + cmd, timeout=4) == "*** TIMEOUT ***"):
                   self.cleanup()
                   f = open(self.options["timeout-log"], "a")
                   f.write(directory + "\n")
                   f.close()
                   if (self.debug >= 1): 
                       print "timed out: " + directory
               tests = directory.rsplit(self.dirtype, 1)
               log = os.path.join(self.options["logdir"], tests[1] + '.log')
-              self.testagent.getFile(remoteLog, log)
+              self.testagent.getFile(remoteLog.replace("%5c", "\\"), log)
               self.cleanup()
           else:
               mydir = directory.replace('\\', '/')
               logfile = os.path.join(self.options["logdir"],  self.getLogFileName(directory))
               self.addCommand(mCommand + " --test-path=" + mydir + " --log-file=" + logfile)
               if self.options['verbose'] and os.path.exists(logfile):
                   print ''.join(open(logfile).readlines())
               self.cleanup() #just for safety measures