Bug 822875 - Make TPS .py files use 4-space indent, r=jgriffin
authorVignesh Sarma <vignesh.sarma@gmail.com>
Fri, 11 Jan 2013 13:32:50 -0800
changeset 118533 4ffaf917d5c0bdf3890bb60c0a3ca7a50b03c333
parent 118532 1e80bc5cbeba5c8dde9c689f654b50260f8d5f50
child 118534 f2c240863a0ca23e06662c5bba128e1a530039d1
push id757
push userjgriffin@mozilla.com
push dateFri, 11 Jan 2013 21:33:09 +0000
treeherderservices-central@4ffaf917d5c0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgriffin
bugs822875
milestone21.0a1
Bug 822875 - Make TPS .py files use 4-space indent, r=jgriffin
testing/tps/tps/cli.py
testing/tps/tps/firefoxrunner.py
testing/tps/tps/mozhttpd.py
testing/tps/tps/phase.py
testing/tps/tps/testrunner.py
testing/tps/tps/thread.py
--- a/testing/tps/tps/cli.py
+++ b/testing/tps/tps/cli.py
@@ -7,93 +7,93 @@ import optparse
 import os
 import sys
 
 from threading import RLock
 
 from tps import TPSTestRunner
 
 def main():
-  parser = optparse.OptionParser()
-  parser.add_option("--mobile",
-                    action = "store_true", dest = "mobile",
-                    default = False,
-                    help = "run with mobile settings")
-  parser.add_option("--testfile",
-                    action = "store", type = "string", dest = "testfile",
-                    default = '../../services/sync/tests/tps/test_sync.js',
-                    help = "path to the test file to run "
-                           "[default: %default]")
-  parser.add_option("--logfile",
-                    action = "store", type = "string", dest = "logfile",
-                    default = 'tps.log',
-                    help = "path to the log file [default: %default]")
-  parser.add_option("--resultfile",
-                    action = "store", type = "string", dest = "resultfile",
-                    default = 'tps_result.json',
-                    help = "path to the result file [default: %default]")
-  parser.add_option("--binary",
-                    action = "store", type = "string", dest = "binary",
-                    default = None,
-                    help = "path to the Firefox binary, specified either as "
-                           "a local file or a url; if omitted, the PATH "
-                           "will be searched;")
-  parser.add_option("--configfile",
-                    action = "store", type = "string", dest = "configfile",
-                    default = None,
-                    help = "path to the config file to use "
-                           "[default: %default]")
-  parser.add_option("--pulsefile",
-                    action = "store", type = "string", dest = "pulsefile",
-                    default = None,
-                    help = "path to file containing a pulse message in "
-                           "json format that you want to inject into the monitor")
-  parser.add_option("--ignore-unused-engines",
-                     default=False,
-                     action="store_true",
-                     dest="ignore_unused_engines",
-                     help="If defined, don't load unused engines in individual tests."
-                           " Has no effect for pulse monitor.")
-  (options, args) = parser.parse_args()
+    parser = optparse.OptionParser()
+    parser.add_option("--mobile",
+                      action = "store_true", dest = "mobile",
+                      default = False,
+                      help = "run with mobile settings")
+    parser.add_option("--testfile",
+                      action = "store", type = "string", dest = "testfile",
+                      default = '../../services/sync/tests/tps/test_sync.js',
+                      help = "path to the test file to run "
+                             "[default: %default]")
+    parser.add_option("--logfile",
+                      action = "store", type = "string", dest = "logfile",
+                      default = 'tps.log',
+                      help = "path to the log file [default: %default]")
+    parser.add_option("--resultfile",
+                      action = "store", type = "string", dest = "resultfile",
+                      default = 'tps_result.json',
+                      help = "path to the result file [default: %default]")
+    parser.add_option("--binary",
+                      action = "store", type = "string", dest = "binary",
+                      default = None,
+                      help = "path to the Firefox binary, specified either as "
+                             "a local file or a url; if omitted, the PATH "
+                             "will be searched;")
+    parser.add_option("--configfile",
+                      action = "store", type = "string", dest = "configfile",
+                      default = None,
+                      help = "path to the config file to use "
+                             "[default: %default]")
+    parser.add_option("--pulsefile",
+                      action = "store", type = "string", dest = "pulsefile",
+                      default = None,
+                      help = "path to file containing a pulse message in "
+                             "json format that you want to inject into the monitor")
+    parser.add_option("--ignore-unused-engines",
+                       default=False,
+                       action="store_true",
+                       dest="ignore_unused_engines",
+                       help="If defined, don't load unused engines in individual tests."
+                             " Has no effect for pulse monitor.")
+    (options, args) = parser.parse_args()
 
-  configfile = options.configfile
-  if configfile is None:
-    if os.environ.get('VIRTUAL_ENV'):
-      configfile = os.path.join(os.path.dirname(__file__), 'config.json')
-    if configfile is None or not os.access(configfile, os.F_OK):
-      raise Exception("Unable to find config.json in a VIRTUAL_ENV; you must "
-                      "specify a config file using the --configfile option")
+    configfile = options.configfile
+    if configfile is None:
+        if os.environ.get('VIRTUAL_ENV'):
+            configfile = os.path.join(os.path.dirname(__file__), 'config.json')
+        if configfile is None or not os.access(configfile, os.F_OK):
+            raise Exception("Unable to find config.json in a VIRTUAL_ENV; you must "
+                            "specify a config file using the --configfile option")
 
-  # load the config file
-  f = open(configfile, 'r')
-  configcontent = f.read()
-  f.close()
-  config = json.loads(configcontent)
+    # load the config file
+    f = open(configfile, 'r')
+    configcontent = f.read()
+    f.close()
+    config = json.loads(configcontent)
 
-  rlock = RLock()
+    rlock = RLock()
 
-  print 'using result file', options.resultfile
+    print 'using result file', options.resultfile
 
-  extensionDir = config.get("extensiondir")
-  if not extensionDir or extensionDir == '__EXTENSIONDIR__':
-    extensionDir = os.path.join(os.getcwd(), "..", "..", "services", "sync", "tps")
-  else:
-    if sys.platform == 'win32':
-      # replace msys-style paths with proper Windows paths
-      import re
-      m = re.match('^\/\w\/', extensionDir)
-      if m:
-        extensionDir = "%s:/%s" % (m.group(0)[1:2], extensionDir[3:])
-        extensionDir = extensionDir.replace("/", "\\")
+    extensionDir = config.get("extensiondir")
+    if not extensionDir or extensionDir == '__EXTENSIONDIR__':
+        extensionDir = os.path.join(os.getcwd(), "..", "..", "services", "sync", "tps", "extensions")
+    else:
+        if sys.platform == 'win32':
+            # replace msys-style paths with proper Windows paths
+            import re
+            m = re.match('^\/\w\/', extensionDir)
+            if m:
+                extensionDir = "%s:/%s" % (m.group(0)[1:2], extensionDir[3:])
+                extensionDir = extensionDir.replace("/", "\\")
 
