summaryrefslogtreecommitdiffstats
path: root/toolkit/components/perfmonitoring/tests/browser/head.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/perfmonitoring/tests/browser/head.js')
-rw-r--r--toolkit/components/perfmonitoring/tests/browser/head.js287
1 files changed, 287 insertions, 0 deletions
diff --git a/toolkit/components/perfmonitoring/tests/browser/head.js b/toolkit/components/perfmonitoring/tests/browser/head.js
new file mode 100644
index 000000000..92258fd1b
--- /dev/null
+++ b/toolkit/components/perfmonitoring/tests/browser/head.js
@@ -0,0 +1,287 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/Promise.jsm", this);
+Cu.import("resource://gre/modules/AddonManager.jsm", this);
+Cu.import("resource://gre/modules/AddonWatcher.jsm", this);
+Cu.import("resource://gre/modules/PerformanceWatcher.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
+
+/**
+ * Base class for simulating slow addons/webpages.
+ */
+function CPUBurner(url, jankThreshold) {
+ info(`CPUBurner: Opening tab for ${url}\n`);
+ this.url = url;
+ this.tab = gBrowser.addTab(url);
+ this.jankThreshold = jankThreshold;
+ let browser = this.tab.linkedBrowser;
+ this._browser = browser;
+ ContentTask.spawn(this._browser, null, CPUBurner.frameScript);
+ this.promiseInitialized = BrowserTestUtils.browserLoaded(browser);
+}
+CPUBurner.prototype = {
+ get windowId() {
+ return this._browser.outerWindowID;
+ },
+ /**
+ * Burn CPU until it triggers a listener with the specified jank threshold.
+ */
+ run: Task.async(function*(burner, max, listener) {
+ listener.reset();
+ for (let i = 0; i < max; ++i) {
+ yield new Promise(resolve => setTimeout(resolve, 50));
+ try {
+ yield this[burner]();
+ } catch (ex) {
+ return false;
+ }
+ if (listener.triggered && listener.result >= this.jankThreshold) {
+ return true;
+ }
+ }
+ return false;
+ }),
+ dispose: function() {
+ info(`CPUBurner: Closing tab for ${this.url}\n`);
+ gBrowser.removeTab(this.tab);
+ }
+};
+// This function is injected in all frames
+CPUBurner.frameScript = function() {
+ try {
+ "use strict";
+
+ const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
+ let sandboxes = new Map();
+ let getSandbox = function(addonId) {
+ let sandbox = sandboxes.get(addonId);
+ if (!sandbox) {
+ sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId });
+ sandboxes.set(addonId, sandbox);
+ }
+ return sandbox;
+ };
+
+ let burnCPU = function() {
+ var start = Date.now();
+ var ignored = [];
+ while (Date.now() - start < 500) {
+ ignored[ignored.length % 2] = ignored.length;
+ }
+ };
+ let burnCPUInSandbox = function(addonId) {
+ let sandbox = getSandbox(addonId);
+ Cu.evalInSandbox(burnCPU.toSource() + "()", sandbox);
+ };
+
+ {
+ let topic = "test-performance-watcher:burn-content-cpu";
+ addMessageListener(topic, function(msg) {
+ try {
+ if (msg.data && msg.data.addonId) {
+ burnCPUInSandbox(msg.data.addonId);
+ } else {
+ burnCPU();
+ }
+ sendAsyncMessage(topic, {});
+ } catch (ex) {
+ dump(`This is the content attempting to burn CPU: error ${ex}\n`);
+ dump(`${ex.stack}\n`);
+ }
+ });
+ }
+
+ // Bind the function to the global context or it might be GC'd during test
+ // causing failures (bug 1230027)
+ this.burnCPOWInSandbox = function(addonId) {
+ try {
+ burnCPUInSandbox(addonId);
+ } catch (ex) {
+ dump(`This is the addon attempting to burn CPOW: error ${ex}\n`);
+ dump(`${ex.stack}\n`);
+ }
+ }
+
+ sendAsyncMessage("test-performance-watcher:cpow-init", {}, {
+ burnCPOWInSandbox: this.burnCPOWInSandbox
+ });
+
+ } catch (ex) {
+ Cu.reportError("This is the addon: error " + ex);
+ Cu.reportError(ex.stack);
+ }
+};
+
+/**
+ * Base class for listening to slow group alerts
+ */
+function AlertListener(accept, {register, unregister}) {
+ this.listener = (...args) => {
+ if (this._unregistered) {
+ throw new Error("Listener was unregistered");
+ }
+ let result = accept(...args);
+ if (!result) {
+ return;
+ }
+ this.result = result;
+ this.triggered = true;
+ return;
+ };
+ this.triggered = false;
+ this.result = null;
+ this._unregistered = false;
+ this._unregister = unregister;
+ registerCleanupFunction(() => {
+ this.unregister();
+ });
+ register();
+}
+AlertListener.prototype = {
+ unregister: function() {
+ this.reset();
+ if (this._unregistered) {
+ info(`head.js: No need to unregister, we're already unregistered.\n`);
+ return;
+ }
+ info(`head.js: Unregistering listener.\n`);
+ this._unregistered = true;
+ this._unregister();
+ info(`head.js: Unregistration complete.\n`);
+ },
+ reset: function() {
+ this.triggered = false;
+ this.result = null;
+ },
+};
+
+/**
+ * Simulate a slow add-on.
+ */
+function AddonBurner(addonId = "fake add-on id: " + Math.random()) {
+ this.jankThreshold = 200000;
+ CPUBurner.call(this, `http://example.com/?uri=${addonId}`, this.jankThreshold);
+ this._addonId = addonId;
+ this._sandbox = Components.utils.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId: this._addonId });
+ this._CPOWBurner = null;
+
+ this._promiseCPOWBurner = new Promise(resolve => {
+ this._browser.messageManager.addMessageListener("test-performance-watcher:cpow-init", msg => {
+ // Note that we cannot resolve Promises with CPOWs now that they
+ // have been outlawed in bug 1233497, so we stash it in the
+ // AddonBurner instance instead.
+ this._CPOWBurner = msg.objects.burnCPOWInSandbox;
+ resolve();
+ });
+ });
+}
+AddonBurner.prototype = Object.create(CPUBurner.prototype);
+Object.defineProperty(AddonBurner.prototype, "addonId", {
+ get: function() {
+ return this._addonId;
+ }
+});
+
+/**
+ * Simulate slow code being executed by the add-on in the chrome.
+ */
+AddonBurner.prototype.burnCPU = function() {
+ Cu.evalInSandbox(AddonBurner.burnCPU.toSource() + "()", this._sandbox);
+};
+
+/**
+ * Simulate slow code being executed by the add-on in a CPOW.
+ */
+AddonBurner.prototype.promiseBurnCPOW = Task.async(function*() {
+ yield this._promiseCPOWBurner;
+ ok(this._CPOWBurner, "Got the CPOW burner");
+ let burner = this._CPOWBurner;
+ info("Parent: Preparing to burn CPOW");
+ try {
+ yield burner(this._addonId);
+ info("Parent: Done burning CPOW");
+ } catch (ex) {
+ info(`Parent: Error burning CPOW: ${ex}\n`);
+ info(ex.stack + "\n");
+ }
+});
+
+/**
+ * Simulate slow code being executed by the add-on in the content.
+ */
+AddonBurner.prototype.promiseBurnContentCPU = function() {
+ return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {addonId: this._addonId});
+};
+AddonBurner.burnCPU = function() {
+ var start = Date.now();
+ var ignored = [];
+ while (Date.now() - start < 500) {
+ ignored[ignored.length % 2] = ignored.length;
+ }
+};
+
+
+function AddonListener(addonId, accept) {
+ let target = {addonId};
+ AlertListener.call(this, accept, {
+ register: () => {
+ info(`AddonListener: registering ${JSON.stringify(target, null, "\t")}`);
+ PerformanceWatcher.addPerformanceListener({addonId}, this.listener);
+ },
+ unregister: () => {
+ info(`AddonListener: unregistering ${JSON.stringify(target, null, "\t")}`);
+ PerformanceWatcher.removePerformanceListener({addonId}, this.listener);
+ }
+ });
+}
+AddonListener.prototype = Object.create(AlertListener.prototype);
+
+function promiseContentResponse(browser, name, message) {
+ let mm = browser.messageManager;
+ let promise = new Promise(resolve => {
+ function removeListener() {
+ mm.removeMessageListener(name, listener);
+ }
+
+ function listener(msg) {
+ removeListener();
+ resolve(msg.data);
+ }
+
+ mm.addMessageListener(name, listener);
+ registerCleanupFunction(removeListener);
+ });
+ mm.sendAsyncMessage(name, message);
+ return promise;
+}
+function promiseContentResponseOrNull(browser, name, message) {
+ if (!browser.messageManager) {
+ return null;
+ }
+ return promiseContentResponse(browser, name, message);
+}
+
+/**
+ * `true` if we are running an OS in which the OS performance
+ * clock has a low precision and might unpredictably
+ * never be updated during the execution of the test.
+ */
+function hasLowPrecision() {
+ let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
+ info(`Running ${sysName} version ${sysVersion}`);
+
+ if (sysName == "Windows_NT" && sysVersion < 6) {
+ info("Running old Windows, need to deactivate tests due to bad precision.");
+ return true;
+ }
+ if (sysName == "Linux" && sysVersion <= 2.6) {
+ info("Running old Linux, need to deactivate tests due to bad precision.");
+ return true;
+ }
+ info("This platform has good precision.")
+ return false;
+}