diff options
Diffstat (limited to 'toolkit/components/perfmonitoring/tests/browser/browser_compartments.js')
-rw-r--r-- | toolkit/components/perfmonitoring/tests/browser/browser_compartments.js | 312 |
1 files changed, 0 insertions, 312 deletions
diff --git a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js b/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js deleted file mode 100644 index f04fefb33..000000000 --- a/toolkit/components/perfmonitoring/tests/browser/browser_compartments.js +++ /dev/null @@ -1,312 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -/** - * Test that we see jank that takes place in a webpage, - * and that jank from several iframes are actually charged - * to the top window. - */ -Cu.import("resource://gre/modules/PerformanceStats.jsm", this); -Cu.import("resource://gre/modules/Services.jsm", this); -Cu.import("resource://testing-common/ContentTask.jsm", this); - - -const URL = "http://example.com/browser/toolkit/components/perfmonitoring/tests/browser/browser_compartments.html?test=" + Math.random(); -const PARENT_TITLE = `Main frame for test browser_compartments.js ${Math.random()}`; -const FRAME_TITLE = `Subframe for test browser_compartments.js ${Math.random()}`; - -const PARENT_PID = Services.appinfo.processID; - -// This function is injected as source as a frameScript -function frameScript() { - try { - "use strict"; - - const { utils: Cu, classes: Cc, interfaces: Ci } = Components; - Cu.import("resource://gre/modules/PerformanceStats.jsm"); - Cu.import("resource://gre/modules/Services.jsm"); - - // Make sure that the stopwatch is now active. - let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks", "compartments"]); - - addMessageListener("compartments-test:getStatistics", () => { - try { - monitor.promiseSnapshot().then(snapshot => { - sendAsyncMessage("compartments-test:getStatistics", {snapshot, pid: Services.appinfo.processID}); - }); - } catch (ex) { - Cu.reportError("Error in content (getStatistics): " + ex); - Cu.reportError(ex.stack); - } - }); - - addMessageListener("compartments-test:setTitles", titles => { - try { - content.document.title = titles.data.parent; - for (let i = 0; i < content.frames.length; ++i) { - content.frames[i].postMessage({title: titles.data.frames}, "*"); - } - console.log("content", "Done setting titles", content.document.title); - sendAsyncMessage("compartments-test:setTitles"); - } catch (ex) { - Cu.reportError("Error in content (setTitles): " + ex); - Cu.reportError(ex.stack); - } - }); - } catch (ex) { - Cu.reportError("Error in content (setup): " + ex); - Cu.reportError(ex.stack); - } -} - -// A variant of `Assert` that doesn't spam the logs -// in case of success. -var SilentAssert = { - equal: function(a, b, msg) { - if (a == b) { - return; - } - Assert.equal(a, b, msg); - }, - notEqual: function(a, b, msg) { - if (a != b) { - return; - } - Assert.notEqual(a, b, msg); - }, - ok: function(a, msg) { - if (a) { - return; - } - Assert.ok(a, msg); - }, - leq: function(a, b, msg) { - this.ok(a <= b, `${msg}: ${a} <= ${b}`); - } -}; - -var isShuttingDown = false; -function monotinicity_tester(source, testName) { - // In the background, check invariants: - // - numeric data can only ever increase; - // - the name, addonId, isSystem of a component never changes; - // - the name, addonId, isSystem of the process data; - // - there is at most one component with a combination of `name` and `addonId`; - // - types, etc. - let previous = { - processData: null, - componentsMap: new Map(), - }; - - let sanityCheck = function(prev, next) { - if (prev == null) { - return; - } - for (let k of ["groupId", "addonId", "isSystem"]) { - SilentAssert.equal(prev[k], next[k], `Sanity check (${testName}): ${k} hasn't changed (${prev.name}).`); - } - for (let [probe, k] of [ - ["jank", "totalUserTime"], - ["jank", "totalSystemTime"], - ["cpow", "totalCPOWTime"], - ["ticks", "ticks"] - ]) { - SilentAssert.equal(typeof next[probe][k], "number", `Sanity check (${testName}): ${k} is a number.`); - SilentAssert.leq(prev[probe][k], next[probe][k], `Sanity check (${testName}): ${k} is monotonic.`); - SilentAssert.leq(0, next[probe][k], `Sanity check (${testName}): ${k} is >= 0.`) - } - SilentAssert.equal(prev.jank.durations.length, next.jank.durations.length); - for (let i = 0; i < next.jank.durations.length; ++i) { - SilentAssert.ok(typeof next.jank.durations[i] == "number" && next.jank.durations[i] >= 0, - `Sanity check (${testName}): durations[${i}] is a non-negative number.`); - SilentAssert.leq(prev.jank.durations[i], next.jank.durations[i], - `Sanity check (${testName}): durations[${i}] is monotonic.`); - } - for (let i = 0; i < next.jank.durations.length - 1; ++i) { - SilentAssert.leq(next.jank.durations[i + 1], next.jank.durations[i], - `Sanity check (${testName}): durations[${i}] >= durations[${i + 1}].`) - } - }; - let iteration = 0; - let frameCheck = Task.async(function*() { - if (isShuttingDown) { - window.clearInterval(interval); - return; - } - let name = `${testName}: ${iteration++}`; - let result = yield source(); - if (!result) { - // This can happen at the end of the test when we attempt - // to communicate too late with the content process. - window.clearInterval(interval); - return; - } - let {pid, snapshot} = result; - - // Sanity check on the process data. - sanityCheck(previous.processData, snapshot.processData); - SilentAssert.equal(snapshot.processData.isSystem, true); - SilentAssert.equal(snapshot.processData.name, "<process>"); - SilentAssert.equal(snapshot.processData.addonId, ""); - SilentAssert.equal(snapshot.processData.processId, pid); - previous.procesData = snapshot.processData; - - // Sanity check on components data. - let map = new Map(); - for (let item of snapshot.componentsData) { - for (let [probe, k] of [ - ["jank", "totalUserTime"], - ["jank", "totalSystemTime"], - ["cpow", "totalCPOWTime"] - ]) { - // Note that we cannot expect components data to be always smaller - // than process data, as `getrusage` & co are not monotonic. - SilentAssert.leq(item[probe][k], 3 * snapshot.processData[probe][k], - `Sanity check (${name}): ${k} of component is not impossibly larger than that of process`); - } - - let isCorrectPid = (item.processId == pid && !item.isChildProcess) - || (item.processId != pid && item.isChildProcess); - SilentAssert.ok(isCorrectPid, `Pid check (${name}): the item comes from the right process`); - - let key = item.groupId; - if (map.has(key)) { - let old = map.get(key); - Assert.ok(false, `Component ${key} has already been seen. Latest: ${item.addonId||item.name}, previous: ${old.addonId||old.name}`); - } - map.set(key, item); - } - for (let item of snapshot.componentsData) { - if (!item.parentId) { - continue; - } - let parent = map.get(item.parentId); - SilentAssert.ok(parent, `The parent exists ${item.parentId}`); - - for (let [probe, k] of [ - ["jank", "totalUserTime"], - ["jank", "totalSystemTime"], - ["cpow", "totalCPOWTime"] - ]) { - // Note that we cannot expect components data to be always smaller - // than parent data, as `getrusage` & co are not monotonic. - SilentAssert.leq(item[probe][k], 2 * parent[probe][k], - `Sanity check (${testName}): ${k} of component is not impossibly larger than that of parent`); - } - } - for (let [key, item] of map) { - sanityCheck(previous.componentsMap.get(key), item); - previous.componentsMap.set(key, item); - } - }); - let interval = window.setInterval(frameCheck, 300); - registerCleanupFunction(() => { - window.clearInterval(interval); - }); -} - -add_task(function* test() { - let monitor = PerformanceStats.getMonitor(["jank", "cpow", "ticks"]); - - info("Extracting initial state"); - let stats0 = yield monitor.promiseSnapshot(); - Assert.notEqual(stats0.componentsData.length, 0, "There is more than one component"); - Assert.ok(!stats0.componentsData.find(stat => stat.name.indexOf(URL) != -1), - "The url doesn't appear yet"); - - let newTab = gBrowser.addTab(); - let browser = newTab.linkedBrowser; - // Setup monitoring in the tab - info("Setting up monitoring in the tab"); - yield ContentTask.spawn(newTab.linkedBrowser, null, frameScript); - - info("Opening URL"); - newTab.linkedBrowser.loadURI(URL); - - if (Services.sysinfo.getPropertyAsAString("name") == "Windows_NT") { - info("Deactivating sanity checks under Windows (bug 1151240)"); - } else { - info("Setting up sanity checks"); - monotinicity_tester(() => monitor.promiseSnapshot().then(snapshot => ({snapshot, pid: PARENT_PID})), "parent process"); - monotinicity_tester(() => promiseContentResponseOrNull(browser, "compartments-test:getStatistics", null), "content process" ); - } - - let skipTotalUserTime = hasLowPrecision(); - - - while (true) { - yield new Promise(resolve => setTimeout(resolve, 100)); - - // We may have race conditions with DOM loading. - // Don't waste too much brainpower here, let's just ask - // repeatedly for the title to be changed, until this works. - info("Setting titles"); - yield promiseContentResponse(browser, "compartments-test:setTitles", { - parent: PARENT_TITLE, - frames: FRAME_TITLE - }); - info("Titles set"); - - let {snapshot: stats} = (yield promiseContentResponse(browser, "compartments-test:getStatistics", null)); - - // Attach titles to components. - let titles = []; - let map = new Map(); - let windows = Services.wm.getEnumerator("navigator:browser"); - while (windows.hasMoreElements()) { - let window = windows.getNext(); - let tabbrowser = window.gBrowser; - for (let browser of tabbrowser.browsers) { - let id = browser.outerWindowID; // May be `null` if the browser isn't loaded yet - if (id != null) { - map.set(id, browser); - } - } - } - for (let stat of stats.componentsData) { - if (!stat.windowId) { - continue; - } - let browser = map.get(stat.windowId); - if (!browser) { - continue; - } - let title = browser.contentTitle; - if (title) { - stat.title = title; - titles.push(title); - } - } - - // While the webpage consists in three compartments, we should see only - // one `PerformanceData` in `componentsData`. Its `name` is undefined - // (could be either the main frame or one of its subframes), but its - // `title` should be the title of the main frame. - info(`Searching for frame title '${FRAME_TITLE}' in ${JSON.stringify(titles)} (I hope not to find it)`); - Assert.ok(!titles.includes(FRAME_TITLE), "Searching by title, the frames don't show up in the list of components"); - - info(`Searching for window title '${PARENT_TITLE}' in ${JSON.stringify(titles)} (I hope to find it)`); - let parent = stats.componentsData.find(x => x.title == PARENT_TITLE); - if (!parent) { - info("Searching by title, we didn't find the main frame"); - continue; - } - info("Found the main frame"); - - if (skipTotalUserTime) { - info("Not looking for total user time on this platform, we're done"); - break; - } else if (parent.jank.totalUserTime > 1000) { - info("Enough CPU time detected, we're done"); - break; - } else { - info(`Not enough CPU time detected: ${parent.jank.totalUserTime}`); - } - } - isShuttingDown = true; - - // Cleanup - gBrowser.removeTab(newTab, {skipPermitUnload: true}); -}); |