Bug 652494 - Report uncaught JS exceptions in chrome mochitests as test failures. r=jmaher
authorCameron McCormack <cam@mcc.id.au>
Sat, 12 Nov 2011 22:06:54 +1100
changeset 80992 e804728b128b5ddca05ce09bfee4ae7b49be466d
parent 80991 e5c077ef3d19ee0120ad5d568d0ce62a30f1dec6
child 80993 2da841d9692593db16236bba80ab0c6f53c49843
push idunknown
push userunknown
push dateunknown
reviewersjmaher
bugs652494
milestone11.0a1
Bug 652494 - Report uncaught JS exceptions in chrome mochitests as test failures. r=jmaher
browser/base/content/test/browser_bug422590.js
browser/base/content/test/browser_bug599325.js
browser/components/places/tests/browser/browser_sidebarpanels_click.js
browser/devtools/highlighter/test/browser_inspector_breadcrumbs.js
dom/plugins/test/mochitest/test_crash_submit.xul
extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul
layout/xul/base/test/test_resizer.xul
testing/mochitest/browser-test.js
testing/mochitest/tests/SimpleTest/SimpleTest.js
--- a/browser/base/content/test/browser_bug422590.js
+++ b/browser/base/content/test/browser_bug422590.js
@@ -1,10 +1,11 @@
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
   
   // test the main (normal) browser window
   testCustomize(window, testChromeless);
 }
 
 function testChromeless() {
   // test a chromeless window
   var newWin = openDialog("chrome://browser/content/", "_blank",
--- a/browser/base/content/test/browser_bug599325.js
+++ b/browser/base/content/test/browser_bug599325.js
@@ -1,10 +1,11 @@
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   testCustomize(window, finish);
 }
 
 function testCustomize(aWindow, aCallback) {
   var addonBar = aWindow.document.getElementById("addon-bar");
   ok(addonBar, "got addon bar");
   ok(!isElementVisible(addonBar), "addon bar initially hidden");
--- a/browser/components/places/tests/browser/browser_sidebarpanels_click.js
+++ b/browser/components/places/tests/browser/browser_sidebarpanels_click.js
@@ -35,16 +35,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 // This test makes sure that the items in the bookmarks and history sidebar
 // panels are clickable in both LTR and RTL modes.
 
 function test() {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   const BOOKMARKS_SIDEBAR_ID = "viewBookmarksSidebar";
   const BOOKMARKS_SIDEBAR_TREE_ID = "bookmarks-view";
   const HISTORY_SIDEBAR_ID = "viewHistorySidebar";
   const HISTORY_SIDEBAR_TREE_ID = "historyTree";
   const TEST_URL = "http://mochi.test:8888/browser/browser/components/places/tests/browser/sidebarpanels_click_test_page.html";
 
   // If a sidebar is already open, close it.
--- a/browser/devtools/highlighter/test/browser_inspector_breadcrumbs.js
+++ b/browser/devtools/highlighter/test/browser_inspector_breadcrumbs.js
@@ -1,14 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test()
 {
   waitForExplicitFinish();
+  ignoreAllUncaughtExceptions();
 
   let nodes = [
     {nodeId: "i1111", result: "i1 i11 i111 i1111"},
     {nodeId: "i22", result: "i2 i22 i221"},
     {nodeId: "i2111", result: "i2 i21 i211 i2111"},
     {nodeId: "i21", result: "i2 i21 i211 i2111"},
     {nodeId: "i22211", result: "i2 i22 i222 i2221 i22211"},
     {nodeId: "i22", result: "i2 i22 i222 i2221 i22211"},
--- a/dom/plugins/test/mochitest/test_crash_submit.xul
+++ b/dom/plugins/test/mochitest/test_crash_submit.xul
@@ -11,16 +11,17 @@
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
 <body xmlns="http://www.w3.org/1999/xhtml" onload="runTests()">
 <embed id="plugin1" type="application/x-test" width="200" height="200"></embed>
 </body>
 <script class="testbody" type="application/javascript">
 <![CDATA[
 SimpleTest.waitForExplicitFinish();
+SimpleTest.ignoreAllUncaughtExceptions();
 
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 var crashReporter =
   Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
     .getService(Components.interfaces.nsICrashReporter);
 var oldServerURL = crashReporter.serverURL;
--- a/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul
+++ b/extensions/spellcheck/tests/chrome/test_add_remove_dictionaries.xul
@@ -7,16 +7,17 @@
   <title>Add and remove dictionaries test</title>
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript">
   <![CDATA[
 SimpleTest.waitForExplicitFinish();
+SimpleTest.ignoreAllUncaughtExceptions();
 
 function getMisspelledWords(editor) {
   return editor.selectionController.getSelection(Components.interfaces.nsISelectionController.SELECTION_SPELLCHECK).toString();
 }
 
 function getDictionaryList(editor) {
   var spellchecker = editor.getInlineSpellChecker(true).spellChecker;
   var o1 = {};
--- a/layout/xul/base/test/test_resizer.xul
+++ b/layout/xul/base/test/test_resizer.xul
@@ -11,16 +11,17 @@ XUL <resizer> tests
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
     SimpleTest.waitForExplicitFinish();
+    SimpleTest.ignoreAllUncaughtExceptions();
 
     function openPopup()
     {
       document.getElementById("panel").
         openPopupAtScreen(Math.round(window.mozInnerScreenX) + window.innerWidth - 130,
                           Math.round(window.mozInnerScreenY) + window.innerHeight - 130);
     }
 
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -466,16 +466,20 @@ function testScope(aTester, aTest) {
   this.copyToProfile = function test_copyToProfile(filename) {
     self.SimpleTest.copyToProfile(filename);
   };
 
   this.expectUncaughtException = function test_expectUncaughtException() {
     self.SimpleTest.expectUncaughtException();
   };
 
+  this.ignoreAllUncaughtExceptions = function test_ignoreAllUncaughtExceptions() {
+    self.SimpleTest.ignoreAllUncaughtExceptions();
+  };
+
   this.finish = function test_finish() {
     self.__done = true;
     if (self.SimpleTest._expectingUncaughtException) {
       self.ok(false, "expectUncaughtException was called but no uncaught exception was detected!");
     }
     if (self.__waitTimer) {
       self.executeSoon(function() {
         if (self.__done && self.__waitTimer) {
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -10,34 +10,45 @@
  *
  * NOTE: Pay attention to cross-browser compatibility in this file. For
  * instance, do not use const or JS > 1.5 features which are not yet
  * implemented everywhere.
  *
 **/
 
 var SimpleTest = { };
+var parentRunner = null;
+var isPrimaryTestWindow = !!parent.TestRunner;
 
-var parentRunner = null;
-if (parent) {
-    parentRunner = parent.TestRunner;
-    if (!parentRunner && parent.wrappedJSObject) {
-        parentRunner = parent.wrappedJSObject.TestRunner;
+// Finds the TestRunner for this test run and the SpecialPowers object (in
+// case it is not defined) from a parent/opener window.
+//
+// Finding the SpecialPowers object is needed when we have ChromePowers in
+// harness.xul and we need SpecialPowers in the iframe, and also for tests
+// like test_focus.xul where we open a window which opens another window which
+// includes SimpleTest.js.
+(function() {
+    function ancestor(w) {
+        return w.parent != w ? w.parent : w.opener;
     }
 
-    // This is the case where we have ChromePowers in harness.xul and we need it in the iframe
-    if (window.SpecialPowers == undefined && parent.SpecialPowers !== undefined) {
-        window.SpecialPowers = parent.SpecialPowers;
+    var w = ancestor(window);
+    while (w && (!parentRunner || !window.SpecialPowers)) {
+        if (!parentRunner) {
+            parentRunner = w.TestRunner;
+            if (!parentRunner && w.wrappedJSObject) {
+                parentRunner = w.wrappedJSObject.TestRunner;
+            }
+        }
+        if (!window.SpecialPowers) {
+            window.SpecialPowers = w.SpecialPowers;
+        }
+        w = ancestor(w);
     }
-}
-
-// Workaround test_focus.xul where we open a window which opens another window which includes SimpleTest.js
-if (window.SpecialPowers == undefined && window.opener && window.opener.SpecialPowers !== undefined) {
-    window.SpecialPowers = window.opener.SpecialPowers;
-}
+})();
 
 /* Helper functions pulled out of various MochiKit modules */
 if (typeof(repr) == 'undefined') {
     function repr(o) {
         if (typeof(o) == "undefined") {
             return "undefined";
         } else if (o === null) {
             return "null";
@@ -698,22 +709,32 @@ SimpleTest.expectChildProcessCrash = fun
 /**
  * Indicates to the test framework that the next uncaught exception during
  * the test is expected, and should not cause a test failure.
  */
 SimpleTest.expectUncaughtException = function () {
     SimpleTest._expectingUncaughtException = true;
 };
 
+/**
+ * Indicates to the test framework that all of the uncaught exceptions
+ * during the test are known problems that should be fixed in the future,
+ * but which should not cause the test to fail currently.
+ */
+SimpleTest.ignoreAllUncaughtExceptions = function () {
+    SimpleTest._ignoringAllUncaughtExceptions = true;
+};
 
-addLoadEvent(function() {
-    if (SimpleTest._stopOnLoad) {
-        SimpleTest.finish();
-    }
-});
+if (isPrimaryTestWindow) {
+    addLoadEvent(function() {
+        if (SimpleTest._stopOnLoad) {
+            SimpleTest.finish();
+        }
+    });
+}
 
 //  --------------- Test.Builder/Test.More isDeeply() -----------------
 
 
 SimpleTest.DNE = {dne: 'Does not exist'};
 SimpleTest.LF = "\r\n";
 SimpleTest._isRef = function (object) {
     var type = typeof(object);
@@ -919,29 +940,28 @@ var isDeeply = SimpleTest.isDeeply;
 var info = SimpleTest.info;
 
 var gOldOnError = window.onerror;
 window.onerror = function simpletestOnerror(errorMsg, url, lineNumber) {
     var funcIdentifier = "[SimpleTest/SimpleTest.js, window.onerror]";
 
     // Log the message.
     // XXX Chrome mochitests sometimes trigger this window.onerror handler,
-    // but there are a number of uncaught JS exceptions from those tests
-    // currently, so we can't log them as errors just yet.  For now, when
-    // not in a plain mochitest, just dump it so that the error is visible but
-    // doesn't cause a test failure.  See bug 652494.
+    // but there are a number of uncaught JS exceptions from those tests.
+    // For now, for tests that self identify as having unintentional uncaught
+    // exceptions, just dump it so that the error is visible but doesn't cause
+    // a test failure.  See bug 652494.
     var message = "An error occurred: " + errorMsg + " at " + url + ":" + lineNumber;
     var href = SpecialPowers.getPrivilegedProps(window, 'location.href');
-    var isPlainMochitest = href.substring(0,7) != "chrome:";
     var isExpected = !!SimpleTest._expectingUncaughtException;
-    if (isPlainMochitest) {
+    if (!SimpleTest._ignoringAllUncaughtExceptions) {
         SimpleTest.ok(isExpected, funcIdentifier, message);
         SimpleTest._expectingUncaughtException = false;
     } else {
-        SimpleTest.info(funcIdentifier + " " + message);
+        SimpleTest.todo(false, funcIdentifier, message);
     }
     // There is no Components.stack.caller to log. (See bug 511888.)
 
     // Call previous handler.
     if (gOldOnError) {
         try {
             // Ignore return value: always run default handler.
             gOldOnError(errorMsg, url, lineNumber);