summaryrefslogtreecommitdiffstats
path: root/testing/modules/MockRegistrar.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'testing/modules/MockRegistrar.jsm')
-rw-r--r--testing/modules/MockRegistrar.jsm123
1 files changed, 123 insertions, 0 deletions
diff --git a/testing/modules/MockRegistrar.jsm b/testing/modules/MockRegistrar.jsm
new file mode 100644
index 000000000..830888eec
--- /dev/null
+++ b/testing/modules/MockRegistrar.jsm
@@ -0,0 +1,123 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "MockRegistrar",
+];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, manager: Cm} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Log.jsm");
+var logger = Log.repository.getLogger("MockRegistrar");
+
+this.MockRegistrar = Object.freeze({
+ _registeredComponents: new Map(),
+ _originalCIDs: new Map(),
+ get registrar() {
+ return Cm.QueryInterface(Ci.nsIComponentRegistrar);
+ },
+
+ /**
+ * Register a mock to override target interfaces.
+ * The target interface may be accessed through _genuine property of the mock.
+ * If you register multiple mocks to the same contract ID, you have to call
+ * unregister in reverse order. Otherwise the previous factory will not be
+ * restored.
+ *
+ * @param contractID The contract ID of the interface which is overridden by
+ the mock.
+ * e.g. "@mozilla.org/file/directory_service;1"
+ * @param mock An object which implements interfaces for the contract ID.
+ * @param args An array which is passed in the constructor of mock.
+ *
+ * @return The CID of the mock.
+ */
+ register(contractID, mock, args) {
+ let originalCID = this._originalCIDs.get(contractID);
+ if (!originalCID) {
+ originalCID = this.registrar.contractIDToCID(contractID);
+ this._originalCIDs.set(contractID, originalCID);
+ }
+
+ let originalFactory = Cm.getClassObject(originalCID, Ci.nsIFactory);
+
+ let factory = {
+ createInstance(outer, iid) {
+ if (outer) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+
+ let wrappedMock;
+ if (mock.prototype && mock.prototype.constructor) {
+ wrappedMock = Object.create(mock.prototype);
+ mock.apply(wrappedMock, args);
+ } else {
+ wrappedMock = mock;
+ }
+
+ try {
+ let genuine = originalFactory.createInstance(outer, iid);
+ wrappedMock._genuine = genuine;
+ } catch(ex) {
+ logger.info("Creating original instance failed", ex);
+ }
+
+ return wrappedMock.QueryInterface(iid);
+ },
+ lockFactory(lock) {
+ throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+ };
+
+ this.registrar.unregisterFactory(originalCID, originalFactory);
+ this.registrar.registerFactory(originalCID,
+ "A Mock for " + contractID,
+ contractID,
+ factory);
+
+ this._registeredComponents.set(originalCID, {
+ contractID: contractID,
+ factory: factory,
+ originalFactory: originalFactory
+ });
+
+ return originalCID;
+ },
+
+ /**
+ * Unregister the mock.
+ *
+ * @param cid The CID of the mock.
+ */
+ unregister(cid) {
+ let component = this._registeredComponents.get(cid);
+ if (!component) {
+ return;
+ }
+
+ this.registrar.unregisterFactory(cid, component.factory);
+ if (component.originalFactory) {
+ this.registrar.registerFactory(cid,
+ "",
+ component.contractID,
+ component.originalFactory);
+ }
+
+ this._registeredComponents.delete(cid);
+ },
+
+ /**
+ * Unregister all registered mocks.
+ */
+ unregisterAll() {
+ for (let cid of this._registeredComponents.keys()) {
+ this.unregister(cid);
+ }
+ }
+
+});