-  TPS = TPSTestRunner(extensionDir,
-                      testfile=options.testfile,
-                      logfile=options.logfile,
-                      binary=options.binary,
-                      config=config,
-                      rlock=rlock,
-                      mobile=options.mobile,
-                      resultfile=options.resultfile,
-                      ignore_unused_engines=options.ignore_unused_engines)
-  TPS.run_tests()
+    TPS = TPSTestRunner(extensionDir,
+                        testfile=options.testfile,
+                        logfile=options.logfile,
+                        binary=options.binary,
+                        config=config,
+                        rlock=rlock,
+                        mobile=options.mobile,
+                        resultfile=options.resultfile,
+                        ignore_unused_engines=options.ignore_unused_engines)
+    TPS.run_tests()
 
 if __name__ == "__main__":
-  main()
+    main()
--- a/testing/tps/tps/firefoxrunner.py
+++ b/testing/tps/tps/firefoxrunner.py
@@ -9,86 +9,86 @@ import shutil
 
 import mozinstall
 
 from mozprofile import Profile
 from mozrunner import FirefoxRunner
 
 class TPSFirefoxRunner(object):
 
-  PROCESS_TIMEOUT = 240
-
-  def __init__(self, binary):
-    if binary is not None and ('http://' in binary or 'ftp://' in binary):
-      self.url = binary
-      self.binary = None
-    else:
-      self.url = None
-      self.binary = binary
-    self.runner = None
-    self.installdir = None
-
-  def __del__(self):
-    if self.installdir:
-      shutil.rmtree(self.installdir, True)
-
-  def download_url(self, url, dest=None):
-    h = httplib2.Http()
-    resp, content = h.request(url, "GET")
-    if dest == None:
-        dest = os.path.basename(url)
-
-    local = open(dest, 'wb')
-    local.write(content)
-    local.close()
-    return dest
-
-  def download_build(self, installdir='downloadedbuild', appname='firefox'):
-    self.installdir = os.path.abspath(installdir)
-    buildName = os.path.basename(self.url)
-    pathToBuild = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                               buildName)
-
-    # delete the build if it already exists
-    if os.access(pathToBuild, os.F_OK):
-      os.remove(pathToBuild)
-
-    # download the build
-    print "downloading build"
-    self.download_url(self.url, pathToBuild)
-
-    # install the build
-    print "installing %s" % pathToBuild
-    shutil.rmtree(self.installdir, True)
-    binary = mozinstall.install(src=pathToBuild, dest=self.installdir)
-
-    # remove the downloaded archive
-    os.remove(pathToBuild)
-
-    return binary
-
-  def run(self, profile=None, timeout=PROCESS_TIMEOUT, env=None, args=None):
-    """Runs the given FirefoxRunner with the given Profile, waits
-       for completion, then returns the process exit code
-    """
-    if profile is None:
-      profile = Profile()
-    self.profile = profile
-
-    if self.binary is None and self.url:
-      self.binary = self.download_build()
-
-    if self.runner is None:
-      self.runner = FirefoxRunner(self.profile, binary=self.binary)
-
-    self.runner.profile = self.profile
-
-    if env is not None:
-      self.runner.env.update(env)
-
-    if args is not None:
-      self.runner.cmdargs = copy.copy(args)
-
-    self.runner.start()
-
-    status = self.runner.process_handler.waitForFinish(timeout=timeout)
-
-    return status
+    PROCESS_TIMEOUT = 240
+  
+    def __init__(self, binary):
+        if binary is not None and ('http://' in binary or 'ftp://' in binary):
+            self.url = binary
+            self.binary = None
+        else:
+            self.url = None
+            self.binary = binary
+        self.runner = None
+        self.installdir = None
+    
+    def __del__(self):
+        if self.installdir:
+            shutil.rmtree(self.installdir, True)
+      
+    def download_url(self, url, dest=None):
+        h = httplib2.Http()
+        resp, content = h.request(url, "GET")
+        if dest == None:
+            dest = os.path.basename(url)
+    
+        local = open(dest, 'wb')
+        local.write(content)
+        local.close()
+        return dest
+    
+    def download_build(self, installdir='downloadedbuild', appname='firefox'):
+        self.installdir = os.path.abspath(installdir)
+        buildName = os.path.basename(self.url)
+        pathToBuild = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                                   buildName)
+    
+        # delete the build if it already exists
+        if os.access(pathToBuild, os.F_OK):
+            os.remove(pathToBuild)
+      
+        # download the build
+        print "downloading build"
+        self.download_url(self.url, pathToBuild)
+    
+        # install the build
+        print "installing %s" % pathToBuild
+        shutil.rmtree(self.installdir, True)
+        binary = mozinstall.install(src=pathToBuild, dest=self.installdir)
+    
+        # remove the downloaded archive
+        os.remove(pathToBuild)
+    
+        return binary
+    
+    def run(self, profile=None, timeout=PROCESS_TIMEOUT, env=None, args=None):
+        """Runs the given FirefoxRunner with the given Profile, waits
+           for completion, then returns the process exit code
+        """
+        if profile is None:
+            profile = Profile()
+        self.profile = profile
+    
+        if self.binary is None and self.url:
+            self.binary = self.download_build()
+      
+        if self.runner is None:
+            self.runner = FirefoxRunner(self.profile, binary=self.binary)
+      
+        self.runner.profile = self.profile
+    
+        if env is not None:
+            self.runner.env.update(env)
+      
+        if args is not None:
+            self.runner.cmdargs = copy.copy(args)
+      
+        self.runner.start()
+    
+        status = self.runner.process_handler.waitForFinish(timeout=timeout)
+    
+        return status
--- a/testing/tps/tps/mozhttpd.py
+++ b/testing/tps/tps/mozhttpd.py
@@ -13,20 +13,20 @@ import urllib
 import re
 from urlparse import urlparse
 from SocketServer import ThreadingMixIn
 
 DOCROOT = '.'
 
 class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
     allow_reuse_address = True
-    
+
 class MozRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
     def translate_path(self, path):
