Tracer build fixes. (b=588021, r=dvander)
[mozilla-central.git] / testing / mochitest / runtests.py
blobc9cea7cdb4caab227fb24d750e8c2b654ad98bb0
2 # ***** BEGIN LICENSE BLOCK *****
3 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 # The contents of this file are subject to the Mozilla Public License Version
6 # 1.1 (the "License"); you may not use this file except in compliance with
7 # the License. You may obtain a copy of the License at
8 # http://www.mozilla.org/MPL/
10 # Software distributed under the License is distributed on an "AS IS" basis,
11 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 # for the specific language governing rights and limitations under the
13 # License.
15 # The Original Code is mozilla.org code.
17 # The Initial Developer of the Original Code is
18 # the Mozilla Foundation.
19 # Portions created by the Initial Developer are Copyright (C) 1998
20 # the Initial Developer. All Rights Reserved.
22 # Contributor(s):
23 # Robert Sayre <sayrer@gmail.com>
24 # Jeff Walden <jwalden+bmo@mit.edu>
25 # Serge Gautherie <sgautherie.bz@free.fr>
27 # Alternatively, the contents of this file may be used under the terms of
28 # either the GNU General Public License Version 2 or later (the "GPL"), or
29 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 # in which case the provisions of the GPL or the LGPL are applicable instead
31 # of those above. If you wish to allow use of your version of this file only
32 # under the terms of either the GPL or the LGPL, and not to allow others to
33 # use your version of this file under the terms of the MPL, indicate your
34 # decision by deleting the provisions above and replace them with the notice
35 # and other provisions required by the GPL or the LGPL. If you do not delete
36 # the provisions above, a recipient may use your version of this file under
37 # the terms of any one of the MPL, the GPL or the LGPL.
39 # ***** END LICENSE BLOCK *****
41 """
42 Runs the Mochitest test harness.
43 """
45 from __future__ import with_statement
46 from datetime import datetime
47 import optparse
48 import os
49 import os.path
50 import sys
51 import time
53 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
54 sys.path.insert(0, SCRIPT_DIR);
56 import shutil
57 from urllib import quote_plus as encodeURIComponent
58 import urllib2
59 import commands
60 from automation import Automation
61 from automationutils import *
62 import tempfile
64 VMWARE_RECORDING_HELPER_BASENAME = "vmwarerecordinghelper"
66 #######################
67 # COMMANDLINE OPTIONS #
68 #######################
70 class MochitestOptions(optparse.OptionParser):
71 """Parses Mochitest commandline options."""
72 def __init__(self, automation, scriptdir, **kwargs):
73 self._automation = automation
74 optparse.OptionParser.__init__(self, **kwargs)
75 defaults = {}
77 # we want to pass down everything from self._automation.__all__
78 addCommonOptions(self, defaults=dict(zip(self._automation.__all__,
79 [getattr(self._automation, x) for x in self._automation.__all__])))
80 self._automation.addCommonOptions(self)
82 self.add_option("--close-when-done",
83 action = "store_true", dest = "closeWhenDone",
84 help = "close the application when tests are done running")
85 defaults["closeWhenDone"] = False
87 self.add_option("--appname",
88 action = "store", type = "string", dest = "app",
89 help = "absolute path to application, overriding default")
90 defaults["app"] = os.path.join(scriptdir, self._automation.DEFAULT_APP)
92 self.add_option("--utility-path",
93 action = "store", type = "string", dest = "utilityPath",
94 help = "absolute path to directory containing utility programs (xpcshell, ssltunnel, certutil)")
95 defaults["utilityPath"] = self._automation.DIST_BIN
97 self.add_option("--certificate-path",
98 action = "store", type = "string", dest = "certPath",
99 help = "absolute path to directory containing certificate store to use testing profile")
100 defaults["certPath"] = self._automation.CERTS_SRC_DIR
102 self.add_option("--log-file",
103 action = "store", type = "string",
104 dest = "logFile", metavar = "FILE",
105 help = "file to which logging occurs")
106 defaults["logFile"] = ""
108 self.add_option("--autorun",
109 action = "store_true", dest = "autorun",
110 help = "start running tests when the application starts")
111 defaults["autorun"] = False
113 self.add_option("--timeout",
114 type = "int", dest = "timeout",
115 help = "per-test timeout in seconds")
116 defaults["timeout"] = None
118 self.add_option("--total-chunks",
119 type = "int", dest = "totalChunks",
120 help = "how many chunks to split the tests up into")
121 defaults["totalChunks"] = None
123 self.add_option("--this-chunk",
124 type = "int", dest = "thisChunk",
125 help = "which chunk to run")
126 defaults["thisChunk"] = None
128 self.add_option("--chunk-by-dir",
129 type = "int", dest = "chunkByDir",
130 help = "group tests together in the same chunk that are in the same top chunkByDir directories")
131 defaults["chunkByDir"] = 0
133 self.add_option("--shuffle",
134 dest = "shuffle",
135 action = "store_true",
136 help = "randomize test order")
137 defaults["shuffle"] = False
139 LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL")
140 LEVEL_STRING = ", ".join(LOG_LEVELS)
142 self.add_option("--console-level",
143 action = "store", type = "choice", dest = "consoleLevel",
144 choices = LOG_LEVELS, metavar = "LEVEL",
145 help = "one of %s to determine the level of console "
146 "logging" % LEVEL_STRING)
147 defaults["consoleLevel"] = None
149 self.add_option("--file-level",
150 action = "store", type = "choice", dest = "fileLevel",
151 choices = LOG_LEVELS, metavar = "LEVEL",
152 help = "one of %s to determine the level of file "
153 "logging if a file has been specified, defaulting "
154 "to INFO" % LEVEL_STRING)
155 defaults["fileLevel"] = "INFO"
157 self.add_option("--chrome",
158 action = "store_true", dest = "chrome",
159 help = "run chrome Mochitests")
160 defaults["chrome"] = False
162 self.add_option("--test-path",
163 action = "store", type = "string", dest = "testPath",
164 help = "start in the given directory's tests")
165 defaults["testPath"] = ""
167 self.add_option("--browser-chrome",
168 action = "store_true", dest = "browserChrome",
169 help = "run browser chrome Mochitests")
170 defaults["browserChrome"] = False
172 self.add_option("--a11y",
173 action = "store_true", dest = "a11y",
174 help = "run accessibility Mochitests");
175 defaults["a11y"] = False
177 self.add_option("--setenv",
178 action = "append", type = "string",
179 dest = "environment", metavar = "NAME=VALUE",
180 help = "sets the given variable in the application's "
181 "environment")
182 defaults["environment"] = []
184 self.add_option("--browser-arg",
185 action = "append", type = "string",
186 dest = "browserArgs", metavar = "ARG",
187 help = "provides an argument to the test application")
188 defaults["browserArgs"] = []
190 self.add_option("--leak-threshold",
191 action = "store", type = "int",
192 dest = "leakThreshold", metavar = "THRESHOLD",
193 help = "fail if the number of bytes leaked through "
194 "refcounted objects (or bytes in classes with "
195 "MOZ_COUNT_CTOR and MOZ_COUNT_DTOR) is greater "
196 "than the given number")
197 defaults["leakThreshold"] = 0
199 self.add_option("--fatal-assertions",
200 action = "store_true", dest = "fatalAssertions",
201 help = "abort testing whenever an assertion is hit "
202 "(requires a debug build to be effective)")
203 defaults["fatalAssertions"] = False
205 self.add_option("--extra-profile-file",
206 action = "append", dest = "extraProfileFiles",
207 help = "copy specified files/dirs to testing profile")
208 defaults["extraProfileFiles"] = []
210 self.add_option("--profile-path", action = "store",
211 type = "string", dest = "profilePath",
212 help = "Directory where the profile will be stored."
213 "This directory will be deleted after the tests are finished")
214 defaults["profilePath"] = tempfile.mkdtemp()
216 self.add_option("--use-vmware-recording",
217 action = "store_true", dest = "vmwareRecording",
218 help = "enables recording while the application is running "
219 "inside a VMware Workstation 7.0 or later VM")
220 defaults["vmwareRecording"] = False
222 # -h, --help are automatically handled by OptionParser
224 self.set_defaults(**defaults)
226 usage = """\
227 Usage instructions for runtests.py.
228 All arguments are optional.
229 If --chrome is specified, chrome tests will be run instead of web content tests.
230 If --browser-chrome is specified, browser-chrome tests will be run instead of web content tests.
231 See <http://mochikit.com/doc/html/MochiKit/Logging.html> for details on the logging levels."""
232 self.set_usage(usage)
234 def verifyOptions(self, options, mochitest):
235 """ verify correct options and cleanup paths """
237 if options.totalChunks is not None and options.thisChunk is None:
238 self.error("thisChunk must be specified when totalChunks is specified")
240 if options.totalChunks:
241 if not 1 <= options.thisChunk <= options.totalChunks:
242 self.error("thisChunk must be between 1 and totalChunks")
244 if options.xrePath is None:
245 # default xrePath to the app path if not provided
246 # but only if an app path was explicitly provided
247 if options.app != self.defaults['app']:
248 options.xrePath = os.path.dirname(options.app)
249 else:
250 # otherwise default to dist/bin
251 options.xrePath = self._automation.DIST_BIN
253 # allow relative paths
254 options.xrePath = mochitest.getFullPath(options.xrePath)
256 options.profilePath = mochitest.getFullPath(options.profilePath)
258 options.app = mochitest.getFullPath(options.app)
259 if not os.path.exists(options.app):
260 msg = """\
261 Error: Path %(app)s doesn't exist.
262 Are you executing $objdir/_tests/testing/mochitest/runtests.py?"""
263 print msg % {"app": options.app}
264 return None
266 options.utilityPath = mochitest.getFullPath(options.utilityPath)
267 options.certPath = mochitest.getFullPath(options.certPath)
268 if options.symbolsPath and not isURL(options.symbolsPath):
269 options.symbolsPath = mochitest.getFullPath(options.symbolsPath)
271 options.webServer = self._automation.DEFAULT_WEB_SERVER
272 options.httpPort = self._automation.DEFAULT_HTTP_PORT
273 options.sslPort = self._automation.DEFAULT_SSL_PORT
274 options.webSocketPort = self._automation.DEFAULT_WEBSOCKET_PORT
276 if options.vmwareRecording:
277 if not self._automation.IS_WIN32:
278 self.error("use-vmware-recording is only supported on Windows.")
279 mochitest.vmwareHelperPath = os.path.join(
280 options.utilityPath, VMWARE_RECORDING_HELPER_BASENAME + ".dll")
281 if not os.path.exists(mochitest.vmwareHelperPath):
282 self.error("%s not found, cannot automate VMware recording." %
283 mochitest.vmwareHelperPath)
285 return options
288 #######################
289 # HTTP SERVER SUPPORT #
290 #######################
292 class MochitestServer:
293 "Web server used to serve Mochitests, for closer fidelity to the real web."
295 def __init__(self, automation, options):
296 self._automation = automation
297 self._closeWhenDone = options.closeWhenDone
298 self._utilityPath = options.utilityPath
299 self._xrePath = options.xrePath
300 self._profileDir = options.profilePath
301 self.webServer = options.webServer
302 self.httpPort = options.httpPort
303 self.shutdownURL = "http://%(server)s:%(port)s/server/shutdown" % { "server" : self.webServer, "port" : self.httpPort }
305 def start(self):
306 "Run the Mochitest server, returning the process ID of the server."
308 env = self._automation.environment(xrePath = self._xrePath)
309 env["XPCOM_DEBUG_BREAK"] = "warn"
310 if self._automation.IS_WIN32:
311 env["PATH"] = env["PATH"] + ";" + self._xrePath
313 args = ["-g", self._xrePath,
314 "-v", "170",
315 "-f", "./" + "httpd.js",
316 "-e", "const _PROFILE_PATH = '%(profile)s';const _SERVER_PORT = '%(port)s'; const _SERVER_ADDR ='%(server)s';" %
317 {"profile" : self._profileDir.replace('\\', '\\\\'), "port" : self.httpPort, "server" : self.webServer },
318 "-f", "./" + "server.js"]
320 xpcshell = os.path.join(self._utilityPath,
321 "xpcshell" + self._automation.BIN_SUFFIX)
322 self._process = self._automation.Process([xpcshell] + args, env = env)
323 pid = self._process.pid
324 if pid < 0:
325 print "Error starting server."
326 sys.exit(2)
327 self._automation.log.info("INFO | runtests.py | Server pid: %d", pid)
329 def ensureReady(self, timeout):
330 assert timeout >= 0
332 aliveFile = os.path.join(self._profileDir, "server_alive.txt")
333 i = 0
334 while i < timeout:
335 if os.path.exists(aliveFile):
336 break
337 time.sleep(1)
338 i += 1
339 else:
340 print "Timed out while waiting for server startup."
341 self.stop()
342 sys.exit(1)
344 def stop(self):
345 try:
346 c = urllib2.urlopen(self.shutdownURL)
347 c.read()
348 c.close()
350 rtncode = self._process.poll()
351 if rtncode is None:
352 self._process.terminate()
353 except:
354 self._process.kill()
356 class WebSocketServer(object):
357 "Class which encapsulates the mod_pywebsocket server"
359 def __init__(self, automation, options, scriptdir):
360 self.port = options.webSocketPort
361 self._automation = automation
362 self._scriptdir = scriptdir
364 def start(self):
365 script = os.path.join(self._scriptdir, 'pywebsocket/standalone.py')
366 cmd = [sys.executable, script, '-p', str(self.port), '-w', self._scriptdir, '-l', os.path.join(self._scriptdir, "websock.log"), '--log-level=debug']
368 self._process = self._automation.Process(cmd)
369 pid = self._process.pid
370 if pid < 0:
371 print "Error starting websocket server."
372 sys.exit(2)
373 self._automation.log.info("INFO | runtests.py | Websocket server pid: %d", pid)
375 def stop(self):
376 self._process.kill()
378 class Mochitest(object):
379 # Path to the test script on the server
380 TEST_PATH = "/tests/"
381 CHROME_PATH = "/redirect.html";
382 A11Y_PATH = "/redirect-a11y.html"
383 urlOpts = []
384 runSSLTunnel = True
385 vmwareHelper = None
387 oldcwd = os.getcwd()
389 def __init__(self, automation):
390 self.automation = automation
392 # Max time in seconds to wait for server startup before tests will fail -- if
393 # this seems big, it's mostly for debug machines where cold startup
394 # (particularly after a build) takes forever.
395 if self.automation.IS_DEBUG_BUILD:
396 self.SERVER_STARTUP_TIMEOUT = 180
397 else:
398 self.SERVER_STARTUP_TIMEOUT = 90
400 self.SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
401 os.chdir(self.SCRIPT_DIRECTORY)
403 def getFullPath(self, path):
404 " Get an absolute path relative to self.oldcwd."
405 return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path)))
407 def buildTestPath(self, options):
408 """ Build the url path to the specific test harness and test file or directory """
409 testHost = "http://mochi.test:8888"
410 testURL = testHost + self.TEST_PATH + options.testPath
411 if options.chrome:
412 testURL = testHost + self.CHROME_PATH
413 if options.testPath:
414 self.urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
415 elif options.a11y:
416 testURL = testHost + self.A11Y_PATH
417 if options.testPath:
418 self.urlOpts.append("testPath=" + encodeURIComponent(options.testPath))
419 elif options.browserChrome:
420 testURL = "about:blank"
421 return testURL
423 def startWebSocketServer(self, options):
424 """ Launch the websocket server """
425 if options.webServer != '127.0.0.1':
426 return
428 self.wsserver = WebSocketServer(self.automation, options, self.SCRIPT_DIRECTORY)
429 self.wsserver.start()
431 def stopWebSocketServer(self, options):
432 if options.webServer != '127.0.0.1':
433 return
435 self.wsserver.stop()
437 def startWebServer(self, options):
438 if options.webServer != '127.0.0.1':
439 return
441 """ Create the webserver and start it up """
442 self.server = MochitestServer(self.automation, options)
443 self.server.start()
445 # If we're lucky, the server has fully started by now, and all paths are
446 # ready, etc. However, xpcshell cold start times suck, at least for debug
447 # builds. We'll try to connect to the server for awhile, and if we fail,
448 # we'll try to kill the server and exit with an error.
449 self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT)
451 def stopWebServer(self, options):
452 """ Server's no longer needed, and perhaps more importantly, anything it might
453 spew to console shouldn't disrupt the leak information table we print next.
455 if options.webServer != '127.0.0.1':
456 return
458 self.server.stop()
460 def getLogFilePath(self, logFile):
461 """ return the log file path relative to the device we are testing on, in most cases
462 it will be the full path on the local system
464 return self.getFullPath(logFile)
466 def buildProfile(self, options):
467 """ create the profile and add optional chrome bits and files if requested """
468 self.automation.initializeProfile(options.profilePath, options.extraPrefs, useServerLocations = True)
469 manifest = self.addChromeToProfile(options)
470 self.copyExtraFilesToProfile(options)
471 return manifest
473 def buildBrowserEnv(self, options):
474 """ build the environment variables for the specific test and operating system """
475 browserEnv = self.automation.environment(xrePath = options.xrePath)
477 # These variables are necessary for correct application startup; change
478 # via the commandline at your own risk.
479 browserEnv["XPCOM_DEBUG_BREAK"] = "stack"
481 for v in options.environment:
482 ix = v.find("=")
483 if ix <= 0:
484 print "Error: syntax error in --setenv=" + v
485 return None
486 browserEnv[v[:ix]] = v[ix + 1:]
488 browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.leak_report_file
490 if options.fatalAssertions:
491 browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort"
493 return browserEnv
495 def buildURLOptions(self, options):
496 """ Add test control options from the command line to the url
498 URL parameters to test URL:
500 autorun -- kick off tests automatically
501 closeWhenDone -- runs quit.js after tests
502 logFile -- logs test run to an absolute path
503 totalChunks -- how many chunks to split tests into
504 thisChunk -- which chunk to run
505 timeout -- per-test timeout in seconds
508 # allow relative paths for logFile
509 if options.logFile:
510 options.logFile = self.getLogFilePath(options.logFile)
511 if options.browserChrome:
512 self.makeTestConfig(options)
513 else:
514 if options.autorun:
515 self.urlOpts.append("autorun=1")
516 if options.timeout:
517 self.urlOpts.append("timeout=%d" % options.timeout)
518 if options.closeWhenDone:
519 self.urlOpts.append("closeWhenDone=1")
520 if options.logFile:
521 self.urlOpts.append("logFile=" + encodeURIComponent(options.logFile))
522 self.urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel))
523 if options.consoleLevel:
524 self.urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel))
525 if options.totalChunks:
526 self.urlOpts.append("totalChunks=%d" % options.totalChunks)
527 self.urlOpts.append("thisChunk=%d" % options.thisChunk)
528 if options.chunkByDir:
529 self.urlOpts.append("chunkByDir=%d" % options.chunkByDir)
530 if options.shuffle:
531 self.urlOpts.append("shuffle=1")
533 def cleanup(self, manifest, options):
534 """ remove temporary files and profile """
535 os.remove(manifest)
536 shutil.rmtree(options.profilePath)
538 def startVMwareRecording(self, options):
539 """ starts recording inside VMware VM using the recording helper dll """
540 assert(self.automation.IS_WIN32)
541 from ctypes import cdll
542 self.vmwareHelper = cdll.LoadLibrary(self.vmwareHelperPath)
543 if self.vmwareHelper is None:
544 self.automation.log.warning("WARNING | runtests.py | Failed to load "
545 "VMware recording helper")
546 return
547 self.automation.log.info("INFO | runtests.py | Starting VMware recording.")
548 try:
549 self.vmwareHelper.StartRecording()
550 except Exception, e:
551 self.automation.log.warning("WARNING | runtests.py | Failed to start "
552 "VMware recording: (%s)" % str(e))
553 self.vmwareHelper = None
555 def stopVMwareRecording(self):
556 """ stops recording inside VMware VM using the recording helper dll """
557 assert(self.automation.IS_WIN32)
558 if self.vmwareHelper is not None:
559 self.automation.log.info("INFO | runtests.py | Stopping VMware "
560 "recording.")
561 try:
562 self.vmwareHelper.StopRecording()
563 except Exception, e:
564 self.automation.log.warning("WARNING | runtests.py | Failed to stop "
565 "VMware recording: (%s)" % str(e))
566 self.vmwareHelper = None
568 def runTests(self, options):
569 """ Prepare, configure, run tests and cleanup """
570 debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs,
571 options.debuggerInteractive);
573 self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
575 browserEnv = self.buildBrowserEnv(options)
576 if browserEnv is None:
577 return 1
579 manifest = self.buildProfile(options)
580 if manifest is None:
581 return 1
582 self.startWebServer(options)
583 self.startWebSocketServer(options)
585 testURL = self.buildTestPath(options)
586 self.buildURLOptions(options)
587 if len(self.urlOpts) > 0:
588 testURL += "?" + "&".join(self.urlOpts)
590 # Remove the leak detection file so it can't "leak" to the tests run.
591 # The file is not there if leak logging was not enabled in the application build.
592 if os.path.exists(self.leak_report_file):
593 os.remove(self.leak_report_file)
595 # then again to actually run mochitest
596 if options.timeout:
597 timeout = options.timeout + 30
598 elif not options.autorun:
599 timeout = None
600 else:
601 timeout = 330.0 # default JS harness timeout is 300 seconds
603 if options.vmwareRecording:
604 self.startVMwareRecording(options);
606 self.automation.log.info("INFO | runtests.py | Running tests: start.\n")
607 status = self.automation.runApp(testURL, browserEnv, options.app,
608 options.profilePath, options.browserArgs,
609 runSSLTunnel = self.runSSLTunnel,
610 utilityPath = options.utilityPath,
611 xrePath = options.xrePath,
612 certPath=options.certPath,
613 debuggerInfo=debuggerInfo,
614 symbolsPath=options.symbolsPath,
615 timeout = timeout)
617 if options.vmwareRecording:
618 self.stopVMwareRecording();
620 self.stopWebServer(options)
621 self.stopWebSocketServer(options)
622 processLeakLog(self.leak_report_file, options.leakThreshold)
623 self.automation.log.info("\nINFO | runtests.py | Running tests: end.")
625 if manifest is not None:
626 self.cleanup(manifest, options)
627 return status
629 def makeTestConfig(self, options):
630 "Creates a test configuration file for customizing test execution."
631 def boolString(b):
632 if b:
633 return "true"
634 return "false"
636 logFile = options.logFile.replace("\\", "\\\\")
637 testPath = options.testPath.replace("\\", "\\\\")
638 content = """\
640 autoRun: %(autorun)s,
641 closeWhenDone: %(closeWhenDone)s,
642 logPath: "%(logPath)s",
643 testPath: "%(testPath)s"
644 })""" % {"autorun": boolString(options.autorun),
645 "closeWhenDone": boolString(options.closeWhenDone),
646 "logPath": logFile,
647 "testPath": testPath}
649 config = open(os.path.join(options.profilePath, "testConfig.js"), "w")
650 config.write(content)
651 config.close()
654 def addChromeToProfile(self, options):
655 "Adds MochiKit chrome tests to the profile."
657 chromedir = os.path.join(options.profilePath, "chrome")
658 os.mkdir(chromedir)
660 chrome = """
661 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
662 toolbar,
663 toolbarpalette {
664 background-color: rgb(235, 235, 235) !important;
666 toolbar#nav-bar {
667 background-image: none !important;
671 # write userChrome.css
672 chromeFile = open(os.path.join(options.profilePath, "userChrome.css"), "a")
673 chromeFile.write(chrome)
674 chromeFile.close()
677 # register our chrome dir
678 chrometestDir = os.path.abspath(".") + "/"
679 if self.automation.IS_WIN32:
680 chrometestDir = "file:///" + chrometestDir.replace("\\", "/")
682 manifest = os.path.join(options.profilePath, 'tests.manifest')
684 browser_chrome = ""
685 if options.browserChrome:
686 browser_chrome = """overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul
687 overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul
689 elif (options.chrome == False) and (options.a11y == False):
690 #only do the ipc-overlay.xul for mochitest-plain.
691 #Currently there are focus issues in chrome tests and issues with new windows and dialogs when using ipc
692 browser_chrome += "overlay chrome://browser/content/browser.xul chrome://mochikit/content/ipc-overlay.xul\n"
694 jarDir = 'mochijar'
695 if not os.path.exists(os.path.join(self.SCRIPT_DIRECTORY, jarDir)):
696 print "TEST-UNEXPECTED-FAIL | invalid setup: missing mochikit extension"
697 return None
699 manifestFile = open(manifest, "w")
700 if self.installTestsJar(options):
701 manifestFile.write("content mochitests jar:tests.jar!/content/\n");
702 else:
703 manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir)
704 manifestFile.close()
706 self.installChromeJar(jarDir, browser_chrome, options)
707 return manifest
709 def installChromeJar(self, jarDirName, browser_chrome, options):
711 copy mochijar directory to profile as an extension so we have chrome://mochikit for all harness code
713 jarDir = os.path.join(options.profilePath, 'extensions', 'mochikit@mozilla.org')
714 shutil.copytree(os.path.join(self.SCRIPT_DIRECTORY, jarDirName), jarDir)
715 with open(os.path.join(jarDir, "chrome.manifest"), 'a') as mfile:
716 mfile.write(browser_chrome)
718 return jarDir
720 def installTestsJar(self, options):
721 """ copy tests.jar to the profile directory so we can auto register it in the .xul harness """
722 if os.path.exists(os.path.join(self.SCRIPT_DIRECTORY, 'tests.jar')):
723 shutil.copy(os.path.join(self.SCRIPT_DIRECTORY, 'tests.jar'), options.profilePath)
724 return True
725 return False
727 def copyExtraFilesToProfile(self, options):
728 "Copy extra files or dirs specified on the command line to the testing profile."
729 for f in options.extraProfileFiles:
730 abspath = self.getFullPath(f)
731 dest = os.path.join(options.profilePath, os.path.basename(abspath))
732 if os.path.isdir(abspath):
733 shutil.copytree(abspath, dest)
734 else:
735 shutil.copy(abspath, dest)
737 def main():
738 automation = Automation()
739 mochitest = Mochitest(automation)
740 parser = MochitestOptions(automation, mochitest.SCRIPT_DIRECTORY)
741 options, args = parser.parse_args()
743 options = parser.verifyOptions(options, mochitest)
744 if options == None:
745 sys.exit(1)
747 options.utilityPath = mochitest.getFullPath(options.utilityPath)
748 options.certPath = mochitest.getFullPath(options.certPath)
749 if options.symbolsPath and not isURL(options.symbolsPath):
750 options.symbolsPath = mochitest.getFullPath(options.symbolsPath)
752 automation.setServerInfo(options.webServer,
753 options.httpPort,
754 options.sslPort,
755 options.webSocketPort)
756 sys.exit(mochitest.runTests(options))
758 if __name__ == "__main__":
759 main()