Bug 1446833: Part 1 - Stop throwing raw numeric error codes in xpcshell/head.js. r?florian draft
authorKris Maglione <maglione.k@gmail.com>
Sun, 18 Mar 2018 15:27:18 -0700
changeset 769189 79692462549fc0240459000b0b490d046f473b67
parent 769188 bd30cc25e44bd60e3c4eb832b5689cb76cd11105
child 769190 7fd21a311567e1979e73494cc26b6ba04a36cf2b
push id103063
push usermaglione.k@gmail.com
push dateSun, 18 Mar 2018 23:28:42 +0000
reviewersflorian
bugs1446833
milestone61.0a1
Bug 1446833: Part 1 - Stop throwing raw numeric error codes in xpcshell/head.js. r?florian The xpcshell harness expects to catch the Cr result codes that it throws, but it often fails when an assertion is used in asynchronous code. That results in a raw numeric error code being reported, with no location information available to figure out why or where the test is failing. Throwing an actual Exception object maintains location information for use when the error is reported. MozReview-Commit-ID: InuytWhDxBP
testing/xpcshell/head.js
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -30,23 +30,25 @@ var _cleanupFunctions = [];
 var _pendingTimers = [];
 var _profileInitialized = false;
 
 // Assigned in do_load_child_test_harness.
 var _XPCSHELL_PROCESS;
 
 // Register the testing-common resource protocol early, to have access to its
 // modules.
-var _Services = ChromeUtils.import("resource://gre/modules/Services.jsm", {}).Services;
+var _Services = ChromeUtils.import("resource://gre/modules/Services.jsm", null).Services;
 _register_modules_protocol_handler();
 
 var _PromiseTestUtils = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", {}).PromiseTestUtils;
-var _Task = ChromeUtils.import("resource://gre/modules/Task.jsm", {}).Task;
+var _Task = ChromeUtils.import("resource://gre/modules/Task.jsm", null).Task;
 
