Bug 1534969 - Ensure that extension.id/uuid is set in browser tests r=aswan
authorRob Wu <rob@robwu.nl>
Wed, 13 Mar 2019 18:48:53 +0000
changeset 521989 57a544b33b511811350a56cd2f4206c2a4dc155a
parent 521988 f32dc8409c3de7b2f7181f034df2176219a728ad
child 521990 f28acbccbc62a92d3116b30f35afcd7203ff07d7
push id10870
push usernbeleuzu@mozilla.com
push dateFri, 15 Mar 2019 20:00:07 +0000
treeherdermozilla-beta@c594aee5b7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1534969
milestone67.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 1534969 - Ensure that extension.id/uuid is set in browser tests r=aswan Differential Revision: https://phabricator.services.mozilla.com/D23311
testing/specialpowers/content/SpecialPowersObserverAPI.js
toolkit/components/extensions/ExtensionTestCommon.jsm
toolkit/components/extensions/ExtensionXPCShellUtils.jsm
toolkit/components/extensions/test/browser/browser.ini
toolkit/components/extensions/test/browser/browser_ext_test_mock.js
toolkit/components/extensions/test/xpcshell/test_ext_test_mock.js
toolkit/components/extensions/test/xpcshell/xpcshell.ini
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -596,19 +596,28 @@ SpecialPowersObserverAPI.prototype = {
         extension.on("test-message", messageListener);
 
         this._extensions.set(id, extension);
         return undefined;
       }
 
       case "SPStartupExtension": {
         let id = aMessage.data.id;
+        // This is either an Extension, or (if useAddonManager is set) a MockExtension.
         let extension = this._extensions.get(id);
-        extension.on("startup", () => {
-          this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [extension.id, extension.uuid]});
+        extension.on("startup", (eventName, ext) => {
+          if (!ext) {
+            // ext is only set by the "startup" event from Extension.jsm.
+            // Unfortunately ext-backgroundPage.js emits an event with the same
+            // name, but without the extension object as parameter.
+            return;
+          }
+          // ext is always the "real" Extension object, even when "extension"
+          // is a MockExtension.
+          this._sendReply(aMessage, "SPExtensionMessage", {id, type: "extensionSetId", args: [ext.id, ext.uuid]});
         });
 
         // Make sure the extension passes the packaging checks when
         // they're run on a bare archive rather than a running instance,
         // as the add-on manager runs them.
         let extensionData = new ExtensionData(extension.rootURI);
         extensionData.loadManifest().then(
           () => {
--- a/toolkit/components/extensions/ExtensionTestCommon.jsm
+++ b/toolkit/components/extensions/ExtensionTestCommon.jsm
@@ -106,16 +106,25 @@ class MockExtension {
   testMessage(...args) {
     return this._extension.testMessage(...args);
   }
 
   on(...args) {
     this._extensionPromise.then(extension => {
       extension.on(...args);
     });
+    // Extension.jsm emits a "startup" event on |extension| before emitting the
+    // "startup" event on |apiManager|. Trigger the "startup" event anyway, to
+    // make sure that the MockExtension behaves like an Extension with regards
+    // to the startup event.
+    if (args[0] === "startup" && !this._extension) {
+      this._extensionPromise.then(extension => {
+        args[1]("startup", extension);
+      });
+    }
   }
 
   off(...args) {
     this._extensionPromise.then(extension => {
       extension.off(...args);
     });
   }
 
--- a/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
+++ b/toolkit/components/extensions/ExtensionXPCShellUtils.jsm
@@ -250,17 +250,16 @@ class ExtensionWrapper {
       } else if (this.state == "unloading") {
         this.testScope.equal(this.state, "unloaded", "Extension not fully unloaded at test shutdown");
       }
       this.destroy();
     });
 
     if (extension) {
       this.id = extension.id;
-      this.uuid = extension.uuid;
       this.attachExtension(extension);
     }
   }
 
   destroy() {
     // This method should be implemented in subclasses which need to
     // perform cleanup when destroyed.
   }
@@ -273,16 +272,17 @@ class ExtensionWrapper {
     if (this.extension) {
       this.extension.off("test-eq", this.handleResult);
       this.extension.off("test-log", this.handleResult);
       this.extension.off("test-result", this.handleResult);
       this.extension.off("test-done", this.handleResult);
       this.extension.off("test-message", this.handleMessage);
       this.clearMessageQueues();
     }
+    this.uuid = extension.uuid;
     this.extension = extension;
 
     extension.on("test-eq", this.handleResult);
     extension.on("test-log", this.handleResult);
     extension.on("test-result", this.handleResult);
     extension.on("test-done", this.handleResult);
     extension.on("test-message", this.handleMessage);
 
--- a/toolkit/components/extensions/test/browser/browser.ini
+++ b/toolkit/components/extensions/test/browser/browser.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_ext_management_themes.js]
 skip-if = verify
+[browser_ext_test_mock.js]
 [browser_ext_themes_additional_backgrounds_alignment.js]
 [browser_ext_themes_alpha_accentcolor.js]
 [browser_ext_themes_chromeparity.js]
 [browser_ext_themes_dynamic_getCurrent.js]
 [browser_ext_themes_dynamic_onUpdated.js]
 [browser_ext_themes_dynamic_updates.js]
 [browser_ext_themes_experiment.js]
 [browser_ext_themes_getCurrent_differentExt.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/browser/browser_ext_test_mock.js
@@ -0,0 +1,45 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// This test verifies that the extension mocks behave consistently, regardless
+// of test type (xpcshell vs browser test).
+// See also toolkit/components/extensions/test/xpcshell/test_ext_test_mock.js
+
+// Check the state of the extension object. This should be consistent between
+// browser tests and xpcshell tests.
+async function checkExtensionStartupAndUnload(ext) {
+  await ext.startup();
+  Assert.ok(ext.id, "Extension ID should be available");
+  Assert.ok(ext.uuid, "Extension UUID should be available");
+  await ext.unload();
+  // Once set nothing clears the UUID.
+  Assert.ok(ext.uuid, "Extension UUID exists after unload");
+}
+
+add_task(async function test_MockExtension() {
+  // When "useAddonManager" is set, a MockExtension is created in the main
+  // process, which does not necessarily behave identically to an Extension.
+  let ext = ExtensionTestUtils.loadExtension({
+    // xpcshell/test_ext_test_mock.js tests "temporary", so here we use
+    // "permanent" to have even more test coverage.
+    useAddonManager: "permanent",
+    manifest: {applications: {gecko: {id: "@permanent-mock-extension"}}},
+  });
+
+  Assert.ok(!ext.id, "Extension ID is initially unavailable");
+  Assert.ok(!ext.uuid, "Extension UUID is initially unavailable");
+  await checkExtensionStartupAndUnload(ext);
+  Assert.ok(ext.id, "Extension ID exists after unload");
+});
+
+add_task(async function test_generated_Extension() {
+  let ext = ExtensionTestUtils.loadExtension({
+    manifest: {},
+  });
+
+  Assert.ok(!ext.id, "Extension ID is initially unavailable");
+  Assert.ok(!ext.uuid, "Extension UUID is initially unavailable");
+  await checkExtensionStartupAndUnload(ext);
+  Assert.ok(ext.id, "Extension ID exists after unload");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_test_mock.js
@@ -0,0 +1,53 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// This test verifies that the extension mocks behave consistently, regardless
+// of test type (xpcshell vs browser test).
+// See also toolkit/components/extensions/test/browser/browser_ext_test_mock.js
+
+// Check the state of the extension object. This should be consistent between
+// browser tests and xpcshell tests.
+async function checkExtensionStartupAndUnload(ext) {
+  await ext.startup();
+  Assert.ok(ext.id, "Extension ID should be available");
+  Assert.ok(ext.uuid, "Extension UUID should be available");
+  await ext.unload();
+  // Once set nothing clears the UUID.
+  Assert.ok(ext.uuid, "Extension UUID exists after unload");
+}
+
+add_task(async function setup() {
+  await ExtensionTestUtils.startAddonManager();
+});
+
+add_task(async function test_MockExtension() {
+  let ext = ExtensionTestUtils.loadExtension({
+    useAddonManager: "temporary",
+    manifest: {},
+  });
+
+  Assert.equal(ext.constructor.name, "InstallableWrapper", "expected class");
+  Assert.ok(!ext.id, "Extension ID is initially unavailable");
+  Assert.ok(!ext.uuid, "Extension UUID is initially unavailable");
+  await checkExtensionStartupAndUnload(ext);
+  // When useAddonManager is set, AOMExtensionWrapper clears the ID upon unload.
+  // TODO: Fix AOMExtensionWrapper to not clear the ID after unload, and move
+  // this assertion inside |checkExtensionStartupAndUnload| (since then the
+  // behavior will be consistent across all test types).
+  Assert.ok(!ext.id, "Extension ID is cleared after unload");
+});
+
+add_task(async function test_generated_Extension() {
+  let ext = ExtensionTestUtils.loadExtension({
+    manifest: {},
+  });
+
+  Assert.equal(ext.constructor.name, "ExtensionWrapper", "expected class");
+  // Without "useAddonManager", an Extension is generated and their IDs are
+  // immediately available.
+  Assert.ok(ext.id, "Extension ID is initially available");
+  Assert.ok(ext.uuid, "Extension UUID is initially available");
+  await checkExtensionStartupAndUnload(ext);
+  Assert.ok(ext.id, "Extension ID exists after unload");
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -44,16 +44,17 @@ skip-if = toolkit == 'android' # browser
 [test_ext_schemas.js]
 [test_ext_schemas_roots.js]
 [test_ext_schemas_async.js]
 [test_ext_schemas_allowed_contexts.js]
 [test_ext_schemas_interactive.js]
 [test_ext_schemas_manifest_permissions.js]
 [test_ext_schemas_privileged.js]
 [test_ext_schemas_revoke.js]
+[test_ext_test_mock.js]
 [test_ext_unknown_permissions.js]
 [test_load_all_api_modules.js]
 [test_locale_converter.js]
 [test_locale_data.js]
 [test_ext_ipcBlob.js]
 
 [test_ext_runtime_sendMessage_args.js]