Bug 1419102 - Add hot-restart of the engine to support test, and create test infrastructure. r=Mossop draft
authorFelipe Gomes <felipc@gmail.com>
Fri, 19 Jan 2018 12:48:59 -0200
changeset 722686 1793ca584c5cc13c843b227192c98d3ff34888a2
parent 722685 28caf609b51c46bff19a5bc143cb9c01ee5d60b7
child 722687 8e36f77b1c8fd2cc3b4889ab978f45de6b7afde8
push id96199
push userfelipc@gmail.com
push dateFri, 19 Jan 2018 15:05:44 +0000
reviewersMossop
bugs1419102
milestone59.0a1
Bug 1419102 - Add hot-restart of the engine to support test, and create test infrastructure. r=Mossop MozReview-Commit-ID: BXbwXlDh3nP
browser/components/enterprisepolicies/EnterprisePolicies.js
browser/components/enterprisepolicies/EnterprisePoliciesContent.js
browser/components/enterprisepolicies/Policies.jsm
browser/components/enterprisepolicies/moz.build
browser/components/enterprisepolicies/tests/browser/.eslintrc.js
browser/components/enterprisepolicies/tests/browser/browser.ini
browser/components/enterprisepolicies/tests/browser/browser_policies_broken_json.js
browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
browser/components/enterprisepolicies/tests/browser/config_broken_json.json
browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
browser/components/enterprisepolicies/tests/browser/head.js
browser/components/enterprisepolicies/tests/moz.build
--- a/browser/components/enterprisepolicies/EnterprisePolicies.js
+++ b/browser/components/enterprisepolicies/EnterprisePolicies.js
@@ -58,16 +58,17 @@ const EnterprisePoliciesFactory = {
 
 // Constructor
 function EnterprisePoliciesManager() {
   LOG("STARTING CONSTRUCTOR");
 
   Services.obs.addObserver(this, "profile-after-change", true);
   Services.obs.addObserver(this, "final-ui-startup", true);
   Services.obs.addObserver(this, "sessionstore-windows-restored", true);
+  Services.obs.addObserver(this, "EnterprisePolicies:Restart", true);
 }
 
 EnterprisePoliciesManager.prototype = {
   // for XPCOM
   classID:          Components.ID("{ea4e1414-779b-458b-9d1f-d18e8efbc145}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIEnterprisePolicies]),
@@ -148,30 +149,71 @@ EnterprisePoliciesManager.prototype = {
     while (callbacks.length > 0) {
       let callback = callbacks.shift();
       try {
         callback();
       } catch (ex) {}
     }
   },
 
+  _restart() {
+    if (!Cu.isInAutomation) {
+      return;
+    }
+    LOG("Restarting");
+
+    DisallowedFeatures = {};
+
+    this._status = Ci.nsIEnterprisePolicies.UNINITIALIZED;
+    for (let timing of Object.keys(this._callbacks)) {
+      this._callbacks[timing] = [];
+    }
+    delete Services.ppmm.initialProcessData.policies;
+    Services.ppmm.broadcastAsyncMessage("EnterprisePolicies:Restart", null);
+
+    // Simulate the startup process. This is ugly but it tries to ensure
+    // the same behavior as of a normal startup.
+    Services.tm.idleDispatchToMainThread(() => {
+      this.observe(null, "profile-after-change", null);
+
+      Services.tm.idleDispatchToMainThread(() => {
+        this.observe(null, "final-ui-startup", null);
+
+        Services.tm.idleDispatchToMainThread(() => {
+          this.observe(null, "sessionstore-windows-restored", null);
+        });
+      });
+    });
+  },
+
+
   // nsIObserver implementation
   observe: function BG_observe(subject, topic, data) {
     switch (topic) {
       case "profile-after-change":
+        // Before the first set of policy callbacks runs, we must
+        // initialize the service.
         this._initialize();
         this._runPoliciesCallbacks("ProfileAfterChange");
         break;
 
       case "final-ui-startup":
         this._runPoliciesCallbacks("BeforeUIStartup");
         break;
 
       case "sessionstore-windows-restored":
         this._runPoliciesCallbacks("AllWindowsRestored");
+
+        // After the last set of policy callbacks ran, notify the test observer.
+        Services.obs.notifyObservers(null,
+                                     "EnterprisePolicies:AllPoliciesApplied");
+        break;
+
+      case "EnterprisePolicies:Restart":
+        this._restart();
         break;
     }
   },
 
   disallowFeature(feature, neededOnContentProcess = false) {
     DisallowedFeatures[feature] = true;
 
     // NOTE: For optimization purposes, only features marked as needed
--- a/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
+++ b/browser/components/enterprisepolicies/EnterprisePoliciesContent.js
@@ -37,35 +37,41 @@ function EnterprisePoliciesManagerConten
   if (policies) {
     this._status = policies.status;
     // make a copy of the array so that we can keep adding to it
     // in a way that is not confusing.
     this._disallowedFeatures = policies.disallowedFeatures.slice();
   }
 
   Services.cpmm.addMessageListener("EnterprisePolicies:DisallowFeature", this);
+  Services.cpmm.addMessageListener("EnterprisePolicies:Restart", this);
 }
 
 EnterprisePoliciesManagerContent.prototype = {
   // for XPCOM
   classID:          Components.ID("{dc6358f8-d167-4566-bf5b-4350b5e6a7a2}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                          Ci.nsIEnterprisePolicies]),
 
   // redefine the default factory for XPCOMUtils
   _xpcom_factory: EnterprisePoliciesFactory,
 
   _status: Ci.nsIEnterprisePolicies.INACTIVE,
 
   _disallowedFeatures: [],
 
   receiveMessage({name, data}) {
-    LOG(`received message: {name}, with data.feature: ${data.feature}`);
-    if (name == "EnterprisePolicies:DisallowFeature") {
-      this._disallowedFeatures.push(data.feature);
+    switch (name) {
+      case "EnterprisePolicies:DisallowFeature":
+        this._disallowedFeatures.push(data.feature);
+        break;
+
+      case "EnterprisePolicies:Restart":
+        this._disallowedFeatures = [];
+        break;
     }
   },
 
   get status() {
     return this._status;
   },
 
   isAllowed(feature) {
--- a/browser/components/enterprisepolicies/Policies.jsm
+++ b/browser/components/enterprisepolicies/Policies.jsm
@@ -32,10 +32,10 @@ this.Policies = {
   "block_devtools": {
     onProfileAfterChange(manager, param) {
       if (param == true) {
         manager.disallowFeature("devtools");
       }
     }
   },
 
-  "bookmarks_on_menu": {}
+  "bookmarks_on_menu": {},
 };
--- a/browser/components/enterprisepolicies/moz.build
+++ b/browser/components/enterprisepolicies/moz.build
@@ -7,29 +7,29 @@
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Enterprise Policies")
 
 DIRS += [
     'helpers',
     'schemas',
 ]
 
+TEST_DIRS += [
+	'tests'
+]
+
 XPIDL_SOURCES += [
     'nsIEnterprisePolicies.idl',
 ]
 
 XPIDL_MODULE = 'enterprisepolicies'
 
 EXTRA_COMPONENTS += [
     'EnterprisePolicies.js',
     'EnterprisePolicies.manifest',
     'EnterprisePoliciesContent.js',
 ]
 
 EXTRA_JS_MODULES.policies += [
     'Policies.jsm',
 ]
 
-#BROWSER_CHROME_MANIFESTS += [
-#    'tests/browser/browser.ini'
-#]
-
 FINAL_LIBRARY = 'browsercomps'
copy from browser/components/newtab/tests/browser/.eslintrc.js
copy to browser/components/enterprisepolicies/tests/browser/.eslintrc.js
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+prefs =
+  browser.policies.enabled=true
+support-files =
+  head.js
+  config_simple_policies.json
+  config_broken_json.json
+
+[browser_policies_broken_json.js]
+[browser_policies_simple_policies.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_broken_json.js
@@ -0,0 +1,15 @@
+/* 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";
+
+add_task(async function test_clean_slate() {
+  await startWithCleanSlate();
+});
+
+add_task(async function test_broken_json() {
+  await setupPolicyEngineWithJson("config_broken_json.json");
+
+  is(Services.policies.status, Ci.nsIEnterprisePolicies.FAILED, "Engine was correctly set to the error state");
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/browser_policies_simple_policies.js
@@ -0,0 +1,88 @@
+/* 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";
+
+add_task(async function test_clean_slate() {
+  await startWithCleanSlate();
+});
+
+add_task(async function test_simple_policies() {
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+    // Initialize the service in the content process, in case it hasn't
+    // already started.
+    Services.policies;
+  });
+
+  let { Policies } = Cu.import("resource:///modules/policies/Policies.jsm", {});
+
+  let policy1Ran = false, policy2Ran = false, policy3Ran = false;
+
+  // Implement functions to handle the three simple policies that will be added
+  // to the schema.
+  Policies.simple_policy1 = {
+    onProfileAfterChange(manager, param) {
+      is(param, true, "Param matches what was passed in config file");
+      manager.disallowFeature("feature1", /* needed in content process */ true);
+      policy1Ran = true;
+    }
+  };
+
+  Policies.simple_policy2 = {
+    onBeforeUIStartup(manager, param) {
+      is(param, true, "Param matches what was passed in config file");
+      manager.disallowFeature("feature2", /* needed in content process */ false);
+      policy2Ran = true;
+    }
+  };
+
+  Policies.simple_policy3 = {
+    onAllWindowsRestored(manager, param) {
+      is(param, false, "Param matches what was passed in config file");
+      policy3Ran = true;
+    }
+  };
+
+  await setupPolicyEngineWithJson(
+    "config_simple_policies.json",
+    /* custom schema */
+    {
+      properties: {
+        "simple_policy1": {
+          "type": "boolean"
+        },
+
+        "simple_policy2": {
+          "type": "boolean"
+        },
+
+        "simple_policy3": {
+          "type": "boolean"
+        }
+
+      }
+    }
+  );
+
+  is(Services.policies.status, Ci.nsIEnterprisePolicies.ACTIVE, "Engine is active");
+  is(Services.policies.isAllowed("feature1"), false, "Dummy feature was disallowed");
+  is(Services.policies.isAllowed("feature2"), false, "Dummy feature was disallowed");
+
+  ok(policy1Ran, "Policy 1 ran correctly through onProfileAfterChange");
+  ok(policy2Ran, "Policy 2 ran correctly through onBeforeUIStartup");
+  ok(policy3Ran, "Policy 3 ran correctly through onAllWindowsRestored");
+
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
+    if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+      is(Services.policies.isAllowed("feature1"), false, "Correctly disallowed in the content process");
+      // Feature 2 wasn't explictly marked as needed in the content process, so it is not marked
+      // as disallowed there.
+      is(Services.policies.isAllowed("feature2"), true, "Correctly missing in the content process");
+    }
+  });
+
+  delete Policies.simple_policy1;
+  delete Policies.simple_policy2;
+  delete Policies.simple_policy3;
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/config_broken_json.json
@@ -0,0 +1,3 @@
+{
+  "policies
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/config_simple_policies.json
@@ -0,0 +1,7 @@
+{
+  "policies": {
+    "simple_policy1": true,
+    "simple_policy2": true,
+    "simple_policy3": false
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/browser/head.js
@@ -0,0 +1,34 @@
+/* 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";
+
+async function setupPolicyEngineWithJson(jsonName, customSchema) {
+  let filePath = getTestFilePath(jsonName ? jsonName : "non-existing-file.json");
+  Services.prefs.setStringPref("browser.policies.alternatePath", filePath);
+
+  let resolve = null;
+  let promise = new Promise((r) => resolve = r);
+
+  Services.obs.addObserver(function observer() {
+    Services.obs.removeObserver(observer, "EnterprisePolicies:AllPoliciesApplied");
+    resolve();
+  }, "EnterprisePolicies:AllPoliciesApplied");
+
+  // Clear any previously used custom schema
+  Cu.unload("resource:///modules/policies/schema.jsm");
+
+  if (customSchema) {
+    let schemaModule = Cu.import("resource:///modules/policies/schema.jsm", {});
+    schemaModule.schema = customSchema;
+  }
+
+  Services.obs.notifyObservers(null, "EnterprisePolicies:Restart");
+  return promise;
+}
+
+async function startWithCleanSlate() {
+  await setupPolicyEngineWithJson("");
+  is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/enterprisepolicies/tests/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("Firefox", "General")
+
+BROWSER_CHROME_MANIFESTS += [
+    'browser/browser.ini'
+]