diff options
Diffstat (limited to 'devtools/client/memory/test/browser/head.js')
-rw-r--r-- | devtools/client/memory/test/browser/head.js | 279 |
1 files changed, 279 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 0000000000..b15402d3e6 --- /dev/null +++ b/devtools/client/memory/test/browser/head.js @@ -0,0 +1,279 @@ +/* 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. +/* import-globals-from ../../../shared/test/shared-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", + this +); + +// Load the shared Redux helpers into this compartment. +/* import-globals-from ../../../shared/test/shared-redux-head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js", + this +); + +var { + censusDisplays, + censusState, + 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 = async function(tab) { + info("Opening memory panel."); + const target = await TargetFactory.forTab(tab); + const toolbox = await gDevTools.showToolbox(target, "memory"); + info("Memory panel shown successfully."); + const panel = toolbox.getCurrentPanel(); + return { tab, panel }; +}; + +/** + * Close the memory panel for the given tab. + */ +this.closeMemoryPanel = async function(tab) { + info("Closing memory panel."); + const target = await TargetFactory.forTab(tab); + const toolbox = gDevTools.getToolbox(target); + await 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, async function ({ tab, panel }) { + * // Your tests go here... + * }); + */ +function makeMemoryTest(url, generator) { + return 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 = await addTab(url); + const results = await openMemoryPanel(tab); + + try { + await generator(results); + } catch (err) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(err)); + } + + await closeMemoryPanel(tab); + await 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) { + const predicate = () => { + const 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) { + const { gStore, document } = window; + const 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) { + const { 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}...`); + const { 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, () => { + const 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) { + const snapshots = store.getState().snapshots; + const 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) { + const predicate = () => { + const 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) => { + const 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 = []; + const mock = { timesCalled: 0 }; + + mock.nextFrame = function() { + const thisQueue = queuedFns; + queuedFns = []; + for (let 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; +} |