Bug 932144 - Add option to break into debugger on test failure. r=jmaher
authorFelipe Gomes <felipc@gmail.com>
Fri, 01 Nov 2013 17:23:34 -0200
changeset 153144 fdc73bb2dba896f7541027e989dabd0c4d6167fb
parent 153143 9720ae71914da92ac3c841dbd5cb3e800cb02589
child 153145 a9473ddc1cbf2e2eba307ae624252d68d77f4296
push id25569
push userkwierso@gmail.com
push dateSat, 02 Nov 2013 03:28:23 +0000
treeherdermozilla-central@d6136cbe47d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmaher
bugs932144
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 932144 - Add option to break into debugger on test failure. r=jmaher
testing/mochitest/browser-test.js
testing/mochitest/mach_commands.py
testing/mochitest/mochitest_options.py
testing/mochitest/runtests.py
testing/mochitest/tests/SimpleTest/TestRunner.js
testing/mochitest/tests/SimpleTest/setup.js
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -596,16 +596,22 @@ function testResult(aCondition, aName, a
         this.msg += "    " + frame + "\n";
         frame = frame.caller;
       }
     }
     if (aIsTodo)
       this.result = "TEST-UNEXPECTED-PASS";
     else
       this.result = "TEST-UNEXPECTED-FAIL";