-        # It appears that the default path is '/' and os.path.join makes the '/' 
+        # It appears that the default path is '/' and os.path.join makes the '/'
         o = urlparse(path)
 
         sep = '/'
         if sys.platform == 'win32':
             sep = ''
 
         ret = '%s%s' % ( sep, DOCROOT.strip('/') )
 
@@ -87,19 +87,18 @@ class MozHttpd(object):
             webline = re.sub('\<[a-zA-Z0-9\-\_\.\=\"\'\/\\\%\!\@\#\$\^\&\*\(\) ]*\>', '', line.strip('\n')).strip('/').strip().strip('@')
             if webline != "":
                 if webline == "Directory listing for":
                     found = True
                 else:
                     for fileName in fileList:
                         if fileName == webline:
                             found = True
-                
+
                 if (found == False):
-                    print "NOT FOUND: " + webline.strip()                
+                    print "NOT FOUND: " + webline.strip()
 
     def stop(self):
         if self.httpd:
             self.httpd.shutdown()
             self.httpd.server_close()
 
     __del__ = stop
-
--- a/testing/tps/tps/phase.py
+++ b/testing/tps/tps/phase.py
@@ -1,76 +1,75 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 
 class TPSTestPhase(object):
 
-  lineRe = re.compile(
-      r"^(.*?)test phase (?P<matchphase>\d+): (?P<matchstatus>.*)$")
+    lineRe = re.compile(
+        r"^(.*?)test phase (?P<matchphase>\d+): (?P<matchstatus>.*)$")
 
-  def __init__(self, phase, profile, testname, testpath, logfile, env,
-               firefoxRunner, logfn, ignore_unused_engines=False):
-    self.phase = phase
-    self.profile = profile
-    self.testname = str(testname) # this might be passed in as unicode
-    self.testpath = testpath
-    self.logfile = logfile
-    self.env = env
-    self.firefoxRunner = firefoxRunner
-    self.log = logfn
-    self.ignore_unused_engines = ignore_unused_engines
-    self._status = None
-    self.errline = ''
+    def __init__(self, phase, profile, testname, testpath, logfile, env,
+                 firefoxRunner, logfn, ignore_unused_engines=False):
+        self.phase = phase
+        self.profile = profile
+        self.testname = str(testname) # this might be passed in as unicode
+        self.testpath = testpath
+        self.logfile = logfile
+        self.env = env
+        self.firefoxRunner = firefoxRunner
+        self.log = logfn
+        self.ignore_unused_engines = ignore_unused_engines
+        self._status = None
+        self.errline = ''
 
-  @property
-  def phasenum(self):
-    match = re.match('.*?(\d+)', self.phase)
-    if match:
-      return match.group(1)
+    @property
+    def phasenum(self):
+        match = re.match('.*?(\d+)', self.phase)
+        if match:
+            return match.group(1)
 
-  @property
-  def status(self):
-    return self._status if self._status else 'unknown'
+    @property
+    def status(self):
+        return self._status if self._status else 'unknown'
 
-  def run(self):
-    # launch Firefox
-    args = [ '-tps', self.testpath,
-             '-tpsphase', self.phasenum,
-             '-tpslogfile', self.logfile ]
+    def run(self):
+        # launch Firefox
+        args = [ '-tps', self.testpath,
+                 '-tpsphase', self.phasenum,
+                 '-tpslogfile', self.logfile ]
 
-    if self.ignore_unused_engines:
-        args.append('--ignore-unused-engines')
+        if self.ignore_unused_engines:
+            args.append('--ignore-unused-engines')
 
-    self.log("\nlaunching Firefox for phase %s with args %s\n" %
-             (self.phase, str(args)))
-    self.firefoxRunner.run(env=self.env,
-                           args=args,
-                           profile=self.profile)
+        self.log("\nlaunching Firefox for phase %s with args %s\n" %
+                 (self.phase, str(args)))
+        self.firefoxRunner.run(env=self.env,
+                               args=args,
+                               profile=self.profile)
 
-    # parse the logfile and look for results from the current test phase
-    found_test = False
-    f = open(self.logfile, 'r')
-    for line in f:
+        # parse the logfile and look for results from the current test phase
+        found_test = False
+        f = open(self.logfile, 'r')
+        for line in f:
 
-      # skip to the part of the log file that deals with the test we're running
-      if not found_test:
-        if line.find("Running test %s" % self.testname) > -1:
-          found_test = True
-        else:
-          continue
+            # skip to the part of the log file that deals with the test we're running
+            if not found_test:
+                if line.find("Running test %s" % self.testname) > -1:
+                    found_test = True
+                else:
+                    continue
 
-      # look for the status of the current phase
-      match = self.lineRe.match(line)
-      if match:
-        if match.group("matchphase") == self.phasenum:
-          self._status = match.group("matchstatus")
-          break
+            # look for the status of the current phase
+            match = self.lineRe.match(line)
+            if match:
+                if match.group("matchphase") == self.phasenum:
+                    self._status = match.group("matchstatus")
+                    break
 
-      # set the status to FAIL if there is TPS error
-      if line.find("CROSSWEAVE ERROR: ") > -1 and not self._status:
-        self._status = "FAIL"
-        self.errline = line[line.find("CROSSWEAVE ERROR: ") + len("CROSSWEAVE ERROR: "):]
+            # set the status to FAIL if there is TPS error
+            if line.find("CROSSWEAVE ERROR: ") > -1 and not self._status:
+                self._status = "FAIL"
+                self.errline = line[line.find("CROSSWEAVE ERROR: ") + len("CROSSWEAVE ERROR: "):]
 
-    f.close()
-
+        f.close()
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -13,418 +13,418 @@ import traceback
 
 from mozprofile import Profile
 
 from tps.firefoxrunner import TPSFirefoxRunner
 from tps.phase import TPSTestPhase
 from tps.mozhttpd import MozHttpd
 
 class TempFile(object):
-  """Class for temporary files that delete themselves when garbage-collected.
-  """
+    """Class for temporary files that delete themselves when garbage-collected.
+    """
 
-  def __init__(self, prefix=None):
-    self.fd, self.filename = self.tmpfile = tempfile.mkstemp(prefix=prefix)
+    def __init__(self, prefix=None):
+        self.fd, self.filename = self.tmpfile = tempfile.mkstemp(prefix=prefix)
 
-  def write(self, data):
-    if self.fd:
-      os.write(self.fd, data)
+    def write(self, data):
+        if self.fd:
+            os.write(self.fd, data)
 
-  def close(self):
-    if self.fd:
-      os.close(self.fd)
-      self.fd = None
+    def close(self):
+        if self.fd:
+            os.close(self.fd)
+            self.fd = None
 