-let _NetUtil = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {}).NetUtil;
+let _NetUtil = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", null).NetUtil;
+
+let _XPCOMUtils = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", null).XPCOMUtils;
 
 Cu.importGlobalProperties(["XMLHttpRequest"]);
 
 // Support a common assertion library, Assert.jsm.
 var AssertCls = ChromeUtils.import("resource://testing-common/Assert.jsm", null).Assert;
 // Pass a custom report function for xpcshell-test style reporting.
 var Assert = new AssertCls(function(err, message, stack) {
   if (err) {
@@ -126,23 +128,17 @@ try {
 // of the test.
 try {
   let levelNames = {};
   for (let level of ["debug", "info", "warn", "error"]) {
     levelNames[Ci.nsIConsoleMessage[level]] = level;
   }
 
   let listener = {
-    QueryInterface(iid) {
-      if (!iid.equals(Ci.nsISupports) &&
-          !iid.equals(Ci.nsIConsoleListener)) {
-        throw Cr.NS_NOINTERFACE;
-      }
-      return this;
-    },
+    QueryInterface: _XPCOMUtils.generateQI(["nsIConsoleListener"]),
     observe(msg) {
       if (typeof info === "function")
         info("CONSOLE_MESSAGE: (" + levelNames[msg.logLevel] + ") " + msg.toString());
     }
   };
   // Don't use _Services.console here as it causes one of the devtools tests
   // to fail, probably due to initializing Services.console too early.
   // eslint-disable-next-line mozilla/use-services
@@ -173,23 +169,17 @@ function _Timer(func, delay) {
   var timer = Cc["@mozilla.org/timer;1"]
                 .createInstance(Ci.nsITimer);
   timer.initWithCallback(this, delay + _timerFuzz, timer.TYPE_ONE_SHOT);
 
   // Keep timer alive until it fires
   _pendingTimers.push(timer);
 }
 _Timer.prototype = {
-  QueryInterface(iid) {
-    if (iid.equals(Ci.nsITimerCallback) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
+  QueryInterface: _XPCOMUtils.generateQI(["nsITimerCallback"]),
 
   notify(timer) {
     _pendingTimers.splice(_pendingTimers.indexOf(timer), 1);
 
     // The current nsITimer implementation can undershoot, but even if it
     // couldn't, paranoia is probably a virtue here given the potential for
     // random orange on tinderboxen.
     var end = Date.now();
@@ -274,30 +264,24 @@ var _fakeIdleService = {
       delete this.originalFactory;
     }
   },
 
   factory: {
     // nsIFactory
     createInstance(aOuter, aIID) {
       if (aOuter) {
-        throw Cr.NS_ERROR_NO_AGGREGATION;
+        throw Components.Exception("", Cr.NS_ERROR_NO_AGGREGATION);
       }
       return _fakeIdleService.QueryInterface(aIID);
     },
     lockFactory(aLock) {
-      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+      throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
     },
-    QueryInterface(aIID) {
-      if (aIID.equals(Ci.nsIFactory) ||
-          aIID.equals(Ci.nsISupports)) {
-        return this;
-      }
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: _XPCOMUtils.generateQI(["nsIFactory"]),
   },
 
   // nsIIdleService
   get idleTime() {
     return 0;
   },
   addIdleObserver() {},
   removeIdleObserver() {},
@@ -306,17 +290,17 @@ var _fakeIdleService = {
     // Useful for testing purposes, see test_get_idle.js.
     if (aIID.equals(Ci.nsIFactory)) {
       return this.factory;
     }
     if (aIID.equals(Ci.nsIIdleService) ||
         aIID.equals(Ci.nsISupports)) {
       return this;
     }
-    throw Cr.NS_ERROR_NO_INTERFACE;
+    throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
   }
 };
 
 /**
  * Restores the idle service factory if needed and returns the service's handle.
  * @return A handle to the idle service.
  */
 function do_get_idle() {
@@ -539,17 +523,17 @@ function _execute_test() {
   } catch (e) {
     _passed = false;
     // do_check failures are already logged and set _quit to true and throw
     // NS_ERROR_ABORT. If both of those are true it is likely this exception
     // has already been logged so there is no need to log it again. It's
     // possible that this will mask an NS_ERROR_ABORT that happens after a
     // do_check failure though.
 
-    if (!_quit || e != Cr.NS_ERROR_ABORT) {
+    if (!_quit || e.result != Cr.NS_ERROR_ABORT) {
       let extra = {};
       if (e.fileName) {
         extra.source_file = e.fileName;
         if (e.lineNumber) {
           extra.line_number = e.lineNumber;
         }
       } else {
         extra.source_file = "xpcshell/head.js";
@@ -684,17 +668,17 @@ function executeSoon(callback, aName) {
       try {
         callback();
       } catch (e) {
         // do_check failures are already logged and set _quit to true and throw
         // NS_ERROR_ABORT. If both of those are true it is likely this exception
         // has already been logged so there is no need to log it again. It's
         // possible that this will mask an NS_ERROR_ABORT that happens after a
         // do_check failure though.
-        if (!_quit || e != Cr.NS_ERROR_ABORT) {
+        if (!_quit || e.result != Cr.NS_ERROR_ABORT) {
           let stack = e.stack ? _format_stack(e.stack) : null;
           _testLogger.testStatus(_TEST_NAME,
                                  funcName,
                                  "FAIL",
                                  "PASS",
                                  _exception_message(e),
                                  stack);
           _do_quit();
@@ -731,17 +715,17 @@ function do_throw(error, stack) {
                     });
   _abort_failed_test();
 }
 
 function _abort_failed_test() {
   // Called to abort the test run after all failures are logged.
   _passed = false;
   _do_quit();
-  throw Cr.NS_ERROR_ABORT;
+  throw Components.Exception("", Cr.NS_ERROR_ABORT);
 }
 
 function _format_stack(stack) {
   let normalized;
   if (stack instanceof Ci.nsIStackFrame) {
     let frames = [];
     for (let frame = stack; frame; frame = frame.caller) {
       frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
@@ -782,17 +766,17 @@ function do_report_unexpected_exception(
 
   _passed = false;
   _testLogger.error(text + "Unexpected exception " + _exception_message(ex),
                     {
                       source_file: filename,
                       stack: _format_stack(ex.stack)
                     });
   _do_quit();
-  throw Cr.NS_ERROR_ABORT;
+  throw Components.Exception("", Cr.NS_ERROR_ABORT);
 }
 
 function do_note_exception(ex, text) {
   let filename = Components.stack.caller.filename;
   _testLogger.info(text + "Swallowed exception " + _exception_message(ex),
                    {
                      source_file: filename,
                      stack: _format_stack(ex.stack)
@@ -1113,23 +1097,17 @@ function do_get_profile(notifyProfileAft
     getFile(prop, persistent) {
       persistent.value = true;
       if (prop == "ProfD" || prop == "ProfLD" || prop == "ProfDS" ||
           prop == "ProfLDS" || prop == "TmpD") {
         return file.clone();
       }
       return null;
     },
-    QueryInterface(iid) {
-      if (iid.equals(Ci.nsIDirectoryServiceProvider) ||
-          iid.equals(Ci.nsISupports)) {
-        return this;
-      }
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+    QueryInterface: _XPCOMUtils.generateQI(["nsIDirectoryServiceProvider"]),
   };
   _Services.dirsvc.QueryInterface(Ci.nsIDirectoryService)
            .registerProvider(provider);
 
   // We need to update the crash events directory when the profile changes.
   if (runningInParent &&
       "@mozilla.org/toolkit/crash-reporter;1" in Cc) {
     let crashReporter =