+
+    if (gConfig.debugOnFailure) {
+      // You've hit this line because you requested to break into the
+      // debugger upon a testcase failure on your test run.
+      debugger;
+    }
   }
 }
 
 function testMessage(aName) {
   this.msg = aName || "";
   this.info = true;
   this.result = "TEST-INFO";
 }
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -181,17 +181,17 @@ class MochitestRunner(MozbuildObject):
         options.httpdPath = self.mochitest_dir
         options.xrePath = xre_path
         return mochitest.run_remote_mochitests(parser, options)
 
     def run_desktop_test(self, suite=None, test_file=None, debugger=None,
         debugger_args=None, shuffle=False, keep_open=False, rerun_failures=False,
         no_autorun=False, repeat=0, run_until_failure=False, slow=False,
         chunk_by_dir=0, total_chunks=None, this_chunk=None, jsdebugger=False,
-        start_at=None, end_at=None):
+        debug_on_failure=False, start_at=None, end_at=None):
         """Runs a mochitest.
 
         test_file is a path to a test file. It can be a relative path from the
         top source directory, an absolute filename, or a directory containing
         test files.
 
         suite is the type of mochitest to run. It can be one of ('plain',
         'chrome', 'browser', 'metro', 'a11y').
@@ -282,16 +282,17 @@ class MochitestRunner(MozbuildObject):
         options.runSlower = slow
         options.testingModulesDir = os.path.join(self.tests_dir, 'modules')
         options.extraProfileFiles.append(os.path.join(self.distdir, 'plugins'))
         options.symbolsPath = os.path.join(self.distdir, 'crashreporter-symbols')
         options.chunkByDir = chunk_by_dir
         options.totalChunks = total_chunks
         options.thisChunk = this_chunk
         options.jsdebugger = jsdebugger
+        options.debugOnFailure = debug_on_failure
         options.startAt = start_at
         options.endAt = end_at
 
         options.failureFile = failure_file_path
 
         if test_path:
             test_root = runner.getTestRoot(options)
             test_root_file = mozpack.path.join(self.mochitest_dir, test_root, test_path)
@@ -411,16 +412,21 @@ def MochitestCommand(func):
     chunk_total = CommandArgument('--total-chunks', type=int,
         help='Total number of chunks to split tests into.')
     func = chunk_total(func)
 
     this_chunk = CommandArgument('--this-chunk', type=int,
         help='If running tests by chunks, the number of the chunk to run.')
     func = this_chunk(func)
 
+    debug_on_failure = CommandArgument('--debug-on-failure', action='store_true',
+        help='Breaks execution and enters the JS debugger on a test failure. ' \
+             'Should be used together with --jsdebugger.')
+    func = debug_on_failure(func)
+
     jsdebugger = CommandArgument('--jsdebugger', action='store_true',
         help='Start the browser JS debugger before running the test. Implies --no-autorun.')
     func = jsdebugger(func)
 
     path = CommandArgument('test_file', default=None, nargs='?',
         metavar='TEST',
         help='Test to run. Can be specified as a single file, a ' \
             'directory, or omitted. If omitted, the entire test suite is ' \
--- a/testing/mochitest/mochitest_options.py
+++ b/testing/mochitest/mochitest_options.py
@@ -335,16 +335,22 @@ class MochitestOptions(optparse.OptionPa
           "help": "defines an extra user preference",
         }],
         [["--jsdebugger"],
         { "action": "store_true",
           "default": False,
           "dest": "jsdebugger",
           "help": "open the browser debugger",
         }],
+        [["--debug-on-failure"],
+        { "action": "store_true",
+          "default": False,
+          "dest": "debugOnFailure",
+          "help": "breaks execution and enters the JS debugger on a test failure. Should be used together with --jsdebugger."
+        }],
     ]
 
     def __init__(self, **kwargs):
 
         optparse.OptionParser.__init__(self, **kwargs)
         for option, value in self.mochitest_options:
             self.add_option(*option, **value)
         addCommonOptions(self)
@@ -437,16 +443,19 @@ class MochitestOptions(optparse.OptionPa
             options.extraPrefs += [
                 "devtools.debugger.remote-enabled=true",
                 "devtools.debugger.chrome-enabled=true",
                 "devtools.chrome.enabled=true",
                 "devtools.debugger.prompt-connection=false"
             ]
             options.autorun = False
 
+        if options.debugOnFailure and not options.jsdebugger:
+          self.error("--debug-on-failure should be used together with --jsdebugger.")
+
         # Try to guess the testing modules directory.
         # This somewhat grotesque hack allows the buildbot machines to find the
         # modules directory without having to configure the buildbot hosts. This
         # code should never be executed in local runs because the build system
         # should always set the flag that populates this variable. If buildbot ever
         # passes this argument, this code can be deleted.
         if options.testingModulesDir is None:
             possible = os.path.join(os.getcwd(), os.path.pardir, 'modules')
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -337,16 +337,18 @@ class MochitestUtilsMixin(object):
         else:
           self.urlOpts.append("runOnly=false")
       if options.manifestFile:
         self.urlOpts.append("manifestFile=%s" % options.manifestFile)
       if options.failureFile:
         self.urlOpts.append("failureFile=%s" % self.getFullPath(options.failureFile))
       if options.runSlower:
         self.urlOpts.append("runSlower=true")
+      if options.debugOnFailure:
+        self.urlOpts.append("debugOnFailure=true")
 
   def buildTestPath(self, options):
     """ Build the url path to the specific test harness and test file or directory
         Build a manifest of tests to run and write out a json file for the harness to read
     """
     if options.manifestFile and os.path.isfile(options.manifestFile):
       manifest = TestManifest(strict=False)
       manifest.read(options.manifestFile)
--- a/testing/mochitest/tests/SimpleTest/TestRunner.js
+++ b/testing/mochitest/tests/SimpleTest/TestRunner.js
@@ -208,16 +208,22 @@ TestRunner.log = function(msg) {
 };
 
 TestRunner.error = function(msg) {
     if (TestRunner.logEnabled) {
         TestRunner.logger.error(msg);
     } else {
         dump(msg + "\n");
     }
+
+    if (TestRunner.debugOnFailure) {
+      // You've hit this line because you requested to break into the
+      // debugger upon a testcase failure on your test run.
+      debugger;
+    }
 };
 
 /**
  * Toggle element visibility
 **/
 TestRunner._toggle = function(el) {
     if (el.className == "noshow") {
         el.className = "";
--- a/testing/mochitest/tests/SimpleTest/setup.js
+++ b/testing/mochitest/tests/SimpleTest/setup.js
@@ -102,16 +102,21 @@ if (params.runUntilFailure) {
 if (params.closeWhenDone) {
   TestRunner.onComplete = SpecialPowers.quit;
 }
 
 if (params.failureFile) {
   TestRunner.setFailureFile(params.failureFile);
 }
 
+// Breaks execution and enters the JS debugger on a test failure
+if (params.debugOnFailure) {
+  TestRunner.debugOnFailure = true;
+}
+
 // logFile to write our results
 if (params.logFile) {
   var spl = new SpecialPowersLogger(params.logFile);
   TestRunner.logger.addListener("mozLogger", fileLevel + "", spl.getLogCallback());
 }
 
 // if we get a quiet param, don't log to the console
 if (!params.quiet) {