-  def cleanup(self):
-    if self.fd:
-      self.close()
-    if os.access(self.filename, os.F_OK):
-      os.remove(self.filename)
+    def cleanup(self):
+        if self.fd:
+            self.close()
+        if os.access(self.filename, os.F_OK):
+            os.remove(self.filename)
 
-  __del__ = cleanup
+    __del__ = cleanup
 
 class TPSTestRunner(object):
 
-  default_env = { 'MOZ_CRASHREPORTER_DISABLE': '1',
-                  'GNOME_DISABLE_CRASH_DIALOG': '1',
-                  'XRE_NO_WINDOWS_CRASH_DIALOG': '1',
-                  'MOZ_NO_REMOTE': '1',
-                  'XPCOM_DEBUG_BREAK': 'warn',
-                }
-  default_preferences = { 'app.update.enabled' : False,
-                          'extensions.getAddons.get.url': 'http://127.0.0.1:4567/en-US/firefox/api/%API_VERSION%/search/guid:%IDS%',
-                          'extensions.update.enabled'    : False,
-                          'extensions.update.notifyUser' : False,
-                          'browser.shell.checkDefaultBrowser' : False,
-                          'browser.tabs.warnOnClose' : False,
-                          'browser.warnOnQuit': False,
-                          'browser.sessionstore.resume_from_crash': False,
-                          'services.sync.addons.ignoreRepositoryChecking': True,
-                          'services.sync.firstSync': 'notReady',
-                          'services.sync.lastversion': '1.0',
-                          'services.sync.log.rootLogger': 'Trace',
-                          'services.sync.log.logger.engine.addons': 'Trace',
-                          'services.sync.log.logger.service.main': 'Trace',
-                          'services.sync.log.logger.engine.bookmarks': 'Trace',
-                          'services.sync.log.appender.console': 'Trace',
-                          'services.sync.log.appender.debugLog.enabled': True,
-                          'toolkit.startup.max_resumed_crashes': -1,
-                          'browser.dom.window.dump.enabled': True,
-                          # Allow installing extensions dropped into the profile folder
-                          'extensions.autoDisableScopes': 10,
-                          # Don't open a dialog to show available add-on updates
-                          'extensions.update.notifyUser' : False,
-                        }
-  syncVerRe = re.compile(
-      r"Sync version: (?P<syncversion>.*)\n")
-  ffVerRe = re.compile(
-      r"Firefox version: (?P<ffver>.*)\n")
-  ffDateRe = re.compile(
-      r"Firefox builddate: (?P<ffdate>.*)\n")
-
-  def __init__(self, extensionDir,
-               testfile="sync.test",
-               binary=None, config=None, rlock=None, mobile=False,
-               logfile="tps.log", resultfile="tps_result.json",
-               ignore_unused_engines=False):
-    self.extensions = []
-    self.testfile = testfile
-    self.logfile = os.path.abspath(logfile)
-    self.resultfile = resultfile
-    self.binary = binary
-    self.ignore_unused_engines = ignore_unused_engines
-    self.config = config if config else {}
-    self.repo = None
-    self.changeset = None
-    self.branch = None
-    self.numfailed = 0
-    self.numpassed = 0
-    self.nightly = False
-    self.rlock = rlock
-    self.mobile = mobile
-    self.tpsxpi = None
-    self.firefoxRunner = None
-    self.extensionDir = extensionDir
-    self.productversion = None
-    self.addonversion = None
-    self.postdata = {}
-    self.errorlogs = {}
-
-  @property
-  def mobile(self):
-    return self._mobile
-
-  @mobile.setter
-  def mobile(self, value):
-    self._mobile = value
-    self.synctype = 'desktop' if not self._mobile else 'mobile'
-
-  def log(self, msg, printToConsole=False):
-    """Appends a string to the logfile"""
-
-    f = open(self.logfile, 'a')
-    f.write(msg)
-    f.close()
-    if printToConsole:
-      print msg
-
-  def writeToResultFile(self, postdata, body=None,
-                        sendTo=['crossweave@mozilla.com']):
-    """Writes results to test file"""
-
-    results = {'results': []}
+    default_env = { 'MOZ_CRASHREPORTER_DISABLE': '1',
+                    'GNOME_DISABLE_CRASH_DIALOG': '1',
+                    'XRE_NO_WINDOWS_CRASH_DIALOG': '1',
+                    'MOZ_NO_REMOTE': '1',
+                    'XPCOM_DEBUG_BREAK': 'warn',
+                  }
+    default_preferences = { 'app.update.enabled' : False,
+                            'extensions.getAddons.get.url': 'http://127.0.0.1:4567/en-US/firefox/api/%API_VERSION%/search/guid:%IDS%',
+                            'extensions.update.enabled'    : False,
+                            'extensions.update.notifyUser' : False,
+                            'browser.shell.checkDefaultBrowser' : False,
+                            'browser.tabs.warnOnClose' : False,
+                            'browser.warnOnQuit': False,
+                            'browser.sessionstore.resume_from_crash': False,
+                            'services.sync.addons.ignoreRepositoryChecking': True,
+                            'services.sync.firstSync': 'notReady',
+                            'services.sync.lastversion': '1.0',
+                            'services.sync.log.rootLogger': 'Trace',
+                            'services.sync.log.logger.engine.addons': 'Trace',
+                            'services.sync.log.logger.service.main': 'Trace',
+                            'services.sync.log.logger.engine.bookmarks': 'Trace',
+                            'services.sync.log.appender.console': 'Trace',
+                            'services.sync.log.appender.debugLog.enabled': True,
+                            'toolkit.startup.max_resumed_crashes': -1,
+                            'browser.dom.window.dump.enabled': True,
+                            # Allow installing extensions dropped into the profile folder
+                            'extensions.autoDisableScopes': 10,
+                            # Don't open a dialog to show available add-on updates
+                            'extensions.update.notifyUser' : False,
+                          }
+    syncVerRe = re.compile(
+        r"Sync version: (?P<syncversion>.*)\n")
+    ffVerRe = re.compile(
+        r"Firefox version: (?P<ffver>.*)\n")
+    ffDateRe = re.compile(
+        r"Firefox builddate: (?P<ffdate>.*)\n")
 
