diff options
Diffstat (limited to 'devtools/client/memory/test/browser/head.js')
-rw-r--r-- | devtools/client/memory/test/browser/head.js | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/devtools/client/memory/test/browser/head.js b/devtools/client/memory/test/browser/head.js new file mode 100644 index 000000000..cb9b470ff --- /dev/null +++ b/devtools/client/memory/test/browser/head.js @@ -0,0 +1,248 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Load the shared test helpers into this compartment. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", + this); + +// Load the shared Redux helpers into this compartment. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js", + this); + +var { censusDisplays, snapshotState: states } = require("devtools/client/memory/constants"); +var { L10N } = require("devtools/client/memory/utils"); + +Services.prefs.setBoolPref("devtools.memory.enabled", true); + +/** + * Open the memory panel for the given tab. + */ +this.openMemoryPanel = Task.async(function* (tab) { + info("Opening memory panel."); + const target = TargetFactory.forTab(tab); + const toolbox = yield gDevTools.showToolbox(target, "memory"); + info("Memory panel shown successfully."); + let panel = toolbox.getCurrentPanel(); + return { tab, panel }; +}); + +/** + * Close the memory panel for the given tab. + */ +this.closeMemoryPanel = Task.async(function* (tab) { + info("Closing memory panel."); + const target = TargetFactory.forTab(tab); + const toolbox = gDevTools.getToolbox(target); + yield toolbox.destroy(); + info("Closed memory panel successfully."); +}); + +/** + * Return a test function that adds a tab with the given url, opens the memory + * panel, runs the given generator, closes the memory panel, removes the tab, + * and finishes. + * + * Example usage: + * + * this.test = makeMemoryTest(TEST_URL, function* ({ tab, panel }) { + * // Your tests go here... + * }); + */ +function makeMemoryTest(url, generator) { + return Task.async(function* () { + waitForExplicitFinish(); + + // It can take a long time to save a snapshot to disk, read the snapshots + // back from disk, and finally perform analyses on them. + requestLongerTimeout(2); + + const tab = yield addTab(url); + const results = yield openMemoryPanel(tab); + + try { + yield* generator(results); + } catch (err) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err)); + } + + yield closeMemoryPanel(tab); + yield removeTab(tab); + + finish(); + }); +} + +function dumpn(msg) { + dump(`MEMORY-TEST: ${msg}\n`); +} + +/** + * Returns a promise that will resolve when the provided store matches + * the expected array. expectedStates is an array of dominatorTree states. + * Expectations : + * - store.getState().snapshots.length == expected.length + * - snapshots[i].dominatorTree.state == expected[i] + * + * @param {Store} store + * @param {Array<string>} expectedStates [description] + * @return {Promise} + */ +function waitUntilDominatorTreeState(store, expected) { + let predicate = () => { + let snapshots = store.getState().snapshots; + return snapshots.length === expected.length && + expected.every((state, i) => { + return snapshots[i].dominatorTree && + snapshots[i].dominatorTree.state === state; + }); + }; + info(`Waiting for dominator trees to be of state: ${expected}`); + return waitUntilState(store, predicate); +} + +function takeSnapshot(window) { + let { gStore, document } = window; + let snapshotCount = gStore.getState().snapshots.length; + info("Taking snapshot..."); + document.querySelector(".devtools-toolbar .take-snapshot").click(); + return waitUntilState(gStore, () => gStore.getState().snapshots.length === snapshotCount + 1); +} + +function clearSnapshots(window) { + let { gStore, document } = window; + document.querySelector(".devtools-toolbar .clear-snapshots").click(); + return waitUntilState(gStore, () => gStore.getState().snapshots.every( + (snapshot) => snapshot.state !== states.READ) + ); +} + +/** + * Sets the current requested display and waits for the selected snapshot to use + * it and complete the new census that entails. + */ +function setCensusDisplay(window, display) { + info(`Setting census display to ${display}...`); + let { gStore, gHeapAnalysesClient } = window; + // XXX: Should handle this via clicking the DOM, but React doesn't + // fire the onChange event, so just change it in the store. + // window.document.querySelector(`.select-display`).value = type; + gStore.dispatch(require("devtools/client/memory/actions/census-display") + .setCensusDisplayAndRefresh(gHeapAnalysesClient, display)); + + return waitUntilState(window.gStore, () => { + let selected = window.gStore.getState().snapshots.find(s => s.selected); + return selected.state === states.READ && + selected.census && + selected.census.state === censusState.SAVED && + selected.census.display === display; + }); +} + +/** + * Get the snapshot tatus text currently displayed, or null if none is + * displayed. + * + * @param {Document} document + */ +function getDisplayedSnapshotStatus(document) { + const status = document.querySelector(".snapshot-status"); + return status ? status.textContent.trim() : null; +} + +/** + * Get the index of the currently selected snapshot. + * + * @return {Number} + */ +function getSelectedSnapshotIndex(store) { + let snapshots = store.getState().snapshots; + let selectedSnapshot = snapshots.find(s => s.selected); + return snapshots.indexOf(selectedSnapshot); +} + +/** + * Returns a promise that will resolve when the snapshot with provided index + * becomes selected. + * + * @return {Promise} + */ +function waitUntilSnapshotSelected(store, snapshotIndex) { + return waitUntilState(store, state => + state.snapshots[snapshotIndex] && + state.snapshots[snapshotIndex].selected === true); +} + + +/** + * Wait until the state has censuses in a certain state. + * + * @return {Promise} + */ +function waitUntilCensusState(store, getCensus, expected) { + let predicate = () => { + let snapshots = store.getState().snapshots; + + info("Current census state:" + + snapshots.map(x => getCensus(x) ? getCensus(x).state : null)); + + return snapshots.length === expected.length && + expected.every((state, i) => { + let census = getCensus(snapshots[i]); + return (state === "*") || + (!census && !state) || + (census && census.state === state); + }); + }; + info(`Waiting for snapshot censuses to be of state: ${expected}`); + return waitUntilState(store, predicate); +} + +/** + * Mock out the requestAnimationFrame. + * + * @return {Object} + * @function nextFrame + * Call the last queued function + * @function raf + * The mocked raf function + * @function timesCalled + * How many times the RAF has been called + */ +function createRAFMock() { + let queuedFns = []; + let mock = { timesCalled: 0 }; + + mock.nextFrame = function () { + let thisQueue = queuedFns; + queuedFns = []; + for (var i = 0; i < thisQueue.length; i++) { + thisQueue[i](); + } + }; + + mock.raf = function (fn) { + mock.timesCalled++; + queuedFns.push(fn); + }; + return mock; +} + +/** + * Test to see if two floats are equivalent. + * + * @param {Float} a + * @param {Float} b + * @return {Boolean} + */ +function floatEquality(a, b) { + const EPSILON = 0.00000000001; + const equals = Math.abs(a - b) < EPSILON; + if (!equals) { + info(`${a} not equal to ${b}`); + } + return equals; +} |