From 89b487b6ff77e4b3d177070dbb0939f3e21bbca7 Mon Sep 17 00:00:00 2001 From: Jonathan Griffin Date: Mon, 19 Oct 2009 16:12:09 -0700 Subject: [PATCH] Bug 521457. Add debugger options to runreftest.py and runxpcshelltests.py. r=ted --HG-- extra : rebase_source : 420b33b1d44aae7361c25d8bcc15032c303aa80d --- build/automationutils.py | 82 ++++++++++++++++++++++++++++ layout/tools/reftest/runreftest.py | 6 +- testing/mochitest/runtests.py.in | 77 +------------------------- testing/xpcshell/runxpcshelltests.py | 28 ++++++++-- 4 files changed, 112 insertions(+), 81 deletions(-) diff --git a/build/automationutils.py b/build/automationutils.py index 49d75b19572f..a89526a6c7b3 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -44,8 +44,28 @@ __all__ = [ "checkForCrashes", "dumpLeakLog", "processLeakLog", + "getDebuggerInfo", + "DEBUGGER_INFO", ] +# Map of debugging programs to information about them, like default arguments +# and whether or not they are interactive. +DEBUGGER_INFO = { + # gdb requires that you supply the '--args' flag in order to pass arguments + # after the executable name to the executable. + "gdb": { + "interactive": True, + "args": "-q --args" + }, + + # valgrind doesn't explain much about leaks unless you set the + # '--leak-check=full' flag. + "valgrind": { + "interactive": False, + "args": "--leak-check=full" + } +} + log = logging.getLogger() def addCommonOptions(parser, defaults={}): @@ -60,6 +80,17 @@ def addCommonOptions(parser, defaults={}): action = "store", type = "string", dest = "symbolsPath", default = defaults['SYMBOLS_PATH'], help = "absolute path to directory containing breakpad symbols") + parser.add_option("--debugger", + action = "store", dest = "debugger", + help = "use the given debugger to launch the application") + parser.add_option("--debugger-args", + action = "store", dest = "debuggerArgs", + help = "pass the given args to the debugger _before_ " + "the application on the command line") + parser.add_option("--debugger-interactive", + action = "store_true", dest = "debuggerInteractive", + help = "prevents the test harness from redirecting " + "stdout and stderr for interactive debuggers") def checkForCrashes(dumpDir, symbolsPath, testName=None): stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) @@ -91,6 +122,57 @@ def checkForCrashes(dumpDir, symbolsPath, testName=None): foundCrash = True return foundCrash + +def getFullPath(directory, path): + "Get an absolute path relative to 'directory'." + return os.path.normpath(os.path.join(directory, os.path.expanduser(path))) + +def searchPath(directory, path): + "Go one step beyond getFullPath and try the various folders in PATH" + # Try looking in the current working directory first. + newpath = getFullPath(directory, path) + if os.path.exists(newpath): + return newpath + + # At this point we have to fail if a directory was given (to prevent cases + # like './gdb' from matching '/usr/bin/./gdb'). + if not os.path.dirname(path): + for dir in os.environ['PATH'].split(os.pathsep): + newpath = os.path.join(dir, path) + if os.path.exists(newpath): + return newpath + return None + +def getDebuggerInfo(directory, debugger, debuggerArgs, debuggerInteractive = False): + + debuggerInfo = None + + if debugger: + debuggerPath = searchPath(directory, debugger) + if not debuggerPath: + print "Error: Path %s doesn't exist." % debugger + sys.exit(1) + + debuggerName = os.path.basename(debuggerPath).lower() + + def getDebuggerInfo(type, default): + if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]: + return DEBUGGER_INFO[debuggerName][type] + return default + + debuggerInfo = { + "path": debuggerPath, + "interactive" : getDebuggerInfo("interactive", False), + "args": getDebuggerInfo("args", "").split() + } + + if debuggerArgs: + debuggerInfo["args"] = debuggerArgs.split() + if debuggerInteractive: + debuggerInfo["interactive"] = debuggerInteractive + + return debuggerInfo + def dumpLeakLog(leakLogFile, filter = False): """Process the leak log, without parsing it. diff --git a/layout/tools/reftest/runreftest.py b/layout/tools/reftest/runreftest.py index 83190c1b5eb0..e667b0226d66 100644 --- a/layout/tools/reftest/runreftest.py +++ b/layout/tools/reftest/runreftest.py @@ -45,7 +45,7 @@ import sys, shutil, os, os.path SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) sys.path.append(SCRIPT_DIRECTORY) import automation -from automationutils import addCommonOptions, processLeakLog +from automationutils import * from optparse import OptionParser from tempfile import mkdtemp @@ -127,6 +127,9 @@ Are you executing $objdir/_tests/reftest/runreftest.py?""" \ options.symbolsPath = getFullPath(options.symbolsPath) options.utilityPath = getFullPath(options.utilityPath) + debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs, + options.debuggerInteractive); + profileDir = None try: profileDir = mkdtemp() @@ -165,6 +168,7 @@ Are you executing $objdir/_tests/reftest/runreftest.py?""" \ ["-reftest", reftestlist], utilityPath = options.utilityPath, xrePath=options.xrePath, + debuggerInfo=debuggerInfo, symbolsPath=options.symbolsPath) processLeakLog(leakLogFile, options.leakThreshold) automation.log.info("\nREFTEST INFO | runreftest.py | Running tests: end.") diff --git a/testing/mochitest/runtests.py.in b/testing/mochitest/runtests.py.in index 624cde56893c..1249d4f5344d 100644 --- a/testing/mochitest/runtests.py.in +++ b/testing/mochitest/runtests.py.in @@ -53,7 +53,7 @@ from urllib import quote_plus as encodeURIComponent import urllib2 import commands import automation -from automationutils import addCommonOptions, processLeakLog +from automationutils import * # Path to the test script on the server TEST_SERVER_HOST = "localhost:8888" @@ -87,24 +87,6 @@ PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile") LEAK_REPORT_FILE = os.path.join(PROFILE_DIRECTORY, "runtests_leaks.log") -# Map of debugging programs to information about them, like default arguments -# and whether or not they are interactive. -DEBUGGER_INFO = { - # gdb requires that you supply the '--args' flag in order to pass arguments - # after the executable name to the executable. - "gdb": { - "interactive": True, - "args": "-q --args" - }, - - # valgrind doesn't explain much about leaks unless you set the - # '--leak-check=full' flag. - "valgrind": { - "interactive": False, - "args": "--leak-check=full" - } -} - ####################### # COMMANDLINE OPTIONS # ####################### @@ -251,20 +233,6 @@ class MochitestOptions(optparse.OptionParser): help = "copy specified files/dirs to testing profile") defaults["extraProfileFiles"] = [] - self.add_option("--debugger", - action = "store", dest = "debugger", - help = "use the given debugger to launch the application") - - self.add_option("--debugger-args", - action = "store", dest = "debuggerArgs", - help = "pass the given args to the debugger _before_ " - "the application on the command line") - - self.add_option("--debugger-interactive", - action = "store_true", dest = "debuggerInteractive", - help = "prevents the test harness from redirecting stdout " - "and stderr for interactive debuggers") - # -h, --help are automatically handled by OptionParser self.set_defaults(**defaults) @@ -342,22 +310,6 @@ def getFullPath(path): "Get an absolute path relative to oldcwd." return os.path.normpath(os.path.join(oldcwd, os.path.expanduser(path))) -def searchPath(path): - "Go one step beyond getFullPath and try the various folders in PATH" - # Try looking in the current working directory first. - newpath = getFullPath(path) - if os.path.exists(newpath): - return newpath - - # At this point we have to fail if a directory was given (to prevent cases - # like './gdb' from matching '/usr/bin/./gdb'). - if not os.path.dirname(path): - for dir in os.environ['PATH'].split(os.pathsep): - newpath = os.path.join(dir, path) - if os.path.exists(newpath): - return newpath - return None - ################# # MAIN FUNCTION # ################# @@ -397,31 +349,8 @@ Are you executing $objdir/_tests/testing/mochitest/runtests.py?""" options.certPath = getFullPath(options.certPath) options.symbolsPath = getFullPath(options.symbolsPath) - debuggerInfo = None - - if options.debugger: - debuggerPath = searchPath(options.debugger) - if not debuggerPath: - print "Error: Path %s doesn't exist." % options.debugger - sys.exit(1) - - debuggerName = os.path.basename(debuggerPath).lower() - - def getDebuggerInfo(type, default): - if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]: - return DEBUGGER_INFO[debuggerName][type] - return default - - debuggerInfo = { - "path": debuggerPath, - "interactive" : getDebuggerInfo("interactive", False), - "args": getDebuggerInfo("args", "").split() - } - - if options.debuggerArgs: - debuggerInfo["args"] = options.debuggerArgs.split() - if options.debuggerInteractive: - debuggerInfo["interactive"] = options.debuggerInteractive + debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs, + options.debuggerInteractive); # browser environment browserEnv = automation.environment(xrePath = options.xrePath) diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index d7f2c6673148..13e278705000 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -43,7 +43,7 @@ from optparse import OptionParser from subprocess import Popen, PIPE, STDOUT from tempfile import mkdtemp -from automationutils import addCommonOptions, checkForCrashes, dumpLeakLog +from automationutils import * # Init logging log = logging.getLogger() @@ -51,6 +51,8 @@ handler = logging.StreamHandler(sys.stdout) log.setLevel(logging.INFO) log.addHandler(handler) +oldcwd = os.getcwd() + def readManifest(manifest): """Given a manifest file containing a list of test directories, return a list of absolute paths to the directories contained within.""" @@ -70,7 +72,8 @@ def readManifest(manifest): def runTests(xpcshell, xrePath=None, symbolsPath=None, manifest=None, testdirs=[], testPath=None, - interactive=False, logfiles=True): + interactive=False, logfiles=True, + debuggerInfo=None): """Run xpcshell tests. |xpcshell|, is the xpcshell executable to use to run the tests. @@ -86,6 +89,8 @@ def runTests(xpcshell, xrePath=None, symbolsPath=None, instead of automatically executing the test. |logfiles|, if set to False, indicates not to save output to log files. Non-interactive only option. + |debuggerInfo|, if set, specifies the debugger and debugger arguments + that will be used to launch xpcshell. """ if not testdirs and not manifest: @@ -131,17 +136,24 @@ def runTests(xpcshell, xrePath=None, symbolsPath=None, pStderr = None else: xpcsRunArgs = ['-e', '_execute_test();'] - if sys.platform == 'os2emx': + if (debuggerInfo and debuggerInfo["interactive"]): pStdout = None + pStderr = None else: - pStdout = PIPE - pStderr = STDOUT + if sys.platform == 'os2emx': + pStdout = None + else: + pStdout = PIPE + pStderr = STDOUT # has to be loaded by xpchell: it can't load itself. xpcsCmd = [xpcshell, '-g', xrePath, '-j', '-s'] + \ ['-e', 'const _HTTPD_JS_PATH = "%s";' % httpdJSPath, '-f', os.path.join(testharnessdir, 'head.js')] + if debuggerInfo: + xpcsCmd = [debuggerInfo["path"]] + debuggerInfo["args"] + xpcsCmd + # |testPath| will be the optional path only, or |None|. # |singleFile| will be the optional test only, or |None|. singleFile = None @@ -295,6 +307,9 @@ def main(): sys.argv[0]) sys.exit(1) + debuggerInfo = getDebuggerInfo(oldcwd, options.debugger, options.debuggerArgs, + options.debuggerInteractive); + if options.interactive and not options.testPath: print >>sys.stderr, "Error: You must specify a test filename in interactive mode!" sys.exit(1) @@ -306,7 +321,8 @@ def main(): testdirs=args[1:], testPath=options.testPath, interactive=options.interactive, - logfiles=options.logfiles): + logfiles=options.logfiles, + debuggerInfo=debuggerInfo): sys.exit(1) if __name__ == '__main__':