-    if os.access(self.resultfile, os.F_OK):
-      f = open(self.resultfile, 'r')
-      results = json.loads(f.read())
-      f.close()
-
-    f = open(self.resultfile, 'w')
-    if body is not None:
-      postdata['body'] = body
-    if self.numpassed is not None:
-      postdata['numpassed'] = self.numpassed
-    if self.numfailed is not None:
-      postdata['numfailed'] = self.numfailed
-    if self.firefoxRunner and self.firefoxRunner.url:
-      postdata['firefoxrunnerurl'] = self.firefoxRunner.url
-
-    postdata['sendTo'] = sendTo
-    results['results'].append(postdata)
-    f.write(json.dumps(results, indent=2))
-    f.close()
+    def __init__(self, extensionDir,
+                 testfile="sync.test",
+                 binary=None, config=None, rlock=None, mobile=False,
+                 logfile="tps.log", resultfile="tps_result.json",
+                 ignore_unused_engines=False):
+        self.extensions = []
+        self.testfile = testfile
+        self.logfile = os.path.abspath(logfile)
+        self.resultfile = resultfile
+        self.binary = binary
+        self.ignore_unused_engines = ignore_unused_engines
+        self.config = config if config else {}
+        self.repo = None
+        self.changeset = None
+        self.branch = None
+        self.numfailed = 0
+        self.numpassed = 0
+        self.nightly = False
+        self.rlock = rlock
+        self.mobile = mobile
+        self.tpsxpi = None
+        self.firefoxRunner = None
+        self.extensionDir = extensionDir
+        self.productversion = None
+        self.addonversion = None
+        self.postdata = {}
+        self.errorlogs = {}
 
-  def _zip_add_file(self, zip, file, rootDir):
-    zip.write(os.path.join(rootDir, file), file)
+    @property
+    def mobile(self):
+        return self._mobile
 
-  def _zip_add_dir(self, zip, dir, rootDir):
-    try:
-      zip.write(os.path.join(rootDir, dir), dir)
-    except:
-      # on some OS's, adding directory entries doesn't seem to work
-      pass
-    for root, dirs, files in os.walk(os.path.join(rootDir, dir)):
-      for f in files:
-        zip.write(os.path.join(root, f), os.path.join(dir, f))
+    @mobile.setter
+    def mobile(self, value):
+        self._mobile = value
+        self.synctype = 'desktop' if not self._mobile else 'mobile'
 
-  def run_single_test(self, testdir, testname):
-    testpath = os.path.join(testdir, testname)
-    self.log("Running test %s\n" % testname)
-
-    # Create a random account suffix that is used when creating test
-    # accounts on a staging server.
-    account_suffix = {"account-suffix": ''.join([str(random.randint(0,9))
-                                                 for i in range(1,6)])}
-    self.config['account'].update(account_suffix)
+    def log(self, msg, printToConsole=False):
+        """Appends a string to the logfile"""
 
-    # Read and parse the test file, merge it with the contents of the config
-    # file, and write the combined output to a temporary file.
-    f = open(testpath, 'r')
-    testcontent = f.read()
-    f.close()
-    try:
-      test = json.loads(testcontent)
-    except:
-      test = json.loads(testcontent[testcontent.find("{"):testcontent.find("}") + 1])
+        f = open(self.logfile, 'a')
+        f.write(msg)
+        f.close()
+        if printToConsole:
+            print msg
 
-    testcontent += 'var config = %s;\n' % json.dumps(self.config, indent=2)
-    testcontent += 'var seconds_since_epoch = %d;\n' % int(time.time())
+    def writeToResultFile(self, postdata, body=None,
+                          sendTo=['crossweave@mozilla.com']):
+        """Writes results to test file"""
 
-    tmpfile = TempFile(prefix='tps_test_')
-    tmpfile.write(testcontent)
-    tmpfile.close()
+        results = {'results': []}
 
-    # generate the profiles defined in the test, and a list of test phases
-    profiles = {}
-    phaselist = []
-    for phase in test:
-      profilename = test[phase]
+        if os.access(self.resultfile, os.F_OK):
+            f = open(self.resultfile, 'r')
+            results = json.loads(f.read())
+            f.close()
 
-      # create the profile if necessary
-      if not profilename in profiles:
-        profiles[profilename] = Profile(preferences = self.preferences,
-                                        addons = self.extensions)
+        f = open(self.resultfile, 'w')
+        if body is not None:
+            postdata['body'] = body
+        if self.numpassed is not None:
+            postdata['numpassed'] = self.numpassed
+        if self.numfailed is not None:
+            postdata['numfailed'] = self.numfailed
+        if self.firefoxRunner and self.firefoxRunner.url:
+            postdata['firefoxrunnerurl'] = self.firefoxRunner.url
 
-      # create the test phase
-      phaselist.append(TPSTestPhase(
-          phase,
-          profiles[profilename],
-          testname,
-          tmpfile.filename,
-          self.logfile,
-          self.env,
-          self.firefoxRunner,
-          self.log,
-          ignore_unused_engines=self.ignore_unused_engines))
+        postdata['sendTo'] = sendTo
+        results['results'].append(postdata)
+        f.write(json.dumps(results, indent=2))
+        f.close()
+
+    def _zip_add_file(self, zip, file, rootDir):
+        zip.write(os.path.join(rootDir, file), file)
 
-    # sort the phase list by name
-    phaselist = sorted(phaselist, key=lambda phase: phase.phase)
-
-    # run each phase in sequence, aborting at the first failure
-    for phase in phaselist:
-      phase.run()
-
-      # if a failure occurred, dump the entire sync log into the test log
-      if phase.status != "PASS":
-        for profile in profiles:
-          self.log("\nDumping sync log for profile %s\n" %  profiles[profile].profile)
-          for root, dirs, files in os.walk(os.path.join(profiles[profile].profile, 'weave', 'logs')):
+    def _zip_add_dir(self, zip, dir, rootDir):
+        try:
+            zip.write(os.path.join(rootDir, dir), dir)
+        except:
+            # on some OS's, adding directory entries doesn't seem to work
+            pass
+        for root, dirs, files in os.walk(os.path.join(rootDir, dir)):
             for f in files:
-              weavelog = os.path.join(profiles[profile].profile, 'weave', 'logs', f)
-              if os.access(weavelog, os.F_OK):
-                with open(weavelog, 'r') as fh:
-                  for line in fh:
-                    possible_time = line[0:13]
-                    if len(possible_time) == 13 and possible_time.isdigit():
-                      time_ms = int(possible_time)
-                      formatted = time.strftime('%Y-%m-%d %H:%M:%S',
-                              time.localtime(time_ms / 1000))
-                      self.log('%s.%03d %s' % (
-                          formatted, time_ms % 1000, line[14:] ))
-                    else:
-                      self.log(line)
-        break;
+                zip.write(os.path.join(root, f), os.path.join(dir, f))
+
+    def run_single_test(self, testdir, testname):
+        testpath = os.path.join(testdir, testname)
+        self.log("Running test %s\n" % testname)
+
+        # Create a random account suffix that is used when creating test
+        # accounts on a staging server.
+        account_suffix = {"account-suffix": ''.join([str(random.randint(0,9))
+                                                     for i in range(1,6)])}
+        self.config['account'].update(account_suffix)
+
+        # Read and parse the test file, merge it with the contents of the config
+        # file, and write the combined output to a temporary file.
+        f = open(testpath, 'r')
+        testcontent = f.read()
+        f.close()
+        try:
+            test = json.loads(testcontent)
+        except:
+            test = json.loads(testcontent[testcontent.find("{"):testcontent.find("}") + 1])
+
+        testcontent += 'var config = %s;\n' % json.dumps(self.config, indent=2)
+        testcontent += 'var seconds_since_epoch = %d;\n' % int(time.time())
+
+        tmpfile = TempFile(prefix='tps_test_')
+        tmpfile.write(testcontent)
+        tmpfile.close()
 
-    # grep the log for FF and sync versions
-    f = open(self.logfile)
-    logdata = f.read()
-    match = self.syncVerRe.search(logdata)
-    sync_version = match.group("syncversion") if match else 'unknown'
-    match = self.ffVerRe.search(logdata)
-    firefox_version = match.group("ffver") if match else 'unknown'
-    match = self.ffDateRe.search(logdata)
-    firefox_builddate = match.group("ffdate") if match else 'unknown'
-    f.close()
-    if phase.status == 'PASS':
-      logdata = ''
-    else:
-      # we only care about the log data for this specific test
-      logdata = logdata[logdata.find('Running test %s' % (str(testname))):]
+        # generate the profiles defined in the test, and a list of test phases
+        profiles = {}
+        phaselist = []
+        for phase in test:
+            profilename = test[phase]
+
+            # create the profile if necessary
+            if not profilename in profiles:
+                profiles[profilename] = Profile(preferences = self.preferences,
+                                                addons = self.extensions)
 
-    result = {
-      'PASS': lambda x: ('TEST-PASS', ''),
-      'FAIL': lambda x: ('TEST-UNEXPECTED-FAIL', x.rstrip()),
-      'unknown': lambda x: ('TEST-UNEXPECTED-FAIL', 'test did not complete')
-    } [phase.status](phase.errline)
-    logstr = "\n%s | %s%s\n" % (result[0], testname, (' | %s' % result[1] if result[1] else ''))
+            # create the test phase
+            phaselist.append(TPSTestPhase(
+                phase,
+                profiles[profilename],
+                testname,
+                tmpfile.filename,
+                self.logfile,
+                self.env,
+                self.firefoxRunner,
+                self.log,
+                ignore_unused_engines=self.ignore_unused_engines))
 
-    try:
-      repoinfo = self.firefoxRunner.runner.get_repositoryInfo()
-    except:
-      repoinfo = {}
-    apprepo = repoinfo.get('application_repository', '')
-    appchangeset = repoinfo.get('application_changeset', '')
+        # sort the phase list by name
+        phaselist = sorted(phaselist, key=lambda phase: phase.phase)
+
+        # run each phase in sequence, aborting at the first failure
+        for phase in phaselist:
+            phase.run()
 
-    # save logdata to a temporary file for posting to the db
-    tmplogfile = None
-    if logdata:
-      tmplogfile = TempFile(prefix='tps_log_')
-      tmplogfile.write(logdata)
-      tmplogfile.close()
-      self.errorlogs[testname] = tmplogfile
+            # if a failure occurred, dump the entire sync log into the test log
+            if phase.status != "PASS":
+                for profile in profiles:
+                    self.log("\nDumping sync log for profile %s\n" %  profiles[profile].profile)
+                    for root, dirs, files in os.walk(os.path.join(profiles[profile].profile, 'weave', 'logs')):
+                        for f in files:
+                            weavelog = os.path.join(profiles[profile].profile, 'weave', 'logs', f)
+                            if os.access(weavelog, os.F_OK):
+                                with open(weavelog, 'r') as fh:
+                                    for line in fh:
+                                        possible_time = line[0:13]
+                                        if len(possible_time) == 13 and possible_time.isdigit():
+                                            time_ms = int(possible_time)
+                                            formatted = time.strftime('%Y-%m-%d %H:%M:%S',
+                                                    time.localtime(time_ms / 1000))
+                                            self.log('%s.%03d %s' % (
+                                                formatted, time_ms % 1000, line[14:] ))
+                                        else:
+                                            self.log(line)
+                break;
 
-    resultdata = ({ "productversion": { "version": firefox_version,
-                                        "buildid": firefox_builddate,
-                                        "builddate": firefox_builddate[0:8],
-                                        "product": "Firefox",
-                                        "repository": apprepo,
-                                        "changeset": appchangeset,
-                                      },
-                    "addonversion": { "version": sync_version,
-                                      "product": "Firefox Sync" },
-                    "name": testname,
-                    "message": result[1],
-                    "state": result[0],
-                    "logdata": logdata
-                  })
+        # grep the log for FF and sync versions
+        f = open(self.logfile)
+        logdata = f.read()
+        match = self.syncVerRe.search(logdata)
+        sync_version = match.group("syncversion") if match else 'unknown'
+        match = self.ffVerRe.search(logdata)
+        firefox_version = match.group("ffver") if match else 'unknown'
+        match = self.ffDateRe.search(logdata)
+        firefox_builddate = match.group("ffdate") if match else 'unknown'
+        f.close()
+        if phase.status == 'PASS':
+            logdata = ''
+        else:
+            # we only care about the log data for this specific test
+            logdata = logdata[logdata.find('Running test %s' % (str(testname))):]
 
-    self.log(logstr, True)
-    for phase in phaselist:
-      print "\t%s: %s" % (phase.phase, phase.status)
-      if phase.status == 'FAIL':
-        break
+        result = {
+          'PASS': lambda x: ('TEST-PASS', ''),
+          'FAIL': lambda x: ('TEST-UNEXPECTED-FAIL', x.rstrip()),
+          'unknown': lambda x: ('TEST-UNEXPECTED-FAIL', 'test did not complete')
+        } [phase.status](phase.errline)
+        logstr = "\n%s | %s%s\n" % (result[0], testname, (' | %s' % result[1] if result[1] else ''))
 
-    return resultdata
-
-  def run_tests(self):
-    # delete the logfile if it already exists
-    if os.access(self.logfile, os.F_OK):
-      os.remove(self.logfile)
+        try:
+            repoinfo = self.firefoxRunner.runner.get_repositoryInfo()
+        except:
+            repoinfo = {}
+        apprepo = repoinfo.get('application_repository', '')
+        appchangeset = repoinfo.get('application_changeset', '')
 
-    # Make a copy of the default env variables and preferences, and update
-    # them for mobile settings if needed.
-    self.env = self.default_env.copy()
-    self.preferences = self.default_preferences.copy()
-    if self.mobile:
-      self.preferences.update({'services.sync.client.type' : 'mobile'})
+        # save logdata to a temporary file for posting to the db
+        tmplogfile = None
+        if logdata:
+            tmplogfile = TempFile(prefix='tps_log_')
+            tmplogfile.write(logdata)
+            tmplogfile.close()
+            self.errorlogs[testname] = tmplogfile
 
-    # Acquire a lock to make sure no other threads are running tests
-    # at the same time.
-    if self.rlock:
-      self.rlock.acquire()
-
-    try:
-      # Create the Firefox runner, which will download and install the
-      # build, as needed.
-      if not self.firefoxRunner:
-        self.firefoxRunner = TPSFirefoxRunner(self.binary)
+        resultdata = ({ "productversion": { "version": firefox_version,
+                                            "buildid": firefox_builddate,
+                                            "builddate": firefox_builddate[0:8],
+                                            "product": "Firefox",
+                                            "repository": apprepo,
+                                            "changeset": appchangeset,
+                                          },
+                        "addonversion": { "version": sync_version,
+                                          "product": "Firefox Sync" },
+                        "name": testname,
+                        "message": result[1],
+                        "state": result[0],
+                        "logdata": logdata
+                      })
 
-      # now, run the test group
-      self.run_test_group()
+        self.log(logstr, True)
+        for phase in phaselist:
+            print "\t%s: %s" % (phase.phase, phase.status)
+            if phase.status == 'FAIL':
+                break
+
+        return resultdata
+
+    def run_tests(self):
+        # delete the logfile if it already exists
+        if os.access(self.logfile, os.F_OK):
+            os.remove(self.logfile)
 
-    except:
-      traceback.print_exc()
-      self.numpassed = 0
-      self.numfailed = 1
-      try:
-        self.writeToResultFile(self.postdata,
-                               '<pre>%s</pre>' % traceback.format_exc())
-      except:
-        traceback.print_exc()
-    else:
-      try:
+        # Make a copy of the default env variables and preferences, and update
+        # them for mobile settings if needed.
+        self.env = self.default_env.copy()
+        self.preferences = self.default_preferences.copy()
+        if self.mobile:
+            self.preferences.update({'services.sync.client.type' : 'mobile'})
 
-        if self.numfailed > 0 or self.numpassed == 0:
-          To = self.config['email'].get('notificationlist')
-        else:
-          To = self.config['email'].get('passednotificationlist')
-        self.writeToResultFile(self.postdata,
-                               sendTo=To)
-      except:
-        traceback.print_exc()
+        # Acquire a lock to make sure no other threads are running tests
+        # at the same time.
+        if self.rlock:
+            self.rlock.acquire()
+
         try:
-          self.writeToResultFile(self.postdata,
-                                 '<pre>%s</pre>' % traceback.format_exc())
-        except:
-          traceback.print_exc()
+            # Create the Firefox runner, which will download and install the
+            # build, as needed.
+            if not self.firefoxRunner:
+                self.firefoxRunner = TPSFirefoxRunner(self.binary)
 
-    # release our lock
-    if self.rlock:
-      self.rlock.release()
+            # now, run the test group
+            self.run_test_group()
 
-    # dump out a summary of test results
-    print 'Test Summary\n'
-    for test in self.postdata.get('tests', {}):
-      print '%s | %s | %s' % (test['state'], test['name'], test['message'])
-
-  def run_test_group(self):
-    self.results = []
-    self.extensions = []
+        except:
+            traceback.print_exc()
+            self.numpassed = 0
+            self.numfailed = 1
+            try:
+                self.writeToResultFile(self.postdata,
+                                       '<pre>%s</pre>' % traceback.format_exc())
+            except:
+                traceback.print_exc()
+        else:
+            try:
 
-    # set the OS we're running on
-    os_string = platform.uname()[2] + " " + platform.uname()[3]
-    if os_string.find("Darwin") > -1:
-      os_string = "Mac OS X " + platform.mac_ver()[0]
-    if platform.uname()[0].find("Linux") > -1:
-      os_string = "Linux " + platform.uname()[5]
-    if platform.uname()[0].find("Win") > -1:
-      os_string = "Windows " + platform.uname()[3]
+                if self.numfailed > 0 or self.numpassed == 0:
+                    To = self.config['email'].get('notificationlist')
+                else:
+                    To = self.config['email'].get('passednotificationlist')
+                self.writeToResultFile(self.postdata,
+                                       sendTo=To)
+            except:
+                traceback.print_exc()
+                try:
+                    self.writeToResultFile(self.postdata,
+                                           '<pre>%s</pre>' % traceback.format_exc())
+                except:
+                    traceback.print_exc()
+
+        # release our lock
+        if self.rlock:
+            self.rlock.release()
 
-    # reset number of passed/failed tests
-    self.numpassed = 0
-    self.numfailed = 0
+        # dump out a summary of test results
+        print 'Test Summary\n'
+        for test in self.postdata.get('tests', {}):
+            print '%s | %s | %s' % (test['state'], test['name'], test['message'])
 
-    # build our tps.xpi extension
-    self.extensions.append(os.path.join(self.extensionDir, 'tps'))
-    self.extensions.append(os.path.join(self.extensionDir, "mozmill"))
+    def run_test_group(self):
+        self.results = []
+        self.extensions = []
 
-    # build the test list
-    try:
-      f = open(self.testfile)
-      jsondata = f.read()
-      f.close()
-      testfiles = json.loads(jsondata)
-      testlist = testfiles['tests']
-    except ValueError:
-      testlist = [os.path.basename(self.testfile)]
-    testdir = os.path.dirname(self.testfile)
+        # set the OS we're running on
+        os_string = platform.uname()[2] + " " + platform.uname()[3]
+        if os_string.find("Darwin") > -1:
+            os_string = "Mac OS X " + platform.mac_ver()[0]
+        if platform.uname()[0].find("Linux") > -1:
+            os_string = "Linux " + platform.uname()[5]
+        if platform.uname()[0].find("Win") > -1:
+            os_string = "Windows " + platform.uname()[3]
 
-    self.mozhttpd = MozHttpd(port=4567, docroot=testdir)
-    self.mozhttpd.start()
+        # reset number of passed/failed tests
+        self.numpassed = 0
+        self.numfailed = 0
+
+        # build our tps.xpi extension
+        self.extensions.append(os.path.join(self.extensionDir, 'tps'))
+        self.extensions.append(os.path.join(self.extensionDir, "mozmill"))
 
-    # run each test, and save the results
-    for test in testlist:
-      result = self.run_single_test(testdir, test)
+        # build the test list
+        try:
+            f = open(self.testfile)
+            jsondata = f.read()
+            f.close()
+            testfiles = json.loads(jsondata)
+            testlist = testfiles['tests']
+        except ValueError:
+            testlist = [os.path.basename(self.testfile)]
+        testdir = os.path.dirname(self.testfile)
 
-      if not self.productversion:
-        self.productversion = result['productversion']
-      if not self.addonversion:
-        self.addonversion = result['addonversion']
+        self.mozhttpd = MozHttpd(port=4567, docroot=testdir)
+        self.mozhttpd.start()
+
+        # run each test, and save the results
+        for test in testlist:
+            result = self.run_single_test(testdir, test)
 
-      self.results.append({'state': result['state'], 
-                           'name': result['name'], 
-                           'message': result['message'],
-                           'logdata': result['logdata']})
-      if result['state'] == 'TEST-PASS':
-        self.numpassed += 1
-      else:
-        self.numfailed += 1
+            if not self.productversion:
+                self.productversion = result['productversion']
+            if not self.addonversion:
+                self.addonversion = result['addonversion']
 
-    self.mozhttpd.stop()
+            self.results.append({'state': result['state'],
+                                 'name': result['name'],
+                                 'message': result['message'],
+                                 'logdata': result['logdata']})
+            if result['state'] == 'TEST-PASS':
+                self.numpassed += 1
+            else:
+                self.numfailed += 1
 
-    # generate the postdata we'll use to post the results to the db
-    self.postdata = { 'tests': self.results, 
-                      'os':os_string,
-                      'testtype': 'crossweave',
-                      'productversion': self.productversion,
-                      'addonversion': self.addonversion,
-                      'synctype': self.synctype,
-                    }
+        self.mozhttpd.stop()
+
+        # generate the postdata we'll use to post the results to the db
+        self.postdata = { 'tests': self.results,
+                          'os':os_string,
+                          'testtype': 'crossweave',
+                          'productversion': self.productversion,
+                          'addonversion': self.addonversion,
+                          'synctype': self.synctype,
+                        }
--- a/testing/tps/tps/thread.py
+++ b/testing/tps/tps/thread.py
@@ -3,62 +3,62 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from threading import Thread
 
 from testrunner import TPSTestRunner
 
 class TPSTestThread(Thread):
 
-  def __init__(self, extensionDir, builddata=None,
-               testfile=None, logfile=None, rlock=None, config=None):
-    assert(builddata)
-    assert(config)
-    self.extensionDir = extensionDir
-    self.builddata = builddata
-    self.testfile = testfile
-    self.logfile = logfile
-    self.rlock = rlock
-    self.config = config
-    Thread.__init__(self)
+    def __init__(self, extensionDir, builddata=None,
+                 testfile=None, logfile=None, rlock=None, config=None):
+        assert(builddata)
+        assert(config)
+        self.extensionDir = extensionDir
+        self.builddata = builddata
+        self.testfile = testfile
+        self.logfile = logfile
+        self.rlock = rlock
+        self.config = config
+        Thread.__init__(self)
 
-  def run(self):
-    # run the tests in normal mode ...
-    TPS = TPSTestRunner(self.extensionDir,
-                        testfile=self.testfile,
-                        logfile=self.logfile,
-                        binary=self.builddata['buildurl'],
-                        config=self.config,
-                        rlock=self.rlock,
-                        mobile=False)
-    TPS.run_tests()
+    def run(self):
+        # run the tests in normal mode ...
+        TPS = TPSTestRunner(self.extensionDir,
+                            testfile=self.testfile,
+                            logfile=self.logfile,
+                            binary=self.builddata['buildurl'],
+                            config=self.config,
+                            rlock=self.rlock,
+                            mobile=False)
+        TPS.run_tests()
 
-    # Get the binary used by this TPS instance, and use it in subsequent
-    # ones, so it doesn't have to be re-downloaded each time.
-    binary = TPS.firefoxRunner.binary
+        # Get the binary used by this TPS instance, and use it in subsequent
+        # ones, so it doesn't have to be re-downloaded each time.
+        binary = TPS.firefoxRunner.binary
 
-    # ... and then again in mobile mode
-    TPS_mobile = TPSTestRunner(self.extensionDir,
-                               testfile=self.testfile,
-                               logfile=self.logfile,
-                               binary=binary,
-                               config=self.config,
-                               rlock=self.rlock,
-                               mobile=True)
-    TPS_mobile.run_tests()
+        # ... and then again in mobile mode
+        TPS_mobile = TPSTestRunner(self.extensionDir,
+                                   testfile=self.testfile,
+                                   logfile=self.logfile,
+                                   binary=binary,
+                                   config=self.config,
+                                   rlock=self.rlock,
+                                   mobile=True)
+        TPS_mobile.run_tests()
 
-    # ... and again via the staging server, if credentials are present
-    stageaccount = self.config.get('stageaccount')
-    if stageaccount:
-      username = stageaccount.get('username')
-      password = stageaccount.get('password')
-      passphrase = stageaccount.get('passphrase')
-      if username and password and passphrase:
-        stageconfig = self.config.copy()
-        stageconfig['account'] = stageaccount.copy()
-        TPS_stage = TPSTestRunner(self.extensionDir,
-                                  testfile=self.testfile,
-                                  logfile=self.logfile,
-                                  binary=binary,
-                                  config=stageconfig,
-                                  rlock=self.rlock,
-                                  mobile=False)#, autolog=self.autolog)
-        TPS_stage.run_tests()
+        # ... and again via the staging server, if credentials are present
+        stageaccount = self.config.get('stageaccount')
+        if stageaccount:
+            username = stageaccount.get('username')
+            password = stageaccount.get('password')
+            passphrase = stageaccount.get('passphrase')
+            if username and password and passphrase:
+                stageconfig = self.config.copy()
+                stageconfig['account'] = stageaccount.copy()
+                TPS_stage = TPSTestRunner(self.extensionDir,
+                                          testfile=self.testfile,
+                                          logfile=self.logfile,
+                                          binary=binary,
+                                          config=stageconfig,
+                                          rlock=self.rlock,
+                                          mobile=False)#, autolog=self.autolog)
+                TPS_stage.run_tests()